Swift - Cleaner Syntax for Reducing an Array inside Guard - swift

I have a UIView driven by two arrays: [Float] "data" and [UIColor] "colors". If the view isn't given proper data I am displaying an empty state, controlled by the guard statement you see here:
private func unwrap() -> ([Float], [UIColor]) {
guard
let data = data, !data.isEmpty,
let colors = colors, data.count == colors.count
else { return ([1], [UIColor.lightGray]) } // empty state
let total = data.reduce(0) { $0 + $1 }
if total == 0 { return ([1], [UIColor.lightGray]) }
return (data, colors)
}
I don't like the repetition of the empty state return ([1], [UIColor.lightGray]) being used twice. I've tried to add the data.reduce call inside the guard statement, something like:
private func unwrap() -> ([Float], [UIColor]) {
guard
let data = data, !data.isEmpty,
let colors = colors, data.count == colors.count,
data.reduce(0) { $0 + $1 } != 0
else { return ([1], [UIColor.lightGray]) } // empty state
return (data, colors)
}
...the compiler doesn't understand the syntax
private func unwrap() -> ([Float], [UIColor]) {
guard
let data = data, !data.isEmpty,
let colors = colors, data.count == colors.count,
let total = data.reduce(0) { $0 + $1 }, total != 0
else { return ([1], [UIColor.lightGray]) } // empty state
return (data, colors)
}
...the result of the closure data.reduce(0) { $0 + $1 } isn't optional so can't be included in the guard declaration.
Is there a cleaner way to do this?

This is my simplified version:
private func unwrap() -> ([Float], [UIColor]) {
guard let data = data, let colors = colors,
data.count == colors.count, data.reduce(0, +) != 0 else {
return ([1], [UIColor.lightGray])
} // empty state
return (data, colors)
}
Reduce can just use Float's + function, and a reduce on an empty data array will always be 0, so you don't need to check the empty status.

Figured it out.
private func unwrap() -> ([Float], [UIColor]) {
guard
let data = data, !data.isEmpty,
let colors = colors, data.count == colors.count,
(data.reduce(0) { $0 + $1 }) != 0
else { return ([1], [UIColor.lightGray]) } // empty state
return (data, colors)
}

How about making your return value a computed variable:
private func unwrap() -> ([Float], [UIColor]) {
var emptyVal: ([Float], [UIColor]) {
return ([1], [UIColor.lightGray])
}
guard
let data = data, !data.isEmpty,
let colors = colors, data.count == colors.count
else { return emptyVal } // empty state
let total = data.reduce(0) { $0 + $1 }
if total == 0 { return emptyVal }
return (data, colors)
}

Related

Sometimes methods fails with Fatal error: UnsafeMutablePointer.initialize overlapping range

