Navigate to specific view with URL Scheme - ios

I added a url scheme newConversation to my info.plist so that when a user clicks on a link in a browser/email it will redirect him to the app. This works perfectly fine.
I was wondering how I can open the app to a specific view controller when clicking this url?
I tried using:
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
if ([url.scheme isEqualToString:#"newConversation"]) {
NSLog(#"url schemeeeee");
emailLink = YES;
[self initWindow];
}
return YES;
}
but not getting anywhere with that.
Thanks!

Your app should only have one scheme (such as myappscheme) registered for your app. Then if you need to handle different actions, you provide more specific URLs with that scheme:
myappscheme:///newConversation
myappscheme:///doSomethingElse
Then you get /newConversation for the URL's path. Then your code becomes:
if ([url.path isEqualToString:#"/newConversation"]) {
} else if ([url.path isEqualToString:#"/doSomethingElse"]) {
}

To be honest, this is kind of a pain to implement on your own. A basic custom URL scheme link isn't an ideal solution and has a ton of nasty edge cases, most notably the 'Cannot Open Page" error users will see before if they don't have your app installed, and the fact that many apps actually don't recognize custom scheme URLs as 'clickable' (they just show up as regular text).
A somewhat better approach is to use a regular http:// link, and then redirect the visitor to your app — if they have it installed — or to a fallback URL/App Store page. Until iOS 9, a reasonable basic implementation was a JavaScript redirect like this:
setTimeout(function() {
window.location = "https://itunes.apple.com/path/to/your/app/";
}, 25);
// If "yourapp://" is registered, the user will see a dialog
// asking if want to open your app. If they agree, your app will
// launch immediately and the timer won't fire.
// If not installed, you'll get an ugly "Cannot Open Page"
// dialogue and the App Store will launch when the timer expires.
window.location = "yourapp://";
Unfortunately this would still show a 'Cannot Open Page' error, but until recently it was possible to get around this in a reasonably user-friendly way by using a more nuanced version of this script. Sadly, Apple intentionally broke that with the iOS 9.2 update, so custom URL schemes are actually pretty much useless for deep linking now, unless you are certain the app is already installed on that device.
The best solution is a combination of custom URL scheme links (with intelligent JavaScript redirections) and Apple's new Universal Links. Universal Links let you use a normal http:// URL to a page on your website (the page could be a simple redirection to the App Store without the custom URL trigger that causes the 'Cannot Open Page' error), which is intercepted by your phone and sent directly into your app if installed. Unfortunately Universal Links only work in iOS 9+, and don't work yet when opened inside a lot of apps.
This is quite a lot to handle, so the best option might be a free service like Branch.io (full disclosure: I work with the team) to take care of all the technical aspects.

Related

Custom URL scheme without confirmation prompt (Swift)

I've found two options to open my app from a Safari web page: a custom URL scheme created in my app project's Info.plist or Apple's Universal Linking. Obviously the custom URL scheme is the easiest one to set up, but the problem I'm having with this is that Safari shows a confirmation window asking "Open myapp?" first and the user has to tap OK before the app actually opens. I want my app to open automatically as the scheme is opened, and I'm being told the only way to do this is through Universal Linking (please correct me if this is not true). If this is true, however, I would like to know if it's possible in any way to put the required apple-app-site-association file on a http:// domain instead of https://? According the official Apple documentation the format of a correct Universal Link starts explicitly with https:// but my domain name can't be loaded on https:// without redirecting a few times and that messes up the web services I've written to execute other tasks in my app. The two main questions I'm left with after this issue:
1) Is it really impossible to work around the confirmation prompt using a custom URL scheme (myscheme://)? If it's not impossible, how can I do this?
2) If I have to use Apple Universal Linking, can I use a http:// domain? If so, how do I do it? Right now if I load up the universal link, it just shows the dictionary inside the apple-app-site-association file, which I'm pretty sure is not supposed to happen. I'm told it's supposed to send a NSUserActivity object to my app delegate. How can I accomplish this with a http:// link?
It is not possible to trigger a custom URI scheme without showing an alert to the user. This used to be possible in iOS 8, but iOS 9 started showing the alert for all apps. And iOS 10.3 has extended that even to the App Store itself. You cannot bypass this. Universal Links were created to replace URI schemes for this behavior, so you do need to use them instead.
From your description, I believe you may be misunderstanding how Universal Links work. To answer the literal questions you asked first, no the Universal Link URL itself does not need to be on the https:// protocol, and yes, the apple-app-site-association must be served over https:// without redirects.
However, it sounds like you're trying to serve the content of the apple-app-site-association file for every Universal Link. That is not the correct implementation — the AASA file is hosted only at https://example.com/apple-app-site-association, and iOS automatically retrieves it when the app is installed. After that, any URL on example.com that matches the criteria in the AASA file will be eligible for Universal Links.
All of that said, you really don't want to built out this system on your own. I suggest looking into Firebase Dynamic Links or Branch.io (full disclosure: I'm on the Branch team).
Is it really impossible to work around the confirmation prompt using a custom URL scheme (myscheme://)? If it's not impossible, how can I do this?
That is possible with some hacky tricks and BAD user experience. It requires user to press "add to home screen" button, so I don't recommend this solution in most cases.
set your app scheme like myapp
create the following html file and put it into the web
window.onload = function() {
if (("standalone" in window.navigator) && window.navigator.standalone) {
window.location.href = 'myapp://open'
}
}
open the html file with safari and "add to home screen"
open the home screen icon and your native app will launch
The point is the meta tag.
<meta name="apple-mobile-web-app-capable" content="yes" />
Without this, safari will launch and confirmation prompt will appear.

Is there a way to recognize, that the app was installed thru firebase dynamic link in didFinishLaunchingWithOptions?

I am implementing firebase dynamic links in my iOS app and I can already parse the link, redirect to AppStore etc. Now I want to distinguish the first run of the app, when user installs it from the dynamic link - I want to skip the intro and show him the content that is expected to be shown.
Is there some parameter, that I could catch in application(_:didFinishLaunchingWithOptions:) so I could say that it was launched thru the dynamic link?
The method application(_:continueUserActivity:userActivity:restorationHandler:) is called later, so the intro is already launched.
This case is difficult to test, because you have to have your app published on the AppStore.
You actually don't need to have the app published in the App Store for this to work — clicking a link, closing the App Store, and then installing an app build through Xcode (or any other beta distribution platform like TestFlight or Fabric) has exactly the same effect.
According to the Firebase docs, the method that is called for the first install is openURL (no, this makes no sense to me either). The continueUserActivity method is for Universal Links, and is only used if the app is already installed when a link is opened.
I am not aware of any way to detect when the app is opening for the first time after install from a 'deferred' link, but you could simply route directly to the shared content (skipping the intro) whenever a deep link is present. If a deep link is NOT present, show the regular intro.
Alternative Option
You could check out Branch.io (full disclosure: I'm on the Branch team). Amongst other things, Branch is a great, free drop-in replacement for Firebase Dynamic Links with a ton of additional functionality. Here is an example of all the parameters Branch returns immediately in didFinishLaunchingWithOptions:
{
"branch_view_enabled" = 0;
"browser_fingerprint_id" = "<null>";
data = "{
\"+is_first_session\":false,
\"+clicked_branch_link\":true,
\"+match_guaranteed\":true,
\"$canonical_identifier\":\"room/OrangeOak\",
\"$exp_date\":0,
\"$identity_id\":\"308073965526600507\",
\"$og_title\":\"Orange Oak\",
\"$one_time_use\":false,
\"$publicly_indexable\":1,
\"room_name\":\"Orange Oak\", // this is a custom param, of which you may have an unlimited number
\"~channel\":\"pasteboard\",
\"~creation_source\":3,
\"~feature\":\"sharing\",
\"~id\":\"319180030632948530\",
\"+click_timestamp\":1477336707,
\"~referring_link\":\"https://branchmaps.app.link/qTLPNAJ0Jx\"
}";
"device_fingerprint_id" = 308073965409112574;
"identity_id" = 308073965526600507;
link = "https://branchmaps.app.link/?%24identity_id=308073965526600507";
"session_id" = 319180164046538734;
}
You can read more about these parameters on the Branch documentation here.
Hmm... as far as I'm aware, there's not really anything you can catch in the application:(_:didFinishLaunchingWithOptions) phase that would let you know the app was being opened by a dynamic link. You're going to have to wait until the continueUserActivity call, as you mentioned.
That said, FIRDynamicLinks.dynamicLinks()?.handleUniversalLink returns a boolean value nearly instantly, so you should be able to take advantage of that to short-circuit your into animation without it being a bad user experience. The callback itself might not happen until several milliseconds later, depending on if it's a shortened dynamic link (which requires a network call) or an expanded one (which doesn't).

iOS - Return to Safari from Native App without Opening New Tab

I am having trouble figuring out how to switch to Safari from a native app in iOS 7+. I've used UIApplication.sharedApplication.openURL(), but that opens a new tab. I would like to return the user to the current page he/she was viewing before without opening a new tab. I found this SO post, but it is a few years old, so I was hoping things have changed since then.
Here is the workflow I am envisioning:
User taps on a link on an HTML page on Safari to open/install my app
User performs an action on my app
After the user is done performing the action, my app opens Safari automatically, and the user is back on the page where he/she left off
Google has somehow done this with their Google Maps app. If you search for an address on google.com on Safari, you can tap on the map that appears in the search results, and it will open the Maps app. At the top of the Maps app will be a "Return to Safari" bar that you can tap. Once you tap it, you are returned to Safari without loading another tab. I can't seem to find anything regarding how Google did this. If I can replicate that behavior in my app, that would work just fine.
Any help would be greatly appreciated!
There is a way to accomplish what you want using standard iOS APIs. No need to use external components.
You control your webpage and your app, so you know the exact URL that has the link to your app.
These are the steps:
1) In your app, define a custom URL scheme. In this case let's assume you use the scheme myawesomeapp://. You can do this in your Xcode project by going to the Info section of your target. See below
2) In your web page you need to handle the two scenarios: app installed / not installed. It is just a matter of detecting if an app responds to the scheme myawesomeapp://.
To detect from your webpage if your app is not installed please refer to this post
I will explain the case where your app is already installed.
Let's say the webpage that contains the link is:
http://www.mywebsite.com/mypage.html#mytag
The link you provide in your webpage should pass some parameters to the app and one of these should be the URL that you want the app to return. Following with the example the link could be:
myawesomeapp://?action=my_action_1&sourceurl=http%3A%2F%2Fwww.mywebsite.com%2Fmypage.html%23mytag
Note that the URL you pass as a parameter inside the scheme has to be URL encoded or it won't work properly.
3) In your app delegate you need to implement the method:
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
In this method, parse the URL, decode the query items and pass the sourceURL to the view controller responsible of handling the action prior to calling it. In this case I set a public property in the ViewController that will store the URL.
#property (nonatomic, strong) NSURL *sourceURL;
4) In the view controller when the user finishes the interaction, you just call:
[[UIApplication sharedApplication] openURL:self.sourceURL];
Because self.sourceURL contains the URL of your webpage, Safari will be launched with the URL. However, because that page is already opened, iOS detects this and re-opens that page.
I have a sample project in my Github page that implements all this.
And to finalize, after you install the sample project in your iPhone, open this stack overflow post from mobile Safari and open my awesome app
Once the app is opened, click on the button and you will return to this stack overflow post.
The behaviour you described is exactly what FB's AppLinks is designed for, and you'll get the same behaviour with all iOS apps that support it (which is quite a lot) out of box!
By the way Google Maps uses the same component: you can see it if you open Google Maps from let's say Fantastical.app!

