Issues with custom Url Schemes - ios

I have an app lets say 2 apps - app1,app2
Lets say I open from app1 app2.
How can I send data back from app2 with custom url schemes,like in facebook app?
If I reopen app1 with url,I will see go back to app2,and its not good idea for my case.
I want to open app2 like presented modally,and dismiss it with returned data.Is it possible?

You can define custom URL Schema for your app from info.plist file. you can check existing Thread on SO for this.
In your case,for example In app1- define custom url myAppOneScheme and in your app2, Define custom url myAppTwoScheme.
When you open app2 from app 1, Pass app1's url like this:
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"myAppOneScheme://test?callerURL= myAppOneScheme"]];
from app2, handle openURL method:
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
NSLog(#"url recieved: %#", url);
NSLog(#"query string: %#", [url query]);
NSLog(#"host: %#", [url host]);
NSLog(#"url path: %#", [url path]);
NSDictionary *dict = [self parseQueryString:[url query]];
NSLog(#"query dict: %#", dict);
// NSString callerurl = parse callerURL from query
// store callerurl in user default or global variable.
return YES;
}
when you are done with operations in app2 and want to go back to app1, open caller url
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithFormat:"%#//testback?response=%#", caller url , datayouwanttoSendback]]];
Now, in your app1, again handle open url method and parse response

Related

Open Setting app in iOS 10.1 via custom application

I am trying to open the Settings app from my own iOS app.
My code is in Objective-C.
UIApplication *app=[UIApplication sharedApplication];
NSURL *url=[NSURL URLWithString:UIApplicationOpenSettingsURLString];
NSDictionary *dict=[[NSDictionary alloc] initWithObjectsAndKeys:[[NSNumber alloc] initWithBool:YES],UIApplicationOpenURLOptionUniversalLinksOnly, nil];
[app openURL:url options:dict completionHandler:^(BOOL success) {
NSLog(#"in open Url");
}];
This open URL method is the new method given by Apple. What should I pass in the options dictionary?
As you can see in iOS SDK:
Options are specified in the section below for openURL options. An empty options dictionary will result in the same behavior as the older openURL call, aside from the fact that this is asynchronous and calls the completion handler rather than returning a result. The completion handler is called on the main queue.
So you can just send nil to get behaviour of old openURL: method.
Try this to open settings app
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]]

Using openScheme with POST

