I have two scenarios here.
One is, my App is active in the background on my iPad. If I go to safari and click a link with my URL scheme, the App opens and displays an alert with the URL.
This is what I want!
Second scenario is when the App is inactive, and not in the background. Here the App launches, but the alert is never displayed. I can alert the URL out from "didFinishLaunchingWithOptions", but I need it in my JavaScript function: handleOpenURL(url).
It seems to me that handleOpenURL in my AppDelegate.m is only fired when the App is in the background. Is there any way to make it do the same while not running in the background?
Here is my obj-c handleOpenUrl:
if (!url) { return NO; }
// calls into javascript global function 'handleOpenURL'
NSString* jsString = [NSString stringWithFormat:#"window.setTimeout(function(){ handleOpenURL(\"%#\"); }, 1)", url];
[self.viewController.webView stringByEvaluatingJavaScriptFromString:jsString];
// all plugins will get the notification, and their handlers will be called
[[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginHandleOpenURLNotification object:url]];
return YES;
It should output to this javascript function:
function handleOpenURL(url) {
alert('invoke: ' + url);
}
Now when the App starts initially, it runs didFinishLaunchingWithOptions:
NSURL* url = [launchOptions objectForKey:UIApplicationLaunchOptionsURLKey];
NSString* invokeString = nil;
if (url) {
invokeString = [url absoluteString];
NSLog(#"iPaperReeder launchOptions = %#", url);
}
self.viewController.invokeString = invokeString;
Should I modify the didFinishLaunchingWithOptions method, to make it run handleOpenURL?
You can easily sort out this problem.
In "CDVHandleOpenURL.m" file you need to change code as below
NSString* jsString = [NSString stringWithFormat:#"document.addEventListener('deviceready',function(){if (typeof handleOpenURL === 'function') { handleOpenURL(\"%#\");}});", url];
To
NSString* jsString = [NSString stringWithFormat:#"if (typeof handleOpenURL === 'function') { handleOpenURL(\"%#\");} else { window._savedOpenURL = \"%#\"; }", url, url];
this will work perfectly.
Best of luck
I figured it out. I was missing a call in the onDeviceReady function:
if (invokeString) handleOpenURL(invokeString);
Related
decidePolicyForNavigationAction is not being triggered when the webview loads a new page. It works when everything is initially loaded but then never gets triggered after that.
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction
decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
NSLog(#"decidePolicyForNavigationActionnavigationAction.request.URL.absoluteString:%#|", navigationAction.request.URL.absoluteString);
if (kTestLocalHTML == 1) {
decisionHandler(WKNavigationActionPolicyAllow);
return;
}
NSURL *url = navigationAction.request.URL;
if (url != nil) {
//
// TODO: Trim the url string below as well?
// No, because this is being trimmed already in the AppManager methods related to web view URL
//
NSString *urlString = url.absoluteString;
WKNavigationType navType = navigationAction.navigationType;
//
// Display the club mobile native login page and log the user out
// if the webview is about to load the blazor login page URL
//
if ([urlString isEqualToString:[AppManager getMobileLoginUrl]]) {
self.appManager.userLoginDetails = nil;
[self displayLoginPage];
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
//
// Check if link is tapped from webview and if URL string contains the
// prepended identifier within the JavaScript injection which can be found in
// "webViewDidFinishLoad" delegate method of webview
//
if (navType == WKNavigationTypeLinkActivated && [AppManager isWebViewURL_containsTargetBlank:urlString]) {
//
// Make sure to ignore the prepended identifier on the URL string
// before opening it on an external browser
//
NSInteger targetBlankIdentifierLength = kIdentifier_TargetBlank.length;
NSURL *url = [NSURL URLWithString:[urlString substringFromIndex:targetBlankIdentifierLength]];
[[UIApplication sharedApplication] openURL:url];
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
//
// Check if url has custom scheme other than "http" & "https" (If is either one of the tags for Telephone "tel", Mail "mailto", SMS "sms", Web Calendar "webcal", etc.)
//
// Solution Reference: https://stackoverflow.com/questions/47040471/wkwebview-not-opening-custom-url-scheme-js-opens-custom-scheme-link-in-new-wind
// - (Answers of both "hstdt" & "mattblessed")
//
// NOTE: It seems loading telephone and mail links do not work when testing on an iOS simulator.
//
NSString *urlScheme = url.scheme;
BOOL isCustomURLscheme = ( ! [urlScheme isEqualToString:#"http"] && ! [urlScheme isEqualToString:#"https"]);
if (isCustomURLscheme) {
NSURL *url = [NSURL URLWithString:urlString];
[[UIApplication sharedApplication] openURL:url];
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
//
// Get reference to the current URL being requested so that if there
// will be failure in loading the webview, we have a reference to that URL.
//
self.currentUrlRequest = navigationAction.request;
// Check if web view URL contains a calendar file to download
if ([AppManager isWebViewURL_containsCalendarDownload:urlString]) {
//
// Download and process calendar file either from Events or Dining module
// and display "New Event" page where user can view its details, edit and save to device calendar
//
[self processCalendarFileAndDisplayEventPageWithURLrequest:self.currentUrlRequest];
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
// Check if web view URL contains a PDF file attachment to download
else if ([AppManager isWebViewURL_containsPDFAttachmentDownload:urlString]) {
// Display document viewer page and pass few parameters for page customization (initial URL to load, navigation bar title)
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:kStoryboardName bundle:nil];
UINavigationController *navigationController = [storyboard instantiateViewControllerWithIdentifier:kStoryboardID_OverlayBrowserNavigationController];
OverlayBrowserViewController *vcOverlay = (OverlayBrowserViewController *)[navigationController topViewController];
vcOverlay.initialURLtoLoad = urlString;
vcOverlay.navigationBarTitle = [self getPDFDocumentNameFromURLString:urlString];
[self presentViewController:navigationController animated:YES completion:nil];
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
}
decisionHandler(WKNavigationActionPolicyAllow);
}
This function is supposed to trigger and detect if the webview is loading the web login page and if that is the case it will logout the user and instead show the apps native login page. However the function is never triggered after the main screen is loaded initially.
"Are you sure the web page is actually performing a redirect and is not a SPA like Angular which may not be updating the URL? Also how are you setting self.wkWebMain? – RunLoop"
Thanks for this, you were correct and the webpage was acting as an SPA which was the reason the function would call at first on the initial load but not after.
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) {});
}
}
}
I am following the Spotify SDK tutorial, and trying to make a RN module for my application. This is my SpotifyModule.m code:
#import "SpotifyModule.h"
#import "React/RCTLog.h"
#import "React/RCTBridge.h"
#implementation SpotifyModule
RCT_EXPORT_MODULE()
+ (id)sharedManager {
static SpotifyModule *sharedManager = nil;
#synchronized(self) {
if (sharedManager == nil)
sharedManager = [[self alloc] init];
}
return sharedManager;
}
RCT_EXPORT_METHOD(authenticate:(RCTResponseSenderBlock)callback)
{
// Your implementation here
RCTLogInfo(#"authenticate");
self.auth = [SPTAuth defaultInstance];
// The client ID you got from the developer site
self.auth.clientID = #"8fff6cbb84d147e383060be62cec5dfa";
// The redirect URL as you entered it at the developer site
self.auth.redirectURL = [NSURL URLWithString:#"my-android-auth://callback"];
// Setting the `sessionUserDefaultsKey` enables SPTAuth to automatically store the session object for future use.
self.auth.sessionUserDefaultsKey = #"current session";
// Set the scopes you need the user to authorize. `SPTAuthStreamingScope` is required for playing audio.
self.auth.requestedScopes = #[SPTAuthPlaylistReadPrivateScope, SPTAuthUserReadPrivateScope];
//save the login callback
SpotifyModule *spotifyModule = (SpotifyModule *)[SpotifyModule sharedManager];
spotifyModule.loginCallback = callback;
//setup event dispatcher
spotifyModule.eventDispatcher = [[RCTEventDispatcher alloc] init];
[spotifyModule.eventDispatcher setValue:self.bridge forKey:#"bridge"];
// Start authenticating when the app is finished launching
dispatch_async(dispatch_get_main_queue(), ^{
[self startAuthenticationFlow];
});
}
- (void)startAuthenticationFlow
{
// Check if we could use the access token we already have
if ([self.auth.session isValid]) {
// Use it to log in
SpotifyModule *spotifyModule = (SpotifyModule *)[SpotifyModule sharedManager];
NSString *accessToken = self.auth.session.accessToken;
spotifyModule.loginCallback(#[accessToken]);
} else {
// Get the URL to the Spotify authorization portal
NSURL *authURL = [self.auth spotifyWebAuthenticationURL];
// Present in a SafariViewController
self.authViewController = [[SFSafariViewController alloc] initWithURL:authURL];
UIViewController *rootViewController = [UIApplication sharedApplication].delegate.window.rootViewController;
[rootViewController presentViewController:self.authViewController animated:YES completion:nil];
}
}
- (BOOL) application:(UIApplication *)app
openURL:(NSURL *)url
options:(NSDictionary *)options
{
// If the incoming url is what we expect we handle it
if ([self.auth canHandleURL:url]) {
// Close the authentication window
[self.authViewController.presentingViewController dismissViewControllerAnimated:YES completion:nil];
self.authViewController = nil;
// Parse the incoming url to a session object
[self.auth handleAuthCallbackWithTriggeredAuthURL:url callback:^(NSError *error, SPTSession *session) {
if (session) {
// Send auth token
SpotifyModule *spotifyModule = (SpotifyModule *)[SpotifyModule sharedManager];
NSString *accessToken = session.accessToken;
spotifyModule.loginCallback(#[accessToken]); }
}];
return YES;
}
return NO;
}
#end
The way I want to use it from the RN end, is call authenticate, with a callback for the access token. I got this working on Android fine.
Native.authenticate(function(token) {
store.dispatch(actions.loginSuccess(token));
});
On iOS, with the above code, I get to the attached screen, and when clicking Ok I get the following error:
SpotiFind[5475:29641] *** Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: '+[SpotifyModule
application:openURL:sourceApplication:annotation:]: unrecognized
selector sent to class 0x10cb406f8'
So from my minimal ObjectiveC understanding, its trying to call a different method, than the one that the tutorial instructs to implement.
Any recommendations on how to make this work ?
If its any relevant, I build against iOS 10, and use the latest Spotify iOS SDK
p.s I realize the name might be against some copyrighting, its just temp for development :)
Thanks to your tips (in the comments), we managed to make our Spotify authentication work with React-native.
We used the code from your Pastebin to create a reusable module so that nobody has to waste time anymore.
You can find the module here: emphaz/react-native-ios-spotify-sdk
There is a tutorial for the setup and we even created a boilerplate project
Thanks a lot Giannis !
I'm developing an iOS app using phonegap and HTML5.
After launchImage in app, I open InAppBrowser, which opens promptly.
But, tapping on links in it sometimes does not open the desired page and event "falls through" to main webView.
i.e., InAppBrowser is closed on tapping the link.
Please do help me out as I'm stuck with this for the past two days.
Try this one
Put this code in your MainViewController.m before #end tag
- (BOOL) webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType
{
NSURL *url = [request URL];
// Intercept the external http requests and forward to Safari.app
// Otherwise forward to the PhoneGap WebView
if ([[url scheme] isEqualToString:#"http"] || [[url scheme] isEqualToString:#"https"]){
[[UIApplication sharedApplication] openURL:url];
return NO;
}
else {
return [super webView:theWebView shouldStartLoadWithRequest:request navigationType:navigationType];
}
}
Resolved this issue by bypassing the error code. It is a bit of dirty workaround but couldn't help it. In "CDVInAppBrowser.m", modified as below and InAppBrowser loads my url successfully.
- (void)webView:(UIWebView*)theWebView didFailLoadWithError:(NSError*)error
{
//By passing error code
if(error.code == -999)
return;
// log fail message, stop spinner, update back/forward
NSLog(#"webView:didFailLoadWithError - %i: %#", error.code, [error localizedDescription]);
self.backButton.enabled = theWebView.canGoBack;
self.forwardButton.enabled = theWebView.canGoForward;
[self.spinner stopAnimating];
self.addressLabel.text = NSLocalizedString(#"Load Error", nil);
[self.navigationDelegate webView:theWebView didFailLoadWithError:error];
}
I want to open the google search link like 'https://www.google.co.in/#q=adam+scott' in sencha touch hybrid ios app. I tried to use var ref = window.open(url, '_blank','location=yes'); but it is not loading the page and if I change the _blank to _system it is loading the page but not showing done button to move to previous page.
Please let me know if some body has done it.
I think this is what you are looking for:
navigator.app.loadUrl('https://www.google.co.in/#q=adam+scott', { openExternal:true } );
open your MainViewController.m class and add this line of code before #end
- (BOOL) webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType
{
NSURL *url = [request URL];
// Intercept the external http requests and forward to Safari.app
// Otherwise forward to the PhoneGap WebView
if ([[url scheme] isEqualToString:#"http"] || [[url scheme] isEqualToString:#"https"]){
[[UIApplication sharedApplication] openURL:url];
return NO;
}
else {
return [super webView:theWebView shouldStartLoadWithRequest:request navigationType:navigationType];
}
}