📜 ⬆️ ⬇️

Universal links: the palace of pitfalls

Given how much mobile apps they gave to humanity, they “broke” the Internet at the same time. Instead of clear links to sites that can be copied and shared, it became necessary to explain "put such an application and go there somewhere."

Fortunately, the developers of mobile platforms have realized this problem and proposed the concept of “universal links”, which with one click open the right one on any platform. But the fact that for the user "one click", for the programmer - "with sweat and blood." On the way to success there is a whole heap of unexpected nuances, and Konstantin Yakushev got to know them on personal experience when introducing universal links into Badoo . And then at our conference, Mobius told how to do everything right and get around problems. The audience liked the report, and we decided that it was useless to use only video recordings, so the text version is under the cut.

The report considers the situation from the side of iOS, but universal links to universal and universal ones, in order to unite different platforms, so not only iOS developers can benefit.
')

Introduction


My name is Kostya Yakushev and I spent several months debugging universal links in Badoo for everything at once: iOS, Android, web. I coordinated the actions of all these beautiful guys, because I am a mother architect .

And today I will tell you the following:



What is universal links in general? The story is quite simple. You use some application, for example, Badoo. If you opened someone’s user profile there and you wanted to send a link to this profile to a friend, you press this button with an arrow:



You send a link to a friend, she is beautifully displayed in the correspondence, a friend clicks on her, and the page of the same girl opens:



When a link opens up some content inside the application, this is traditionally referred to as deep linking. But this is the ideal scenario when a friend has an application installed, and it is under iOS.

But in practice, a friend may not have an application, and then we want to send it to the App Store.

Or, if we do not really want installations, but we really want to immediately show the page, send it to the mobile version.

Either a friend even opened a link on the desktop, and then he needed to send a desktop site.

But the worst thing that a friend might have is Android! And this should work too.



When a link has all these properties, or at least some of them, it is called a universal link. And about them I will tell you how to do it.

This is how it looks from a more algorithmic point of view. If the link is open from the desktop, we open the desktop web, if from the smartphone we’re looking to see if the application is installed, and depending on the answer, either open it in the application or enter the application store. In the case of the store, ideally, we want the user to immediately go to the place where he originally wanted after the first launch of the application after installation.



Why is all this necessary? Million applications.

For example, you want to send a letter to the user "see, you have a new like." It would be great if you click on “Take a look” to open a new like.



For example, the Serebro group has opened a profile in your application. You want them to publish a link under their clips that would also open this profile.



For example, you advertise on Facebook. Facebook is very scrupulous about the fact that if something specific is shown on the ad, the link should also lead to that particular one. That is, if you see a profile of a girl in an advertisement, then they insist that the same girl could be seen in the application.



Not necessarily we should talk about the dating, in other cases, too many applications.

And the “share” button, about which I have already spoken, is the most important, the coolest, the coolest, but also the most difficult.

The idea here is that users want to share something in your application. This is even more important for other non-dated applications. Suppose if you have a travel app, users will definitely want to share tickets or hotels. They send links to their friends for free, thereby making advertising to your application. Ideally, this leads to new installations, to organic growth, and at the same time almost free of charge (at a price, in fact, the implementation of this technology).

And here we understood that it is cool and we want it, we gathered and began to think how to do it. There are several services: everyone knows branch.io, there are AppsFlyer, there are still some strange services, and you can make your own bike. It all ended with the fact that we made our bike, but tell you how it came to this.

First we went to look at branch.io. Everything that they do is connected solely with deep links: on the site deep linking is written on the site, but two points on the sides, in general, also mean diplinking.



They have a lot of cool customers, our favorite competitor Tinder uses them. And in some cases they are free. But in our case, they were not free at all.

They are, of course, experts in the subject, they write a million blog posts that tell about the same problems that I’m going to tell you about. However, when in a post they move from problem to solution, they knead it a little bit and say “well, use our service, we know how to do it”. Fortunately, you have me.



AppsFlyer is a marketing service that is primarily intended for tracking installations, to determine where users came from, measure the effectiveness of advertising campaigns, and so on. When we started doing universal links, Badoo already used this service for other tasks, and our marketing specialists simply adore it. If you lean our marketer against a wall and ask if AppsFlyer is cool, he will say: “Very cool, all other services are much worse. And don't lean me more against the wall, please. ”

