iOS14: How to using delegate like WCSessionDelegate without NSObject?

I am a new swifter from objective-c.

In the code:

Code Block swift
import SwiftUI
@main
struct MySwiftAppApp: App {
  @SceneBuilder var body: some Scene {
    WindowGroup {
      NavigationView {
        ContentView()
      }
    }
    WKNotificationScene(controller: NotificationController.self, category: "LandmarkNear")
  }
}


Here is no 'WKHostingController' yet.
So, How to using 'WCSessionDelegate'?


I tried like that:
Code Block swift
import SwiftUI
import WatchConnectivity
struct WatchInfo: View {
  @State private var showMessage: String = "Wating"
  
  init() {
    ABWatchSessionManager.sharedInstance.addDelegateObject(WatchSessionDelegate())
  }
   
  var body: some View {
    Text(showMessage)
  }
   
  class WatchSessionDelegate: NSObject, WCSessionDelegate {
    func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void) {
      let data: Dictionary<String, String> = message["data"] as! Dictionary<String, String>
      if data["dataType"] == DataType.ping.rawValue {
      } else if data["dataType"] == DataType.data.rawValue {
      }
    }
     
    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
    }
     
    func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) {
    }
  }
}
struct WatchInfo_Previews: PreviewProvider {
  static var previews: some View {
    WatchInfo()
  }
}




Is it the best way to using Delegate in SwiftUI-only code?

And how can I changing the 'showMessage'?

Thanks!
Answered by Abenx in 618459022
I found a way:

Code Block swift
import SwiftUI
import WatchConnectivity
struct WatchInfo: View {
  @State private var showMessage: String = "Wating"
   
  var body: some View {
    VStack {
      Text(showMessage).onAppear() {
        ABWatchSessionManager.sharedInstance.addDelegateObject(WatchSessionDelegate(self))
      }
    }
  }
   
  class WatchSessionDelegate: NSObject, WCSessionDelegate {
    var parent: WatchInfo
    init(_ watchInfo: WatchInfo) {
      self.parent = watchInfo
    }
     
    func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void) {
      let data: Dictionary<String, String> = message["data"] as! Dictionary<String, String>
      if data["dataType"] == DataType.ping.rawValue {
        parent.showMessage = "Get PING"
      } else if data["dataType"] == DataType.data.rawValue {
        parent.showMessage = "Get DATA"
      }
    }
     
    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
    }
     
    func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) {
    }
  }
}
struct WatchInfo_Previews: PreviewProvider {
  static var previews: some View {
    WatchInfo()
  }
}


The other part of my code:

Code Block swift
import Foundation
import WatchConnectivity
typealias WeakDelegate = () -> WCSessionDelegate?
func makeWeakDelegate(_ delegate: WCSessionDelegate?) -> WeakDelegate? {
  //weak var weakDelegate: WCSessionDelegate? = delegate
  return {
    return delegate
  }
}
func weakDelegateObject(_ weakDelegate: WeakDelegate?) -> WCSessionDelegate? {
  return weakDelegate?() as WCSessionDelegate?
}
class ABWatchSessionManager: NSObject {
   
  private(set) var delegates: [WeakDelegate]? = []
   
  private(set) var session: WCSession = WCSession.default
   
  static let sharedInstance = ABWatchSessionManager()
   
  private override init() {
    super.init()
  }
   
  func startSession() {
    if !WCSession.isSupported() {
      return
    }
    self.session.delegate = self;
    self.session.activate()
  }
   
  #if os(iOS)
  func isValidSession() -> Bool {
    if self.session.isPaired && self.session.isWatchAppInstalled {
      return true
    } else {
      return false
    }
  }
  #endif
   
  func addDelegateObject(_ delegate: WCSessionDelegate?) {
    let abc = makeWeakDelegate(delegate)!
    self.delegates?.append(abc)
  }
   
  func removeDelegateObject(_ delegate: WCSessionDelegate?) {
    self.delegates?.removeAll { $0 as AnyObject === makeWeakDelegate(delegate) as AnyObject }
  }
   
  func sendMessage(_ message: [String : Any], replyHandler: (([String : Any]) -> Void)?, errorHandler: ((Error) -> Void)? = nil) {
    #if os(iOS)
    if !self.isValidSession() {
      return
    }
    #endif
     
    if self.session.isReachable {
      self.session.sendMessage(message, replyHandler: replyHandler, errorHandler: errorHandler)
    } else {
      do {
        try self.session.updateApplicationContext(message)
      } catch (let error) {
        print(error)
      }
    }
  }
   
  func sendMessageData(_ data: Data, replyHandler: ((Data) -> Void)?, errorHandler: ((Error) -> Void)? = nil) {
    #if os(iOS)
    if !self.isValidSession() {
      return
    }
    #endif
     
    if self.session.isReachable {
      self.session.sendMessageData(data, replyHandler: replyHandler, errorHandler: errorHandler)
      return
    }
  }
}
extension ABWatchSessionManager: WCSessionDelegate {
   
  // #if IPHONE_9_3 WATCHOS_2_2
  func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
    self.delegates?.forEach({ (delegate) in
      weakDelegateObject(delegate)?.session(session, activationDidCompleteWith: activationState, error: error)
    })
  }
   
  // #if TARGET_OS_IOS && IPHONE_9_3
  #if os(iOS)
  func sessionDidBecomeInactive(_ session: WCSession) {
    self.delegates?.forEach({ (delegate) in
      weakDelegateObject(delegate)?.sessionDidBecomeInactive(session)
    })
  }
   
  func sessionDidDeactivate(_ session: WCSession) {
    self.delegates?.forEach({ (delegate) in
      weakDelegateObject(delegate)?.sessionDidDeactivate(session)
    })
  }
......
}


Accepted Answer
I found a way:

Code Block swift
import SwiftUI
import WatchConnectivity
struct WatchInfo: View {
  @State private var showMessage: String = "Wating"
   
  var body: some View {
    VStack {
      Text(showMessage).onAppear() {
        ABWatchSessionManager.sharedInstance.addDelegateObject(WatchSessionDelegate(self))
      }
    }
  }
   
  class WatchSessionDelegate: NSObject, WCSessionDelegate {
    var parent: WatchInfo
    init(_ watchInfo: WatchInfo) {
      self.parent = watchInfo
    }
     
    func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void) {
      let data: Dictionary<String, String> = message["data"] as! Dictionary<String, String>
      if data["dataType"] == DataType.ping.rawValue {
        parent.showMessage = "Get PING"
      } else if data["dataType"] == DataType.data.rawValue {
        parent.showMessage = "Get DATA"
      }
    }
     
    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
    }
     
    func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) {
    }
  }
}
struct WatchInfo_Previews: PreviewProvider {
  static var previews: some View {
    WatchInfo()
  }
}

iOS14: How to using delegate like WCSessionDelegate without NSObject?
 
 
Q