Reputation: 123
Hi I was wonder if anyone can help. I have watch app that does a check then passes two bit data to main app (a String and a Int). Then I want it to save them to CoreData. I have tried a few things. The Comment out code is want I need to save them to 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
Upvotes: 1
Views: 517
Reputation: 29414
If you make all this available to your iOS and WatchKit Extension you will have a working sample
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
Upvotes: 2