App Clips From Testing to the App Store

Badr Bujbara
11 min readMar 24, 2021

In this article, we’ll explore App Clips, the new technology from Apple that allows for express access to certain features in your app.

In our example, we’ll expand on our bakery app we created in the Swift Packages tutorial: https://badrbujbara.medium.com and create an app clip for it. You can follow along here without visiting that tutorial.

We need to create one app clip with two experiences, one for ordering cakes and one for ordering coffee. An App Clip is launched using an invocation URL. Here, we need to create an app clip with two invocation URLs. One URL displays the cake ordering experience, and the other one displays the coffee ordering experience.

Add a new target

We start by adding a new app clip target to the project.

Go to File -> New -> Target… and select App Clip

Click Next and give the clip a name, I called it CakeCoffeeClip. Thin press Activate scheme if Xcode prompts it.

Cool, now our app contains a mini app “App Clip” with its own app delegate, info.plist, Asset catalog, storyboard, view controller, and launch screen:

Design the App Clip main screen

The app clip main screen will have two buttons, Cakes and Coffee.

This app clip will be invoked using a URL. We want to show either the Cakes button or the Coffee button depending on the URL’s path, so we need to associate our app clip with a domain.

Add Associated Domains Entitlement

Associating our app clip with a domain helps us determine what functionality to show based on the URL passed, and it’s how Apple wants to associate an app with a domain.

Go to the app Targets -> our app clip -> Signing & Capabilities tab -> press + Capability -> Associated Domains

Then, add the “appclips:” prefix with only the domain of your website without https:// or path slashes:

The app clip only gets invoked if your main app isn’t installed. If you want the same functionality even if your app is installed, you need to add associated domains to the main app target as well:

In case of the domains for the app itself, we need to add a domain with “applinks:” prefix as well because we’ll treat the invoking URL as a deep link to navigate swomewhere if the app is installed.

Please consider this from Apple documentation:

Make sure to only include the desired subdomain and the top-level domain. Don’t include path and query components or a trailing slash (/).

Add Apple-App-Site-Association file to your server

The iOS system will check the website of the domain associated with an app looking for a file named apple-app-site-association. This file declares the apps associated with this website.

This file needs to be in the root of your website files in a folder named “.well-known” — for example:

https://bakerystore.app.com/.well-known/apple-app-site-association

To create this file, we use the Terminal with the following commands:

First, let’s make sure we’re saving on, for example, the desktop:

cd desktop

Then, use this command to create the file:

touch apple-app-site-association

The file is created on the desktop without extensions and that how it should be.

Open the file and add the services we want to associate with the app. For example, we have here two services “appclips” and “applinks”

{
"appclips":{
"apps":[
"AU5FMJ88V5.com.app.BakeryStore.Clip"
]
},
"applinks":{
"apps":[ ],
"details":[
{
"appID":"AU5FMJ88V5.com.app.BakeryStore",
"paths":[
"/appclip/cakes",
"/appclip/coffee"
],
"components":[
{
"/":"/appclip/cakes"
},
{
"/":"/appclip/coffee"
}
]
}
]
}
}

Each service should define the bundle identifier with the team id, for example:

YOUR_TEAM_ID.yourAppClipBundleIdentifierAU5FMJ88V5.com.app.BakeryStoreApp.Clip

and

YOUR_TEAM_ID.yourAppBundleIdentifierAU5FMJ88V5.com.app.BakeryStoreApp

Now, the apple-app-site-association file is ready to be included in our website. Just let the website developer create “.well-known” folder in the root of the website files, and drop this file in it.

Handle App Clip invocation

An app clip can be invoked using an App Clip code, QR code, NFC tag, or different other ways, such as, from a place on Apple Maps or as a Siri suggestion.

In any case, there will be a URL passed to the app clip’s delegate class. There and based on the passed URL, we can determine what screen or feature to show.

Please pay close attention to this part from the Supporting Universal Links in Your App documentation:

If your app has opted into Scenes, and your app is not running, the system delivers the universal link to the scene(_:willConnectTo:options:) delegate method after launch, and to scene(_:continue:) when the universal link is tapped while your app is running or suspended in memory.

Therefore, we should use both methods to handle the invoking URL.

Here how :willConnectTo method looks like:

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {guard let _ = (scene as? UIWindowScene) else { return }// Get URL components from the incoming user activity. 
guard let userActivity = connectionOptions.userActivities.first,
userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let incomingURL = userActivity.webpageURL,
let components = NSURLComponents(url: incomingURL,
resolvingAgainstBaseURL: true)
else {
return
}
// Check for specific URL components that you need.
guard let path = components.path else {
return
}
if let vc = window?.rootViewController as? AppClipVC {
if path.lowercased().contains("cakes") {
vc.clipType = "cakes"
}
if path.lowercased().contains("coffee") {
vc.clipType = "coffee"
}
}
}

Here, we check whether the Passed URL contains “cakes” or “coffee” to set a value in the root view controller, so it shows the appropriate UI. The AppClipVC is just to make it clear that we meant our App Clip’s main ViewController.

Don’t forget to do the same logic in the app clip’s scene(_:continue:) delegate method, as well.

That was for the case when the main app isn’t installed. If the main app is installed, the app clip won’t work, but instead, it’ll open the main app.

We need to handle the incoming URL to show the appropriate UI.

In the AppDelegate class of the main app, we use continue userActivity delegate method to handle the passed deep link URL:

func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {// 1-
guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let url = userActivity.webpageURL else {
return false
}
guard let components = URLComponents(url: url,
resolvingAgainstBaseURL: true)else{
return false
}
// 2-
if let appClipVC = AppClipVC.getInstance() as? AppClipVC{
if components.path.lowercased().contains("cakes"){
window?.rootViewController = appClipVC
window?.makeKeyAndVisible()
// 3-
appClipVC.showClipView(clipType: "cakes")
}
if components.path.lowercased().contains("coffee"){
window?.rootViewController = appClipVC
window?.makeKeyAndVisible()
appClipVC.showClipView(clipType: "coffee")
}
}
return true
}

1) We are retrieving the URL.

2) We are setting the main app’s root view controller to be the App Clip’s view controller.

3) We are calling a function in the AppClipVC view controller to handle showing the appropriate UI based on the URL passed.

Generate App Clip code

The App Clip code design is a distinctive way to advertise and distribute your app clip to the public.

To create an App Clip code, follow this Apple documentation.

In summary

For example, this command will generate an app clip code with these features:

%AppClipCodeGenerator generate --url https://bakerystoreapp.app.com/cakes --foreground eb4025 --background FFFFFF --output ~/desktop/filename.svg

I used an app called Gapplin to convert .svg file to a .png file.

This app clip code won’t work yet. To test it, we need to build the app clip while its invoking URL enabled as an environment variable. Then, we need to add an App Clip experience to the device.

Add the invoking URL to the app clip’s Environment Variables

This step is only to enable testing the app clip experience locally with URLs that contain specific paths. It’s not required when you submit the app clip along with the main app to the App Store.

Click CakeCoffeeClip target on top -> Edit Scheme…

Then click Run -> Arguments -> tick _XCAppClipURL and add the invoking URL to its value

Add Local Experience to the device that will run the app clip

When testing App Clips, the app clip needs to be installed on device and added as a local expereince. It can be added as a local experience from the Settings. When we invoke the app clip using the URL, the device will recognize it because the URL is registered as part of the Local Experience.

Run the app clip on your device just to make sure it’s registered for development. Go to Settings -> Developer -> Local Experiences -> Register Local Experience

  • Enter the same URL we used to create the app clip code but without paths:
https://bakerystoreapp.app.com
  • Then enter our app clip’s app identifier:
com.app.BakeryStoreApp.Clip
  • Enter a title and subtitle, for example, we are making the Cake ordering app clip, so the title is “A’s Bakery Store,”and the subtitle is “Order cakes and muffins on the go.”
  • You can also select a photo as your app clip card’s main image. The Human Interface Guidelines states that about the card’s image size:

