AttributionDeep Linking - GeneralProduct UpdatesTechnical

100% Matching Accuracy with iOS 9 Safari View Controller

By September 1, 2015 14 Comments

Branch is excited to announce that we now offer 100% accurate install attribution for clicked links on iOS 9.

With iOS 9, Apple has given developers some great gifts. Two of my favorites are Universal Links and the Safari View Controller, both of which allow for a more seamless user experience across apps. For the sake of this post, let’s focus on using Safari View Controller to power perfect install attribution.

Using Safari View Controller for Install Attribution

Prior to iOS 9, your could only pass data through the App Store using a Apple-provided banner. This banner was not customizable at all. To fix this, we crafted a solution using digital snapshotting and javascript redirects. Our solution allows developers to get individual-level install attribution—meaning they can know the exact link a user clicked even if they clicked it before the app was installed. (And by the way, we created a fully customizable app download banner for those who love banners.)

That all changes with a new feature in iOS 9. The Safari View Controller allows developers to pull up a full-screen, Safari-like window for accessing web pages. This is different from viewing web pages prior to iOS 9 (in UIWebView and WKWebView) in that it is effectively a sandbox, with almost everything hidden from the developer. Also, it’s a view controller, not a view that can be embedded in other views.

iOS SafariViewController

Perhaps most crucially–and this is why we love it–cookies are shared between all the iOS View Controller and Safari. Put another way, cookies for a given site can be shared across all apps.

This is great news! This means that you can seamlessly transition users from your mobile website to your app. If a user is logged into your site in Safari then downloads your app, you can open up SFSafariViewController, navigate to www.yoursite.com/welcome-user, and display a page welcoming that person to your app.

At Branch, we’ve offered this for about a year now, using a technique called “digital snapshotting”. We use a combination of a user’s operating system, operating system version, and IP address to compare users clicking on links in the browser to users opening apps.

Digital snapshotting is imperfect but works the vast majority of the time, and we only have to use it once per device. Then we have them tagged based on browser cookie and IDFA. We’ve described this process in more detail on our developer portal: https://dev.branch.io/recipes/matching_accuracy/

However, with iOS 9, we no longer need to resort to digital snapshotting.

iOS View Controller for 100% Matching

Practically speaking, you can’t just open up SFSafariViewController and read the cookies from any website (that would be terrifying). In reality, cookies dropped in Safari are attached to requests made in SFSafariViewController, and vice versa.

The team at Apple responsible for the iOS View Controller has clearly decided that security is a top priority here. In fact, security and protecting user data is a large part of the branding surrounding iOS 9.

If you use Branch for 100% matching, please be sure to include the SafariServices framework as an optional dependency. When opening SFSafariViewController, you can specify an initial page to load and whether to use the reader view if it’s available. For delegate methods, you can see when the first page has loaded, and when the user taps close. There’s not much you can do, but it’s enough.

We’re using this view controller in a creative way to offer 100% matching. That is, we can tell you 100% of the time if a user clicked on a link and what link they clicked on, even if their digital snapshots are identical.

SFSafariViewController *safController = [[SFSafariViewController alloc] initWithURL:[NSURL URLWithString:urlString]];
safController.delegate = self;
[windowRootController presentViewController:safController animated:NO completion:NULL];

At Branch, we’ve decided on a simple approach:

  1. In the iOS app, create a URL pointing to our servers and including a unique identifier for the device, like IDFA.
  2. Open the SFSafariViewController and point it to that URL
  3. Server-side, the API endpoint receives the IDFA and parses the cookie (if any) sent up with the request. We send down a new cookie if it didn’t exist previously. We now have a link between the user in the browser (cookie) and the user in the app IDFA.

