You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 06:04:49