AppsFlyer also has a lot of cool customers, Tinder also uses it - for everything except diplinks, but then it didn't bother us. We go to the site, we see the inscription "The most world's powerful deeplinking platform" - well, we must take. In addition, at that time it was for us, in general, free of charge, because it was part of the product for which we already pay.



We went to their site. Then there was such a scheme, but now it is no longer on the site. In principle, this is the same as I showed before. You follow the link; if the application is installed, it starts; if not, go to the App Store. But!

As it turned out, the part where the application is not installed, we go to the AppStore and after installation we open the content, it works. And this is logical: these guys are engaged in tracking installations, they ate this dog.

But here the “application is already installed” part, when we implemented it, for some reason, instead of opening the content, also showed the question “Open this page in the App Store?”.



Then we stopped blindly following the SDK and sat down to figure out how it basically works. And to explain why they have this error and what we have done with it, you need to start from the very beginning.

From the very beginning


Here is the archetypical diplink:



There is a custom badoo: // scheme, some thing that we want to open (say, a user), and there is an ID of this user.

It is clear that in raw form this link can not be used for a very simple reason. If the application is installed, it works as a link, but if not installed, it results in the error "Safari cannot open the page". This is logical, Safari has no idea how to work with it.

Until recently, it was quite easy. We do the usual HTML thing, in which we have a few things:



There is an iframe that gets the parameters from the URL and has the same deep link. And there is JavaScript that redirects in the App Store. The idea is that if you have an application installed - the iframe will work, if it is not installed, the redirect will work, and everyone is happy, the error will not come out.

In addition, to the HTML page, you can add Open Graph tags that will allow the link to look as beautiful as I showed: it has a picture, a preview, and all that.



So, starting around iOS 9, this beautiful scheme has stopped working.

What does "stop working" mean? This means that it began to work as if you were just fumbling the badoo: // link. If the application is installed - it works, if not installed - it shows an error (in general, on top of the redirect). That is, if the user follows the link and it does not have an application installed, an error occurs. And Apple broke it completely consciously.

What for? Because they came up with something "fundamentally new and fantastic": universal links.

In their idea, you simply register the entire link, the entire domain as a universal link. And when I say “fundamentally new and fantastic” way, I mean “almost fundamentally new”, in Android it has been for many years.



Why did they do it?

Thirdly, it helps to get rid of hacks with an i-frame.

Finally, (ideally) works equally on all platforms.

Unfortunately, when we got rid of hacks with iframes, we got a lot of other hacks, but more on that later. Now about AppsFlyer. Remember, it all started with the fact that they do not support universal links.

In fact, as it became clear from the documentation, they support them, but they require the use of such a scheme: they expect that we will create a third level domain and the second level domain will belong to them.



In our case, it was a completely unjustified risk and a very unpleasant story. Because they could increase their price tag, they could simply close, and our users themselves fumble these links, they should ideally work forever. We did not like it.

But we can solve these problems, right? Usually we redirect, redirects work on the web, right?



And we began to test. Ok, onelink.me works, we have integrated everything correctly, the SDK starts up, opens Badoo, opens content. Our link, which is a blunt redirect ... opens Safari. Wow, we thought.



And then we began to maintain a list that was getting longer with each day of our implementation. It is called "When universal links do not open the application, although you expect it to open."

And here is the first item on this list:



Redirects for universal links do not work. This greatly confused us, then we decided to quit AppsFlyer (for this reason, and for some others), having done everything ourselves. Especially since we already had a ready-made android scheme:



It consisted of two parts. We had a page id (the id of the page to open) and the content id (for example, the user id). And in principle, it’s not difficult to do, Apple has a manual on the site, it takes literally three pages and represents three points.

Firstly, you need to add apple-app-site-association - a json-file in which you will have your appID and paths that the application should open in the universal links.



The second. You need to add domains to the entitlements:



And third: you need to write some code that, in general, will verify that ActivityType is BrowsingWeb, and the URL is there.



We did this, tested a couple of cases, everything worked, and went to argue on the topic of how links should look like.

That is the story. We have our first scheme, which I said, which indicates page id. There is a second scheme, it is used just by branch.io and AppsFlyer: they usually make it so that you do not have an identifier, but there is just some kind of ref link that the application sends to the server when it starts up, receives in reply where to send it, and landit there.



The problem with the second option is that while the application receives content, we would like to show some kind of content framework, and the second scheme does not allow us to do this (in particular, with AppsFlyer, this does not do anything).

