Developing for the web feels like a world thatâs simply left me behind. I made a concerted effort to double down on iOS development several years ago. Iâve never looked back or regretted it, and I donât even know what the rear view mirror looks like. Itâs caked in dust by this point, no doubt.
Though Iâve mentioned web related things here before, the last time I did any meaningful web development in a professional context, I was using JQuery v4.x.
So, yeahâââitâs been a few years and (seemingly) several thousand Javascript frameworks later. I donât venture into that ecosystem much, and my last trips were on account of the things Iâm about to mention here.
Which is, there are some incredibly trivial things one can do on their website to couple it to their iOS app counterpart. If you have the next thirty minutes free, Iâd wager you could do all of them listed here.
Letâs rock đ¤
Give your SaaS some sass
First, whatâs the end game here? To simply leverage some hosted .json files on your server and put meta tags in your markup to allow users to jump straight into your app or display information about it more effectively. Thatâs it.
This stuff is typically key for SaaS businessâ or solopreneurs because in the world we live in, where there is a SaaS website a (hopefully) native app will follow. We want to help them dance together.
It helps that iOS 11 built features specifically for this. Open up your camera, point it at a QR code. BoomâââiOS shows us the actionable details. Share a link in messages. Get a rich text preview. The list goes on.
QR Code Detection
Letâs start with everyoneâs favorite technology, quick response codes.
//TODO: (insert QR code joke here)
Creating a QR code that users can point to and then be prompted to download, open or route to a part of your app can be useful. Doing so can be accomplished by setting up an apple-app-site-association JSON file with an âapplinksâ key value pair and hosting it. You may already have this file for hand off support.
For this purpose, it looks like this:
{
"applinks": {
"apps": [],
"details": [
{
"appID":"DJGHSDJGH24.com.blogPost.yolo",
"paths": ["*"]
}
]
}
}
let concatenatedThoughts = """
The appID key simply must be your team or app ID,(.), followed by your bundle ID.
"""
An easy place for the JSON file to live is typically in the .well-known subdirectory:
https://wwww.anExample.com/.well-known/apple-app-site-association
Additionally, planting it at the root of your server works just fine too.
If you host such a file, the end result is that your app will immediately show to open via a notification when the user points at the QR code. The only metadata your QR code needs is the URL for your website.
No external QR code download, no more tapsââânothing. It just happens.
If the user doesnât have your app, Safari will kick in instead, taking them to your website.
Getting Particular
You have the ability to specify a few things within your applinks key-value pairs. Letâs look at whatâs available:
- appsâââYou supply an empty array here. I would tell you why, but I would be making it up because Apple literally says nothing else about it besides that soâŚÂŻ(°_o)/ÂŻ
- detailsâââAn array of dictionaries, one for each app that your site ultimately supports. Ordering matters in the array, as iOS look up links according to it. This allows you to specify an app to handle a certain part of your website.
- details[appid]âââThe team ID or app ID with a period, and your appâs bundle ID tacked on.
- details[paths]âââAn array or strings that determine which parts of your website are supported by the app. Additionally, you can say which parts **arenât **supported.
The first three entries donât merit much more discussion. The paths entry does have some nifty options for excluding content where applicable.
For example, including âNOT â at the beginning of the path will basically blacklist that content from being used as a universal link:
{
"applinks": {
"apps": [],
"details": [
{
"appID":"DJGHSDJGH24.com.blogPost.yolo",
"paths": ["/posts/iOS",
"NOT /oldPosts/outdated/*"]
}
]
}
}
This works much like regex, with support for wildcards:
{
"applinks": {
"apps": [],
"details": [
{
"appID":"DJGHSDJGH24.com.blogPost.yolo",
"paths": ["/posts/iOS",
"/oldPosts/201?/*"]
}
]
}
}
A few random words of advice:
- Keep in mind these matches are case sensitive.
- If youâre rocking a few domains, youâll need to roll an
apple-app-site-associationfile for each one that your app supports. Example: apple.com would have different motives than someProducts.apple.com - One doesnât actually need to appended â.jsonâ to the JSON file. Donât ask me how I know this, or how much hypothetical time was lost to it.
- If you associate several apps within one
apple-app-site-associationfile, iOS will handle it by presenting an alert showing each one to determine which is opened. The next time this happens, iOS will suggest the last choice as a defaultâââbut users can still switch this via a force touch or by pulling down on the notification. - Safari handles all of this as well. If you hosted the QR code as an image, and an iOS user 3D touches on itâââthis exact same flows happens.
Associated Domains
As an iOS developer, youâve got some duties too. Chief among them is to let Xcode know youâre expecting this to happen. This is done by the âAssociated Domainsâ capability for your app binary.
Flip it on within Xcode and add some entries for each one you wish to support. Again, you mightâve spent time here already if youâve developed for hand off capabilities.
For this particular type of routing, though, it would look like this:
// i.e. applinks:
applinks:anExample.com
As weâll see later, this creates a handshake of sorts that confirms both the app and website trust one another. Xcode will generate an entitlement to reconcile the security aspect and apply it on your appâs behalf. Spoiler alert, this opens up the door for quick credential retrieval too, as weâll see later.
Of course, this means youâll need to handle the user activity passed to you within your app delegate. But, thatâs a blog post for another day.
Using universal app links is great, and I hope more apps use it as time goes on. They can happen outside of a QR code context too. They enjoy certain advantages over custom URL schemes in that they are more secure due to the handshake, they canât be hijacked or claimed by any other app and the only party who can associate one to your app, is you.
Web Credentials
This nifty trick is absolutely essential for you if your app has a log in mechanism or any user portal system online. By employing password autofill, users can tap on the lock icon in the quicktype bar that appears over the keyboard, authenticate and then have their information automatically filled out.
Itâs beautiful, and I use it daily.
To allow for this to happen, there is minor housekeeping that you need to do for the text fields or text views representing your log in. Simply assign the correct values added in iOS 11 to their textContentType property so iOS knows what goes where:
loginField.textContentType = .username
passwordField.textContentType = .password
Thatâs all it takes from a coding standpointââânothing more than assigning to a property (if you donât, you leave it up to iOSâ heuristics to make the assumptions). Thereâs nothing left other than to respond accordingly to the incoming âdid changeâ delegate methods or notifications thatâll be fired off with the credentials.
Technically, you could stop here and things would workâŚ.ish.
Users would just get the quick type bar, but your siteâs credentials wouldnât be offered up within it. Theyâd just see the lock icon, then have to auth up, search for it and select it.
Amateur hour. Obviously, weâd prefer the userâs credentials to show up immediately once either one of those text controls becomes first responder.
Establishing Trust, Again
Which brings us back to our apple-app-site-association file.
Going deeper into the same thought above, a signed entitlement tells iOS which sites you are associated with. The secure, two way link is established in particular when your app is installed or updated. At that point iOS does a wellness check by traversing your domains listed within your Associated Domains (which we mentioned above) and pings each one to see if it gets a valid association file.
If the app points to the website and the website points to the appâââweâre cooking.
The entry under Associated Domains for password autofill looks a bit different than the above app links we entered before, as itâs prefixed with âwebcredentialsâ:
webcredentials:anExample.com
let concatenatedThoughts = """
XCode gonna' Xcode. If you're still seeing an error at this point, you might need to hop over to the developer portal and enable Associated Domains for your app ID.
"""
As youâve likely keyed in on, the Associated Domains entries follow the same pattern of including a service (i.e. activitycontinuation, applinks, etc) and domain:
<service>:<fully qualified domain>[:port number]
From there, itâs a case of adding that to your association file:
{
"applinks": {
"apps": [],
"details": [
{
"appID":"DJGHSDJGH24.com.blogPost.yolo",
"paths": ["/posts/iOS",
"/oldPosts/201?/*"]
}
]
},
"webcredentials": {
"apps": ["DJGHSDJGH24.com.blogPost.yolo"]
}
}
The only key to worry about is âappsâ, which is just an array of strings (their value constructed the same way as above with app links) that represent the apps your site provides log in information for.
Once you throw that up on your siteâââthe entire web credentials and password autofill pipeline is all set. I canât tell you how much times this saves from a user perspective. It goes up by an order of magnitude if one uses Safariâs password suggestions as well.
Image Links
Sharing links within Messages is a ubiquitous practice among iOS users. Nothing is more rewarding than tapping on the link cards, with their inviting .png hero images sitting there beckoning us to load up a website within Safari.
Except when that doesnât happen, and itâs just plain text đŹ.
This is so easy to avoid, though, as you easily can control the title, icon, image and even video that displays. Using the open graph protocol, you can supply all of this stuff inside your siteâs tag.
Title (if this isnât present, itâll take your siteâs title):
// Just in case, Javascript isn't run when generating rich links - so the value needs to be in the source. They can't be created dynamically.
<meta name="og:title" content="The Title" />
Icons (derived from a favicon or apple touch icon if itâs there. If not, you can roll with something like this):
<link rel="icon" href="path/to/icon" type="image/png" />
Image (replaces the icon if present, but still provide both because sometimes Messages prefers the icon over an image in situations like poor networking conditions)
<meta name="og:image" content="path/to/image.png" />
Video (of note, you can supply a URL to a YouTube video, which is the only video player network thatâll work if you donât use a file iOS can natively play)
<meta name="og:video" content="path/to/video.mp4" />
Thatâs all it takes to get the pretty, informational rich links when your website is shared within Messages. Nice.
The âYou probably are already doing this but Iâll add it anywaysâ Paragraph
Smart banners. At this point, youâre probably using them.
But just in case you arenât, add a meta tag with your app ID to take care of it. When potential users visit your site, theyâll get that call to action banner at the top to download it.
If youâre wanting to hunt down your app ID quickly, just visit iTunes Link Maker. The entire meta tag can be as simple as this:
<meta name="apple-iunes-app" content="app-id=123456789">
If you need to, you can also include an iTunes affiliate string and app argument parameter to deep link to a specific controller to keep context consistent with where a user might be on your website.
Generally, though, this is tacked on your landing page and thatâs good enough to serve its purpose. Now, go forth and take back what couldâve been just an ephemeral thought about your app and convert it to an install đŞ.
Wrapping Up
The lot of us craft software for our 9â5 and craft software to scratch our own creative itch. Eventually, all roads lead to a website talking about one or the other. Do them both justice, step away from Xcode and throw up a json file or two or include some simple meta tags that could help slip users right into your beautiful code.
Things like this are almost like the chore list for iOS engineers. We may not think about them first, we may not wake up to crank out config filesâââbut hey, they can help our endgame and move the needle towards exposure. So why not?
Until next time âď¸.