iOS 9 Universal Links - no parameters recieved - objective-c

Using the new iOS 9 feature - Universal links, from my understanding, is supposed top open my app whenever a specific domain is opened in browser (or other apps?). I have gone through the documentation and through this guide.
However, when the app opens I do not receive the parameter that is meant to help me open the correct page for the user to view....
I would share the code I'm using, but it's quite a big infrastructure and not really a couple of lines of code (server side JSON, plist rows and some IDs on the developer portal).
Anyone encountered it and could give me a hand here, please?

The Branch guide you linked to (full disclosure: I work with the Branch team) unfortunately doesn't cover a rather important step: what to do after your app opens. Which is exactly the issue you're encountering :). But the good news is you've already done the hard part with all the server and entitlement config.
What you need to complete the loop is a continueUserActivity handler in your AppDelegate.m file. This will pass you a webpageURL property containing the actual URL of the Universal Link that opened your app, which you can then parse and use for routing. It'll look something like this:
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *))restorationHandler {
if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
NSString *myUrl = [userActivity.webpageURL absoluteString];
// parse URL string or access query params
}
return YES;
}
Also, when testing keep in mind that Universal Links unfortunately don't work everywhere yet:
P.S., gotta ask...since you found the Branch blog already, had you considered using the service to handle the link routing for you? It can definitely help simplify things!

Related

Fetch preferences / permissions for NSUserNotificationCenter