Web service for deep linking

This is my first time create an ios application that required deep linking. I need to create a web service for my custom url scheme for ios in order to publish it online. Please give some pointer on regarding which web service i should use or is there an alternative way to create a deep linking for custom url scheme for iOS. Thanks.
You can do it yourself with any server platform - Rails, PHP, Dot.Net, etc.
Here is a very simple PHP snippet. Replace "myappname" with your app's URL scheme. The param/value query is optional - you can use any other text and parse it in your App Delegate's openUrl method.
if (strpos($_SERVER['HTTP_USER_AGENT'], 'iPhone OS') !== FALSE) {
// redirect
header("location: myappname://?key=value");
exit();
}
Client use-cases:
iOS Safari, your app installed - will open your app.
iOS Safari, your app not installed - Safari will complain that it cannot open the link.
Another iOS app, your app installed - will switch to your app.
Another iOS app, your app not installed - same as Safari. However, if the other app is implementing UIApplication's canOpenURL: - it may gracefully take the user to the App Store, but it's up to the other app developer.
Any other device or browser - will continue to render the page, where you can add your html including AppStore links.
If you don't want to create the server code, you can use a tool I created for this purpose. You have it here:
http://www.uppurl.com/
It's mainly a short link tool that checks for user device and give him the right url based on his devices. With this tool you don't need to write any server code and it also takes care of different devices, operating systems and browsers.
Take care of Tal answer as latest versions of Chrome has changed the way to open app and now you need to provide a link in different format, they use something like "intent://..."

