Find the right value in an Array, or in an Array of Dictionaries

From time to time I discover a new way of coding something that significantly improves readability & minimizes how much I need to code. 

Here’s one of such latest discoveries. Typically to find a value in an array (or in an array of dictionaries) with a specific condition I would do a for loop with an if statement checking for the right value. This works great of course, but its a lot of code to do something that simple. 

Here’s a simple way to find a value in an array of dictionaries:

let matchingArticle = arrayOfItems.first(where: {$0.exampleKey == 3843})

 

We ask the arrayOfItems to give us the first value where the dictionary key exampleKey takes the value 3843. 

And if it’s only an array of int or string, for example, is even easier:

 let matchingArticle = arrayOfItems.first(where: {$0 == 859})

We simply ask the arrayOfItems to give us the first value where the its value equals 859. 

Note, in this code example we assume you will always have only up to 1 match. 

I’ve come to love this function and I think you will too. 

 

Questions / comments / suggestions? I’m at @MarcMasVi 

Marc

 

Inserting cells at the top of a UITableView with no scrolling

If you would like your table to be refreshed “ala Twitter”, with new data being added on top, here’s a quick an easy way to do it for iOS: 

func updateTableViewAfterAdditionInNewArray(){

        //Get new feeds and check if counts are different

        let countOfAddedItems = oldArray.count – newArray.count

        

        //If we’re adding information

        if (oldArray) > 0 {

            if debugMode == true{

                print(“We have a difference in counts of \(oldArray)”)

            }

            

            if oldArray.count != 0 {

                //Update data model

                oldArray = newArray

                

                var initialContentOffSet = tableView.contentOffset.y

                //If offset is less than 0 due to refresh up gesture, assume 0

                if initialContentOffSet < 0 {

                    initialContentOffSet = 0

                }

                //Reload, scroll and offset

                tableView.reloadData()

                tableView.scrollToRow(at: IndexPath(row: countOfAddedItems, section: 0), at: .top, animated: false)

                tableView.contentOffset.y = tableView.contentOffset.y  + initialContentOffSet

            }else{

                if debugMode == true{

                    print(“There was no data in oldArray, avoid scrolling. “)

                }

                //Update data model

                oldArray = newArray

                

                tableView.reloadData()

            }

            

        }

 

    }

Hope it helps! I’m really enjoying going back to iOS development, expect more iOS posts this next couple of months as I’m ramping up on my next project. 

Questions / comments / suggestions? I’m at @MarcMasVi 

 

Marc

Random numbers in Swift

Swift 4.2 has introduced a native random number API in the standard library, making it cross platform. 

Int.random(in: 1...1000) // → 691
Double.random(in: 0..<1) // → 0.8741555749903935
UInt32.random(in: 0xD800...0xDFFF) // → 55666

Read more in the great article from Ole Begemann, worth a read

 

Marc

Resize NSImage Proportionally (Swift)

I wanted a simple function to resize an image to a desired height, while keeping proportions. Here’s what I came up with (based on Marco implementation):

extension NSImage {

    

    func resizedImageTo(sourceImage: NSImage, newSize: NSSize) -> NSImage?{

        if sourceImage.isValid == false {

            return nil

        }

        let representation = NSBitmapImageRep(bitmapDataPlanes: nil, pixelsWide: Int(newSize.width), pixelsHigh: Int(newSize.height), bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false, colorSpaceName: .calibratedRGB, bytesPerRow: 0, bitsPerPixel: 0)

        representation?.size = newSize

        

        NSGraphicsContext.saveGraphicsState()

        NSGraphicsContext.current = NSGraphicsContext.init(bitmapImageRep: representation!)

        sourceImage.draw(in: NSRect(x: 0, y: 0, width: newSize.width, height: newSize.height), from: NSZeroRect, operation: .copy, fraction: 1.0)

        NSGraphicsContext.restoreGraphicsState()

        

        let newImage = NSImage(size: newSize)

        newImage.addRepresentation(representation!)

        

        return newImage

        

    }

    

 

}

Questions / comments / suggestions? I’m at @MarcMasVi 

Marc

Updated after I discovered the great implementation Marco Arment suggested. 

Open links in the Background

Here’s a quick tip on how to open a URL in the background or foreground depending on a user-set preference. 

if(openInBackgroundPreferenceSet == true){ 
                 NSWorkspace.shared.open([linkToOpen], withAppBundleIdentifier: nil, options: NSWorkspace.LaunchOptions.withoutActivation, additionalEventParamDescriptor: nil, launchIdentifiers: nil)
}else{
                 NSWorkspace.shared.open(linkToOpen)
}

 

Questions / comments? I’m at @MarcMasVi 

 

 Marc

Denarius 1.6 & the slippery bug

This last couple of months I’ve been hard at work on two new projects, the first of which will be released fairly soon! But, today I wanted to write about something else: CoreData Concurrency.

Since the release of Denarius about a year ago I’ve been regularly improving the personal finance app with new features and, of course, bug fixes. However there was this one bug that kept popping up in crash logs but that I was unable to track down. I knew it had something to do with Core Data but that’s the extend of it. 

CoreData in Denarius is implemented with two Managed Object Context, one of them being the background one for complex tasks and a second one for standard operation (as explained in this post).  So, everything was supposedly working as intended. 

However, after way too many hours I saw what was going on: I was doing a change in the main thread with information computed on the background thread… So, how did I solve it? Using objectID of course, the only safe way to transfer objects between threads. 

So instead of:

mainThreadObject.attributeX = computedThreadObjectInBackgroundQueue.attributeX

Now I had (and remember this piece of code is being executed in the background):

let transactionObjectId = mainThreadObject1.objectID
let typeObjectId = computedThreadObject2InBackgroundQueue.objectID
DispatchQueue.main.async { //Here I’m calling the main thread and push the complex background computation to the right object
      (self.managedObjectContext.object(with: transactionObjectId) as! object1).attributeX = (self.managedObjectContext.object(with: typeObjectId) as! object2).attributeX

}

A bit longer, but did the trick! This improvement will ship very soon with the new Denarius 1.6 release. 

Questions / comments? I’m at @MarcMasVi 

Marc