Check if URL can be opened from an iOS today widget - ios

My problem is that I can't find out if a certain URL can be opened from an iOS widget.
The method canOpenURL: is not available on today's widget because there is no UIApplication class.
Moreover the method openURL: of NSExtensionContext returns YES for the boolean "success", even with an invalid URL.
The code below enters the else condition (success BOOL is always YES) but in the same time the simulator shows a popup error, as you can seen in the attached image.
NSURL* invalidURL = [NSURL URLWithString:#"fake://blablabla"];
[self.extensionContext openURL:invalidURL completionHandler:^(BOOL success) {
if (success == NO) {
DDLogWarn(#"Can't open URL: %#", invalidURL);
}
else{
DDLogInfo(#"Successfully opened URL: %#",invalidURL);
}
}];

It's a known bug. I filed this issue with Apple last year (rdar://18107612) when iOS 8.0b5 was current, and it's still an open issue.
File your own bug with Apple at http://bugreport.apple.com and hope for the best.

You can get to the shared UIApplication instance by using performSelector:, something like
UIApplication *sharedApplication = [[UIApplication class] performSelector:NSSelectorFromString(#"sharedApplication")];

Related

How to get last seen page analytics?

Some users of my app are unexpectedly quitting the app before they complete what they should do. I am suspecting if the app gets frozen. How to check which page user saw at last? I would like to save that data to firebase to analyze.
Simply I want to know where users are quitting the app.
There isn't any default method which you can use in this case. This is what you can do, in your every view controller's viewDidAppear save that screen's name in UserDefaults overwriting the previous value. Now on every app launch, check if the app was crashed last time it was opened, if it was crashed then get the screen name from UserDefaults and save it to firebase.
If you don't know how to detect an app crash, use this link.
https://stackoverflow.com/a/37220742/1811810
Apple offer that NSSetUncaughtExceptionHandler to listen whether the app is crash,
so ,you can when app launch use this listener to save the crash log,
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
//get crash report
[self installUncaughtExceptionHandler];
}
and the implementation is:
- (void)installUncaughtExceptionHandler {
NSSetUncaughtExceptionHandler(&UncaughtExceptionHandler);
}
void UncaughtExceptionHandler(NSException *exception) {
NSArray *arr = [exception callStackSymbols];
NSString *reason = [exception reason];
NSString *name = [exception name];
NSString *currentVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleVersion"];
NSString *urlStr = [NSString stringWithFormat:#"mailto://developer#googlemail.com?subject=CrashReport&body=YourSuggest!<br><br><br>""errorInfo(%#):<br>%#<br>-----------------------<br>%#<br>---------------------<br>%#",currentVersion,name,reason,[arr componentsJoinedByString:#"<br>"]];
NSURL *url = [NSURL URLWithString:[urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
[[UIApplication sharedApplication]openURL:url];
}
And suggest that you can send email to tell developer,if the customer allow that.
I think this error info can contain the crash page's info,hope this can help you.

Alternate Icon - Disable alert on icon change (UPDATE)

This is an update to this outdates question: Alternate Icon in iOS 10.3: avoid notification dialog for icon change
func setAppIcon(Type: String) {
if #available(iOS 10.3, *) {
UIApplication.shared.setAlternateIconName(Type)
}
}
With the few lines above it is possible to change the Appicon dynamically, the feature was added with iOS 10.3.
The code above is working fine but every time the app icon changed iOS triggers an alert like this:
So is there a way to get rid of this alert? (I know that apple could reject application for disabling user-information but I'd like to use it just for test purposes)
Any help would be SUPER appreciated, thanks! :-)
Try the following code, however, it is written with Objective-c. It makes use of private method, I guess that you will never mind.
- (void)lc_setAlternateIconName:(NSString*)iconName
{
//anti apple private method call analyse
if ([[UIApplication sharedApplication] respondsToSelector:#selector(supportsAlternateIcons)] &&
[[UIApplication sharedApplication] supportsAlternateIcons])
{
NSMutableString *selectorString = [[NSMutableString alloc] initWithCapacity:40];
[selectorString appendString:#"_setAlternate"];
[selectorString appendString:#"IconName:"];
[selectorString appendString:#"completionHandler:"];
SEL selector = NSSelectorFromString(selectorString);
IMP imp = [[UIApplication sharedApplication] methodForSelector:selector];
void (*func)(id, SEL, id, id) = (void *)imp;
if (func)
{
func([UIApplication sharedApplication], selector, iconName, ^(NSError * _Nullable error) {});
}
}
}

Handle http url with my app (if installed)

I would like to handle a url like: http://www.something.com/product/3/ in this way:
If you have the App installed => handle it with the App.
Else => handle it with safari.
Is it this possible?
The idea is I can send that url via email and if the receiver has the app installed, then the app raises up and do something, and if not installed just open it via safari.
I know about the custom schemes, which work fine in the app but they obviously don't work in safari because they are not http protocol.
1) Create a custom URL scheme in your app. Follow the link if you don't know how to create a custom URL scheme: http://www.idev101.com/code/Objective-C/custom_url_schemes.html
2) Then add following script to the URLs that you want your app to open.
<script language="javascript" type="text/javascript">
var iOS = (navigator.userAgent.match(/(iPad|iPhone|iPod)/g) ? true : false);
var appUrlScheme = "myappscheme://" + document.URL;
if (iOS) {
window.open(appUrlScheme, "_self");
}
</script>
The script is self explanatory, if its an iOS device then it simply tries to open current url with your custom url scheme i.e. myappscheme://whateverurl.com'. If you app is installed on the device then iOS is going to launch your app and pass this URL tohandleOpenURLfunction, otherwise mobile safari will silently ignorewindow.open` call and your webpage will load as normal:
3) Implement handleOpenURL callback method in your AppDelegate to handle the URL:
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
NSLog(#"url: %#", url);
NSLog(#"query string: %#", [url query]);
NSLog(#"host: %#", [url host]);
NSLog(#"url path: %#", [url path]);
//REDIRECT USER TO A VIEW CONTROLLER
return YES;
}
Yes you can do by using URL Scheme,
First create your own custom url scheme in Info.plist file
Add a new row by going to the menu and clicking Editor > Add Item. Set up a URL Types item by adding a new item. Expand the URL Types key, expand Item 0, and add a new item, “URL schemes”. Fill in “readtext” for Item 0 of “URL schemes” and your company identifier for the “URL Identifier”.
Then parse your url for different url for same url scheme to open your app with different screens.. Here i have shown only alert, use to open your specific page as per your link.. And if the app is not installed then it will open in web browser.
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
// Display text
UIAlertView *alertView;
NSString *text = [[url host] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
alertView = [[UIAlertView alloc] initWithTitle:#"Text" message:text delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
[alertView release];
return YES;
}
application:handleOpenURL: (used for earlier iOS 4.1)
application:openURL:sourceApplication:annotation: (used for later iOS 4.1).
No that is not possible.
However you can include in the mail both links:
To open it in safari as normal website: http://www.somewebsite.com
To open the app using custom urls: yourapp://

Dropbox Error Opens Another App on device, not on simulator

I have three apps that upload files to Dropbox. Same code for all three. They all share the same folder so I've used same secret key etc
Heres where it gets weird
1. All was fine for few months
2. Now on apps 2 and 3 when the user tries to log in it opens the first app?
3. Logging out and in, no help, just says Theres a an error connecting to Dropbox and to try later
What ive tried
Creating seprate secret keys etc for all three apps rathe rthan sharing the same, still get the same behaviour?
Some research on this suggested that Dropbox has changed the way it links to users accounts through applications and remins linked even if you delete the app? Has anyone else got any experience with this?
Appdelegate
NSString* appKey = #"00000000000";
NSString* appSecret = #"0000000000";
NSString *root = kDBRootAppFolder;
DBSession* session =
[[DBSession alloc] initWithAppKey:appKey appSecret:appSecret root:root];
session.delegate = self; // DBSessionDelegate methods allow you to handle re-authenticating
[DBSession setSharedSession:session];
[DBRequest setNetworkRequestDelegate:self];
Button Handler in viewController
LogCmd();
self.publishButtonPressed = YES;
if (![[DBSession sharedSession] isLinked]) {
[self loginLogoutButtonPressed:nil];
} else {
DBRestClient *restClient = [[DBRestClient alloc] initWithSession:[DBSession sharedSession]];
restClient.delegate = self;
NSError *error = nil;
NSString *filePath = [ICUtils pathForDocument:self.fileName];
[self.pdfData writeToFile:filePath options:0 error:&error];
if (nil == error) {
[restClient uploadFile:self.fileName
toPath:#"/"
withParentRev:nil
fromPath:filePath];
} else {
[ICUtils raiseAlertWithTitle:#"An error occurred" message:[error localizedDescription]];
}
}
}
Note works ok on the simulator, problem is only present on device
That is because you are using the same key for multiple applications. The Dropbox app is communicating with your app through a custom URL scheme - to launch your app (because there is no other way to launch apps programmatically on iOS).
In other words, the Dropbox app tells the system to open "db-yoursecretkey://somemessage" which opens the registered app for that custom URL scheme. Unfortunately, all of your apps use the same custom scheme as they are all using the same key, so the system just picks one: most likely the first one.
However, you can grant your apps access to all folders in dropbox, thus effectively sharing folders. So it's not really necessary to have all three apps using the same key.

Can you download a passbook coupon from a UIWebView inside an IOS app?

Is it possible to download and add a passbook from within a webview without modifying the app to support the new MIME type or unknown MIME types like Sparrow did?
I have a news ios app with a webview. In the webview I display the news items and a banner. When you click the banner I want to open a url to a .pkpass file and add it to my passbook. Instead I get a FrameLoadInterrupted error and nothing visible happens.
If I open the url from safari this works fine, chrome, since earlier this week (version 23) also opens the url like intended.
Is this some weird strategy from Apple maybe? not allowing this MIME type to properly open from a webview?
My best bet is that the UIWebView is just not capable of handling the Passbook passes. You could however try and catch the downloading in the UIWebViewDelegate method -(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType.
What I mean to say is that you have to handle this part your self, since the http://passkit.com/samples/ I used does not return an url which ends pkpass thus it is totally depended on how you request the passbook files.
If you do in include the .pkpass extension you can check for the extension in the request.
If you know what kind of URL the passbook file is at you write your own download code here and pass it to passbook via the passbook api.
There does not seem to be any great on fix for this, you could load the failed ULR in safari:
- (void) webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
NSLog(#"Webview: %#", error);
if ([error.domain isEqualToString:#"WebKitErrorDomain"] && error.code == 102) {
NSString *failedURL = [error.userInfo objectForKey:NSURLErrorFailingURLStringErrorKey];
if (failedURL == nil) {
return;
}
NSURL *url = [NSURL URLWithString:failedURL];
[[UIApplication sharedApplication] openURL:url];
}
}
But this is just really bad coding.
Okay, talked to the engineers at WWDC and this is a know bug in UIWebView but Apple probably won't fix it because they're encouraging people to adopt the new SFSafariViewController. We did come up with a hack to fix it should you need to support iOS 6-8:
Add the PassKit framework to the project if it isn't already.
#import <PassKit/PassKit.h>
Set up a delegate for the UIWebView (for example the view controller launching the UIWebView)
<UIWebViewDelegate>
Add a class variable to cache the UIWebView requests
NSURLRequest *_lastRequest;
Set the delegate
self.webView.delegate = self;
Add the callback to grab all requests and cache in case of failure
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
_lastRequest = request;
return YES;
}
Add the failure callback and re-fetch the URL to see if it is a pass and if so, present the pass to the user
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
// try to get headers in case of passbook pass
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:_lastRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
// check for PKPass
if ([response.MIMEType isEqualToString:#"application/vnd.apple.pkpass"]) {
NSError *error;
PKPass *pass = [[PKPass alloc] initWithData:data error:&error];
if (error) {
NSLog(#"Error: %#", error);
} else {
PKAddPassesViewController *apvc = [[PKAddPassesViewController alloc] initWithPass:pass];
[self presentViewController:apvc animated:YES completion:nil];
}
}
}];
}
It's a horrible hack for what should be supported, but it works regardless of the extension and should support re-directs. If you want to pile on the bug train, you can reference radar://21314226

Resources