I'm currently trying to set up a notification service using the old NSUserNotification API on macOSX (not iOS) in objective c++.
The catch is that I would need to know the system preferences associated with notifications that the user set (and preferably during the rest of runtime too) to determine if my notifications are actually seen. The application is meant to get the users attention in some way to guarantee things like reminders don't go unnoticed, so the thing I want to avoid is a notification delivered that silently doesn't produce any sound nor image on the screen whatsoever without the application knowing. If the notification is not allowed the application should handle it in some other way.
I am working on a macOS 10.14 Mojave machine. My overall code with NSUserNotificationCenter works fine with a custom Delegate that implements shouldDeliver, didActivate etc. Its just that I am currently assuming the user didn't press the "frick off" button on my notifications.
Unfortunately, NSUserNotificationCenter and related classes don't appear to feature a function that determines the permissions the way the new API (UNUserNotificationCenter) does. I have been trying to resolve this for a few hours so I was wondering if anyone else has found a solution to this. I can't really use the new API since this is strictly for back-compatibility in favour of the possibly rather prevalent amount of people who don't use mojave yet.
Things I have tried to make it work so far:
Try and see if there is a .plist somewhere where these settings are stored, in the User-specific Library folder as well as the general Library folder.
Try and find said .plist using CFPreferencesCopyKeyList and related methods. Apparently I don't really know the right domain name.
This answer from 2012 which appears to be outdated since I cannot locate the database on my machine anymore. Might be due to me using a mojave machine, or the file has since moved somewhere else.
Try and determine whether we can retro-actively check our permissions by checking a NSUserNotification's "presented" property on didDeliverNotification, shouldPresentNotification in the delegate. Unfortunately as stated in NSUserNotificationCenter itself it still behaves the same whether the preferences allow notifications or not.
Some code I tried includes the following:
CFPreferencesCopyKeyList((CFStringRef) #"com.apple.systemPreferences.plist", kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
CFPreferencesCopyKeyList((CFStringRef) #"com.apple.systemPreferences.plist", kCFPreferencesAnyUser, kCFPreferencesAnyHost);
//Unfortunately returns null with any combination of com.apple.notificationcenter.plist, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost
CFPreferencesCopyKeyList((CFStringRef) #"com.apple.notificationcenter", kCFPreferencesAnyUser, kCFPreferencesAnyHost);
On my system (only macOS High Sierra), it looks like the file you're after is:
~/Library/Preferences/com.apple.ncprefs.plist
That would correspond to a combination of kCFPreferencesCurrentUser, and kCFPreferencesAnyHost. (For the record, kCFPreferencesCurrentUser + kCFPreferencesCurrentHost equates to pref files in the ~/Library/Preferences/ByHost/ folder).
I don't think any settings would be stored in the local domain (by that I mean in the root /Library/Preferences/ folder).
I was able to get a list of apps and settings using the following code:
[[NSUserDefaults standardUserDefaults] addSuiteNamed:#"com.apple.ncprefs"];
NSArray *apps = [[NSUserDefaults standardUserDefaults] objectForKey:#"apps"];
NSLog(#"[%# %#] apps == %#", NSStringFromClass([self class]), NSStringFromSelector(_cmd), apps);
NOTE: this will not work if your app is sanboxed, as access to other pref files like that will be denied.

deepLinkHandler vs continueUserActivity

For architectural reasons, I would prefer not to use the deeplink handler in the appdelegate to redirect the app upon entry.
Assuming I do not care about the initial install deeplink for now, can i do this?
- (BOOL)application:(UIApplication *)application
continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void (^)(NSArray *))restorationHandler {
BOOL handledByBranch = [[Branch getInstance] continueUserActivity:userActivity];
if (handledByBranch) {
// REDIRECT APP TO WHERE I NEED
}
return handledByBranch;
}
Alex from Branch here: this may work in theory, but it is likely not the best approach and will miss some edge cases. While Branch uses Universal Links (the continueUserActivity method) wherever possible, there are still some situations in which your app would be launched via a URI scheme (the openURL method). You will need to handle both separately, and may run into some situations with undesirable side-effects.
You might find our instructions on how to build a custom deep link router useful.

Documentation for Xcode Source Editor Extension

I'm looking for some documentation of the new Xcode Source Editor Extensions in Xcode 8.
As far as I can see there is only the "documentation" found in the header file for XcodeKit. Would be great to get something that's more detailed and more official.
Very preliminary XcodeKit reference documentation is now available.
Our WWDC 2016 presentation introducing Xcode Source Editor Extensions remains the best walkthrough.
The very shortest version, however, is: Because App Extensions need to be embedded in an application, you need to first create a new macOS Cocoa Application, and then add a new Xcode Source Editor Extension to that application. Then the XcodeKit reference should help some in implementing that.
Not really a documentation but a good reference also
https://developer.apple.com/videos/play/wwdc2016/414/
Extensions, at the moment, are poorly documented. There are a lot of assumptions made (for example, did you know that you can execute the container app? Yup, it’s really nice for settings GUI - see this How To Execute Container App - Second Answer)
At the moment, there are a lot of things missing: for example, there isn’t a structure that shows the corresponding lines with the data object - though this is quickly created with the following code:
var matches: [NSTextCheckingResult] = []
do {
let regex = try NSRegularExpression(pattern: "\n", options: [])
matches = regex.matches(in: completeBuffer,
options: [],
range: NSMakeRange(0, completeBuffer.count))
}
catch {
}
This gives you the location of all the \n’s - you should be able to fill out the rest to give you starting and ending positions which should match up to the lines.
All in all, there is a lot to like about the extension, but there are quite a few things missing as well.
Currently the only available documentation is in the headers; there's nothing "unofficial" about them. If you have specific questions, please ask.

Stumped that [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil] keeps returning nil

I have written a simple test application (Mac/Cocoa) to connect to the Key-Value store in iCloud. I seem to be falling at the first fence.
You can see the code for my simple app here and you'll see that I am not getting a URL back from "URLForUbiquityContainerIdentifier" call.
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
NSURL *ubiq = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
if (!ubiq)
NSLog(#"No iCloud");
else
NSLog(#"Yes iCloud");
}
Screenshot of the app running with output:
In term of trying to see if I've everything setup correctly I looked at the setup of my App Id (Test app is called Radio) in the developer provisioning centre:
So that seemed to be ok. Then I re-created my provisioning profile (called Radio Dev 2) and checked that it was using the correct App Id:
Ok, so that also seemed to be ok. I then checked that entitlements was switched on and Key-Value container was activated:
So they are activated. I then took a look at the file itself, just to see if anything jumped out:
So I couldn't see anything jumping out at me in the entitlements file. Lastly, I confirmed that I was signing the build with the correct cert using the provisioning profile that I expected:
I was happy enough with that.
So I'm stumped. I've regenerated everything, etc, to no avail.
I'd really appreciate if anybody has any ideas. Have I missed anything?
Damien's comment on the question had the solution for me. For anyone using Xcode 4.5, you need to click the + below the Ubiquity Containers box. Until I did that, nothing worked.
I faced same problem. I noticed that in my device setting, I didnt logged in my icloud account. once done with adding my account iCloud. The issue was resolved.
In my case, I implement the Parts "Add Document Type" and "Add Exported UTI" of Section "Configuring your Project for iCloud". And, it will not keep return nil anymore.
http://www.raywenderlich.com/12816/icloud-and-uidocument-beyond-the-basics-part-3

iOS access to Safari Reader feature through UIWebView

I am using iOS 4.3 & was wondering if there is any way that I can access the Safari's "Reader" feature through which webpages are removed of ads & other riff raff & the content takes the center stage.
If one opens any article in Safari (on say Wikipedia website), then a "Reader" button appears on the URL bar. Clicking on it presents a new window presenting the content beautifully.
How can I leverage this this functionality in iOS through UIWebView ?
PS: I know there is something called Readability Project. But I have no idea how to use this through UIWebView. Also for some websites Safari's Reader takes a call not to enable "Reader" feature, maybe it has no sufficient confidence?
Important: THIS ANSWER NO LONGER WORKS!
Readability shut down on September 30, 2016.
Here is something they recommend as a replacement:
https://mercury.postlight.com/web-parser/
Keeping the answer as a historical reference
--- Original answer ---
You can use Readability mobilizer for this. You will get a cleaned up version of any article, in the Readability styling:
http://www.readability.com/m?url=http://{URLOFTHEARTICLE}
Just prepare the URL and load it in your UIWebView. Here is how it looks in action:
http://www.readability.com/m?url=http%3A%2F%2Fwww.cnn.com%2F2013%2F01%2F11%2Fshowbiz%2Ftv%2Fgolden-globes-tv-vineyard%2Findex.html%3Fhpt%3Dhp_abar
Apple is making a pretty big deal about the inclusion of "Reader" in iOS 5. I'm assuming by the noise it's not available in 4.3
re: How to use through UIWebView
I can't find any mention of it in the Web Content Guide.
There's nothing about it in the UIWebView class reference.
And there's nothing in QA1630.
Dont parse HTML natively on iOS, I have done it before and its a messy business. Either create your own web service to do all the nasty work or look into using readability (readability.com) they provide an API.
There is also an open source ruby, python and php readability port that you can find here
https://github.com/iterationlabs/ruby-readability
https://github.com/gfxmonk/python-readability
http://code.fivefilters.org/p/php-readability/source/tree/master/
For you ruby enthusiasts, readability is also available as a gem, just google it.
Actually reader button do a bit of analysis where it parse the HTML Page and then it sees a clear body tag to parse. If that plugin is able to extract the exact body it will enable the reader button (My understanding from the readability source code). Now to implement the same for webview you just need to embed java script in your code (this java script is already available in the readability source code) and then you can achieve the same effect.
But I suspect the future plan from apple for the same. Because they can not just let anyone else do this content extraction with the huge business opportunity associated with iCloud with the combination of readability.
If you want you can simple extract the HTML from UIWebView and then extract the body and use it for your purpose. It's not a very rocket science to extract.
For analysis point of view, just have randomly some 10 HTML pages with Reader button enabled, you will see the core cotent belongs to body only and rest of the add, header, footer are separated.
I believe this is the time to re-invent the web content we use, and this is the perfect example of doing the same.
You can even do this by injecting javascript.
#define readJS #"(function(){window.baseUrl='https://www.readability.com';window.readabilityToken='';var s=document.createElement('script');s.setAttribute('type','text/javascript');s.setAttribute('charset','UTF-8');s.setAttribute('src',baseUrl+'/bookmarklet/read.js');document.documentElement.appendChild(s);})()"
And then when your webpage finishes loading
- (void)webViewDidFinishLoad:(UIWebView *)webview
{
[webview stringByEvaluatingJavaScriptFromString:readJS];
You can do it in iOS9.
first import SafariServices:
#import <SafariServices/SafariServices.h>
Afterwards we are instantiating SFSafariViewController and adding it as a subview. We have two options doing so:
Creating with only base URL
Creating with bas URL as well as entering 'Reading Mode' in case it is available
NSString *sURL = #"http://google.com";
NSURL *URL = [NSURL URLWithString:sURL];
SFSafariViewController *safari = [[SFSafariViewController alloc] initWithURL:URL]; // 1.
SFSafariViewController *safari = [[SFSafariViewController alloc] initWithURL:URL entersReaderIfAvailable:YES]; // 2.
[self presentViewController:safari animated:YES completion:nil];