为啥我的领域通知令牌仅在我的活动设备上触发,而不是在所有具有开放领域的设备上触发?

Posted

技术标签:

【中文标题】为啥我的领域通知令牌仅在我的活动设备上触发,而不是在所有具有开放领域的设备上触发?【英文标题】:Why does my realm notification token only trigger on my active device and not all devices with the open realm?为什么我的领域通知令牌仅在我的活动设备上触发,而不是在所有具有开放领域的设备上触发? 【发布时间】:2021-12-06 22:18:01 【问题描述】:

我正在学习 ios/swift 编程并使用 MongoDB 领域和领域同步开发应用程序。我是编程和领域的新手,所以请随时更正任何术语。我的问题是关于侦听领域通知,我认为这被称为更改侦听器和通知令牌。无论如何,这是信息:

我的申请有一个位置列表,其状态为(已确认/待定/已取消)。我从我的领域打开此列表作为领域托管对象并创建我的通知处理程序:

//This is called in it's own function, but assigns the locations 
locations = publicRealm?.objects(Location.self)
    
//This is run after the function is called 
self?.notificationToken = self?.locations!.observe  [weak self] (_) in
                                  self?.tableView.reloadData() 
print("Notification Token!!!")

然后我填充我的表格视图并让用户点击一个位置,这会将位置和领域传递给另一个视图控制器,用户可以在其中更新状态。该更新是在单独的视图控制器中进行的。

    do
        try publicRealm?.write 
            selectedLocation?.statusMessage = locationStatusTextField.text!
            selectedLocation?.status = selectedStatus
        
    catch
        print("Error saving location data: \(error)")
    

此时我的通知令牌已在我进行位置更新的设备上成功触发。更改立即显示。但是,在显示位置表视图的任何其他打开的设备上都不会发生通知令牌或领域刷新。他们不响应更改,只有在我强制使用 realm.refresh() 时才会响应。不过,更改显示在 MongoDB 服务器上的 Atlas 中。

我正在多个模拟器和我自己的个人手机上进行测试,所有这些都在 Xcode 中。

我很困惑我的通知令牌如何在一台设备上触发,而不是在另一台设备上触发。

当我第一次开始这个项目时,它是一个更简单的领域模型,我可以在模拟器中运行两个设备,更新一个设备会立即触发更改通知,并导致第二个设备显示正确的通知。

我已经更新到一个新的领域版本,也使领域模型更加复杂。尽管出于此目的,我试图通过在一个领域中通过 swift 进行所有更改来保持简单。

我也有领域自定义用户函数正在运行和更改数据,但我认为阅读文档我意识到那些不会触发通知 - 我不确定这是不是真的?我现在才知道,如果我通过用户函数更改数据库中的数据,则不会在任何地方触发任何通知 - 但如果我执行 realm.refresh() 则更改显示。

我在使用这些通知时缺少什么?

***更新公共领域信息:

保存领域: var publicRealm:Realm?

以匿名用户身份登录,然后打开领域:

    let configuration = user.configuration(partitionValue: "PUBLIC")

    Realm.asyncOpen(configuration: configuration)  [weak self](result) in
                    DispatchQueue.main.async 
                        switch result 
                        case .failure(let error):
                            fatalError("Failed to open realm: \(error)")
                        case .success(let publicRealm):
                            self!.publicRealm = publicRealm
                            
                            guard let syncConfiguration = self?.publicRealm?.configuration.syncConfiguration else 
                                fatalError("Sync configuration not found! Realm not opened with sync?")
                            

在此领域打开之后,加载位置并创建通知令牌。

我使用 segue 将位置对象和领域传递给下一个 VC:

        override func prepare(for segue: UIStoryboardSegue, sender: Any?) 
        let destinationVC = segue.destination as! UpdateLocationViewController
        destinationVC.delegate = self
        
        if let indexPath = tableView.indexPathForSelectedRow 
            destinationVC.selectedLocation = locations?[indexPath.row]
        
        
        if indexPathRow != nil 
            destinationVC.selectedLocation = locations?[indexPathRow!]
        
        
        destinationVC.publicRealm = self.publicRealm
    

一些注意事项:

