Facebook-ios-sdk: Main Thread Checker: UI API called on a background thread: -[UIApplication canOpenURL:] issue in FBSDKCoreKit

Created on 21 Oct 2020  路  3Comments  路  Source: facebook/facebook-ios-sdk

Checklist

Environment

Describe your dev environment here, giving as many details as possible. If you have them, make sure to include:

  • Xcode Version: 11.7
  • Swift Version: 5 (if issue is Swift related)
  • Installation Platform & Verison: Cocoapods version 1.9.3

Goals

Get a list of friends who also have the game enabled

Expected Results

Return a response with the associated data

Actual Results

Unbalanced calls to begin/end appearance transitions for <app.AIGameSelected: 0x7fa01061f850>.
=================================================================
Main Thread Checker: UI API called on a background thread: -[UIApplication canOpenURL:]
PID: 80231, TID: 13037456, Thread name: (none), Queue name: com.apple.root.default-qos, QoS: 0
Backtrace:
4   FBSDKCoreKit                        0x000000010372bafa +[FBSDKInternalUtility _canOpenURLScheme:] + 218
5   FBSDKCoreKit                        0x000000010372b71c +[FBSDKInternalUtility isFacebookAppInstalled] + 140
6   FBSDKLoginKit                       0x0000000103915855 -[FBSDKLoginManager logInParametersWithPermissions:serverConfiguration:] + 853
7   FBSDKLoginKit                       0x0000000103916a76 -[FBSDKLoginManager logIn] + 86
8   FBSDKLoginKit                       0x0000000103916228 -[FBSDKLoginManager logInWithPermissions:handler:] + 328
9   FBSDKLoginKit                       0x0000000103913fb8 -[FBSDKLoginManager logInWithPermissions:fromViewController:handler:] + 232
10  app                    0x0000000103348919 $s16app20FacebookGameSelectedC11viewDidLoadyyFyycfU_ + 681
11  app                    0x000000010334a6e0 $sIeg_IeyB_TR + 48
12  libdispatch.dylib                   0x0000000104607f11 _dispatch_call_block_and_release + 12
13  libdispatch.dylib                   0x0000000104608e8e _dispatch_client_callout + 8
14  libdispatch.dylib                   0x000000010460b2d8 _dispatch_queue_override_invoke + 1022
15  libdispatch.dylib                   0x000000010461a399 _dispatch_root_queue_drain + 351
16  libdispatch.dylib                   0x000000010461aca6 _dispatch_worker_thread2 + 135
17  libsystem_pthread.dylib             0x00007fff523019f7 _pthread_wqthread + 220
18  libsystem_pthread.dylib             0x00007fff52300b77 start_wqthread + 15
2020-10-20 19:00:04.365379-0400 app[80231:13037456] [reports] Main Thread Checker: UI API called on a background thread: -[UIApplication canOpenURL:]
PID: 80231, TID: 13037456, Thread name: (none), Queue name: com.apple.root.default-qos, QoS: 0
Backtrace:
4   FBSDKCoreKit                        0x000000010372bafa +[FBSDKInternalUtility _canOpenURLScheme:] + 218
5   FBSDKCoreKit                        0x000000010372b71c +[FBSDKInternalUtility isFacebookAppInstalled] + 140
6   FBSDKLoginKit                       0x0000000103915855 -[FBSDKLoginManager logInParametersWithPermissions:serverConfiguration:] + 853
7   FBSDKLoginKit                       0x0000000103916a76 -[FBSDKLoginManager logIn] + 86
8   FBSDKLoginKit                       0x0000000103916228 -[FBSDKLoginManager logInWithPermissions:handler:] + 328
9   FBSDKLoginKit                       0x0000000103913fb8 -[FBSDKLoginManager logInWithPermissions:fromViewController:handler:] + 232
10  app                                          0x0000000103348919 $s16app20FacebookGameSelectedC11viewDidLoadyyFyycfU_ + 681
11  app                                          0x000000010334a6e0 $sIeg_IeyB_TR + 48
12  libdispatch.dylib                   0x0000000104607f11 _dispatch_call_block_and_release + 12
13  libdispatch.dylib                   0x0000000104608e8e _dispatch_client_callout + 8
14  libdispatch.dylib                   0x000000010460b2d8 _dispatch_queue_override_invoke + 1022
15  libdispatch.dylib                   0x000000010461a399 _dispatch_root_queue_drain + 351
16  libdispatch.dylib                   0x000000010461aca6 _dispatch_worker_thread2 + 135
17  libsystem_pthread.dylib             0x00007fff523019f7 _pthread_wqthread + 220
18  libsystem_pthread.dylib             0x00007fff52300b77 start_wqthread + 15```

## Code Samples & Details

That happens when I render this screen:

override func viewDidLoad() {
super.viewDidLoad()

    DispatchQueue.global().async {
        let dispatchGroup = DispatchGroup()

        // Sign in to Facebook if not already signed in
        if (AccessToken.current == nil) {
            LoginManager().logIn(permissions: ["public_profile", "email", "user_friends"], from: self)
        }

        dispatchGroup.enter()
        // Get current player ID and name
        let player1Request : GraphRequest = GraphRequest.init(graphPath: "me", parameters: ["fields": "id,name"])
        let connection = GraphRequestConnection()
        connection.add(player1Request, completionHandler: {_, response, error in
            if error != nil {
                NSLog(error.debugDescription)
                return
            }

            if let response = response as? [String:String] {
                self.player1.initWithPlayerData(playerData: response, turn: "X")
            }
            dispatchGroup.leave()
        })
        connection.start()
        dispatchGroup.wait()

        dispatchGroup.enter()
        // Get friends
        let connection2 = GraphRequestConnection()
        let player1FriendsRequest = GraphRequest.init(graphPath: String(self.player1.playerFBID) + "/friends", parameters: ["fields": "id,name"])
        connection2.add(player1FriendsRequest, completionHandler: {_, response, error in
            if error != nil {
                NSLog(error.debugDescription)
                return
            }

            if let response = response as? [String:Any] {
                for friendData in response["data"] as! [[String:String]] {
                    let friend = Player()
                    friend.initWithPlayerData(playerData: friendData, turn: "O")
                    self.friends.append(friend)
                }
            }
            dispatchGroup.leave()
        })
        connection2.start()
        dispatchGroup.wait()

        DispatchQueue.main.async {
            // Finally, display all friends for selection using table view data source
            self.friendsTableView.dataSource = self
            self.friendsTableView.delegate = self
            self.friendsTableView.reloadData()

            // Assign LoginButton Delegate
            let loginButton = FBLoginButton()
            self.fbLoginButtonView.addSubview(loginButton)
            loginButton.translatesAutoresizingMaskIntoConstraints = false
            loginButton.centerXAnchor.constraint(equalTo: self.fbLoginButtonView.centerXAnchor).isActive = true
            loginButton.centerYAnchor.constraint(equalTo: self.fbLoginButtonView.centerYAnchor).isActive = true
            loginButton.delegate = self
        }
    }
}

```

