• Stars
    star
    468
  • Rank 90,248 (Top 2 %)
  • Language
    Swift
  • License
    MIT License
  • Created almost 7 years ago
  • Updated about 4 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A simple way to consume custom deep link URLs in a Swift app

Simple Deep Linking in Swift

A simple way to consume custom deep link URLs in a Swift app.

It creates a data object from values in a URL, which can be used to perform an action in your app.

Highlights

  • An intuitive API that helps you easily work with custom deep link URLs
  • Supports extracting values from a URL's path, query string, and fragment
  • It's all contained in just one small Swift file
  • Written in Swift 3
  • This repository includes a demo app and unit tests

How to add it to your project

Add the file DeepLinking.swift to your Xcode project.

Conceptual overview

Your app is passed a deep link URL, inspects the URL to determine what it represents, and then performs the appropriate action for that deep link. Not all URLs represent a deep link your app knows how to handle, but all deep links can be expressed as a URL. The API presented here figures out which deep link a URL matches, and extracts data values from the URL so that your app can perform the appropriate action.

Simple example

Suppose that the URL my.url.scheme://show/photo?name=cat can be interpreted by your app as a command to show the photo in an image file whose name is cat.

Using the Deep Linking API in this repository, here's how to declare that your app supports this deep link…

/// Represents presenting an image to the user.
/// Example - my.url.scheme://show/photo?name=cat
struct ShowPhotoDeepLink: DeepLink {

    static let template = DeepLinkTemplate()
        .term("show")
        .term("photo")
        .queryStringParameters([
            .requiredString(named: "name")
            ])
    
    init(values: DeepLinkValues) {
        imageName = values.query["name"] as! String
    }
    
    let imageName: String
    
}

This struct adopts the DeepLink protocol, which requires it to have a static property named template and an initializer whose sole parameter is DeepLinkValues.

  • template - A typed description of a URL from which an instance of the deep link type can be created.
  • init(values:) - An initializer that receives values extracted from a URL, already converted to the template-specified data types.

A more complicated example

Here's another example, from the unit tests.

// Examples:
// my.url.scheme://display/upgrade?mustAccept=true&username=Josh
// my.url.scheme://display/upgrade?mustAccept=false
struct DisplayMessageDeepLink: DeepLink {

    static let template = DeepLinkTemplate()
        .term("display")
        .string(named: "messageType")
        .queryStringParameters([
            .optionalString(named: "username"),
            .requiredBool(named: "mustAccept")
            ])
    
    init(values: DeepLinkValues) {
        self.messageType = values.path["messageType"] as! String
        self.mustAccept = values.query["mustAccept"] as! Bool
        self.username = values.query["username"] as? String
    }
    
    let messageType: String
    let mustAccept: Bool
    let username: String?
    
}

Notice that the DeepLinkTemplate includes a term and a string.

  • term - Represents a hard-coded string that must appear in the URL path, at the specified location, for a URL to match this deep link type.
  • string - Represents a string variable that must appear in the URL path, at the specified location, for a URL to match this deep link type. The value of the string will be included in the DeepLinkValues object passed to the deep link's initializer.

Aside from terms and strings, a URL path variable can be of type int, double, or bool.

Similarly, a deep link can declare what query string parameters must/can appear in a matching URL. A query string parameter can be required or optional. If a required parameter is not found in a URL, then that URL cannot be used to create the template's associated deep link type. A query parameter can be of type int, double, bool, or string.

Deep link recognition

The job of detecting which kind of deep link a URL matches is handled by DeepLinkRecognizer. Here is how DisplayMessageDeepLink from the previous section can be detected and created.

func test_display_message_deep_link() {
    // A deep link recognizer that knows about the custom deep link type.
    let recognizer = DeepLinkRecognizer(deepLinkTypes: [DisplayMessageDeepLink.self])
    
    // A URL which conforms to the "display message" deep link schema.
    let url = URL(string: "test://display/upgrade?mustAccept=true&username=Billy%20Bob")!
    
    // Verify that the recognizer creates a properly configured deep link.
    if let deepLink = recognizer.deepLink(matching: url) as? DisplayMessageDeepLink {
        XCTAssertEqual(deepLink.messageType, "upgrade")
        XCTAssertEqual(deepLink.mustAccept, true)
        XCTAssertEqual(deepLink.username, "Billy Bob")
    }
    else {
        XCTFail()
    }
}

When your AppDelegate receives a deep link URL, a DeepLinkRecognizer can be used to create the appropriate DeepLink object, if any of your DeepLink types can handle that URL. This code is from the demo app's AppDelegate:

