Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SwiftUI - Publishing changes from background threads is not allowed #8132

Closed
sonisan opened this issue Feb 14, 2023 · 3 comments · Fixed by #8114 or #8140
Closed

SwiftUI - Publishing changes from background threads is not allowed #8132

sonisan opened this issue Feb 14, 2023 · 3 comments · Fixed by #8114 or #8140

Comments

@sonisan
Copy link

sonisan commented Feb 14, 2023

How frequently does the bug occur?

Sometimes

Description

I am building an iOS app using SwiftUI and Realm flexible sync. When launching the app with a user logged in, the following warning (twice the same, actually) at runtime sometimes appears:

Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive(on:)) on model updates.

from the Sync file on line:

public func receive<S>(subscriber: S) where S: Subscriber, S.Failure == Never, Output == S.Input {
        let token = user.subscribe { _ in
            _ = subscriber.receive(self.user) // <--- WARNING HERE
        }

        subscriber.receive(subscription: UserSubscription(user: user, token: token))
    }

located in {{public class UserPublisher: Publisher }}

I am not getting this warning all the time, but from the below stacktrace it seems to be related to the user's token refresh. The stacktrace does not pinpoint to a specific place in my own code so I am not sure if that's an issue coming from my app or the SDK.

Stacktrace & log output

Not getting any crash, but here is the stacktrace associated to the thread:


