The new SFSafariViewController was introduced during the WWDC15 and it’s built into iOS9. It enables you as a developer to deliver data to the app from your web service (site), avoiding any need for authorization so that the user feels a deep level of integration between the web version of your service (site) and the client mobile application. We’ve already built this feature into our project and want to share our mobile app development experience with you!
We've made a PDF-version of this article so you can read it later. Download
Imagine the following scenario:
- User sign up for some service in web interface
- User receive email with an invitation code and AppStore "download button"
- User clicks the AppStore "download button"
- App is downloading from the AppStore
- When user open an app his/her profile and info is already imported into the app.
In this article I’ll try to answer two questions:
- How does the invitation work?
- How is it implemented?
The trick is simple: when a user clicks on the special link, the website sets cookies in the Safari browser and navigates a user to the AppStore. When the app is opened, it displays Safari View Controller in a hidden UIWindow to open the website special link. When the app opens that page, the website tries to read cookies (that were set when the user clicked the download button in their email), and because Safari View Controller and Safari browser share cookies the website can easily read them and redirect info to the app via the URL scheme. Technically your site just redirects request to your custom scheme like:
“yourApp://some_invitation_info=info”
that iOS interpreters as opening the app by URL scheme and pasting URL inside.
5 small technical steps need to be performed in order to achieve this: 3 steps on the iOS app part and 2 on the backend part.
iOS app part
1) Configure the URL scheme in the app (if the app already uses the URL scheme you can skip this step).
In the project navigator on the left, select your project name, and select your target, then select the Info tab. Expand the URL Types section and click the + button.
Here, you only need to add two things: an Identifier, and the URL scheme.
The Identifier should be a unique string. Apple recommends using a reverse-domain style name to ensure uniqueness. The URL Scheme is the thing that will actually launch the app. The URL scheme you are probably most familiar with is http://—the scheme that precedes all URLs on the web.
So now you can open the application by using the provided the URL Scheme, i.e.:
yourApp://action?param=value
2) Open SFSafariViewController in the hidden window inside the mobile app
import UIKit import SafariServices @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? var safariViewController: SFSafariViewController! func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { window = UIWindow(frame: UIScreen.mainScreen().bounds) window?.makeKeyAndVisible() let url = NSURL(string: "https://yoursite.com/invitation")! safariViewController = SFSafariViewController(URL: url) window?.insertSubview(safariViewController.view, atIndex: 0) return true } }
3) Handle redirection from your website to the app
func application(application: UIApplication, openURL url: NSURL, sourceApplication: String?, annotation: AnyObject) -> Bool { safariViewController.view.removeFromSuperview() safariViewController = nil // do your stuff with “yourApp://action?param=value” url return true }
Backend part
1) Set appropriate cookies in the Safari browser SetCookies.php
<?php setcookie('someKey', 'someValue', time() + 3600 * 24); // setting cookies with expiration date for 24h header(‘Location:#link_to_appstore'); // redirect to App Store ?>
Or as an alternative - JavaScript code
function createCookie(name,value,days) { var expires = ""; if (days) { var date = new Date(); date.setTime(date.getTime()+(days*24*60*60*1000)); expires = "; expires=" + date.toGMTString(); } document.cookie = name + "=" + value + expires + "; path=/"; } createCookie('someKey','someValue',1); window.location = "#link_to_appstore";
2) Read the cookies and redirect to your app with required data ReadCookiesAndRedirect.php
<?php $cookie_name = 'someKey'; if(isset($_COOKIE[$cookie_name])) { // checks if cookies exists $cookie_value = $_COOKIE[$cookie_name]; if(strcmp('someValue', $cookie_value) == 0) { // validates cookies setcookie($cookie_name, '', time() - 3600); // removes old cookies header(‘Location:yourApp://invitation?code=1Q2W3E'); //redirects data to your app } } ?>
Or as an alternative - JavaScript code
function readCookie(name) { var nameEQ = name + "="; var ca = document.cookie.split(';'); for(var i=0;i < ca.length;i++) { var c = ca[i]; while (c.charAt(0)==' ') c = c.substring(1,c.length); if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length); } return null; } function eraseCookie(name) { createCookie(name,"",-1); } var x = readCookie('someKey') if (x === 'someValue') { window.location = "yourApp://invitation?code=1Q2W3E"; eraseCookie('someKey') }
Summary
To summarise, Apple have introduced yet another handy feature that helps to improve user experience by breaking down a wall between services. So it doesn’t matter where the user last logged in or entered their information, all your service clients will be able to show their latest content. It is clear that this feature has at least one great potential implementation - guest apps can fully skip their sign-in process if the user is already logged in through Safari, giving a real WOW effect to people who are using the app.
Read our full review of iOS 14 supported devices, release date, and more.