Most helpful comment

Good question on whether the login methods could dispatch whatever UI they need on the main thread. We could absolutely do this but I worry that it might violate a general principle of predictability. If I'm dispatching to a background thread then generally I expect that code to execute on that thread. This would, in effect, subvert the will of the developer. In this case the consequences of dispatching login methods to the main thread seem pretty negligible but I'm still hesitant to make that change. Open to discussing more if I'm missing something obvious.

On the second point. I will absolutely add some documentation to warn that it should be called from the main thread. Great callout!

All 3 comments

I don't think this is a bug. Login requires a UI interaction. The actual network requests are already performed asynchronously but the entire flow cannot be done asynchronously for the reason you've stated. Presenting a controller to enter your credentials requires usage of a UI API and therefore must be called from the main thread.

@joesus can you elaborate?
Are you saying that this method should be called only from the main thread? Can't it just dispatch the UI interaction on the main thread?
And if not, I think it should be documented clearly that this method should only be called from the main thread.

Good question on whether the login methods could dispatch whatever UI they need on the main thread. We could absolutely do this but I worry that it might violate a general principle of predictability. If I'm dispatching to a background thread then generally I expect that code to execute on that thread. This would, in effect, subvert the will of the developer. In this case the consequences of dispatching login methods to the main thread seem pretty negligible but I'm still hesitant to make that change. Open to discussing more if I'm missing something obvious.

On the second point. I will absolutely add some documentation to warn that it should be called from the main thread. Great callout!

Was this page helpful?
0 / 5 - 0 ratings