Swift中Realm Flexible Sync报错:初始同步未完成时无法连接
问题:Realm Flexible Sync 同步失败,报错"Client tried to connect using flexible sync before initial sync is complete"
问题背景
我是Swift和MongoDB Realm新手,尝试用Realm将数据同步到MongoDB Atlas,但文档始终不显示在云端,反复出现错误:Client tried to connect using flexible sync before initial sync is complete。
代码实现
主逻辑代码
import SwiftUI import RealmSwift import UIKit typealias SwiftApp = SwiftUI.App typealias RealmApp = RealmSwift.App typealias SwiftList = SwiftUI.List typealias RealmList = RealmSwift.List typealias RealmUser = RealmSwift.User typealias UserSchema = User func openSyncedRealm(user: RealmUser) async throws -> Realm { var flexSyncConfig = Globals.app.currentUser!.flexibleSyncConfiguration(initialSubscriptions: { subs in subs.append( QuerySubscription<UserSchema> { try! $0._id == ObjectId(string: user.id) }) }, rerunOnOpen: true) flexSyncConfig.objectTypes = [User.self] print("Got to let realm") let realm = try await Realm(configuration: flexSyncConfig, downloadBeforeOpen: .never) print("finished let realm") return realm } func login(userCredentials: Credentials) async { do { let user = try await Globals.app.login(credentials: userCredentials) print("Successfully logged in user: \(user)") } catch { print("Error logging in: \(error.localizedDescription)") } } func registerUser(email: String, password: String, app: RealmApp) async { let authInstance: EmailPasswordAuth = EmailPasswordAuth(app: app) do { try await authInstance.registerUser(email: email, password: password) print("Successfully registered user.") } catch { print("Failed to register: \(error.localizedDescription)") } } @main struct Main { @MainActor static func main() async { let dummyuser = UserSchema( _id: try! ObjectId(string: Globals.app.currentUser!.id), username: "jennierubyjane", displayName: "Jennie Kim" ) Globals.app.syncManager.errorHandler = { error,session in print("ERROR LOCALIZED DESCRIPTION: \(error.localizedDescription)") } let email = "jenniekim@gmail.com" let password = "BLACKP1NK_in_your_area!" await registerUser(email: email, password: password, app: Globals.app) await login(userCredentials: Credentials.emailPassword( email: email, password: password)) do{ let realm: Realm = try await openSyncedRealm(user:Globals.app.currentUser!) let subscriptions = realm.subscriptions print(subscriptions.count) subscriptions.update { subscriptions.append( QuerySubscription<UserSchema> { try! $0._id == ObjectId(string: Globals.app.currentUser!.id) }) } onComplete: { error in if let error=error { print("Failed to subscribe: \(error.localizedDescription)") } } print("subscriptions.update ran") print(realm.syncSession!) do { try! realm.write { realm.add(dummyuser) } print("REALM CONNECTION STATE: \(String(describing: realm.syncSession?.connectionState))") print("Got to write copy") print("Wrote copy") } catch { if error.localizedDescription.contains("existing primary key value") { print("User already exists") } else { throw error } } print("realm.write") } catch { print("Error opening realm: \(error.localizedDescription)") } MyApp.main() } } struct MyApp: SwiftApp { var body: some Scene { WindowGroup { StartupPage() } } } class Globals { static var appConfig: AppConfiguration = AppConfiguration(baseURL: "https://realm.mongodb.com") static var app: RealmApp = RealmApp(id: "MyAPP-asdfghjkl", configuration: appConfig) }
User模型代码
import RealmSwift import Foundation import UIKit class User: Object { @Persisted(primaryKey: true) var _id: ObjectId? @Persisted var displayName: String = "" @Persisted var profilePic: List<Int> @Persisted var username: String = "" convenience init(_id: ObjectId? = nil, username: String, displayName: String) { self.init() self._id = _id self.displayName = displayName self.username = username } }
运行日志
Successfully registered user. Successfully logged in user: <RLMUser: 0x60000283f5c0> Got to let realm 2023-07-11 15:39:50.303804+0800 MyApp[16275:219469] Info: Realm sync client ([realm-core-13.15.1]) 2023-07-11 15:39:50.304049+0800 MyApp[16275:219469] Info: Platform: iOS Darwin 21.6.0 Darwin Kernel Version 21.6.0: Wed Aug 10 14:25:27 PDT 2022; root:xnu-8020.141.5~2/RELEASE_X86_64 x86_64 2023-07-11 15:39:50.388790+0800 MyApp[16275:219522] Info: Connection[1]: Session[1]: Binding '/Users/.../Documents/mongodb-realm/MyApp-asdfghjkl/64ad0745b4aa2cb84f6760b8/flx_sync_default.realm' to '' 2023-07-11 15:39:50.389977+0800 MyApp[16275:219522] Info: Connection[1]: Session[1]: client_reset_config = false, Realm exists = true, client reset = false 2023-07-11 15:39:50.391207+0800 MyApp[16275:219522] Info: Connection[1]: Connecting to 'wss://ws.us-east-1.aws.realm.mongodb.com:443/api/client/v2.0/app/MyApp-asdfghjkl/realm-sync' finished let realm 1 subscriptions.update ran <RLMSyncSession: 0x600002813da0> { state = 0; connectionState = 1; realmURL = wss://ws.us-east-1.aws.realm.mongodb.com/api/client/v2.0/app/MyApp-asdfghjkl/realm-sync; user = 64ad0745b4aa2cb84f6760b8; } REALM CONNECTION STATE: Optional(__C.RLMSyncConnectionState) Got to write copy Wrote copy realm.write 2023-07-11 15:39:50.611646+0800 MyApp[16275:219522] Info: Connected to endpoint '1.1.1.1:111' (from '1.1.1.1:1234') 2023-07-11 15:39:51.558997+0800 MyApp[16275:219522] Info: Connection[1]: Connected to app services with request id: "64ad0747e675f16fea06eade" 2023-07-11 15:39:51.846057+0800 MyApp[16275:219522] Info: Connection[1]: Session[1]: Received: ERROR "Client tried to connect using flexible sync before initial sync is complete" (error_code=229, try_again=true, error_action=Transient) 2023-07-11 15:39:54.072277+0800 MyApp[16275:219522] Info: Connection[1]: Session[1]: Received: ERROR "Client tried to connect using flexible sync before initial sync is complete" (error_code=229, try_again=true, error_action=Transient) 2023-07-11 15:39:58.327243+0800 MyApp[16275:219522] Info: Connection[1]: Session[1]: Received: ERROR "Client tried to connect using flexible sync before initial sync is complete" (error_code=229, try_again=true, error_action=Transient) 2023-07-11 15:40:06.551432+0800 MyApp[16275:219522] Info: Connection[1]: Session[1]: Received: ERROR "Client tried to connect using flexible sync before initial sync is complete" (error_code=229, try_again=true, error_action=Transient) 2023-07-11 15:40:22.774068+0800 MyApp[16275:219522] Info: Connection[1]: Session[1]: Received: ERROR "Client tried to connect using flexible sync before initial sync is complete" (error_code=229, try_again=true, error_action=Transient) 2023-07-11 15:40:52.776975+0800 MyApp[16275:219522] Info: Connection[1]: Disconnected
关键测试现象
- 本地写入成功,数据可跨会话持久化;重复写入同主键User对象第三次会因主键重复崩溃,可能和用户注册及ObjectId生成逻辑有关。
- RealmUser的订阅会跨测试会话保留,
subscriptions.count随测试次数递增。 - 用户注册成功,可在App Services UI中查看。
- 使用
writeCopy()时提示Could not write file as not all client changes are integrated in server。
解决方案
1. 修正Realm打开逻辑,等待初始同步完成
当前使用downloadBeforeOpen: .never会跳过初始同步检查,直接打开Realm写入数据触发错误。改为等待初始同步完成后再打开:
func openSyncedRealm(user: RealmUser) async throws -> Realm { var flexSyncConfig = user.flexibleSyncConfiguration(initialSubscriptions: { subs in // 避免重复添加订阅,先检查是否已存在 guard subs.first(where: { $0.name == "userSelf" }) == nil else { return } subs.append( QuerySubscription<UserSchema>(name: "userSelf") { $0._id == ObjectId(string: user.id) } ) }, rerunOnOpen: true) flexSyncConfig.objectTypes = [User.self] print("Got to let realm") // 等待初始同步完成后打开Realm let realm = try await Realm(configuration: flexSyncConfig, downloadBeforeOpen: .always) print("finished let realm") return realm }
2. 移除重复订阅代码
初始订阅已在Realm配置中设置,无需在打开Realm后再次添加,避免订阅数量递增:
// 移除以下重复添加订阅的代码块 /* let subscriptions = realm.subscriptions print(subscriptions.count) subscriptions.update { subscriptions.append( QuerySubscription<UserSchema> { try! $0._id == ObjectId(string: Globals.app.currentUser!.id) }) } onComplete: { error in if let error=error { print("Failed to subscribe: \(error.localizedDescription)") } } */
3. 等待订阅同步完成后再写入数据
确保同步会话准备就绪后再执行写入操作,避免本地数据无法同步:
// 打开Realm后添加以下代码,等待订阅同步完成 try await realm.subscriptions.waitForSynchronization() // 执行写入操作时先检查用户是否已存在,避免主键崩溃 do { try realm.write { if realm.object(ofType: UserSchema.self, forPrimaryKey: dummyuser._id) == nil { realm.add(dummyuser) } else { print("User already exists") } } print("REALM CONNECTION STATE: \(String(describing: realm.syncSession?.connectionState))") print("Wrote user to realm") } catch { print("Error writing to realm: \(error.localizedDescription)") }
4. 修正ObjectId初始化的崩溃风险
将dummyuser的初始化移到登录成功后,避免强制解包未登录的用户ID:
@MainActor static func main() async { Globals.app.syncManager.errorHandler = { error,session in print("ERROR LOCALIZED DESCRIPTION: \(error.localizedDescription)") } let email = "jenniekim@gmail.com" let password = "BLACKP1NK_in_your_area!" await registerUser(email: email, password: password, app: Globals.app) await login(userCredentials: Credentials.emailPassword( email: email, password: password)) // 登录成功后再初始化dummyuser guard let currentUser = Globals.app.currentUser else { print("No logged in user") return } let dummyuser = UserSchema( _id: ObjectId(string: currentUser.id), username: "jennierubyjane", displayName: "Jennie Kim" ) // 后续打开Realm、写入逻辑... }
5. 网络环境优化(针对中国地区)
- 切换至稳定网络,检查防火墙/代理是否阻挡Realm的WebSocket连接(
wss://ws.us-east-1.aws.realm.mongodb.com)。
内容的提问来源于stack exchange,提问作者Eric Liu
相关产品推荐
相关产品推荐