I have the following code to decompress some Data back to a String in Swift 5. The method mostly works fine, but sometimes it fails with the following error message:
Thread 1: Fatal error: UnsafeMutablePointer.initialize overlapping range
extension Data
{
func decompress(destinationSize: Int) -> String?
{
let destinationBuffer = UnsafeMutablePointer<UInt8>.allocate(capacity: destinationSize)
let decodedString = self.withUnsafeBytes
{
unsafeRawBufferPointer -> String? in
let unsafeBufferPointer = unsafeRawBufferPointer.bindMemory(to: UInt8.self)
if let unsafePointer = unsafeBufferPointer.baseAddress
{
let decompressedSize = compression_decode_buffer(destinationBuffer, destinationSize, unsafePointer, self.count, nil, COMPRESSION_ZLIB)
if decompressedSize == 0
{
return String.empty
}
let string = String(cString: destinationBuffer)
let substring = string.substring(0, decompressedSize)
return substring
}
return nil
}
return decodedString
}
}
The error occurs at the following line:
let string = String(cString: destinationBuffer)
Can someone please explain why this (sometimes) fails?
I have switched to the following code and now everything works fine (Swift 5):
import Compression
extension Data
{
func compress() -> Data?
{
return self.withUnsafeBytes
{
dataBytes in
let sourcePtr: UnsafePointer<UInt8> = dataBytes.baseAddress!.assumingMemoryBound(to: UInt8.self)
return self.perform(operation: COMPRESSION_STREAM_ENCODE, source: sourcePtr, sourceSize: self.count)
}
}
func decompress() -> Data?
{
return self.withUnsafeBytes
{
unsafeRawBufferPointer -> Data? in
let unsafeBufferPointer = unsafeRawBufferPointer.bindMemory(to: UInt8.self)
if let unsafePointer = unsafeBufferPointer.baseAddress
{
return self.perform(operation: COMPRESSION_STREAM_DECODE, source: unsafePointer, sourceSize: self.count)
}
return nil
}
}
fileprivate func perform(operation: compression_stream_operation, source: UnsafePointer<UInt8>, sourceSize: Int, preload: Data = Data()) -> Data?
{
guard sourceSize > 0 else { return nil }
let streamBase = UnsafeMutablePointer<compression_stream>.allocate(capacity: 1)
defer { streamBase.deallocate() }
var stream = streamBase.pointee
let status = compression_stream_init(&stream, operation, COMPRESSION_ZLIB)
guard status != COMPRESSION_STATUS_ERROR else { return nil }
defer { compression_stream_destroy(&stream) }
var result = preload
var flags: Int32 = Int32(COMPRESSION_STREAM_FINALIZE.rawValue)
let blockLimit = 64 * 1024
var bufferSize = Swift.max(sourceSize, 64)
if sourceSize > blockLimit
{
bufferSize = blockLimit
}
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufferSize)
defer { buffer.deallocate() }
stream.dst_ptr = buffer
stream.dst_size = bufferSize
stream.src_ptr = source
stream.src_size = sourceSize
while true
{
switch compression_stream_process(&stream, flags)
{
case COMPRESSION_STATUS_OK:
guard stream.dst_size == 0 else { return nil }
result.append(buffer, count: stream.dst_ptr - buffer)
stream.dst_ptr = buffer
stream.dst_size = bufferSize
if flags == 0 && stream.src_size == 0
{
flags = Int32(COMPRESSION_STREAM_FINALIZE.rawValue)
}
case COMPRESSION_STATUS_END:
result.append(buffer, count: stream.dst_ptr - buffer)
return result
default:
return nil
}
}
}
}

How to compare custom objects based on different properties using Equatable?