private func executeDeepLink(with url: URL) -> Bool {
    // Create a recognizer with this app's custom deep link types.
    let recognizer = DeepLinkRecognizer(deepLinkTypes: [
        SelectTabDeepLink.self,
        ShowPhotoDeepLink.self])
    
    // Try to create a deep link object based on the URL.
    guard let deepLink = recognizer.deepLink(matching: url) else {
        print("Unable to match URL: \(url.absoluteString)")
        return false
    }
    
    // Navigate to the view or content specified by the deep link.
    switch deepLink {
    case let link as SelectTabDeepLink: return selectTab(with: link)
    case let link as ShowPhotoDeepLink: return showPhoto(with: link)
    default: fatalError("Unsupported DeepLink: \(type(of: deepLink))")
    }
}

The implementation details of how an app responds to a particular deep link is arbitrary, but here's an example just to help solidify the idea of where this deep linking API fits into an app.

private func showPhoto(with deepLink: ShowPhotoDeepLink) -> Bool {
    guard let tabBarController = prepareTabBarController() else {
        return false
    }
    
    // Load an image from the bundle with the provided name.
    guard let image = UIImage(named: deepLink.imageName) else {
        print("There is no image named '\(deepLink.imageName)'")
        return false
    }
    
    // Navigate to the specified image.
    tabBarController.showPhoto(image: image, animated: false)
    return true
}

Short but sweet

There are other deep linking libraries for Swift developers, some of which have much more functionality and flexibility. Based on my experience developing iOS and tvOS apps that need deep linking, this suits my needs. Sometimes less is more.

More Repositories

1

json2swift

A macOS command line tool that generates excellent Swift data models based on JSON data.
Swift
701
star
2

abandoned-strings

Command line program that detects unused resource strings in an iOS or OS X application.
Swift
374
star
3

swift-ascii-art

Swift program that creates ASCII art from an image
Swift
294
star
4

swift-threading

Simplified thread marshaling with a custom Swift operator function
Swift
228
star
5

equatable-code-generator

A Swift utility function that generates Equatable protocol code for any object.
Swift
125
star
6

Wizardry

Reusable way to implement the Wizard UI design in iOS apps
Swift
93
star
7

swift-places

A universal iOS 8 app that makes network calls, written in Swift.
Swift
79
star
8

swift-factory

Shows how to instantiate classes by name in Swift.
Swift
67
star
9

function-composition-in-swift

An interactive introduction to function composition in Swift 3.
Swift
59
star
10

swift-tic-tac-toe

Tic-tac-toe implemented in Swift
Swift
53
star
11

sustainable-coding

A document which describes my ever-evolving perspective on the strive for excellence as a software developer.
51
star
12

reflectable-enum

Shows how to simplify accessing properties in the associated values of Swift enums
Swift
47
star
13

break-a-dollar

Swift code that counts how many ways you can break a dollar
Swift
29
star
14

iOSLogin

This iOS 4 project contains a reusable controller and view for authenticating user credentials. The demo app shows how to replace the default UI with a custom view, which uses the same LoginViewController class.
Objective-C
27
star
15

command-line-calculator

Command line calculator written in Swift.
Swift
26
star
16

iOS-Workflow

A lightweight workflow component for iOS applications.
Objective-C
15
star
17

CustomCellDemo

Shows how to create a custom table view cell using Interface Builder in iOS 4.
Objective-C
11
star
18

Swiftogram

Creating histograms with the Swift language
Swift
10
star
19

Simple-Genetic-Algorithm-in-Objective-C

A "Hello, World!" of genetic algorithms, written in Objective-C.
Objective-C
9
star
20

swift-morse-code

Swift app that converts text to Morse code and plays it out loud
Swift
7
star
21

swift-agent

Thread-safe mutable state in Swift
Swift
6
star
22

elixir_wordsets

Elixir program that uses parallel processing to discover symmetrical word combinations
Elixir
5
star
23

swift-caesar-cipher

A functional implementation of the Caesar cipher in Swift
Swift
5
star
24

dry-munging-kata

Swift implementation of Dave Thomas's DRY Fusion data processing exercise
Swift
4
star
25

transitive-dependencies-kata

Swift implementation of Dave Thomas's Transitive Dependencies programming exercise
Swift
4
star
26

8QueensGeneticAlgorithm

A genetic algorithm written in Objective-C that solves the 8 Queens puzzle.
Objective-C
3
star
27

functional-objc

Reduce, map, and filter methods for NSArray
Objective-C
1
star
28

Auto-Layout-Demo

Shows how to use layout constraints in iOS 6 to center two columns of labels.
Objective-C
1
star