Adhere to image requirements. Use a 1800x1200 px PNG or JPEG image without transparency.

That’s all required to test the app clip experience locally. To test it, we need to use Apple’s own Code Scanner.

Go to Settings -> Control Center -> add Code Scanner

The Code Scanner will show up in the Control Center:

Open it and scan the app clip code we created earlier.

You should see the app clip card:

Click open, and the installed app clip will open.

Good Progress!

Create the App Clip experience UI

We need to modify our app clip’s UI and handle the passed URL and use its path to decide whether to show Cakes or Coffee ordering screens.

The app clip main screen is simply contains two buttons, Cakes and Coffee:

We want to show only one of them depending on the path of the invoking URL.

In an earlier step, we saved a URL with “/cakes” path to the Environment Variables of the app clip. Now we need to handle this URL case as well as the other cases.

Here is our view controller code:

class ViewController: UIViewController {   // 1- 
//MARK: ---------------\\ OUTLETS //---------------
@IBOutlet weak var btnCakes: UIButton!
@IBOutlet weak var btnCoffee: UIButton!
//MARK: ---------------\\ PROPERTIES //---------------
var clipType = ""
// 2-
//MARK: ---------------\\ LifeCycle //---------------
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
showClipView(clipType: clipType)
}
// 3-
//MARK: ---------------\\ HELPER FUNCTIONS //---------------
func showClipView(clipType: String){
switch clipType {
case "cakes":
btnCoffee.isHidden = true
case "coffee":
btnCakes.isHidden = true
default:
btnCakes.isHidden = false
btnCoffee.isHidden = false
} }}

1- We define the outlets and a clipType variable. The clipType variable is set in the delegate classes as we did in an earlier step.

2- in the viewDidLoad method, we pass the clipType variable to showClipView function

3- We implement the showClipView function to handle all cases and display relevant UI.

Run the app clip on device, and you should see only the Cakes button:

If we go back to App Clip’s Edit Scheme… window and change the URL path to “/coffee” then run the app again, we should see only the Coffee button:

Nice!

Our app clip now is able to handle different app clip experiences with different URL paths.

If we scan the App Clip code we created earlier, our app clip will handle the URL path and shows only the Cakes button:

We also can use QR codes or NFC tags programmed with the same URL, and our app clip card will pop up the same way.

Remember we can’t use the regular camera app yet since the camera app will try to communicate with the server and the App Store to check for the app clip. We’ll do that next.

Cool!

Preparing our App Clip for the App Store

We’ve done all the hard work so far. To publish our app clips, we only need to upload our app to the App Store Connect, then configure some App Clip experiences.

If you upload an app that contains an app clip, an App Clip section will appear up on the App’s Version Information page.

The App Store Connect will see the associated domains in your build and will check with your server. It will let you know if you had a valid or invalid domains.

In the App Clip section, you can add app clip experiences. In our case we want to add two app clip experiences, one for Cakes and one for Coffee. Both will have the same URL prefix but with a different path.

The main steps are summarized in the first page:

• Specify your App Clip experience URL

• Customize your App Clip card

• Provide additional experience details

When configuring the experience URL, make sure to enter the same URL prefix that is added in the associated domains. You can change the path to anything you want as long as your app will handle it. Here is our Cakes app clip experience URL:

It’s pretty straight forward process from here. You’ll be able to do things like associate a location to the app clip or generate App Clip codes, customize the app clip card, etc..

After submitting the App clip experience and submit your app for review, the app clip will be reviewed by Apple. If it gets approved! you can enjoy using the iPhone camera to scan the App Clip code to pop the card and download it and play with it.

This was my experience with App Clips and how to implement different app clip experiences, I hope it was helpful to you. If you have comments or questions, please don’t hesitate.

--

--

Badr Bujbara

Software engineer specialized in iOS and Android development.