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.
Related
I have run react-native start in one terminal, and then react-native ios-run in another. My initial console.log rarely show, sometimes they do.
Lot's of times, randomly I do see:
LOG MESSAGE QUOTA EXCEEDED - SOME MESSAGES FROM THIS PROCESS HAVE BEEN DISCARDED
Are my console.log's being discarded? I tried clearing the console to see it more clearly but I can't find a way to clear console either.
On Android, I wouldn't have issue with missing console.log.
react-native logs information using syslog daemon. This daemon attempts to prevent spamming to the log (DoS attack). These limits are set on per process basis.
The simple solution is to stop/start simulator and you will be obtain new process that is not limited by the previous behaviour.
The other solution is to disable syslogd limits what will be heavilly depends on your operation system.
i found that the JavascriptCore engine won't automatically redirect the console.log to either XCode output panel or the system builtin Console.App, not to mention the self-broken log-ios command.
the only way to see console.log without remote debugging in browser is redirect(bind) it ourselves:
//Add this headers
#import <JavaScriptCore/JavaScriptCore.h>
#import <jschelpers/JavaScriptCore. h>
#import <React/RCTBridge+Private.h>
...
...
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSURL *jsCodeLocation;
...
...
[self.window makeKeyAndVisible];
::sleep(2); //<---!!!!!see below
JSGlobalContextRef globalContext = rootView.bridge.jsContextRef;
JSContext *context = [JSC_JSContext(globalContext) contextWithJSGlobalContextRef:globalContext];
context[#"console"][#"log"] = ^(NSString *message) {
NSLog(#"Javascript log: %#",message);
};
return YES;
}
Caution: the JSContext within reactInstance is created in another thread, I don't know how to get the loaded event(in only my project, since i don't like to modify the react-native engine), just wait sometime here for testing purpose.
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!
There is a problem with a poorly chosen, non-composable abstractions in Cocoa that arise when one tries to combine a Quick Look preview panel and a security-scoped URLs.
I have a concrete example:
Imagine we are trying to show a preview for some objects from the MediaLibrary (MediaLibrary.framework allows applications to browse the iPhoto, Aperture... and Photos libraries via convenient API).
The most simple and straightforward way to do so is to adapt the 'MLMediaObject' class (that represent a particular photo or video item) to implement the 'QLPreviewItem' protocol (which can be passed to the QLPreviewPanel):
MLMediaObject+PreviewItem.h
#import <MediaLibrary/MLMediaObject.h>
#import <Quartz/Quartz.h>
#interface MLMediaObject (PreviewItem) <QLPreviewItem>
#end
MLMediaObject+PreviewItem.m
#import "MLMediaObject+PreviewItem.h"
#implementation MLMediaObject (PreviewItem)
- (NSURL*) previewItemURL
{
return self.URL;
}
- (NSString*) previewItemTitle
{
return self.name;
}
#end
Simple. Now image the following QLPreviewPanel data source implementation:
AlbumViewController.m
- (NSInteger) numberOfPreviewItemsInPreviewPanel: (QLPreviewPanel*) panel
{
// 'currentAlbum' property contains the currently-represented MLMediaGroup object.
return self.currentAlbum.count;
}
- (id<QLPreviewItem>) previewPanel: (QLPreviewPanel*) panel previewItemAtIndex: (NSInteger) index
{
return self.currentAlbum[index];
}
So far so good. But if we look into the sparse and usually misleading Apple documentation, we may find out the following important details:
URL
The location of the media object. (read-only)
This property is provided as a security-scoped URL. In order to gain access to the file that this URL refers to, the caller must call startAccessingSecurityScopedResource before and stopAccessingSecurityScopedResource after using the URL to access the file.
So, it is obvious that access to the resource have to be bracketed with a startAccessingSecurityScopedResource/stopAccessingSecurityScopedResource calls pair.
The question is where should I put these calls given the current QLPreviewPanelDataSource protocol definition? It is up to QLPreviewPanel to access the resource, not my code, but unfortunately I hardly ever will believe that Apple updated QL to operate in a sandboxing-wise environment.
How do I handle the cases, when startAccessingSecurityScopedResource call returns NO, stating about failure to obtain an access?
Seems like when you try to startAccessingSecurityScopedResource on an URL that is already being accessed you get the failure flag on return. Like, everything is OK, but you get an error flag. Seems like these start/stop... calls have to be paired precisely, and even a balanced nesting is forbidden. So, how to differentiate between the two possibilities when you get a NO on return: a security-scoped URL that is already being accessed and a security-scoped URL that failed to 'resolve'?
It is an experimentally-proven fact, that your application can only access a finite amount of security-scoped URLs (you can take about ~1500 URLs before it will silently stop working). So, how should I properly relinquish access to the security-scoped URLs after I've passed them to the QLPreviewPanel? When it it a right time to do so? It seems to me like it is a private implementation detail of a QLPreviewPanel class and I can make no assumptions about its internal workings.
You can use:
- (void)beginPreviewPanelControl:(QLPreviewPanel *)panel {
[bookmarkURL startAccessingSecurityScopedResource];
//... Your code
}
and
- (void)endPreviewPanelControl:(QLPreviewPanel *)panel {
//... Your Code
[bookmarkURL stopAccessingSecurityScopedResource];
}
I'm writing a WatchKit extension and I'd like to read a file out of the host application's [NSBundle mainBundle]. I've tried [NSBundle bundleWithIdentifier:] but that just returns nil.
I have several potential workarounds, but nothing that would be as simple as "just read what you need from the host's mainBundle".
Is there a way of doing this?
The host app and your WatchKit extension can share files in only one of two ways, as far as I know:
Shared app group
Including a file in both targets
They run in separate processes and aren't accessible to each other outside of approved methods.
I ran into a similar problem like yours. The main host app has a particular pList that I needed to read, and I couldn't read from watch extension because they are isolated.
So in the watch I invoked the openParentApplication method
and in the main application my handler was something along the lines of
-(void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void (^)(NSDictionary *))reply
{
NSString *request = [userInfo objectForKey:#"request"];
if ([request isEqualToString:ReadFile])
{
//read the file. and then i like to put it into a NSDictionary
NSDictionary *responseDictionary = //whatever
reply(responseDictionary);
}
else{ reply(nil); }
}
And then the contents were returned to me in the callback closure on the watch of the openParentApplication. Seems to work. Though your situation could be different in which case this method might not be viable.
From the Apple WatchKit programming guide:
To share preferences data between apps, create an NSUserDefaults object using the identifier of the shared group. The initWithSuiteName: method of NSUserDefaults creates an object that allows access to the shared user defaults data. Both processes can access this data and write changes to it.
Your main app can write a NSDictionary/NSArray to the shared prefs, and then the watch kit can pull it out, without starting the main app - however, the main app will have to be run at least once to update the shared prefs.
Since updating Facebook to v4.0.x and the latest Parse libraries, my app is hanging, seemingly when trying to log in the user.
My stack trace looks like this:
I had a very similar problem previously, answered here: Parse crash when calling [PFFacebookUtils initializeFacebook] - semaphore_wait_trap
However that solution no longer works, since it seems [PFUser currentUser] has been replaced with [PFUser(Private) _getCurrentUserWithOptions:] and [BFTask(Private) waitForResult:withMainThreadWarning:] where it gets stuck.
In my app, I've subclassed PFUser to a class called MPLUser, and overridden the user method. Not sure if this might be something to do with the issue?
+ (MPLUser *)user
{
return (MPLUser *)[PFUser user];
}
Once this starts occurring, it becomes impossible to launch the app. However, I usually manage to launch the app a few times before the lock starts happening. It usually happens after a crash...
I'm using pod 'ParseFacebookUtilsV4' and have updates all libraries to latest versions.
UPDATE:
Here's more stack trace from another thread, that is seemingly trying to log on:
I initialise Parse and Facebook in the following order. If I reverse the calls, it crashes:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[self initDefaults];
[self initialiseApplicationSpecifics];
[self setupParseWithOptions:launchOptions];
[self enableCrashReporting];
[self setupIAPs];
//etc...
}
- (void)initialiseApplicationSpecifics
{
[Flurry setCrashReportingEnabled:YES];
[self registerParseSubclasses];
[ParseCrashReporting enable];
[Parse enableLocalDatastore];
#ifdef MPL
[Parse setApplicationId:#"xxxyyy"
clientKey:#"xxxyyy"];
[Flurry startSession:#"xxxyyy"];
#elif MGM
[Parse setApplicationId:#"yyyxxx"
clientKey:#"yyyxxx"];
[Flurry startSession:#"yyyxxx"];
#endif
}
- (void)setupParseWithOptions:(NSDictionary *)launchOptions
{
[PFFacebookUtils initializeFacebookWithApplicationLaunchOptions:launchOptions];
[PFTwitterUtils initializeWithConsumerKey:#"aaaabbbb"
consumerSecret:#"bbbbaaaa"];
[PFAnalytics trackAppOpenedWithLaunchOptions:launchOptions];
}
Seems to be fixed with parse 1.7.2
According to v1.7.2 — April 27, 2015
New: Local Data Sharing for Extensions and WatchKit.
Improved nullability annotations for ParseFacebookUtils.
Fixed: logOutInBackground with block callback not called on main thread.
Fixed: Potential compilation error with using imports for PFSubclassing.h.
Fixed: Not persistent currentUser if saving automatic user via saveEventually.
Fixed: Rare deadlock scenario with using ParseFacebookUtils and currentUser.
Fixed: Rare issue with pinning multiple objects in a row to the same pin.
Fixed: Rare scenario when user could be not linked with Facebook.
Improved performance and reliability of Local Datastore.
Performance improvements.
Other small bug fixes.
I'm having the same problem with Parse 1.7.1 & FBSDK 4.0.1 and I reported the bug to Parse but with no luck so far. It has something to do with the local datastore.
https://developers.facebook.com/bugs/779176035499837
Please provide further info there.
I checked with my team working on the iOS SDK and was informed the latest SDK should resolve this. Can you try updating?