<br>
<br>
Attempting to make deeplinking and handling push notifications on iOS, simpler. Work in progress.
DeepLink
:
func application(application: UIApplication, openURL url: NSURL, sourceApplication: String?, annotation: AnyObject) -> Bool {
let deepLink = DeepLink(url: url, sourceApplication: sourceApplication, annotation: annotation)
return Router.handleDeepLink(deepLink)
}
PushNotification
:
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
// Called when foreground or background but not suspended
Router.handlePushNotification(userInfo)
completionHandler(.NoData)
}
### Registering for DeepLink:
class MyViewController: UIViewController, DeepLinkCatcher {
static let deepLinkRegex = "myapp:\\/\\/cool\\/ass\\/stuff" // Matches myapp://cool/ass/stuff
override func viewDidLoad() {
super.viewDidLoad()
Router.setDeepLinkCatcher(self, forRegex: self.dynamicType.deepLinkRegex, referenceStrength: .Weak)
}
func catchDeepLink(deepLink: DeepLink) -> Bool {
if self.view.window != nil {
// View is on screen
// Do some rad crap
return true
}
// Returning false will make DeepLinkRouter call delegate (if there is one, of course)
return false
}
}
DeepLink
:
@objc public class DeepLink: NSObject, Regexable {
let url: NSURL
let sourceApplication: String?
let annotation: AnyObject
public init(url: NSURL, sourceApplication: String?, annotation: AnyObject) {
self.url = url
self.sourceApplication = sourceApplication
self.annotation = annotation
}
public func matchesRegex(regex: Regex) -> Bool {
let link = self.url.absoluteString
let regex = try? NSRegularExpression(pattern: regex, options: .CaseInsensitive)
let numberOfMatches = regex?.numberOfMatchesInString(link, options: [], range: NSMakeRange(0, link.characters.count))
return numberOfMatches > 0
}
}
### Registering for a Push Notification:
Pretty much the same as DeepLink.
class MyViewController: UIViewController, PushNotificationCatcher {
static let notificationRegex = "myapp:\\/\\/^cool_ass_stuff$" // Matches myapp://cool/ass/stuff
override func viewDidLoad() {
super.viewDidLoad()
Router.setPushNotificationCatcher(self, forRegex: self.dynamicType.notificationRegex, referenceStrength: .Weak)
}
func catchPushNotification(pushNotification: PushNotification) {
// Do stuff
}
}
This would be called for this notification:
{
"aps": {
"alert": "message",
"sound": "default",
"badge": "1",
"notification_id": "cool_ass_stuff"
}
}
The regex is compared on a string in the notification payload. By default the key for this string is "notification_id"
. You can change this by:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
PushNotification.setDefaultPushNotificationKey("your_key")
}
PushNotification
:
public typealias PushNotificationPayload = [NSObject : AnyObject]
@objc public class PushNotification: NSObject, Regexable {
public let payload: PushNotificationPayload
public init(payload: PushNotificationPayload) {
self.payload = payload
}
public static func setDefaultPushNotificationKey(key: String) { defaultPushNotificationKey = key }
public func matchesRegex(regex: Regex) -> Bool {
if let payload = self.payload["aps"] as? [NSObject : AnyObject], notification = payload[defaultPushNotificationKey] as? String {
let regex = try? NSRegularExpression(pattern: regex, options: .CaseInsensitive)
let numberOfMatches = regex?.numberOfMatchesInString(notification, options: [], range: NSMakeRange(0, notification.characters.count))
return numberOfMatches > 0
}
return false
}
}
You can also just provide a closure if you'd prefer, just make sure you manage memory properly (e.g. use [weak self]
).
let deepLinkClosure = DeepLinkClosureCatcher(callback: { deepLink -> Bool in [weak self]
// do some stuff
return true
})
Router.setDeepLinkCatcher(deepLinkClosure, forRegex: "^cool_ass_stuff$", referenceStrength: .Strong)
let notificationClosure = PushNotificationClosureCatcher(callback: { notification in [weak self]
// do some stuff
})
Router.setPushNotificationCatcher(notificationClosure, forRegex: "^cool_ass_stuff$", referenceStrength: .Strong)
##
You can decide whether Stargate keeps a .Strong
or a .Weak
reference to your Catcher.
With closures you likely want to choose .Strong
. Make sure you use [weak self]
where appropriate.
Stargate achieves its memory management functionality by using NSMapTable
.
## The delegate
If your ViewController or object that you'd like to handle the DeepLink
and/or PushNotification
does not exist at the time of the DeepLink
or PushNotification
being triggered, you can assign a delegate that will definitely be around (AppDelegate for example) to catch it and do any setup, like, for example, instantiate a ViewController and re-setup its views then call it's catchDeepLink
or catchPushNotification
method.
With DeepLink
s you can also use the delegate to setup the view hierarchy when a ViewController exists but its view is not on screen. To do this just return false
in the callback, like in the example above, which will cause the Router
to call its delegate. The delegate gets passed the DeepLink
, therefore it knows which ViewController to get on screen.
Example:
func catchDeepLink(deepLink: DeepLink) -> Bool {
if deepLink.matchesRegex("^some/rad/shit$") {
// Show a view controller or something
return true
}
return false
}
- Some kind of regex thing for router string
- Fix delegate pointer strength
- Replace NSMapTable with Swift implementation and then
- Refactor code duplication in Router for setting and removing catchers