Improve your ⌘ V efficiency

Want to improve your quality of life in 5 seconds?

1. Go to System Preferences

2. Keyboard

3. Shortcuts -> App Shortcuts

4. Click on add -> All Applications –> add “Paste and Match Style” for keyboard shortcut ⌘V

Thank you Ally

Marc

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