Describe your dev environment here, giving as many details as possible. If you have them, make sure to include:
11.75 (if issue is Swift related)Cocoapods version 1.9.3Get a list of friends who also have the game enabled
Return a response with the associated data
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
}
}
}
```
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!
Most helpful comment
Good question on whether the
loginmethods 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!