Gieta Laksmana
  • Blog
  • Profile
  • Projects
    • VR World Arcade
    • Punch For Fun
    • Spectrum
    • Dimension Battle
  • Resume

Launch macos app from URL

7/21/2018

0 Comments

 
If you want to launch your app from a URL, Cocoa provided a nice way of doing it. 

First, you have to register for the event. You can do it anytime, but I put this code on my AppDelegate class:
open func applicationWillFinishLaunching(_ notification: Notification) {
    initializeURIOptions()
}

fileprivate func initializeURIOptions() {
    // register to listen to the url event
    let appleEventManager = NSAppleEventManager.shared()
    appleEventManager.setEventHandler(self, andSelector: #selector(handleGetURLEvent(event:withReplyEvent:)), forEventClass: AEEventClass(kInternetEventClass), andEventID: AEEventID(kAEGetURL))
}
Then, we have to actually handle the URL parameters. In this example, we assume that the URL is of the form myapp://param1=val1&param2=val2

So we must parse the URL and extract the "param=value" pairs
@objc fileprivate func handleGetURLEvent(event: NSAppleEventDescriptor?, withReplyEvent replyEvent: NSAppleEventDescriptor?) {
    guard let event = event else {
        return
    }
    // our URL event is in this format:
    // myapp://param1=val1&param2=val2&paramN=valN
    
    // grab the query pairs, that is the pairs separated by '&'
    guard let paramDesc = event.paramDescriptor(forKeyword: AEKeyword(keyDirectObject))?.stringValue,
        let url = NSURL(string: paramDesc),
        let queryPairs = url.query?.components(separatedBy: "&") else {
            return
    }
    
    // parse the key/value pair
    // querypairs is assumed to be in the form of "key=value"
    var settingPairs = [String: String]()
    for query in queryPairs {
        let kvp = query.components(separatedBy: "=")
        if kvp.count != 2 {
            continue
        }
        
        let key = kvp[0].lowercased()
        let value = kvp[1].lowercased()
        settingPairs[key] = value
    }
    
    // do stuff with settingPairs
}
Finally, we have to let the OS know to associate the "myapp" URL scheme with our app. 

​We can do this by editing the app's info.plist and adding a URL Schemes item under the URL types item. The value for this key is the scheme that the OS will associate with your app.
Picture
Now, you should be able to launch your app by going to Safari (or any web browser and Terminal), and typing myapp://

If you want to pass parameters to the app you can simply add them to the URL myapp://?param1=val1&param2=val2

When you do this from Safari, you will get a notification such as this:
Picture
To debug an app that is launched this way, you can modify XCode's setting to wait until your app is launched before attaching the debugger to the app.
Picture
Picture
Picture
XCode will now automatically attach the debugger to your app when you launch your app via the URL myapp://
0 Comments

NSSecureTextField Shenanigans

7/14/2018

1 Comment

 
Interesting finding:

From the get go, you're not able to input accented characters with keyboard shortcut (alt+e e, alt+u o, etc) on a NSSecureTextField.
But I find this interesting "feature" (maybe a bug or loophole in Apple's implementation of secure text field?) which allows you to do just that.

Here's what to do:

Make sure key repeat is on at System Preference > Keyboard > Keyboard
Picture
I created a simple app with a basic NSSecureTextField and a label to display the content of the secure text field.

​Go to the NSSecureTextField
​Hold e (or any letter that has accented character), until the key repeats
Picture
After this point, key combinations (alt+e e, alt+y, alt+u o, etc) will work as expected ​
Picture
1 Comment

NSPopUpButton shenanigans

7/8/2018

0 Comments

 
​action/value bound to NSPopupButton is only triggered when user changes the value from the UI. It doesnt get triggered when you change the value programmatically.

For example, if we bind the NSPopupButton's selected index property:
​
Picture
internal var selectedIndex: AnyObject! {
    didSet {
        let oldIndex = oldValue as? Int
        let newIndex = selectedIndex as? Int
        print("selectedIndex didSet, old: \(oldIndex), new: \(newIndex)")
    }
}
changing the selected value via the UI will trigger selectedIndex.didSet as expected
Picture
However, when we change the index programmatically such as this:
popUpButton.selectItem(at: 2)
Doing so will change the value in the UI, but it won't trigger selectedIndex.didSet as expected.

A bigger issue happens when we have this scenario:
  • ​User selects index 1 from the UI
  • We programmatically call popupButton.selectItem(at: 2), the UI changed to show that index 2 is selected
  • User reselects index 1 from the UI
  • selectedIndex.didset is not called for the user action!!
       
This happens because when we change the selected item programmatically to 2, selectedIndex's value doesn't actually change, it stayed at 1. So when the user change the index back to 1 via the UI, Cocoa realized that the value of selectedIndex is still the same (1), so it decided not to call the didset function.
0 Comments

    Archives

    September 2018
    August 2018
    July 2018
    September 2017
    July 2017
    November 2016
    October 2016
    September 2016
    June 2016
    May 2016
    March 2016

    Categories

    All
    Shenanigans
    Software Engineering
    Swift
    Vr
    Xcode

    RSS Feed

Powered by Create your own unique website with customizable templates.