iOS 9 safari iframe src with custom url scheme not working - ios

I use this solution https://gist.github.com/davidwkeith/2662899 to redirect from web page into my app if app installed.
But it doesn't work on iOS 9. It's still working in Google-chrome. But iframe with custom URL scheme can't launch the application from Safari.
If I replace
document.getElementById('loader').src = 'custom-protocol://my-app'
(where loader is iframe) with
window.location = 'custom-protocol://my-app'
it will work.
os: iOS 9 beta4 and beta5
Anybody knows this problem? Is it iOS 9 beta bug? Or it will not be fixed?

The previous answer is a partial implementation of Universal Links that is missing critical details and doesn't include a fallback to the App Store.
First, you can no longer set iframe src in order to trigger a URI scheme. You've correctly identified that issue. As you noted, you can, however, still set window.location = 'custom-protocol://my-app';. So if you know that a user has your app because you've previously opened their app from the browser and have a cookie stored that can be looked up on your backend, you can still safely fire custom-protocol://.
Second, you can detect the user agent string using navigator.userAgent. Pre-iOS 9 you can still use the iframe to fire a URI scheme, then fallback after a timeout. On iOS 9, you can choose whether to fire the URI scheme or not based on cookies, then take the user to the App Store. I work on this at Branch and making use of cookies to recall whether a user likely has the app is something we've implemented. Feel free to reach out if you have more questions about that, or make use of our solution directly.
Implementing Universal Links is not quite as simple as the other answer describes. In reality, there is considerably more complexity. Here's a complete list of steps (I've helped several apps integrate in recent weeks using these steps):
1. Configure your app to register approved domains
i. Registered your app at developer.apple.com if you haven't
ii. Enable ‘Associated Domains’ on your app identifier on developer.apple.com
iii. Enable ‘Associated Domain’ on in your Xcode project
iv. Add the proper domain entitlement, applinks:yourdomain.com, in your app
2. Configure your website to host the ‘apple-app-site-association’ file
i. Buy a domain name or pick from your existing
ii. Acquire SSL certification for the domain name (you can use CloudFlare for this!)
iii. Create structured ‘apple-app-site-association’ JSON file
{
"applinks": {
"apps": [ ],
"details": {
"TEAM-IDENTIFIER.YOUR.BUNDLE.IDENTIFIER": {
"paths": [
"*"
]
}
}
}
}
iv. Sign the JSON file with the SSL certification
v. Configure the file server
The apple-app-site-association file:
- must be sent with the header ‘application/pkcs7-mime’
- must be sent from the endpoint youdomain.com/apple-app-site-association
- must return a 200 http code.
Example Express+Node:
var aasa = fs.readFileSync(__dirname + '/static/apple-app-site-association');
app.get('/apple-app-site-association', function(req, res, next) {
res.set('Content-Type', 'application/pkcs7-mime');
res.status(200).send(aasa);
});
credit: borrowed liberally from this blog post

