App Clips for iOS: Getting Started
In this tutorial, you’ll learn how to design and implement App Clips. By Graham Connolly.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
App Clips for iOS: Getting Started
25 mins
- Getting Started
- What Exactly Is an App Clip?
- Adding an App Clip Target
- Sharing Assets and Code Between Targets
- Sharing Code and Assets
- Designing the App Clip Experience
- Getting App Clip Experience Data
- Simulating a Clip Launch
- What Lemonade Stand Is This?!
- Ordering Some Lemonade
- Can’t Find a Lemonade Stand?
- Setting up Location Confirmation
- Using Custom Flags
- Disabling Ordering
- Showing an Alert
- Simulating Location
- Putting it all Together
- Using Ephemeral Notifications
- Where to Go From Here?
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:
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:
- Register a handler for
NSUserActivityTypeBrowsingWeb
. iOS invokes this handler when it encounters an App Clip experience URL. - Process the URL data.
- Continue execution only if there is a URL containing
queryItems
- Check whether there are queryItems named
lat
andlon
and assigns these tolatValue
andlonValue
. 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 typeString
and you convert them to typeDouble
. - Print the
lat
andlon
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:
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:
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:
- Creates a
CLLocationCoordinate2D
variable using thelat
andlon
values you got from the URL - Queries
standData
to find the first value with a matching location. If you find a stand, set it as theselectedStand
in SwiftyLemonadeClipModel. - 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:
You’ve configured your App Clip to get data from a URL!
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:
- Checks whether the model has a
selectedStand
. - Adds a navigation hierarchy with
MenuList
as the root view. - Instantiates a
MenuList
for the selectedStand to display a list of menu items.
To check this out, build and run:
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:
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:
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.