#0	0x0000000000000000 in ___lldb_unnamed_symbol162166 ()
#1	0x0000000000000000 in ___lldb_unnamed_symbol162165 ()
#2	0x0000000000000000 in ___lldb_unnamed_symbol162185 ()
#3	0x0000000000000000 in ___lldb_unnamed_symbol186536 ()
#4	0x0000000000000000 in closure #1 in UserPublisher.receive<τ_0_0>(subscriber:) at /Users/******-cghdayqxcodbycddurohdjmxfkbe/SourcePackages/checkouts/realm-swift/RealmSwift/Sync.swift:969
#5	0x0000000000000000 in thunk for @escaping @callee_guaranteed (@guaranteed RLMUser) -> () ()
#6	0x0000000000000000 in auto -[RLMUser subscribe:]::$_3::operator()<realm::SyncUser const>(realm::SyncUser const&) const at /Users/******-cghdayqxcodbycddurohdjmxfkbe/SourcePackages/checkouts/realm-swift/Realm/RLMUser.mm:423
#7	0x0000000000000000 in decltype(static_cast<-[RLMUser subscribe:]::$_3&>(fp)(static_cast<realm::SyncUser const&>(fp0))) std::__1::__invoke<-[RLMUser subscribe:]::$_3&, realm::SyncUser const&>(-[RLMUser subscribe:]::$_3&, realm::SyncUser const&) at /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator16.2.sdk/usr/include/c++/v1/type_traits:3918
#8	0x0000000000000000 in void std::__1::__invoke_void_return_wrapper<void, true>::__call<-[RLMUser subscribe:]::$_3&, realm::SyncUser const&>(-[RLMUser subscribe:]::$_3&, realm::SyncUser const&) at /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator16.2.sdk/usr/include/c++/v1/__functional/invoke.h:61
#9	0x0000000000000000 in std::__1::__function::__alloc_func<-[RLMUser subscribe:]::$_3, std::__1::allocator<-[RLMUser subscribe:]::$_3>, void (realm::SyncUser const&)>::operator()(realm::SyncUser const&) at /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator16.2.sdk/usr/include/c++/v1/__functional/function.h:178
#10	0x0000000000000000 in std::__1::__function::__func<-[RLMUser subscribe:]::$_3, std::__1::allocator<-[RLMUser subscribe:]::$_3>, void (realm::SyncUser const&)>::operator()(realm::SyncUser const&) at /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator16.2.sdk/usr/include/c++/v1/__functional/function.h:352
#11	0x0000000000000000 in std::__1::__function::__value_func<void (realm::SyncUser const&)>::operator()(realm::SyncUser const&) const at /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator16.2.sdk/usr/include/c++/v1/__functional/function.h:505
#12	0x0000000000000000 in std::__1::function<void (realm::SyncUser const&)>::operator()(realm::SyncUser const&) const at /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator16.2.sdk/usr/include/c++/v1/__functional/function.h:1182
#13	0x0000000000000000 in realm::Subscribable<realm::SyncUser>::emit_change_to_subscribers(realm::SyncUser const&) const at /Users/*******-cghdayqxcodbycddurohdjmxfkbe/SourcePackages/checkouts/realm-core/src/realm/object-store/sync/subscribable.hpp:149
#14	0x0000000000000000 in auto realm::SyncUser::refresh_custom_data(realm::util::UniqueFunction<void (std::__1::optional<realm::app::AppError>)>)::$_9::operator()<std::__1::optional<realm::app::AppError> >(std::__1::optional<realm::app::AppError>) const at /Users/********-cghdayqxcodbycddurohdjmxfkbe/SourcePackages/checkouts/realm-core/src/realm/object-store/sync/sync_user.cpp:510
#15	0x0000000000000000 in void realm::util::UniqueFunction<void (std::__1::optional<realm::app::AppError>)>::call_regular_void<realm::SyncUser::refresh_custom_data(realm::util::UniqueFunction<void (std::__1::optional<realm::app::AppError>)>)::$_9>(std::__1::integral_constant<bool, true>, realm::SyncUser::refresh_custom_data(realm::util::UniqueFunction<void (std::__1::optional<realm::app::AppError>)>)::$_9&, std::__1::optional<realm::app::AppError>&&) at /Users*******-cghdayqxcodbycddurohdjmxfkbe/SourcePackages/checkouts/realm-core/src/realm/util/functional.hpp:148
#16	0x0000000000000000 in realm::util::UniqueFunction<void (std::__1::optional<realm::app::AppError>)>::SpecificImpl<realm::SyncUser::refresh_custom_data(realm::util::UniqueFunction<void (std::__1::optional<realm::app::AppError>)>)::$_9>::call(std::__1::optional<realm::app::AppError>&&) at /Users/*******-cghdayqxcodbycddurohdjmxfkbe/SourcePackages/checkouts/realm-core/src/realm/util/functional.hpp:168
#17	0x0000000000000000 in realm::util::UniqueFunction<void (std::__1::optional<realm::app::AppError>)>::operator()(std::__1::optional<realm::app::AppError>) const at /Users/******-cghdayqxcodbycddurohdjmxfkbe/SourcePackages/checkouts/realm-core/src/realm/util/functional.hpp:94
#18	0x0000000000000000 in realm::app::App::refresh_access_token(std::__1::shared_ptr<realm::SyncUser> const&, realm::util::UniqueFunction<void (std::__1::optional<realm::app::AppError>)>&&)::$_12::operator()(realm::app::Response const&) const at /Users/*********-cghdayqxcodbycddurohdjmxfkbe/SourcePackages/checkouts/realm-core/src/realm/object-store/sync/app.cpp:1072
#19	0x0000000000000000 in void realm::util::UniqueFunction<void (realm::app::Response const&)>::call_regular_void<realm::app::App::refresh_access_token(std::__1::shared_ptr<realm::SyncUser> const&, realm::util::UniqueFunction<void (std::__1::optional<realm::app::AppError>)>&&)::$_12>(std::__1::integral_constant<bool, true>, realm::app::App::refresh_access_token(std::__1::shared_ptr<realm::SyncUser> const&, realm::util::UniqueFunction<void (std::__1::optional<realm::app::AppError>)>&&)::$_12&, realm::app::Response const&) at /Users/******-cghdayqxcodbycddurohdjmxfkbe/SourcePackages/checkouts/realm-core/src/realm/util/functional.hpp:148
#20	0x0000000000000000 in realm::util::UniqueFunction<void (realm::app::Response const&)>::SpecificImpl<realm::app::App::refresh_access_token(std::__1::shared_ptr<realm::SyncUser> const&, realm::util::UniqueFunction<void (std::__1::optional<realm::app::AppError>)>&&)::$_12>::call(realm::app::Response const&) at /Users/*****-cghdayqxcodbycddurohdjmxfkbe/SourcePackages/checkouts/realm-core/src/realm/util/functional.hpp:168
#21	0x0000000000000000 in realm::util::UniqueFunction<void (realm::app::Response const&)>::operator()(realm::app::Response const&) const at /Users/********-cghdayqxcodbycddurohdjmxfkbe/SourcePackages/checkouts/realm-core/src/realm/util/functional.hpp:94
#22	0x0000000000000000 in realm::app::App::handle_possible_redirect_response(realm::app::Request&&, realm::app::Response const&, realm::util::UniqueFunction<void (realm::app::Response const&)>&&) at /Users/*******-cghdayqxcodbycddurohdjmxfkbe/SourcePackages/checkouts/realm-core/src/realm/object-store/sync/app.cpp:938
#23	0x0000000000000000 in realm::app::App::do_request(realm::app::Request&&, realm::util::UniqueFunction<void (realm::app::Response const&)>&&)::$_9::operator()(realm::app::Request&&, realm::app::Response const&) at /Users/********-cghdayqxcodbycddurohdjmxfkbe/SourcePackages/checkouts/realm-core/src/realm/object-store/sync/app.cpp:919
#24	0x0000000000000000 in void realm::util::UniqueFunction<void (realm::app::Request&&, realm::app::Response const&)>::call_regular_void<realm::app::App::do_request(realm::app::Request&&, realm::util::UniqueFunction<void (realm::app::Response const&)>&&)::$_9>(std::__1::integral_constant<bool, true>, realm::app::App::do_request(realm::app::Request&&, realm::util::UniqueFunction<void (realm::app::Response const&)>&&)::$_9&, realm::app::Request&&, realm::app::Response const&) at /Users/***********-cghdayqxcodbycddurohdjmxfkbe/SourcePackages/checkouts/realm-core/src/realm/util/functional.hpp:148
#25	0x0000000000000000 in realm::util::UniqueFunction<void (realm::app::Request&&, realm::app::Response const&)>::SpecificImpl<realm::app::App::do_request(realm::app::Request&&, realm::util::UniqueFunction<void (realm::app::Response const&)>&&)::$_9>::call(realm::app::Request&&, realm::app::Response const&) at /Users/*****-cghdayqxcodbycddurohdjmxfkbe/SourcePackages/checkouts/realm-core/src/realm/util/functional.hpp:168
#26	0x0000000000000000 in realm::util::UniqueFunction<void (realm::app::Request&&, realm::app::Response const&)>::operator()(realm::app::Request&&, realm::app::Response const&) const at /Users/**********-cghdayqxcodbycddurohdjmxfkbe/SourcePackages/checkouts/realm-core/src/realm/util/functional.hpp:94
#27	0x0000000000000000 in realm::app::GenericNetworkTransport::send_request_to_server(realm::app::Request&&, realm::util::UniqueFunction<void (realm::app::Request&&, realm::app::Response const&)>&&)::'lambda'(realm::app::Response const&)::operator()(realm::app::Response const&) const at /Users/********-cghdayqxcodbycddurohdjmxfkbe/SourcePackages/checkouts/realm-core/src/realm/object-store/sync/generic_network_transport.hpp:254
#28	0x0000000000000000 in void realm::util::UniqueFunction<void (realm::app::Response const&)>::call_regular_void<realm::app::GenericNetworkTransport::send_request_to_server(realm::app::Request&&, realm::util::UniqueFunction<void (realm::app::Request&&, realm::app::Response const&)>&&)::'lambda'(realm::app::Response const&)>(std::__1::integral_constant<bool, true>, realm::app::GenericNetworkTransport::send_request_to_server(realm::app::Request&&, realm::util::UniqueFunction<void (realm::app::Request&&, realm::app::Response const&)>&&)::'lambda'(realm::app::Response const&)&, realm::app::Response const&) at /Users/*******-cghdayqxcodbycddurohdjmxfkbe/SourcePackages/checkouts/realm-core/src/realm/util/functional.hpp:148
#29	0x0000000000000000 in realm::util::UniqueFunction<void (realm::app::Response const&)>::SpecificImpl<realm::app::GenericNetworkTransport::send_request_to_server(realm::app::Request&&, realm::util::UniqueFunction<void (realm::app::Request&&, realm::app::Response const&)>&&)::'lambda'(realm::app::Response const&)>::call(realm::app::Response const&) at /Users/*******-cghdayqxcodbycddurohdjmxfkbe/SourcePackages/checkouts/realm-core/src/realm/util/functional.hpp:168
#30	0x0000000000000000 in realm::util::UniqueFunction<void (realm::app::Response const&)>::operator()(realm::app::Response const&) const at /Users/*******-cghdayqxcodbycddurohdjmxfkbe/SourcePackages/checkouts/realm-core/src/realm/util/functional.hpp:94
#31	0x0000000000000000 in invocation function for block in (anonymous namespace)::CocoaNetworkTransport::send_request_to_server(realm::app::Request const&, realm::util::UniqueFunction<void (realm::app::Response const&)>&&) at /Users/*********-cghdayqxcodbycddurohdjmxfkbe/SourcePackages/checkouts/realm-swift/Realm/RLMApp.mm:70
#32	0x0000000000000000 in -[RLMSessionDelegate URLSession:task:didCompleteWithError:] at /Users********-cghdayqxcodbycddurohdjmxfkbe/SourcePackages/checkouts/realm-swift/Realm/RLMNetworkTransport.mm:166
#33	0x0000000000000000 in ___lldb_unnamed_symbol2742 ()
#34	0x0000000000000000 in _dispatch_call_block_and_release ()
#35	0x0000000000000000 in _dispatch_client_callout ()
#36	0x0000000000000000 in _dispatch_lane_serial_drain ()
#37	0x0000000000000000 in _dispatch_lane_invoke ()
#38	0x0000000000000000 in _dispatch_workloop_worker_thread ()
#39	0x0000000000000000 in _pthread_wqthread ()
#40	0x0000000000000000 in start_wqthread ()