Yes with iOS9 now you can deep link. Check the link for detailed explanation but I laid out the basics.
http://blog.hokolinks.com/how-to-implement-apple-universal-links-on-ios-9/
first you must go to your target and click capabilities. Add the associated domain.
Next you must upload apple-app-site-association file.
Basically open a JSON editor and construct something like this
{
"applinks": {
"apps": [],
"details": {
"TBEJCS6FFP.com.domain.App": {
"paths":[ "*" ]
}
}
}
}
Next you must support Univeral links in your app. You need to implement
extension AppDelegate {
func application(application: UIApplication, continueUserActivity userActivity: NSUserActivity, restorationHandler: ([AnyObject]?) -> Void) -> Bool {
if userActivity.activityType == NSUserActivityTypeBrowsingWeb {
let webpageURL = userActivity.webpageURL! // Always exists
if !handleUniversalLink(URL: webpageURL) {
UIApplication.sharedApplication().openURL(webpageURL)
}
}
return true
}
private func handleUniversalLink(URL url: NSURL) -> Bool {
if let components = NSURLComponents(URL: url, resolvingAgainstBaseURL: true), let host = components.host, let pathComponents = components.path?.pathComponents {
switch host {
case "domain.com":
if pathComponents.count >= 4 {
switch (pathComponents[0], pathComponents[1], pathComponents[2], pathComponents[3]) {
case ("/", "path", "to", let something):
if validateSomething(something) {
presentSomethingViewController(something)
return true
}
default:
return false
}

Related

Universal linking with website by using swift

I using Universal-linking in my application, For that, I make the following apple-app-site-association file, like with the following code.
I place this file into server please find here:- http://dev.2ULaundry.com/apple-app-site-association
I add Domain URL into under associated domain into Capabilities of project file also.
But my Universal linking is not working anyone please help to me.
"applinks": {
"apps": [],
"details": [
{
"appID": "D929J2.com.apple.wwdc",
"paths": [ "/scheduled-pickups/","/new-schedule/", "dashboard-1", "/refer-a-friend/", "/price-list/", "/give-a-gift/", "/locations", "2u-rewards/", "payments/"]
}
]
}
}
And add the following code into appdelegate file also for hadling.
func application(_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: #escaping ([Any]?) -> Void) -> Bool
{
if userActivity.activityType == NSUserActivityTypeBrowsingWeb {
let url = userActivity.webpageURL!
let userurl = url.absoluteString
if userurl.contains("/scheduled-pickups/")
{
let innerPage = UIStoryboard.loadPickupController()
innerPage.selectedfrom = SelectedFromVc.Deeplinking.rawValue
let nav = UINavigationController.init(rootViewController: innerPage)
nav.isNavigationBarHidden = true
self.window?.rootViewController = nav
}
}
You should check is your domain valid for using universal links. You could do it here: https://branch.io/resources/aasa-validator/.
I've checked your domain http://dev.2ulaundry.com there and there are some issues with your domain. Fix them and it should be fine.
Before the implementation, you have to verify your website domain with this AASA validator https://branch.io/resources/aasa-validator/. It has few warnings so please run the validator and fix the issue.
As per you shared, the domain starts with "http" but
Universal link only serves "https"
and also it would be good if you are placing the AASA file in the below path
https://dev.2ulaundry.com/.well-known/apple-app-site-association
Since I spent lot of times on Universal link & App links, my
recommendation is to use Firebase dynamic link for free or
Branch.io before wasting your time.Universal link will create lot of issues
like you can't check the app availability(app installed or not) and
you can't passing the parameter via Apple/Play store.

run iOS app from web (angular)

I'm developing an iOS app
i have a payment page designed by angular
user click on a payment button in ios app and i run a url page with few paramaters :
UIApplication.sharedApplication().openURL(NSURL(string:"http://www.testt.com/price/personId/packageName")!)
price is money user has entered in textfield and package-name id the schema name i should send it to web page that runs my app (return to app with running that string) i have declared in info.plist
then after been successful or unsuccessful payment. it should return to app by clicking on “return to app” button on web site.
actually angular runs the packageName i have sent with url like this way : http://packageName://
i tried to implement this by universal link like this way : packageName:// but wont open this link because of special chars in url.i used encoding method to encode chars but not successful because url removes the chars :// then i tried app site association method which i faced cannot parse app site association file
so i have few question for you :
1_is there any trick to run url with special chars ??
2_what would you do if you were me ??
3_i tried apple-app-site-association too but can not parse error which i have a question about this method how could this file opens my app? this way : applink:http://msite.com ?? because it contains spacial chars in it again
excuse my awful English at the end
talk to me before voting down
update :
var encodedChars="openMyApp" //schema name
encodedChars=encodedChars.addingPercentEncoding(withAllowedCharacters:CharacterSet.alphanumerics)!
let url="http://test.com/#/payment/\(id)/\(price!)/\(encodedChars)"
UIApplication.shared.openURL(NSURL(string: url)! as URL)
angular code :
if (this.accounting.packageName === 'openMyApp') {
this.url = this.accounting.packageName + '://';
} else {
this.url = 'http://' + this.accounting.packageName;
}
<a class="btn btn-default" title="" href="{{url}}"></a>
I have implemented custom url scheme for my app. If i write some thing like this
myappName://
in safari and press enter, my app is opened.
My angular + backend developer is using this line of code to open my app using custom url scheme. he is sending parameters along the scheme which i can read
this.document.location.href = 'myappname://appname.com/login?name='+id+'&id='+token;
Follow these 2 simple steps, i hope this will help you.
1.How to implement custom url schemes
Official docs
Helpful link
2.How to handle custom URL scheme with params in app delegate
handle the custom url and read params in appDelegate in this function
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
if let query = url.query {
//that's my logic. yours url may be different
let components = query.components(separatedBy: "=")
print(components)
}
}
let me know if you need any help

How to launch our app when we click a link in gmail

I am sending a link after signing up to the account from which user has signed up.
Now I want that when user click on that link from the corresponding account then he/she will move to app.
is there any way to launch our app from link clicked on gmail app?
Please help...
Thanks
You need to do perform Universal Linking.For universal linking you need to first create a “apple-app-site-association” file, which look like this :->
{
"applinks": {
"apps": [],
"details": {
"appID": "9JA89QQLNQ.com.apple.wwdc": {
"paths": [
"/wwdc/news/",
"/videos/wwdc/2015/*"
]
}
}
}
}
The applinks tag determines which apps are associated with the website. Leave the apps value as an empty array. Inside the details tag is an array of dictionaries for linking appIDs and URL paths.
The appID consists of your team ID combined with the app’s bundle ID.
After creating file you need to upload it on your domain like www.yourHost/apple-app-site-association.
now you need to go capabilities -> Associated Domains add here a link which you want to make universal i.e, which link you want to tappable to open your app.
Then you need to add this method in your app delegate class.
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([Any]?) -> Void) -> Bool {
//code here to related universal links
/* The activity type used when continuing from a web browsing session to either a web browser or a native app. Only activities of this type can be continued from a web browser to a native app.
*/
guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let url = userActivity.webpageURL,
let components = URLComponents(url: url, resolvingAgainstBaseURL: true) else {
return false
}
//do something
return true
}
Here is the use of URL scheme, you can add it to your application in the info plist file.
And use the below delegate to catch the response
func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool
You just asked for the details so this tutorial will help you

iMessaged-based invitations for GameCenter for iOS 10

I'm trying to update my app to work correctly with the new features of GameCenter in iOS10.
I create a new GKGameSession on device1, get a share URL, and all that works fine. I send the share URL out via a share sheet to device 2.
Device2 clicks the link, the device briefly displays 'Retrieving...' and then launches my app. Great! But, now what? Is there context information available for this URL that I can somehow access? Otherwise I have no way how to respond when the app is launched.
Previously you'd get a callback to something adhering to the GKLocalPlayerListener protocol, to the method player:didAcceptInvite:, and you could join the match that way. But with these iCloud-based messages, the player might not be even logged into GameCenter, right? This part seems to have been glossed over in the WWDC presentation.
Also, as of today (12/28/2016) there is no Apple documentation on these new methods.
Since the GKGameSessionEventListener callback session:didAddPlayer: only fires if the game is already running, to be sure you can process this callback every time requires a work around. I've tested this and it works.
When you send out an iMessage or email invite to the game, don't include the Game Session Invite URL directly in the message. Instead use a registered URL that will open your app when opened on a device on which your app is installed. Check here to see how:
Complete Tutorial on iOS Custom URL Schemes
But add a percent escaped encoding of the game invite URL as a parameter to this URL thusly (I'm assuming the registration of a url e.g. newGameRequest but it will be best to make this quite unique, or even better - though it requires more setup, try Universal Link Support as this will allow you to direct users who don't have your app installed to a webpage with a download link)
let openOverWordForPlayerChallenge = "newGameRequest://?token="
gameState.gameSession?.getShareURL { (url, error) in
guard error == nil else { return }
// No opponent so we need to issue an invite
let encodedChallengeURL = url!.absoluteString.addingPercentEncoding(withAllowedCharacters:.urlHostAllowed)
let nestedURLString = openOverWordForPlayerChallenge + encodedChallengeURL!
let nestedURL = URL(string: nestedURLString)!
}
send the URL in a message or email or WhatsApp or whatever. Then in your app delegate, add the following:
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
var success = false
if let queryString = url.query {
if let urlStringToken = queryString.removingPercentEncoding {
let token = "token="
let startIndex = urlStringToken.startIndex
let stringRange = startIndex..<urlStringToken.index(startIndex, offsetBy: token.characters.count)
let urlString = urlStringToken.replacingOccurrences(of: token, with: "", options: .literal, range: stringRange)
if let url = URL(string: urlString) {
if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
success = true
}
}
}
}
return success
}
Now you can be sure the session:didAddPlayer: will be called. What's the betting this workarround is good for about 2 weeks, and they fix this in the next release of iOS showcased at WWDC 2017 ! Update: this problem hasn't been fixed - so the workaround above remains good!
I agree, the lack of documentation is frustrating. From what I can see, we have to:
add <GKGameSessionEventListener> protocol in the class' header
Then session:didAddPlayer: fires on the joining player's device after accepting an invite link.
update:
Unfortunately, I'm not surprised to hear your results. I hadn't tried all of those scenarios, but GKTurnBasedMatch had similar shortcomings. The way I got around it there was: I added a list of player statuses to match data (invited, active, quit, etc). I gave the player a view of "pending invitations." When they opened that view, I would load all of their matches and display the entries where the player was in invited state. With GKGameSession, that should work too.
Or, it might be easier if you could maintain a local list of sessions that you are aware of. Whenever the game becomes active, pull the entire list of sessions from the server and look for a new entry. The new entry would have to be the match the player just accepted by clicking the share URL.

Determine if user has enabled application's Safari content blocker extension

I'm working on a Safari Content Blocking extension. I intend to show setup instructions if the extension is disabled and to show settings if it is conversely enabled. How can I determine if the extension is enabled by the user?
I've seen this method to detect if a custom keyboard is activated but there's no key on NSUserDefaults that relates to Safari Content Blockers.
As of iOS 10, there is a new method in SFContentBlockerManager to support this:
getStateOfContentBlocker(withIdentifier:completionHandler:)
And you call it like this (Swift 3):
SFContentBlockerManager.getStateOfContentBlocker(withIdentifier: "your.identifier.here", completionHandler: { (state, error) in
if let error = error {
// TODO: handle the error
}
if let state = state {
let contentBlockerIsEnabled = state.isEnabled
// TODO: do something with this value
}
})
You could utilize a SFSafariViewController to load a custom website. This website checks whether it is able to show something that your content blocker should block. Then redirect to the respective custom url (success/failure) that your app previously registered for. You could even use a hidden Safari View Controller without animation to avoid any distraction from the user's perspective (as shown here). (I guess this technique is used by former content blocker Peace)
Steps
App
Register custom URLs for success/failure
Register for notification callback using the NotificationCenter (e.g.
contentBlockerEnabled)
Use SFSafariViewController to show a custom website and include the following rule in blockerList.json:
{
"action": {
"type": "css-display-none",
"selector": ".blocked_selector"
},
"trigger": {
"url-filter": ".*"
}
}
Website
Check for blocked content:
if($('.blocked_selector').css('display') == "none") {
// Content blocker enabled
}
Redirect to custom URL (success/failure)
App
Post notification from
application:openURL:options: (success/failure based on called url)
Update 18/01
Following on from Tilo's hypothesis, I built the proposed solution. I wrote about what I learnt on Medium and you can grab the source files from GitHub.
TL;DR It works but only temperamentally due to the latency incurred of the content blocking rules database to update. A potential workaround is redirecting the test page to create an artificial delay.

Resources