I am using the equatable protocol in order to compare two custom objects based on one property named mediaUID.
Is there a way to switch between comparing on different properties?
In func fetchNotificationsRemovedsometimes I need to compare by mediaUID or by likeUID.
var notificationsArray = [NotificationInformation]()
class NotificationInformation {
let type: String
let mediaUID: String?
let commentUID: String?
let likeUID:String?
}
extension NotificationInformation {
func fetchNotificationsRemoved(query: DatabaseQuery) {
NotificationInformation.observeNewNotificationsChildRemoved(query: query) { [weak self] (newNotification: NotificationInformation?) in
guard let strongSelf = self else {return}
guard let notification = newNotification else {return}
if notification.type == "like" {
// How to compare based on likeUID using equatable?
//compare based on likeUID
//get the index of the item of type 'like' in notificationsArray and do something with it
guard let index = strongSelf.notificationsArray.index(of: notification) else {return}
}else if notification.type == "media" {
// How to compare based on mediaUID using equatable?
//compare based on mediaUID
//get the index of the item of type 'media' in notificationsArray
guard let index = strongSelf.notificationsArray.index(of: notification) else {return}
} else if if notification.type == "commentUID" {
....
}
guard let index = strongSelf.notificationsArray.index(of: notification) else {return}
strongSelf.notificationsArray.remove(at: index)
let visibleIndexes = strongSelf.tableView.indexPathsForVisibleRows
let indexPathOfRemovedNotification = IndexPath(row: index, section: 0)
if let indexes = visibleIndexes,
indexes.contains(indexPathOfRemovedNotification) {
strongSelf.tableView.deleteRows(at: [indexPathOfRemovedNotification], with: .fade)
}
}
}
}//end extension
//enables us to compare two objects of type NotificationInformation
extension NotificationInformation: Equatable { }
func ==(lhs: NotificationInformation ,rhs: NotificationInformation) -> Bool {
guard let mediaUIDLeft = lhs.mediaUID else {return false}
guard let mediaUIDRight = rhs.mediaUID else {return false}
return mediaUIDLeft == mediaUIDRight
}
You could use a static var to establish the field you want to use for comparison:
class NotificationInformation: Equatable {
enum CompareField {
case type, mediaUID, commentUID, likeUID
}
static var compareField: CompareField = .mediaUID
let type: String
let mediaUID: String?
let commentUID: String?
let likeUID:String?
init(type: String, mediaUID: String? = nil, commentUID: String? = nil, likeUID: String? = nil) {
self.type = type
self.mediaUID = mediaUID
self.commentUID = commentUID
self.likeUID = likeUID
}
static func ==(lhs: NotificationInformation, rhs: NotificationInformation) -> Bool {
switch NotificationInformation.compareField {
case .type:
return lhs.type == rhs.type
case .mediaUID:
return lhs.mediaUID == rhs.mediaUID
case .commentUID:
return lhs.commentUID == rhs.commentUID
case .likeUID:
return lhs.likeUID == rhs.likeUID
}
}
}
Example:
let a = NotificationInformation(type: "foo", mediaUID: "123")
let b = NotificationInformation(type: "bar", mediaUID: "123")
NotificationInformation.compareField = .type
if a == b {
print("same type")
}
NotificationInformation.compareField = .mediaUID
if a == b {
print("same mediaUID")
}
Output:
same mediaUID
Comparing multiple fields using an OptionSet
If you replace the enum with an OptionSet, you could select multiple fields to compare:
struct CompareFields: OptionSet {
let rawValue: Int
static let type = CompareFields(rawValue: 1 << 0)
static let mediaUID = CompareFields(rawValue: 1 << 1)
static let commentUID = CompareFields(rawValue: 1 << 2)
static let likeUID = CompareFields(rawValue: 1 << 3)
}
static var compareFields: CompareFields = .mediaUID
static func ==(lhs: NotificationInformation, rhs: NotificationInformation) -> Bool {
var equal = true
if NotificationInformation.compareFields.contains(.type) {
equal = equal && (lhs.type == rhs.type)
}
if NotificationInformation.compareFields.contains(.mediaUID) {
equal = equal && (lhs.mediaUID == rhs.mediaUID)
}
if NotificationInformation.compareFields.contains(.commentUID) {
equal = equal && (lhs.commentUID == rhs.commentUID)
}
if NotificationInformation.compareFields.contains(.likeUID) {
equal = equal && (lhs.likeUID == rhs.likeUID)
}
return equal
}
Example
let a = NotificationInformation(type: "foo", mediaUID: "123", commentUID: "111")
let b = NotificationInformation(type: "bar", mediaUID: "123", commentUID: "111")
NotificationInformation.compareFields = .mediaUID
if a == b {
print("same mediaUID")
}
NotificationInformation.compareFields = [.mediaUID, .commentUID]
if a == b {
print("same mediaUID and commentUID")
}
Output
same mediaUID
same mediaUID and commentUID
Multithreaded issue
There is an issue if your code is modifying the compareFields value in another thread. The meaning of equals would change for all threads. One possible solution is to only change and use equality for NotificationInformation in the main thread.
...
} else if notification.type == "media" {
DispatchQueue.main.async {
NotificationInformation.compareFields = .mediaUID
guard let index = strongSelf.notificationsArray.index(of: notification) else {return}
// use index
...
}
}
...
Change your func to this:
func ==(lhs: NotificationInformation ,rhs: NotificationInformation) -> Bool {
guard let mediaUIDLeft = lhs.mediaUID else {return false}
guard let mediaUIDRight = rhs.mediaUID else {return false}
return (mediaUIDLeft == mediaUIDRight || lhs.likeUID == rhs.likeUID)
}
This means that two NotificationInformation are equal if they have the same mediaUID OR the same likeUID
If you need a conditional check, you can introduce a boolean variable:
class NotificationInformation {
let type: String
let mediaUID: String?
let commentUID: String?
let likeUID:String?
let checkByMediaUID: Bool = true
}
So change your Equatable:
func ==(lhs: NotificationInformation ,rhs: NotificationInformation) -> Bool {
guard let mediaUIDLeft = lhs.mediaUID else {return false}
guard let mediaUIDRight = rhs.mediaUID else {return false}
return (lhs.checkByMediaUID || rhs.checkByMediaUID) ? mediaUIDLeft == mediaUIDRight : lhs.likeUID == rhs.likeUID
}
In more readable way:
func ==(lhs: NotificationInformation ,rhs: NotificationInformation) -> Bool {
guard let mediaUIDLeft = lhs.mediaUID else {return false}
guard let mediaUIDRight = rhs.mediaUID else {return false}
if lhs.checkByMediaUID || rhs.checkByMediaUID{
return mediaUIDLeft == mediaUIDRight
}
return lhs.likeUID == rhs.likeUID
}
This means that if you want to check by mediaUID, just compare two object. If you want to check by likeUID, just change the variable of one of the object.
Example
let a: NotificationInformation = NotificationInformation()
let b: NotificationInformation = NotificationInformation()
//Check by `mediaUID`
if a == b{
....
}
//Check by `likeUID`
a.checkByMediaUID = false
if a == b{
....
}
You can check the type property of the NotificationInformation objects and compare objects according to that.
extension NotificationInformation: Equatable {
static func == (lhs: NotificationInformation, rhs: NotificationInformation) -> Bool {
guard lhs.type == rhs.type else {
print("Types of lhs and rhs are not same ")
return false
}
switch lhs.type {
case "like": return lhs.likeUID == rhs.likeUID
case "media": return lhs.mediaUID == rhs.mediaUID
case "commentUID": return lhs.commentUID == rhs.commentUID
default: return false
}
}
}
And you can use enum for the type property
class NotificationInformation {
enum NotificationType: String {
case like
case media
case commentUID
}
let type: NotificationType
let mediaUID: String?
let commentUID: String?
let likeUID:String?
}
extension NotificationInformation: Equatable {
static func == (lhs: NotificationInformation, rhs: NotificationInformation) -> Bool {
guard lhs.type == rhs.type else {
print("Types of lhs and rhs are not same ")
return false
}
switch lhs.type {
case .like: return lhs.likeUID == rhs.likeUID
case .media: return lhs.mediaUID == rhs.mediaUID
case .commentUID: return lhs.commentUID == rhs.commentUID
}
}
}
Usage
extension NotificationInformation {
func fetchNotificationsRemoved(query: DatabaseQuery) {
NotificationInformation.observeNewNotificationsChildRemoved(query: query) { [weak self] newNotification in
guard let strongSelf = self else {return}
guard let notification = newNotification else {return}
guard let index = strongSelf.notificationsArray.index(of: notification) else {return}
strongSelf.notificationsArray.remove(at: index)
let visibleIndexes = strongSelf.tableView.indexPathsForVisibleRows
let indexPathOfRemovedNotification = IndexPath(row: index, section: 0)
if let indexes = visibleIndexes, indexes.contains(indexPathOfRemovedNotification) {
strongSelf.tableView.deleteRows(at: [indexPathOfRemovedNotification], with: .fade)
}
}
}
}

