【问题标题】:Saving UserInfo received via WatchConnectivity to CoreData将通过 WatchConnectivity 收到的 UserInfo 保存到 CoreData
【发布时间】:2021-09-05 07:47:23
【问题描述】:

您好,我想知道是否有人可以提供帮助。我有手表应用程序进行检查,然后将两位数据传递给主应用程序(一个字符串和一个整数)。然后我希望它将它们保存到 CoreData。我已经尝试了几件事。注释掉代码是希望我需要将它们保存到 CoreData。

#if os(iOS)
// With This get *SwiftUI:0: Fatal error: No ObservableObject of type DataController found. A View.environmentObject(_:) for DataController may be missing as an ancestor of this view.*
@EnvironmentObject var dataController: DataController

// This does saved to CoreData but then crashes the iOS app!
//    @ObservedObject var dataController = DataController()

func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) {
    DispatchQueue.main.async {

    print("Data received \(userInfo)")
//            let viewContext = dataController.container.viewContext

    if let isDefect = userInfo["isDefect"] as? Bool {
        if isDefect {
// This is another data that save to different entity
            print("Defect")
//                let defect = Defect(context: viewContext)
        } else {
            print("Vehicle check")
//                let checkData = CheckData(context: viewContext)
//
            if let bonnet = userInfo["bonnet"] as? String {
//                    checkData.bonnet = bonnet
                print(bonnet)
            }
//
            if let numberDefect = userInfo["numberDefect"] as? Int {
//                    checkData.numberDefect = Int64(numberDefect)
                print("\(numberDefect)")
            }
//
//                checkData.dateCreated = Date()
        }
    }

//            dataController.save()
    }
}

// other Protocol methods that are required
#else
// watch Protocol method
#endif

【问题讨论】:

  • EnvironmentObject 的错误是因为您没有在父视图上通过 .environmentObject() 提供对象。例如,请参阅stackoverflow.com/a/66835156/560942
  • 我认为您错过了问题的重点。这不是如何使用EnvironmentObject,因为我在主应用程序的许多地方都使用了它(.environmentObject()@mainApp 上。这是如何使用userInfo: [String : Any] 并得到它数据到 App 的 CoreData。
  • 观察 SwiftUI 包装器仅适用于 SwiftUI 视图。您显然在一个类中(WCSessionDelegate 需要类/NSObject),因此您的代码可能会失败,因为您在一个类中使用了 SwiftUI 包装器。如果没有Minimal Reproducible Example,就无法帮助您进行故障排除。只是猜测
  • @Nigel 这就是为什么我没有把它作为答案,因为它似乎不是主要问题。但是,您在代码中的第一条评论清楚地表明这是一个问题。

标签: swiftui watchos watchconnectivity


【解决方案1】:

如果您将所有这些都提供给您的 iOS 和 WatchKit 扩展,您将获得一个工作示例

import SwiftUI

struct WatchConnectView: View {
    @Environment(\.managedObjectContext) private var viewContext
    
    @FetchRequest(
        sortDescriptors: [NSSortDescriptor(keyPath: \CheckData.dateCreated, ascending: true)],
        animation: .default) private var items: FetchedResults<CheckData>
    let wcManager = WatchConnectivityManager.shared
    var body: some View {
        VStack{
            Button("send vehicle check", action: {
                wcManager.sendVehicleCheck()
            })
            List {
                ForEach(items) { item in
                    Text("CheckData at \(item)")
                }
            }
            
        }
    }
}

struct WatchConnectView_Previews: PreviewProvider {
    static var previews: some View {
        WatchConnectView()
    }
}
//This would handle all the work for watch connectivity regardless of target
import WatchConnectivity
class WatchConnectivityManager: NSObject, Identifiable {
    let id = UUID()
    private let session: WCSession = .default
    var isReachable: Bool{
        session.isReachable
    }
    //Need singleton because of delegate
    static let shared: WatchConnectivityManager = WatchConnectivityManager()
    //If you have target specific code it is easier to manage if it is separated
    let targetSpecifc = WatchConnectivityManagerTS()
    private override init() {
        super.init()
        session.delegate = self
        session.activate()
    }
    func sendUserInfo(userInfo: [String: Any]){
        if isReachable{
            session.transferUserInfo(userInfo)
        }
    }
    func sendVehicleCheck(bonnet: String = "Some Bonnet", numberDefect: Int = Int.random(in: 0...50)){
        var userInfo: [String: Any] = [:]
        userInfo["isDefect"] = false
        userInfo["bonnet"] = bonnet
        userInfo["numberDefect"] = numberDefect
        
        sendUserInfo(userInfo: userInfo)
    }
}
extension WatchConnectivityManager: WCSessionDelegate{
#if os(iOS)
    func sessionDidBecomeInactive(_ session: WCSession) {
        
    }
    
    func sessionDidDeactivate(_ session: WCSession) {
        
    }
#endif
    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
        if let error = error{
            print(error)
        }
    }
    
    func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) {
        if let isDefect = userInfo["isDefect"] as? Bool {
            if isDefect {
                print("Defect")
            } else {
                print("Vehicle check")
                targetSpecifc.saveVehicleCheck(userInfo: userInfo)
                
            }
        }
    }
    func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void) {

    }
    func session(_ session: WCSession, didReceiveMessage message: [String : Any]) {

    }
}
//Asuming CoreData is only on iOS
#if !os(watchOS)
class DataController{
    //Access to the context depends on how you are creating the stack.
    //This is with the standard PersistenceController setup that
    //comes with XCode sample code
    let context = PersistenceController.shared.container.viewContext
    init(){}
    func newCheckData() -> CheckData{
        let new  = CheckData(context: context)
        new.dateCreated = Date()
        return new
    }
    
    func saveContext(){
        do{
            try context.save()
        }catch{
            print(error)
        }
    }
    
}
class WatchConnectivityManagerTS{
    let dataController = DataController()
    func saveVehicleCheck(userInfo: [String: Any]){
        let checkData = self.dataController.newCheckData()
        
        if let bonnet = userInfo["bonnet"] as? String {
            checkData.bonnet = bonnet
            print(bonnet)
        }
        
        if let numberDefect = userInfo["numberDefect"] as? Int {
            checkData.numberDefect = Int64(numberDefect)
            print("\(numberDefect)")
        }
        dataController.saveContext()
    }
}
#else
class WatchConnectivityManagerTS{
    //In case there is a reason for the watch to receive vechicle checks
    func saveVehicleCheck(userInfo: [String: Any]){
        print("save attempt on watch \(userInfo)")
    }
}
#endif

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-27
    • 2015-03-15
    相关资源
    最近更新 更多