App Clips for iOS: Getting Started

In this tutorial, you’ll learn how to design and implement App Clips. By Graham Connolly.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 2 of 4 of this article. Click here to view the first page.

Getting App Clip Experience Data

Data gets passed to an App Clip via a registered URL in App Store Connect. Registering a URL is outside the scope of this tutorial, but that doesn’t mean you’re on the losing team! To get data from a URL, you must configure your App Clip to do so.

To start, click the SwiftyLemonadeClip target, under the Signing & Capabilities tab, then add a new Associated Domain named appClips:swiftyLemonade.example:

Adding App Clips associated domain

Next, the App Clip must interpret the data. Back in SwiftyLemonadeClipApp.swift, replace body with the following to get the query items from the URL:

var body: some Scene {
  WindowGroup {
    ContentView()
      .environmentObject(model)
      .onContinueUserActivity(
        NSUserActivityTypeBrowsingWeb,
        perform: handleUserActivity) //1
   }
}

// 2
func handleUserActivity(_ userActivity: NSUserActivity) {
  //3
  guard 
    let incomingURL = userActivity.webpageURL,
    let components = URLComponents(
      url: incomingURL, 
      resolvingAgainstBaseURL: true),
    let queryItems = components.queryItems 
  else {
    return
  }

  //4
  guard 
    let latValue = queryItems.first(where: { $0.name == "lat" })?.value,
    let lonValue = queryItems.first(where: { $0.name == "lon" })?.value,
    let lat = Double(latValue),
    let lon = Double(lonValue) 
  else {
    return
  }

  //5
  print("Latitude: \(lat), Longitude: \(lon)")
}

Here’s what that does:

  1. Register a handler for NSUserActivityTypeBrowsingWeb. iOS invokes this handler when it encounters an App Clip experience URL.
  2. Process the URL data.
  3. Continue execution only if there is a URL containing queryItems
  4. Check whether there are queryItems named lat and lon and assigns these to latValue and lonValue. These items represent the latitude and longitude of the associated lemonade stand. If these values don’t exist, then it’s not valid for this App Clip experience. These values are of type String and you convert them to type Double.
  5. Print the lat and lon values to the console.

Simulating a Clip Launch

To test this, create a launch URL. This allows you to simulate launching the App Clip from an App Clip experience URL. To create one, set the active scheme to SwiftyLemonadeClip. Then, edit the scheme and enable the _XCAppClipURL Environment Variable by clicking the checkbox. Finally, set its value to https://swiftyLemonade.example.com/order?lat=33.8644&lon=-118.2611 as illustrated below:

Adding launch URL

The query parameters of the URL you added are lat and lon, and their values represent the latitude and longitude of the lemonade stand.

Now, build and run. You’ll see the latitude and longitude values get printed to console:

Latitude and longitude in console

What Lemonade Stand Is This?!

It’s time to find your nearest lemonade stand. Back in SwiftyLemonadeClipApp, under import SwiftUI, add the following:

import CoreLocation

Next, in handleUserActivity(_:), replace the print() you added earlier with the following code:

//1
let location = CLLocationCoordinate2D(
  latitude: CLLocationDegrees(lat),
  longitude: CLLocationDegrees(lon))

//2
if let stand = standData.first(where: { $0.coordinate == location }) {
  model.selectedStand = stand
  //3
  print("Welcome to \(stand.title)! :]")
}

This code:

  1. Creates a CLLocationCoordinate2D variable using the lat and lon values you got from the URL
  2. Queries standData to find the first value with a matching location. If you find a stand, set it as the selectedStand in SwiftyLemonadeClipModel.
  3. Prints the name of the stand to console

To check this out, build and run and to see a welcome message printed to the console:

Welcome to LA Galaxy

You’ve configured your App Clip to get data from a URL!

Happy!

Ordering Some Lemonade

It’s time to create the App Clip experience for SwiftyLemonade. In this section, you’ll take the selected stand and display its associated menu. A user will then be able to order some lemonade.

First, under SwiftyLemonadeClip, open ContentView.swift. Adding the following property in ContentView :

@EnvironmentObject private var model: SwiftyLemonadeClipModel

Here, you’ve added the model you created earlier as an environment object.

Next, replace body with the following:

var body: some View {
  //1
  if let selectedStand = model.selectedStand {
    //2
    NavigationView {
      //3
      MenuList(stand: selectedStand)
    }
  }   
}

This code:

  1. Checks whether the model has a selectedStand.
  2. Adds a navigation hierarchy with MenuList as the root view.
  3. Instantiates a MenuList for the selectedStand to display a list of menu items.

To check this out, build and run:

LA Galaxy lemonade menu

Now you’ve linked the model to the content view, the user can order a lemonade by scanning a URL code at Swifty’s LA Galaxy Lemonade Stand. App Clip Experiences should focus on a specific task such as ordering lemonade. As a result, notice the lack of a tab bar at the bottom of the screen and that there is no list of stands to choose from:

Ordering lemonade using app clip

Can’t Find a Lemonade Stand?

What if the app can’t find a lemonade stand for a location? It would be nice to show a message to the user. Open SwiftyLemonadeClipModel.swift and add the following property:

@Published var locationFound = true

This property tracks whether the app finds a lemonade stand location. It’s true by default, because it’s likely to find a lemonade stand.

Next, open SwiftyLemonadeClipApp and, in handleUserActivity(_:), add an else clause after the optional-binding code to find the lemonade stand:

else {
  model.locationFound = false
}

If there’s no lemonade stand at the location, set locationFound to false. You can also remove the print statement if you would like; this was only for debugging.

Now, back in ContentView.swift under SwiftyLemonadeClip, add the following to the end of the body:

if model.locationFound == false {
  Text("Error finding stand.")
}

If there’s no stand nearby, you display a nice message.

To test this, update the _XCAppClipURL to include an invalid latitude. Set the value to https://swiftyLemonade.example.com/order?lat=33.8644&lon=0.

Build and run to see the error message:

Lemonade stand not found

Great stuff! Now you’ve updated your App Clip to handle invalid lemonade stand locations. Before you continue, change _XCAPPClipURL back to a valid URL: https://swiftyLemonade.example.com/order?lat=33.8644&lon=-118.2611.

To wrap up this section, you have added an App Clip experience that gets launched by using a URL as a launch argument. This URL gives the location of the lemonade stand you are in and shows its menu. From here, you can place an order.

But what if there is a mix-up at the distribution center and the wrong tags get sent to the wrong lemonade stands? You could be ordering a lemonade at a different stand! Or worse, what if someone has placed an invalid tag at a store to commit fraud? To prevent this, Apple has introduced a new, lightweight Location Confirmation API, which you’ll learn about in the next section.