Sorting tableView data by timestamp with firebase

I'm looking to sort the data / arrays in my tableView by which date it was created. I don't have any code to support this but I have the code for the cells and images from the database.
Database.database().reference().child("Projects").observe(.childAdded, with: { (snapshot) in
if let dictionary = snapshot.value as? [String : AnyObject]
{
let project = Project(dictionary: dictionary)
self.ProjectsArray.append(project)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
else
{
print("fucked ")
}
self.FilterdProjectsArray = self.ProjectsArray
}, withCancel: nil)
Database
You just need to add one line:
self.ProjectsArray.append(project)
self.ProjectsArray = self.ProjectsArray.sorted { (lhs, rhs) in lhs.timestamp < timestamp }
where date is the name of the date property on your Project object
Update
You could extend Optional to conform to Comparable, but a simpler way to handle it might be:
self.ProjectsArray = self.ProjectsArray.sorted { (lhs, rhs) in
let lTimestamp = lhs.timestamp ?? NSNumber(integerLiteral: 0)
let rTimestamp = rhs.timestamp ?? NSNumber(integerLiteral: 0)
return lTimestamp < rTimestamp
}
Update 2
Turns out that NSNumbers aren't comparable. Looks like you'll need to use Int or Double, whichever makes more sense for your data (are you using whole or floating-point numbers?
self.ProjectsArray = self.ProjectsArray.sorted { (lhs: Project, rhs: Project) in
let lTimestamp: Int
if let lNum = lhs.timestamp {
lTimestamp = Int(lNum)
} else {
lTimestamp = 0
}
let rTimestamp: Int
if let rNum = rhs.timestamp {
rTimestamp = Int(rNum)
} else {
rTimestamp = 0
}
return lTimestamp < rTimestamp
}

CANT RESOLVE: unsafeAddressOf is abandoned in Swift 3

I just realized that my old app is not working anymore because unsafeAddressOf is abandoned in Swift 3. I have been searching in Apple documentations and online tutorials but still cant figure out how to change my code to be compliant with Swift 3. Here is my code:
import UIKit
import ImageIO
extension UIImage {
public class func gifWithData(data: NSData) -> UIImage? {
guard let source = CGImageSourceCreateWithData(data, nil) else {
print("SwiftGif: Source for the image does not exist")
return nil
}
return UIImage.animatedImageWithSource(source: source)
}
public class func gifWithName(name: String) -> UIImage? {
guard let bundleURL = Bundle.main.url(forResource: name, withExtension: "gif") else {
print("SwiftGif: This image named \"\(name)\" does not exist")
return nil
}
guard let imageData = NSData(contentsOfURL: bundleURL) else {
print("SwiftGif: Cannot turn image named \"\(name)\" into NSData")
return nil
}
return gifWithData(imageData)
}
class func delayForImageAtIndex(index: Int, source: CGImageSource!) -> Double {
var delay = 0.1
// Get dictionaries
let cfProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil)
let gifProperties: CFDictionary = unsafeBitCast(CFDictionaryGetValue(cfProperties, unsafeAddressOf(kCGImagePropertyGIFDictionary)), to: CFDictionary.self)
// Get delay time
var delayObject: AnyObject = unsafeBitCast(
CFDictionaryGetValue(gifProperties,
unsafeAddressOf(kCGImagePropertyGIFUnclampedDelayTime)),
to: AnyObject.self)
if delayObject.doubleValue == 0 {
delayObject = unsafeBitCast(CFDictionaryGetValue(gifProperties,
unsafeAddressOf(kCGImagePropertyGIFDelayTime)), to: AnyObject.self)
}
delay = delayObject as! Double
if delay < 0.1 {
delay = 0.1 // Make sure they're not too fast
}
return delay
}
class func gcdForPair( a: Int?, var _ b: Int?) -> Int {
// Check if one of them is nil
var a = a
if b == nil || a == nil {
if b != nil {
return b!
} else if a != nil {
return a!
} else {
return 0
}
}
// Swap for modulo
if a < b {
let c = a
a = b
b = c
}
// Get greatest common divisor
var rest: Int
while true {
rest = a! % b!
if rest == 0 {
return b! // Found it
} else {
a = b
b = rest
}
}
}
class func gcdForArray(array: Array<Int>) -> Int {
if array.isEmpty {
return 1
}
var gcd = array[0]
for val in array {
gcd = UIImage.gcdForPair(val, gcd)
}
return gcd
}
class func animatedImageWithSource(source: CGImageSource) -> UIImage? {
let count = CGImageSourceGetCount(source)
var images = [CGImage]()
var delays = [Int]()
// Fill arrays
for i in 0..<count {
// Add image
if let image = CGImageSourceCreateImageAtIndex(source, i, nil) {
images.append(image)
}
// At it's delay in cs
let delaySeconds = UIImage.delayForImageAtIndex(index: Int(i),
source: source)
delays.append(Int(delaySeconds * 1000.0)) // Seconds to ms
}
// Calculate full duration
let duration: Int = {
var sum = 0
for val: Int in delays {
sum += val
}
return sum
}()
// Get frames
let gcd = gcdForArray(array: delays)
var frames = [UIImage]()
var frame: UIImage
var frameCount: Int
for i in 0..<count {
frame = UIImage(CGImage: images[Int(i)])
frameCount = Int(delays[Int(i)] / gcd)
for _ in 0..<frameCount {
frames.append(frame)
}
}
// Heyhey
let animation = UIImage.animatedImage(with: frames,
duration: Double(duration) / 1000.0)
return animation
}
}
Does anyone have an idea how I can fix this code?

Generate random String without repeating in swift

I want the function to generate random String without repeating.
For example this function maybe will print: ABCC
func randomString(length:Int) -> String {
let charSet = "ABCDEF"
var c = charSet.characters.map { String($0) }
var s:String = ""
for _ in (1...length) {
s.append(c[Int(arc4random()) % c.count])
}
return s
} print(randomString(length: 4))
and i want print random unique string only, E.g : ABCD
import GameplayKit
func randomString(length : Int) -> String {
let charSet = Array("ABCDEFGHIJKLMNOPQRSTUVWXYZ".characters)
let shuffled = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: charSet) as! [Character]
let array = shuffled.prefix(length)
return String(array)
}
print(randomString(length: 4))
func randomString(length: Int) -> String {
let charSet = "ABCDEF"
var charSetArray = charSet.characters.map { String($0) }
var randArray: [String] = []
while charSetArray.count > 0 {
let i = Int(arc4random_uniform(UInt32(charSetArray.count)))
randArray.append(charSetArray[i])
charSetArray.remove(at: i)
}
var output: String = ""
for i in 0..<length {
output.append(randArray[i])
}
return output
}
How to use:
let randomString = "ABCDEF".random(length: 3)!
The return value is optional because the length might exceed the length of provided string.
Check out the full implementation:
import UIKit
import PlaygroundSupport
extension MutableCollection where Indices.Iterator.Element == Index {
mutating func shuffle() {
let c = count
guard c > 1 else { return }
for (firstUnshuffled , unshuffledCount) in zip(indices, stride(from: c, to: 1, by: -1)) {
let d: IndexDistance = numericCast(arc4random_uniform(numericCast(unshuffledCount)))
guard d != 0 else { continue }
let i = index(firstUnshuffled, offsetBy: d)
swap(&self[firstUnshuffled], &self[i])
}
}
}
extension Sequence {
func shuffled() -> [Iterator.Element] {
var result = Array(self)
result.shuffle()
return result
}
}
extension String {
func random(length: Int) -> String? {
let uniqueCharacters = Array(Set(characters.map({ String($0) })))
guard length <= uniqueCharacters.count else { return nil }
guard length > 0 else { return nil }
return uniqueCharacters[0..<length].shuffled().joined()
}
}