Test if any app can handle a url scheme in mobile safari on iOS

Is there any way to test if an iOS can handle a custom URL scheme? I have an app that registered a custom url scheme to be able to open the app from a hyperlink in mobile safari. How ever, I'd like to tell the user they need to go to the appstore to download the app if they dont have it installed.
Is there a clever way to test a URL and catch when it fails and the reason for it to fail?
This is the best I can come up with, but it's only tested in iOS 5:
If your link in mobile safari is link text,
change it to: link text,
then at the top of the /launchapp page, put a hidden iframe with the desired URL: <iframe src="myapp://path" style="display:none"></iframe>. In the body of the page, put your message about needing to go to the appstore to download the app.
If the user has the app:
They will not see the launchapp page, they will be directed seemlessly to the app url.
If the user does not have the app:
They will get a nasty alert about 'The URL could not be loaded', click OK, then they will be looking at your 'you need to download the app' page.
Update - March 2013
Check out this comment on a related SO answer. Apparently, if your app isn't already installed, you can seamlessly redirect to your app in the App Store using an approach like this (not tested):
// setup fallback (redirect to App Store)
var start = (new Date()).valueOf();
setTimeout(function() {
var end = (new Date()).valueOf();
// prevent App Store redirect upon returning to safari from myapp
if (end - start > 1000) return;
// get a seamless redirect if you use itms://... instead of http://itunes.com/...
window.location = 'itms://my/app/URI';
}, 25);
// Attempt to load my custom url scheme
window.location = 'myapp://my/path'

Resources