Multi Thread access to shared Variables – Swift 4 Concurrency

Working with background threads really helps improve the app responsiveness & the overall user experience. But, and it’s a big but, you have to be aware of multithread access conflicts. 

Screen Shot 2018 06 17 at 11 01 43 AM

If you access/modify the same variable from multiple threads you’re prone to non-reproducible -seemingly random- crashes.

How to Detect Conflicts?

To help you find where you may have this variable access conflicts you should enable Thread Sanitizer & Pause on issues in your Run Scheme settings. Xcode will highlight conflicts while the app runs, as it detects them. If you want to dig deeper in what the debugger can do for you there’s a great WWDC 2018 session worth watching

Screen Shot 2018 06 17 at 10 29 38 AM

Once we have detected the variables that are being accessed from different threads it’s time to strengthen them.

How to Address Multi-Thread Variable Access Conflicts?

We must protect the access to the variables so that no simultaneous read/write action can occur. The easiest way is to use accessors for the variable, accessors can be accessed from any thread but the value of the variable always will be fetched from one thread (that we create for this purpose). Here’s an example to illustrate:

    //Background queue to synchronize data access

    fileprivate let globalBackgroundSyncronizeDataQueue = DispatchQueue(

        label: globalBackgroundSyncronizeSharedData”)

 

    //Value variable (always accessed from created thread) 

    var arrayOfFeedItems_Value : [String] =  []

 

    //Variable accessor that can be accessed from anywhere (multithread-protected).

    var arrayOfFeedItems : [String] {

        set(newValue){

            globalBackgroundSyncronizeDataQueue.sync(){

                self.arrayOfFeedItems_Value = newValue

            }

        }

        get{

            return globalBackgroundSyncronizeDataQueue.sync{

                arrayOfFeedItems_Value

            }

        }

 

    }

In this case I’m adding multi-thread protection to an array (arrayOfFeedItems), but you could do the same with any variable. 

This has been a fun learning experience for me, hope this post helps other with the same challenges.

Questions / comments? I’m at @MarcMasVi 

Marc

 

Some extra documentation if you want to dig deeper: Swift Access Races & Framework Dispatch.

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. 

NSWindow min and max size (Swift 4)

You can force the views to keep a inferred min and max width/height by using AutoLayout, but what if you’re working with several views simultaneously? Wouldn’t it be simpler to have a window min and max width/height? 

Just add the following in your NSWindowController:

override func windowDidLoad() {

        super.windowDidLoad()

        window?.minSize = NSSize(width: 410, height: 600 )

        window?.manSize = NSSize(width: 600, height: 1200

}

 

Questions / comments? I’m at @MarcMasVi 

Marc

NSImageView Aspect Fill

Setting an image to use AspectFill in iOS is trivial, you can set it by code or directly in storyboard. 

But how do you do it in macOS? In Cocoa/App Kit there is no property for that, you may scale it , but there is no option for AspectFill (or AspectFit for that matter). So, how do we do it?

Once you find the way is quite easy actually. We’ll create a subclass of NSImageView and we’ll override the image property, then, we’ll place the image in a CALayer; and finally we’ll use this CALayer to resize it with the desired AspectFill. 

import Cocoa

 

class ImageAspectFillView: NSImageView {

 

    override var image: NSImage? {

        set {

            self.layer = CALayer()

            self.layer?.contentsGravity = kCAGravityResizeAspectFill

            self.layer?.contents = newValue

            self.wantsLayer = true

            

            super.image = newValue

        }

        

        get {

            return super.image

        }

    }

 

 

}

 

And that’s it! I hope it helped, took me quite a while to find this solution but works like a charm. Questions, comments? I’m at @MarcMasVi

 

Marc

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

Get Date Day, Month or Year in Swift 3.0

Just a quick update to show how to get Date Day, Month or Year in Swift 3

//Here I’m creating the calendar instance that we will operate with:

let calendar = NSCalendar.init(calendarIdentifier: NSCalendar.Identifier.gregorian)

     

 

//Now asking the calendar what month are we in today’s date:

let currentMonthInt = (calendar?.component(NSCalendar.Unit.month, from: Date()))!

       

 

//Now asking the calendar what year are we in today’s date:

let currentYearInt = (calendar?.component(NSCalendar.Unit.year, from: Date()))!

 

Hope this proves useful.

If you would have any suggestions on how to improve reach me @MarcMasVi in Twitter. 

 Marc

Blogging about developing an app: Denarius

Roughly two years ago I started blogging about the challenges of developing a new Mac app. I decided to do so after reading Brent Simmons great series on Vesper development. I thought it was a great way to 1. structure my thoughts and 2. help the community. 

It’s been one of the most fun times I’ve had in years. The kick I get out of creating something from nothing* is not easily found anywhere else.

Also worth mentioning is the Mac community: all fellow Mac developers I engaged with were very helpful and responsive, I’m extremely thankful for that. Thank you. 

Enough rambling, about the app I’ve been -indirectly- blogging about: It’s called Denarius and it’s a Personal finance Mac app. For years I controlled my personal finances with excel; existing Mac apps had a lot to offer but they required a lot of micromanagement as well.

My goal for Denarius was simple: the app should tell you all you need to know with minimal effort.

I hope you like it, you can know all about it here. And you can get in touch with me via Twitter at @MarcMasVi

Will update you on how the app does and what I will be working on next. 

 

Marc 

* leveraging the awesome work of Apple developers who worked on all the frameworks I use.