Ultimately, we decided that we would just use everything at once:



In principle, this is a normal solution: we can use ref both for statistics and to vary the behavior of the server depending on what kind of link it is (for example, we have links that give credits). Page id and user id can be used in order to quickly start and immediately get a get user.

But there was another problem. The managers came to us and said: "Guys, you have a very long link, how people will fumble it."

Well, again, we know the solution ... to redirect ... um.



In principle, in the short link we will save all the properties, there will be both the page identifier and the content identifier, this can be done. But how do we redirect?

It turns out that in fact, a redirect can be done, but for this you need to make a small hack. It is quite simple. Your minifier (it should be your personal minifier) ​​is also declared a link in the application, for it you also do the apple-app-site-association, also write code, and, accordingly, both domains are diplink.



Moreover, it can be implemented quite easily. Simply in the code that opens your application, it makes an http-request for a minicator, looks where the redirect goes, and uses a long link using standard logic.

If you remember, with AppsFlyer, the redirected part to app stores works well, and we applied it to the App Store. This is the only part that we did not do ourselves, but, in principle, you can do it yourself, we just did not want to.

What is the idea. The user opens the AppsFlyer http-link, they do fingerprinting of the user (that is, remembers the IP address, iPhone model, time cut-off and so on, some properties of this phone), and redirect to the App Store. The user installs, and after that, in the application itself, AppsFlyer SDK matches the devices that recently followed the links with the current device and concludes which link should be opened.



Accordingly, the general scheme turned out like this. Minifikator redirects to our link, but if neither one nor the other link is picked up by the application, then the application does not exist, we redirect to AppsFlyer, it redirects to the App Store with tracking and already does what is needed.



While we were engaged in all this garbage, QA came to us and said: "Guys, I send the link to Telegram, to Skype, to HipChat, nothing works." We: "How wait, everything works for us."

It turned out the thing in SafariViewController.



The story of SafariViewController is completely tragic. The point is this. As planned by Apple, if a user opens Safari, enters a universal link into the address, and presses Enter, then it does not open the application. This is logical: if you are a user, you do not expect that when you press Enter in a browser, you will get into the application.

The second part is illogical: when an application opens SafariViewController, exactly the same thing happens as if the user entered a link in the address bar and pressed Enter. There is no way to open the universal link instead of opening SafariViewController.

So we increase this list:



If the user entered the link in Safari himself or opened SafariViewController, nothing works. We thought for a while, and then we saw a solution.

And it is quite logical. We will open with html-preview, and then click on the button to go to the same link:



And since universal links work in SafariViewController, only the opening of a universal link instead of SafariViewController does not work, this should work, right?

Not really.

This solution doesn't work either, and here's why:



If a user clicks on a link in the same domain on which he is currently located, it does not open the application.

What to do?

Well, like that - another hack, of course.

Everything is very simple: we make two domains, and both register as a universal link. Here's what it looks like.



The user opens m.badoo.com, and on the button he will have mlink.badoo.com. You can even copy this link and send, it works in both directions, we have these two domains work as equivalent. Accordingly, if the user opens mlink.badoo.com, he will have on the m.badoo.com button. Victory.

The overall scheme has become even more fun.



Now we have a minifier, which is not shown here. Domain m.badoo.com button direct in mlink, mlink redirect in AppsFlyer, and there is already a redirect on the App Store with tracking. It began to work somewhat better. At least in Safari, in Telegram, people were more or less able to somehow open up.

Then our beloved QA, which is already sad-sad, also comes to us again and says: “You know, very strange things have started to happen here.”



On some devices, only some, for some reason, universal links did not work. At all.

We found. When clicking on the universal link and launching the application, this button appeared in the upper right corner:



And she does two things. First, it opens Safari. Secondly, it breaks diplinks for your application forever!

And then the only way to break them back is to make a long tap on the link, click "open in Badoo", and then everything will work back. But not a single user in my memory has ever thought of this himself. The general idea: do not touch this button.

Fortunately, in its infinite wisdom, Apple has given us a fantastic gift this fall and removed this button from iOS 11.

But if you support iOS 9 and 10, remember this button. And if you suddenly support iOS 9, remember that the apple-app-site-association should not be closed by robots.txt, otherwise it will not work, and this is also a problem that we have encountered.



Versioning



