Main thread checks on iOS 15 - unexpected result #802
-
Describe the bug We're using TCA 0.27 and it looks like the recently added main thread checks aren't working as expected, I'm getting output like this:
The weird thing here is I know that the first action sent to my store would have been on the main thread because we fire an action in To Reproduce Expected behavior Environment
|
Beta Was this translation helpful? Give feedback.
Replies: 20 comments 13 replies
-
Very interesting, thanks for the report!
The "Initial thread" referenced in that message refers to the thread where the root store was created on. Could you breakpoint where that happens and confirm its on the main thread? And that If it really is possible to have two different thread references that both represent the "main" thread then we may have to rethink some of this. |
Beta Was this translation helpful? Give feedback.
-
Hmm, interesting. So our root store is created by a coordinator object that is initialised as a singleton, (e.g. We first reference this live coordinator in the IIRC, "thread 29" appeared to be owned by I will keep an eye on this and see if it happens again as we continue to test. I've only experienced it once so far. It doesn't help that we are still running a more than month old beta. 🤞🏻 for an updated Xcode this week. |
Beta Was this translation helpful? Give feedback.
-
One thing you could try is to temporarily change the |
Beta Was this translation helpful? Give feedback.
-
@lukeredpath Is the app using an "old" |
Beta Was this translation helpful? Give feedback.
-
@tgrapperon we've actually just converted our app over to the SwiftUI app lifecycle but we are still using We do have a few analytics/crash reporting tools too that could potentially be messing with this. I'll give Brandon's suggestion a try, but most of the time it seems to work OK. I might add a precondition check to our |
Beta Was this translation helpful? Give feedback.
-
@lukeredpath Have you managed to find the root of the problem? Was the store somehow being initialized on a background thread? We'd love to know if this is an issue with our thread checking code or not 😄 |
Beta Was this translation helpful? Give feedback.
-
@stephencelis not yet - we'll be running our app on iOS 15 more extensively over the next week now the Xcode RC has dropped, if this wasn't a one-off issue I'm sure we'll see it again, so I'll keep an eye out. I'm 99.9% certain that our store is not being created on a background thread though. |
Beta Was this translation helpful? Give feedback.
-
No sooner had I posted, another member of my team just experienced this bug - except this was on Xcode 12.5.1 on iOS 14.8.
Hmm.... |
Beta Was this translation helpful? Give feedback.
-
@lukeredpath If you create a dedicated |
Beta Was this translation helpful? Give feedback.
-
I'm going with this for now, in the function that creates our root store: if !Thread.isMainThread {
assertionFailure("The main TCA store must be created on the main thread.")
} If that never asserts and we still hit this issue, then there must be something else going on here. |
Beta Was this translation helpful? Give feedback.
-
Right, looks like we can still hit this problem without the above assertion being hit which points to the underlying |
Beta Was this translation helpful? Give feedback.
-
I'd be interested in seeing what We're also experimenting with using thread variables to detect this situation. I would hope that even if there are two different references for the main thread that they would at least still share the same thread variables. |
Beta Was this translation helpful? Give feedback.
-
@lukeredpath Could you point your project to this branch and see if you can reproduce? https://github.com/pointfreeco/swift-composable-architecture/tree/thread-identity |
Beta Was this translation helpful? Give feedback.
-
Right, I think we've tracked down the problem - we were creating a scoped store on a background thread. We have a feature where we prompt for access to the user's contacts and the API for requesting permission calls back on a non-main thread - this is where we were initialising a scoped store to inject into our view. This is also why we didn't see it every time - once you've already granted permission once, subsequent calls to this API just call back immediately on the same thread. So the bug was in our code after all (phew) but its very strange that it is being printed with a name We set a breakpoint on the code that calls
The weird thing is that when TCA prints its warning message, the name has changed:
Anyway, I think we can close this but I do wonder if TCA could also surface a similar warning/breakpoint if you try to call |
Beta Was this translation helpful? Give feedback.
-
Ah glad you tracked it down! Thanks for digging into all of that.
Totally agree, we'll PR something soon. |
Beta Was this translation helpful? Give feedback.
-
Maybe TCA could ship with a Main Thread locked subclass of |
Beta Was this translation helpful? Give feedback.
-
Also I'm going to convert this to a discussion for now. May help others in the future. |
Beta Was this translation helpful? Give feedback.
-
@lukeredpath Hopefully this will help in the future! #803 |
Beta Was this translation helpful? Give feedback.
-
I'm seeing a related issue, also caused by the thread checking code: In our app we have a number of "Services" which are independent TCA Stores and run on a background thread, they are not part of the main App State. We create a separate I was trying to find a workaround in our code for this, by creating the stores using This means it's actually impossible to guarantee that a Store is created on the same thread on which I guess So far the only "workaround" I have found is to remove the thread checking code, which is obviously not a viable long term solution, as it requires a fork of the library. |
Beta Was this translation helpful? Give feedback.
-
Thanks everyone for all the great discussion. We talked about it more this morning and we think there's a happy medium somewhere between what @tgrapperon has explored (which updates the API to opt-in/out of the behavior) and our current solution (which tries to automate the whole thing). PR coming soon. |
Beta Was this translation helpful? Give feedback.
Right, I think we've tracked down the problem - we were creating a scoped store on a background thread. We have a feature where we prompt for access to the user's contacts and the API for requesting permission calls back on a non-main thread - this is where we were initialising a scoped store to inject into our view. This is also why we didn't see it every time - once you've already granted permission once, subsequent calls to this API just call back immediately on the same thread.
So the bug was in our code after all (phew) but its very strange that it is being printed with a name
main
.We set a breakpoint on the code that calls
.scope
and verified that we were on a background thread (on…