ably / ably-cocoa

iOS, tvOS and macOS Objective-C and Swift client library SDK for Ably realtime messaging service
https://ably.com/download
Apache License 2.0
46 stars 25 forks source link

UI stuck at unsubscribe or calling subscribe when connection disconnected #673

Closed shoaibahmedqureshi closed 6 years ago

shoaibahmedqureshi commented 6 years ago

I am experiencing a crashing issue using pod version 'Ably', '1.0.10' . I have seen a couple of crashing issue fixes and I am not sure if it's fix is a part of the new release or not and because of this crash UI gets hanged often and does not respond. Below is the stack trace for the crash for further clearance.

Thread says something like below: KSCrashAblyFork`monitorCachedData(userData=0x000000010db0e54e) at KSCrashCachedData.c:148

Crash back trace.txt

funkyboy commented 6 years ago

@shoaibahmedqureshi on which target is this happening? iOS9, 10, 11?

shoaibahmedqureshi commented 6 years ago

I currently tested it on ios 11

funkyboy commented 6 years ago

@shoaibahmedqureshi Are you by chance adding an observer to NSNotificationCenter in any of your code? If yes, are you removing it in dealloc or (if it's a ViewController) in viewWillDisappear?

shoaibahmedqureshi commented 6 years ago

I am using NSNotificationCenter on main screen and i am not removing it in viewWillDisappear as if someone subscribe on other screens I want that update to be reflected there.

shoaibahmedqureshi commented 6 years ago

Btw I just added removeObserver statement in my viewWillDisappear method.

override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) client.isMapView = false self.navigationController?.navigationBar.isHidden = false NotificationCenter.default.removeObserver(self) }

I am still able to reproduce this issue.

funkyboy commented 6 years ago

@shoaibahmedqureshi It looks like the issue is triggered from here: https://github.com/ably/ably-ios/blob/f87bb50176b89ac8a92318bf4fe03094ea28a6b0/Source/ARTConnection.m#L84 when you (or indirectly the code you wrote) asks for the value of state. According to the crash log, that is performed on a background queue. Correct? Also, are you accessing the state value on line 17 of AppDelegate.swift?

shoaibahmedqureshi commented 6 years ago

yes i am performing ably operations on background thread and before every call i try to check the ably connection status and yes crash occurs somewhere around this. no i am not accessing ably in AppDelegate my app delegate looks something like

class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.
    UIApplication.shared.statusBarStyle = .default //.default
    let statusBar: UIView = UIApplication.shared.value(forKey: "statusBar") as! UIView
    if statusBar.responds(to:#selector(setter: UIView.backgroundColor)) {
        statusBar.backgroundColor = UIColor.hexStringToUIColor(hex:APP_BLUE_COLOR)
    }
    UIApplication.shared.statusBarStyle = .lightContent

    _ = GMSServices.provideAPIKey("xyz")
    Configuration.currentEnvironment = .production // .development   //
    UILabel.appearance().substituteFontName = APP_DEFAULT_FONT
    UIButton.appearance().substituteFontName = APP_DEFAULT_FONT

    UISearchBar.appearance().barTintColor = UIColor.hexStringToUIColor(hex:APP_THEME_COLOR)
    UISearchBar.appearance().tintColor = UIColor.white
    let selectedLang = UserSessionWrapper.getLanguage()
    Bundle.setLanguage(selectedLang)

    let center = UNUserNotificationCenter.current()
    center.requestAuthorization(options:[.badge, .alert, .sound]) { (granted, error) in
        // Enable or disable features based on authorization.
    }
    application.registerForRemoteNotifications()

    ToastManager.shared.tapToDismissEnabled = true
    ToastManager.shared.queueEnabled = true
    Fabric.with([Crashlytics.self])
    return true
}

func applicationWillResignActive(_ application: UIApplication) {
    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
    // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}

func applicationDidEnterBackground(_ application: UIApplication) {
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}

func applicationWillEnterForeground(_ application: UIApplication) {
    // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}

func applicationDidBecomeActive(_ application: UIApplication) {
    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}

func applicationWillTerminate(_ application: UIApplication) {
    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
    Generic.callAysnc {
        UserSessionWrapper.exitMyApp()
    }

}

}

funkyboy commented 6 years ago

@shoaibahmedqureshi Update as per our calls:

Things we explored:

Hint

There should be some code like this (of Swift equivalent):

dispatch_async(queue, ^{
    dispatch_sync(queue, ^{
        ...
    });
});

that triggers the deadlock. It's still not clear if this is part of Ably SDK or not.

Action: build a simple project that tries to reproduce this behavior.

cc @ricardopereira

ricardopereira commented 6 years ago

@funkyboy So, you suspect it's a deadlock? When it hangs, do you saw any _dispatch_sync_wait that doesn't unlocks?

funkyboy commented 6 years ago

@shoaibahmedqureshi in the meantime, see the question above.

shoaibahmedqureshi commented 6 years ago

@ricardopereira during the debugging process we explored the code and in my application's code most of calls I had were asynchronous except a couple of serial ques I was using before marker drawing to avoid race condition so we commented them out to find out if they are the real culprit but issue was reproducible without them too. I am not sure about calls within ably library though.

funkyboy commented 6 years ago

@shoaibahmedqureshi will build a sample project tomorrow, trying to replicate this. Will post the link here when ready.

funkyboy commented 6 years ago

@shoaibahmedqureshi Here is my sample project, but I couldn't manage to make the UI hang. Feel free to fork and tweak.

shoaibahmedqureshi commented 6 years ago

@funkyboy I will try to tweak and will let u know if i am able to and reproduce this issue.

funkyboy commented 6 years ago

@shoaibahmedqureshi Closing this. Feel free to reopen if this is still an issue.