Developers using Branch already have this ability, but now we can avoid the edge cases inherent in snapshotting. When you upgrade to an iOS 9-compatible version of the iOS SDK, you will automatically be making use of Safari View Controller for 100% matching. That means no extra work for you! Click the Get Started button below to visit our dev portal and learn more about the Branch SDK.

  • Luke Rhodes

    …but why (other than to get this 100% match) would we want to show a web page when the user has just installed the app?

    • Derrick

      Luke, great question. There are a ton of use cases for onboarding, like knowing who the user is if they had logged in from the browser, personalizing their first launch experience based on the link they clicked (“Hi Luke, see your friend John’s profile here >>”), etc. In our case, we use this for the case where a user clicks a link to content in an app but doesn’t have the app installed. After they install and open the app, we’re able to immediately know what content they were trying to view, and we pass that data into the app. Then the developer can present the same content/view controller that the user would have seen if they had the app installed when they clicked the link.

      Also, we present the SFSafariViewController invisibly. So the end user does not have an altered/worse experience because of it.

      • Luke Rhodes

        Thanks for the reply Derrick – “Also, we present the SFSafariViewController invisibly.” was the key part for me, it’d be good to incorporate something about it in the post.

        • Derrick

          Ah, yes, that is indeed a key detail. We’ll add that in. Thanks for the feedback!

  • Anonymous_94123

    Hi Derrick. It looks like SafariViewController cannot be launched invisibly with iOS 10+ have you found a way around this?

    • Mada Seghete

      Hi there, we’d be happy to help. Do you have any links to docs pointing to this, or can you help us understand your issue? We’ve been running the Testbed on iOS 10 for a while w/o any errors and we can’t find anything in Apple’s docs – but happy to help you debug your issues. Best way to get in touch with our engineers is support@branch.io.

      • dev1

        Hi Mada, we’ve tested the ability to show an SFSafariViewController invisibly on iOS10 beta 3 and both techniques that are known to work on iOS9 (setting alpha to 0 / displaying the VC on a detached UIWindow) seem to be no longer working.

        We also tried the relevant code from your TestBed project but it doesn’t seem to work on iOS 10 either.

        Did you find a way around this? Will your SDK work for 100% matching using the SafariViewController in iOS10?

        Thanks!

        • http://www.branch.io Tim Chingos

          Hi Dev1 – Tim with Branch here. We actively testing Branch and all of it’s functionality on iOS10 while its in beta to ensure we provide the best experience to our partners and their users. Thanks for flagging this and letting us know.

          • dev1

            Hi Tim, sure no problem. have you had any progress with this? I’m interested in using the technique for my app and having it support iOS9 only would probably mean it wouldn’t be worth the hassle.

            Thanks in advance

          • Alex

            Hey there – We’ve found a path forward and will be submitting a patch closer to the iOS 10 release date along with a couple other upgrades. Watch the changelog on iOS and you’ll see how we do it. Note that this mechanism is not very accurate alone for your app since it only works if the user originated from Safari. From our tests, this is less than 50% of the time for most campaigns. It must be paired with some of the other techniques that Branch uses to get close to the 100%. We describe them all here https://dev.branch.io/getting-started/matching-accuracy/guide/.

          • Alejandro Moreno

            Hi Alex.
            I have the same issue with iOS 10 and hidden SFSafariViewController technique, the request isn’t being delivered as it’s not visible. ha ve you found any solution for this? I’m taking a look to your SDK and haven’t found any solution.

            And it seems that maybe this implementation has some issues with iOS 10 app review guidelines https://developer.apple.com/app-store/review/guidelines/

            “(iv)SafariViewContoller must be used to visibly present information to users; the controller may not be hidden or obscured by other views or layers. Additionally, an app may not use SafariViewController to track users without their knowledge and consent.”

            Thanks

          • Alex

            Hey Alejandro – The new version (0.12.6 https://github.com/BranchMetrics/ios-branch-deep-linking/commit/b6aa1b000e20d6a32607c0c116092f1344a10896) of the Branch SDK has a working SFSVC implementation if you’d like to review so the functionality was not removed. However, even though our SFSVC matching is not enabled by default, we’re planning on removing this feature completely from the SDK in the next few weeks to comply with Apple’s standards.

            At this point, for 100% matching on iOS through install, we’ll rely on Branch’s huge and ever growing pool of cookie-IDFA pairs that we’ve matched deterministically through usage of the platform. https://blog.branch.io/the-importance-of-matching-accuracy-in-deep-linking

  • Charlie Nowacek

    Just a heads up, the way you invisibly show the SafariViewController (by creating a window and making it the key window) introduces a side effect with Facebook’s login behavior that uses the SafariViewController.

    Unless explicitly provided, the FBSDK will try to get the rootviewcontroller of the keyWindow in the application. For 3s after initializing Branch, you all present your SafariViewController in your own key window. This means that the FBSDK will try to present the login controller from your invisible window. Most developers assume that their AppDelegate’s window is the keyWindow so they don’t know to explicitly provide their own viewController to the FB SDK.

    Furthermore, we’ve seen issues with the SafariViewController being presented in a window that _isn’t_ the key window. So even when explicitly providing the FBSDK with the view controller to present from, the presented SafariViewController’s view hierarchy is corrupted.

    We got around this by forcing our AppDelegate’s window to be the key window of the application but it was after several hours of debugging. I don’t know if you all have a way to fix this on your end or to provide a tip to developers on how to get around this issue of integrating both Facebook’s login controller and the Branch SDK.

    • Edward Smith

      Sorry you had to find this out the hard way!

      Thanks for your in-depth explanation. For now we can update the documentation, but finding a work around is pretty crucial. I’m sure plenty of other people are running into this too.