I have an iOS app that need to switch to Safari and open a link using a POST command. Since iOS 10 we are supposed to use 'openScheme', but where do a specify http method POST?
This is where I am so far;
- (void)openScheme:(NSString *)scheme
options:(NSDictionary *)options
{
UIApplication *application = [UIApplication sharedApplication];
NSURL *URL = [NSURL URLWithString:scheme];
[application openURL:URL options:options completionHandler:^(BOOL success) {
if (success) {
NSLog(#"Opened %#",scheme);
}
}];
}
You can achieve it by adding URLSchemes.
Below is the steps.
(i) Open your info tab in targets.
(ii) At the below, you will find URL Types option.
(iii)Fill data as shown in below image.
(iv) Now, open your Safari browser in iPhone/iPad, type YourApp:// and enter. This should open your app.
Hope this helps!

canOpenUrl fails, but openUrl succeed

I am facing a strange problem.
I am using xcode 7.2, iOS 9, working on real device iphone 4S (not simulator).
I have 2 apps, app1 and app2. app1 is supposed to send data to app2 using an url scheme.
app2 has well declared the scheme
app1 has referenced the scheme in plist (as it is required in iOS9)
<key>LSApplicationQueriesSchemes</key>
<array>
<array>
<string>OpenLinkMyData</string>
</array>
</array>
Here is the code i use :
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0) , ^{
// build the url, using the scheme name, and the data
// note that json is escaped from bad url chars.
NSString * MyJsonDataWellEscaped = [[SomeClass getJSonDataToExport] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL * url = [NSURL URLWithString:[NSString stringWithFormat:#"OpenLinkMyData://%#",MyJsonDataWellEscaped]];
// Next line should allow test if the app able to manage that scheme is installed.
// BUT in our case, this allways returning false.
bool can = [[UIApplication sharedApplication] canOpenURL:url];
NSLog(#"canOpenUrl = %#", can?#"true":#"false");
});
// code of the app that do stuff...
}
I get back the following logs :
-canOpenURL: failed for URL: "OpenLinkMyData://(myJsonSuff)" - error: "This app is not allowed to query for scheme OpenLinkMyData"
canOpenUrl = false
But if i use the following code :
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0) , ^{
// build the url, using the scheme name, and the data
// not that json is escaped from bad url chars.
NSString * MyJsonDataWellEscaped = [[Move2MyMHelper getJSonDataToExport] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL * url = [NSURL URLWithString:[NSString stringWithFormat:#"OpenLinkMyData://%#",MyJsonDataWellEscaped]];
if([[UIApplication sharedApplication] openURL:url])
{
NSLog(#"App launched OK");
}
else
{
NSLog(#"App not launched");
}
});
// code of the app that do stuff...
}
If I don't check if scheme is available and I use it directly, App2 is well opened and get all data as required.
(else if the app2 is not installed, i get the "App not launched" log).
here is the App2 source for receiving the data (which works as awaited) :
-(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
NSString *prefixToRemove = #"OpenLinkMyData://";
if(url != nil && [[url absoluteString] hasPrefix:prefixToRemove])
{
NSString * urlStr = [url absoluteString];
NSString * json = [urlStr substringFromIndex:[prefixToRemove length]];
json = [json stringByRemovingPercentEncoding];
NSLog(#"OpenLinkMyData with json : %#", json);
}
return YES;
}
What is the problem with canOpenUrl in my case ?
Thanks for any help.
Making LSApplicationQueriesSchemes be an array of strings instead of an array of arrays of strings:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>OpenLinkMyData</string>
</array>
A side note regarding this topic...
There is a 50 request limit for protocols that are not registered.
In this discussion apple mention that for a specific version of an app you can only query the canOpenUrl a limited number of times and will fail after 50 calls for undeclared schemes. I've also seen that if the protocol is added once you have entered this failing state it will still fail.
Be aware of this, could be useful to someone.

how to manage sqlite database lite version to paid version in iphone application?

I created iPhone application using sqlite db. In this sqlite db I have stories document, directory images url, and other parameters. When the user is using the "lite" version of the app, everything works fine. But when I upgrade app from "lite" version to a "paid" version, I'd like to be able to copy database and latest files in my Documents directory to the "paid" app. Assistance would be appreciated.
If the data base in Documents directory then while updating your app iOS keep the old database.
Because of app sandboxing, you can't do precisely what you want, but there are a number of approaches:
Instead of separate pro version, only have a single version of the app, which offers an In App Purchase that enables certain features. See Convert the "lite app" to "pro app" within the application. Thus, this eliminates the need to import files/settings from one app to another.
You could implement separate custom URL scheme for both free and pro versions of your app to allow them to exchange limited amount of data via a URL. Thus, the pro app would probably, on the first time it's run, check to see if the lite app is installed, and if so, request data from it:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
BOOL runAlready = [userDefaults boolForKey:kRunAlready];
if (!runAlready)
{
[userDefaults setBool:YES forKey:kRunAlready];
[userDefaults synchronize];
[self importDataFromLite];
}
}
- (void)importDataFromLite
{
NSURL *liteURL = [NSURL URLWithString:#"testapp-lite://getdata"];
// if we can open test app, then test app installed, so launch it, providing "getdata" request
if ([[UIApplication sharedApplication] canOpenURL:liteURL])
{
// Getting data from lite app
[[UIApplication sharedApplication] openURL:liteURL];
}
}
The lite version would obviously have to set up its Info.plist to register this custom URL scheme, and its app delegate would need to respond to this request for data, in turn calling a custom URL scheme of the pro app to send the data back:
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
[[[UIAlertView alloc] initWithTitle:nil message:[url absoluteString] delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil] show];
if ([[url absoluteString] isEqualToString:#"testapp-lite://getdata"])
{
// I'm going to create URL from local NSDictionary, but you would presumably retrieve data from your model/database
NSDictionary *dictionary = #{#"name" : #"Rob", #"age" : #"29"};
NSMutableArray *array = [NSMutableArray array];
[dictionary enumerateKeysAndObjectsUsingBlock:^(id key, NSString *obj, BOOL *stop) {
[array addObject:[NSString stringWithFormat:#"%#=%#", key, [obj stringByAddingPercentEscapesForURLParameterUsingEncoding:NSUTF8StringEncoding]]];
}];
// now create the URL
NSURL *proURL = [NSURL URLWithString:[NSString stringWithFormat:#"testapp-pro://data?%#", [array componentsJoinedByString:#"&"]]];
// if we can open pro app, then then do so, providing "getdata" request
if ([[UIApplication sharedApplication] canOpenURL:proURL])
{
NSLog(#"Opening pro app");
[[UIApplication sharedApplication] openURL:proURL];
}
}
return YES;
}
In turn, the pro version could have custom URL scheme to receive the data from the lite version of the app. Thus, the pro app's handler for the data from the lite app might look like:
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
NSArray *absoluteStringComponents = [[url absoluteString] componentsSeparatedByString:#"?"];
NSArray *parameters = [absoluteStringComponents[1] componentsSeparatedByString:#"&"];
for (NSString *parameter in parameters)
{
NSArray *parameterComponents = [parameter componentsSeparatedByString:#"="];
// I'm just logging the results, but you'd presumably integrate the results into your model
NSLog(#"%# is equal to %#", parameterComponents[0], [parameterComponents[1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]);
}
return YES;
}
This works with modest amounts of basic data, but I would not have thought it would be well suited for exchanging image files. If interested, I can show you example of this.
In a variation of the above, you could use the UIDocumentInteractionController to import larger amounts of data, though I think this would request you to present a popover in the lite app for the user to specify to open the data file in the pro app (which seems inelegant).
You could, theoretically, store data on the cloud, possibly iCloud, DropBox, or your own server.

How to show Alert when UIApplication is not able to open URL?

This method opens up the url in Safari when the website string is not null and it is atleast of length 3. But when I have supplierWebsite=#"www.heritage.com", nothing happens. I know that heritage.com is not valid website and so it is not activating in UIApplication. I would like to display atleast a pop up that would tell user that website is not available. Is there any way i can show Alertview telling that website is not available.
- (IBAction)doWebOpen:(UIButton *)sender {
if (self.provider.supplierWebSite && [self.provider.supplierWebSite length] > 3) {
NSString *urlString = [self.provider supplierWebSite];
NSURL *url = [NSURL URLWithString:urlString];
[[UIApplication sharedApplication] openURL:url];
}else {
NSError *err = [NSError errorWithDomain:#"com.cantopenweb" code:509 andDescription:#"This supplier does not have a website."];
[self showErrorAlert:err];
}}
You could use canOpenURL method,
[[UIApplication sharedApplication] canOpenURL:[NSURL
URLWithString:#"your website"]];
The method returns a BOOL, so check that for YES or NO.
If YES, it CAN else NO.
Just use canOpenURL of UIApplication class, like:
if([[UIApplication sharedApplication] canOpenURL:url])
{
[[UIApplication sharedApplication] openURL:url];
}
else
{
//show alert
}
canOpenURL:
Returns whether an application can open a given URL resource.
- (BOOL)canOpenURL:(NSURL *)url
Parameters
url
A URL object that identifies a given resource. The URL’s scheme—possibly a custom scheme—identifies which application can
handle the URL.
Return Value
NO if no application is available that will accept the URL; otherwise,
returns YES. Discussion
This method guarantees that that if openURL: is called, another
application will be launched to handle it. It does not guarantee that
the full URL is valid. Availability
Available in iOS 3.0 and later.
Declared In UIApplication.h

Resources