匿名用户打开的原创公共领域 只有登录用户才能点击位置...所以在通过公共领域的“UpdateLocation”VC 中,我是登录用户。但是,我只是使用 Realm 的 Dev 模式来允许我以我喜欢的方式读/写……我正在直接写到那个公共领域以尽量保持简单。 (我有一个自定义用户函数,可以同时写入公共和用户的组织领域,但我现在停止尝试使用该函数) 我根据从第一个 VC 传入的位置对象确定要更新的对象

【问题讨论】:

欢迎来到 SO!你在提出一个明确的问题方面做得很好,看起来你走在正确的道路上。一般来说,当您观察结果对象时,对该对象的任何更改都会触发通知。但是,有些事情可能会使其不起作用;例如,如果对象在不同的​​线程上。要么。如果您在没有运行循环的后台线程上写入数据。在这种情况下,调用 realm.refresh() 会强制执行 runloop,然后更新数据。所以,这个问题可能与publicRealm1 有关——你能把它包括在你的问题中吗? 谢谢杰!坦率地说,除了我认为异步是后台线程之外,我并不完全理解线程,然后在 VC 中发生的任何事情都是主要的。我认为这可能会导致我的问题,但也不明白我的通知令牌(我认为来自 Realm 服务器?)如何在一个应用程序实例而不是另一个应用程序实例中被触发。我现在正在更新我原来的帖子。 我一直在阅读有关线程的更多内容,但我发现我的理解在上面是错误的。虽然我没有在任何后台线程上强加任何东西,所以我认为我应该对 main 中的所有内容都很好。 真的需要阅读 Realm Threading 注意到这一点 - 通过将繁重的工作卸载到后台线程,UI 线程可以保持高度响应,无论大小工作量。也看看教程;您会想要更新您的 .observe 代码,但更重要的是,请注意它们不会传递 Realms。他们传递领域配置,然后每个 viewController 维护它自己的连接。 我已经通读了它,并且随着我不断发展应用程序,我会继续阅读它。感谢您检查我的问题! 【参考方案1】:

我需要使用 try!在进行我的写电话而不是尝试时。我这样更新了我的写块:

   try! publicRealm?.write 
              selectedLocation?.statusMessage = locationStatusTextField.text!
                selectedLocation?.status = selectedStatus
            

只是想跟进以防有人发现此问题。谢谢!

【讨论】:

感谢您发布此内容,但它与问题中的任何内容无关,实际上可能很危险并导致难以追踪的崩溃。 do/try/throw/catch 块(如您的问题)用于处理错误,例如试试这个函数,如果它失败了,捕获错误并处理它。使用“尝试!”保证永远不会出现错误,因此没有错误处理;这可能是一个问题,尤其是在互联网上使用异步函数时。但底线是它们在功能上相同,因此不会对问题产生影响。 哦,请参阅 Hacking With Swift try, catch, do and throw 我理解并同意,并且仍在研究以实施更好的解决方案。但是,这确实回答了我的问题并导致了正确的通知功能。我注意到,realm 在他们的 iOS 任务跟踪器教学应用程序中就是这样处理它的。 我只是不希望其他用户采用它,因为它会导致应用程序崩溃。例如如果 publicRealm 永远不会是 nil 并且永远不会抛出,那么这个 try! publicRealm?.write 将是不必要的 - 它只是 publicRealm.write 就像 x = 0 一样,因为它不会失败。但是,如果Realm.asyncOpen 失败,publicRealm 可以为 nil,如果写入未完成,它可以抛出 - 因此会发生崩溃。目前尚不清楚为什么使用 try! 而不是 do/try/throw/catch 有效,因为它们在功能上是相同的,除了您可能已经收到错误并且 try! 没有处理它?

以上是关于为啥我的领域通知令牌仅在我的活动设备上触发,而不是在所有具有开放领域的设备上触发?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我会通过 Google Cloud To Device Messaging Service 为我的设备获取多个活动令牌?

我可以从通知中启动对话活动,使其显示在我的应用程序窗口上而不是空白背景上吗?

Flutter Local Notification 仅在 Android 上显示点

单击通知不会仅在 AndroidOS Oreo 中启动预期活动?

为啥我在我的设备上收到成功注册消息,而服务器数据库没有变化?

为啥 Galaxy S21 没有自定义通知声音