In general, we stuffed an infinite number of cones. And then something happened that filled us with a second cone of infinity.

The company Badoo decided to make a new cool feature "Twins" ("Lookalikes").

The idea is very simple. You can take a picture of someone or take a finished photo, and we will find in our service the people most like this person. You can search for celebrities, you can search for friends, you can search for yourself, look at your doppelgangers. Cool topic.

So cool that our managers said about this scheme that they need to change it.



Why? We really want that when our users share with each other twins, the recipients must see this at all costs, nothing should stand in their way. Therefore, if the application is not installed, we no longer divert it to the App Store. We open in the mobile web.



“Oookey,” we said. In principle, everything is not so difficult, right? We say: “Well, if the address is a different page id, we will redirect it to the mobile web”



It was easy, we did. But then it dawned on us that this is a new functionality that was not previously in our applications.

And this means that even if the application is installed, it is still not a fact that we should open it. In fact, the scheme should look like this:



If the application is installed, you need to check if it supports our new functionality. And only if it supports, open in the application, and otherwise, again, lead to the mobile web.

Because on some platforms, and on iOS in particular, there was not even an opportunity to open it yet. And so we approach the topic of versioning.

Again, ideally, everything is very simple. We have an old version and a new version. The idea is that the old version opens only the paths / u, and the new version opens / u and, say, / l / a from “look alikes”.



We rolled up our sleeves, we think, now we’ll do everything. But it was not there.

Remember, I was at the beginning told how to customize links? So, the paths that the application supports are indicated in a special file on the site. And only on the site. Accordingly, as soon as we add a new path there, this thing starts to open in the old application and in the new one:



But in the old application it crashed, because we forgot to think about it. And we could not do anything with it. The only thing we did was wait until the new application rolled for a sufficient number of users, and only then turned it on.

But I can tell you - guys, think about it in advance. Check here if you can open the URL and return false if necessary:



Then, if the users of the old version of the application tries to open something that it does not support, you will open Safari instead. And as soon as the new one supports, canOpenUrl will begin to return true, and it will open already.

Fantastic algorithm of incredible success.


These are the ones we crammed, and the time has come for the fantastic algorithm that I promised you. Important point: this is a fantastic algorithm of incredible success for those who do not want to use branch.io. At this point, we learned that their business is not so much “on nothing”, they are doing something. All these hacks that we did, in principle, are somehow supported in their SDK and infrastructure (but I cannot advise them, because I have never tried it). However, if you want to do everything yourself, if you like to customize everything or you feel sorry for the money, now I will tell you what to do.

First, think up a scheme and do not forget that you have a ref and a page in the scheme. Then you can beautifully display the frames and at the same time keep any statistics.



The second.Very simple, do what Apple tells you : apple-app-site-association, domains and code.

And do not forget in the code about canOpenUrl, check that you can actually open it, otherwise send it to Safari.



Third. Do not forget about the HTML preview and Open Graph tags, otherwise everything will look ugly. By the way, although in this report I don’t disclose the Android theme, for Facebook on Android I need to support separate og tags with Android link . In addition, for some webview android, you can leave the iframe with a delay of redirection, like the one I mentioned at the beginning.



In the HTML preview, again, do not forget that you need two domains: one in the button, the other in the preview.



Do not forget about deferred deep linking (delayed diplink, i.e. diplink to an application that is not yet installed). Here we have AppsFlyer, and you may have your own personal solution that will allow you to track the installation and open the content after installation by the user.

In general, this is all you need to build your palace from the pitfalls:



But I still have a small bonus track, which I added at the last moment. Remember how in the early scheme, starting with iOS 9, we had an error Safari in the event that the user does not have the application installed? This is ugly, we do not want users to see the error. Do you remember this slide?



We send letters to these users, and this button simultaneously logs in the user (there is a token for the login) and opens the content.

So: we send these letters to specific users, and we know if they have the application installed and on which device. And this means that we can use the old scheme for those cases when we appeal to specific users who log off from these devices. In this case, we have logic on the server about whether the application is installed or not.

Accordingly, if the application is installed, we can use the old scheme. It does not need so many hacks, and it is more likely to work in the Gmail client (which uses SafariViewController) and others. And all the rest will be sent to the mobile version. Accordingly, the new version of the fantastic algorithm of incredible success has one more point:



. — 20-21 Mobius 2018 Piter , !

Source: https://habr.com/ru/post/352914/


All Articles