Can you reproduce the bug?

Sometimes

Reproduction Steps

No reproduction steps have been found yet. But it appears once after a while, coinciding with the SDK refreshing the user token.

Version

10.35.0

What Atlas Services are you using?

Both Atlas Device Sync and Atlas App Services

Are you using encryption?

No

Platform OS and version(s)

iOS 16.2, 16.3

Build environment

Xcode version: 14.2

@leemaguire
Copy link
Contributor

Hi @sonisan thanks for reporting this. We will investigate the issue.

@jsflax
Copy link
Contributor

jsflax commented Feb 14, 2023

@sonisan where are you attaching the publisher? Can you show us the code where you install the @ObservedObject?

@sonisan
Copy link
Author

sonisan commented Feb 15, 2023

Thanks @leemaguire .
@jsflax are you referring to the publisher attached to RLMUser?
Here it is:

From

var body: some View {
		switch (deeplinkTarget) {
		case .home:
			if let currentRealmUser = app.currentUser {
				let configuration = currentRealmUser.flexibleSyncConfiguration()
				
				OpenSyncedRealmView(currentRealmUser: currentRealmUser)
						.environment(\.realmConfiguration, configuration)
			}
			...
		}
		...
}
...

leading to:

struct OpenSyncedRealmView: View {
	@ObservedObject var currentRealmUser: RealmSwift.User
	@ObservedResults(User.self) var users
	...
	
	var body: some View {
		if let userId = ...,
		   let currentUser = ... {
		   
		   // show main views
		   ...
		   } else {
		   // add subscriptions
		   ...
		   }
	...
	}
}

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.