Failure to get callbacks when upgrading from 3.0.0 to 3.3.0
Closed this issue · 7 comments
Describe the bug
Faced with issue after updating SDK version.
Old app contains SDK v3.0.0; new app version: v3.3.0.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
Branch.setUseTestBranchKey(true)
Branch.getInstance().initSession(launchOptions: launchOptions, automaticallyDisplayDeepLinkController: false, deepLinkHandler: handleDeepLink)
}
func handleDeepLink(params: [AnyHashable: Any]?, error: Error?) {}
The main issue:
After app update, deepLinkHandler is never called. App restart not help.
The only solution is to delete app and install from scratch. But such case is NOT acceptable for app users.
Network finish operation https://api3.branch.io/v1/open 0.944s. Status 200 error (null).
Expected result: deepLinkHandler call just after app launch.
- Not sure about behavior with TestBranch == false. I had added additional logs to track this issue on prod.
- I had 1 simulator (iOS 17.2) and 2 devices (13 pro 15.7.1; 12 pro max 16.7.2) that have the same issue after first update.
- But I'm not able to reproduce this issue on the same device a second time. Those.: Faced with issue -> remove the app -> install app version with SDK 3.0.0 and run -> Install App with SDK 3.3.0 and run -> All looks good. Maybe I need to use an app for some time, or receive some link before updating to reproduce the issue.
Log file:
Branch_io_log.txt
The same issue as users described here:
#760 (comment)
#914 (comment)
https://github.com/BranchMetrics/react-native-branch-deep-linking-attribution/issues/532#issue-554558963
And a lot of threads at stackoverflow.
Steps to reproduce
Expected behavior
deepLinkHandler call after app launch.
SDK Version
3.3.0
XCode Version
15.2
Device
iPhone 13 pro
OS
15.7.1
Additional Information/Context
No response
@Splash04
Mind opening a support ticket for this? The upgrade scenario should work.
Now the status 200, is actually the http status which is ok. That's just informational and not an error. So I'm going to rename this ticket to correctly describe it as a potential upgrade issue.
Looking at the logs and screenshots, the behavior is as expected. So I think a deeper analysis is necessary.
The else block is to handle a very specific lifecycle situation. Most of the time we actually do NOT want to create a new request. The reason it exists is if we have two lifecycle events come in very rapidly and the second lifecycle has link data, we cannot ignore like we normally do. We must make an attempt to resolve that one as well.
I'm still have one phone with this issue. So I will try to provide some additional details that maybe help:
-
On App launch by some reason we load from file "BNCServerRequestQueue" just one BranchOpenRequest with empty fields:
-
Checking request [self.requestQueue findExistingInstallOrOpen] and not saving initSessionCallback:
-
on processResponse we don't have errors and callback is null, so just load data and [BranchOpenRequest releaseOpenResponseLock]
So no callback call here.
When I compare this flow with clear setup flow, I had seen that findExistingInstallOrOpen don't return anything.
So we're creating BranchOpenRequest and saving initSessionCallback. And initSessionCallback call after the end.
For developers who faced with this issue. This actions should help.
- We should create timer to catch BranchIO freeze and force start the app. I had selected 15 sec timeout.
- We should clear the cache file for branch serverRequestQueue to avoid freeze on the next launch.
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
private var branchIOLaunchTimer: Timer?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// code
startBranchTimeoutTimer()
Branch.getInstance().initSession(launchOptions: launchOptions, automaticallyDisplayDeepLinkController: false, deepLinkHandler: handleDeepLink)
}
func handleDeepLink(params: [AnyHashable: Any]?, error: Error?) {
stopBranchTimeoutTimer()
// handle DeepLink
}
}
// ******************************* MARK: - Branch iOS workaround
extension AppDelegate {
@objc
private func onBranchTimerTimeout() {
logError("Branch IO app launch Timeout")
// Just additional safety (Queue should be already empty), basically do nothing for our issue
Branch.getInstance().clearNetworkQueue()
/*
The main issue, that file for serverRequestQueue cache contains invalid event
so we need to get path to this file and clear it
*/
/* persistImmediately will not clean up the file, because it contains checking count != 0
if let serverRequestQueue = (BranchSDK.BNCServerRequestQueue.getInstance() as? BranchSDK.BNCServerRequestQueue) {
print("serverRequestQueue: \(serverRequestQueue.queueDepth())")
serverRequestQueue.persistImmediately()
}
*/
// Load serverRequestQueue file path from ObjecitiveC code
if let fileUrl = ObjectiveCUtils.serverRequestQueueURL() {
logDebug("Remove cache file for branch serverRequestQueue", data: ["fileUrl" : fileUrl])
removeFile(fileUrl: fileUrl)
}
// Force start the app
handleDeepLink(params: [:], error: nil)
}
func removeFile(fileUrl: URL) {
if FileManager.default.fileExists(atPath: fileUrl.path) {
do {
try FileManager.default.removeItem(atPath: fileUrl.path)
print("File deleted: \(fileUrl)")
} catch {
print("Could not delete file, probably read-only filesystem")
}
} else {
print("File not exist")
}
}
fileprivate func startBranchTimeoutTimer() {
if let _ = self.branchIOLaunchTimer { return }
self.branchIOLaunchTimer = Timer.scheduledTimer(timeInterval: 10.0,
target: self,
selector: #selector(onBranchTimerTimeout),
userInfo: nil,
repeats: false)
}
private func stopBranchTimeoutTimer() {
self.branchIOLaunchTimer?.invalidate()
self.branchIOLaunchTimer = nil
}
}
The main problem here that serverRequestQueue cache file path is a private method in objectiveC class. So, we have to use Objective-C Runtime Utilities function to get selector by name (NSSelectorFromString). Please, check that function name is not changed:
How to get this path:
- Create ObjectiveCUtils.h:
#import <Foundation/Foundation.h>
@interface ObjectiveCUtils : NSObject
+ (NSURL *) serverRequestQueueURL;
@end
- Accept to create -Bridging-Header.h when XCode asks you. It should contain just one line:
#import "ObjectiveCUtils.h"
- Create ObjectiveCUtils.m
#import "ObjectiveCUtils.h"
#import "Branch.h"
@implementation ObjectiveCUtils
+ (NSURL *) serverRequestQueueURL {
SEL privateSelector = NSSelectorFromString(@"URLForQueueFile");
if ([BNCServerRequestQueue respondsToSelector: privateSelector]) {
id url = [BNCServerRequestQueue performSelector: privateSelector];
if ([url isKindOfClass: [NSURL class]]) {
return url;
} else {
printf("Invalid class type for URLForQueueFile url\n");
return NULL;
}
} else {
printf("Selector for URLForQueueFile is not found\n");
}
return NULL;
}
@end
@Splash04
Thanks for the detailed logs and your workaround. Looking to see if I can reproduce this on my own test devices.
Either way, I think an additional safety check for invalid events in storage is necessary in the next patch.
A error recovery safety check similar to the one described here was added in the latest release.
https://github.com/BranchMetrics/ios-branch-deep-linking-attribution/releases/tag/3.4.1