So I am learning how to setup the WCSession delegate for phone and watch.
I feel like I am doing what Apple docs is advising but for whatever reason I am getting this log in my console: "WCSession is missing its delegate." And I say log instead of error, because I setup my delegate to handle errors in the delegate's activationDidCompleteWith function, but that function will not get hit.
Any idea on what I am doing wrong? I am using SwiftUI.
Here is the code on my phones side:
PhoneMain.swift
import SwiftUI
import WatchConnectivity
@main
/// The main starting point for the phone app
struct PhoneMain: App {
var body: some Scene {
WindowGroup {
ContentView()
.onAppear {
print("*** Phone Content View Appeared ***")
// Setting up phone default configurations
PhoneAppDefaults.sharedInstance.configure()
}
}
}
}
ContentView.swift
import SwiftUI
import WatchConnectivity
struct ContentView: View {
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
Text("Hello, world!")
}
.padding()
}
}
PhoneAppDefaults.swift
import Foundation
import WatchConnectivity
/// Where all of the phone app default configurations are setup
class PhoneAppDefaults {
/// The sharedInstance for the PhoneAppDefaults singleton
static var sharedInstance = PhoneAppDefaults()
/// Private init makes this struct a singleton
private init() {
print("*** WatchAppDefaults.sharedInstance initialized ***")
}
/// Initiaties default app configurations
func configure() {
print("*** Configuring watch defaults settings ***")
PhoneWCSessionDelegate().startSession()
}
}
PhoneWCSessionDelegate.swift
import Foundation
import WatchConnectivity
/// The WCSession delegate on the watch side
class PhoneWCSessionDelegate: NSObject, WCSessionDelegate {
/// Assigns this delegate to WCSession and starts the session
func startSession() {
guard WCSession.isSupported() else { return }
print("*** Starting WCSession for phone ***")
let session = WCSession.default
session.delegate = self
print("*** Activating phone WCSession ***")
session.activate()
}
/// A delegate function called everytime WCSession is activated
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
if let error = error {
print("*** Phone WCSession activation error: \(error) ***")
} else {
switch activationState {
case .activated:
print("*** WCSession activated for phone ***")
case .notActivated:
print("*** WCSession failed to activate for phone ***")
case .inactive:
print("*** WCSession inactive for phone ***")
@unknown default:
print("*** WCSession activation result: Unknown, for phone ***")
}
}
}
/// A delegate function called everytime WCSession recieves an application context update
func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) {
print("*** WCSession recieved application context on phone ***")
}
/// A delegate function called everytime WCSession becomes inactive
func sessionDidBecomeInactive(_ session: WCSession) {
print("*** WCSession became inactive on phone ***")
}
/// A delegate function called everytime WCSession deactivates
func sessionDidDeactivate(_ session: WCSession) {
print("*** WCSession deactivated on phone ***")
}
}
And here is the watch side:
WatchMain.swift
import SwiftUI
import WatchConnectivity
@main
/// The main starting point for the watch app
struct WatchMain: App {
var body: some Scene {
WindowGroup {
ContentView()
.onAppear {
print("*** Watch Content View Appeared ***")
// Setting up watch default configurations
WatchAppDefaults.sharedInstance.configure()
}
}
}
}
ContentView.swift
import SwiftUI
import WatchConnectivity
struct ContentView: View {
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
Text("Hello, world!")
}
.padding()
}
}
WatchAppDefaults.swift
import Foundation
import WatchConnectivity
/// Where all of the watch app default configurations are setup
class WatchAppDefaults {
/// The sharedInstance for the WatchAppDefaults singleton
static var sharedInstance = WatchAppDefaults()
/// Private init makes this struct a singleton
private init() {
print("*** WatchAppDefaults.sharedInstance initialized ***")
}
/// Initiaties default app configurations
func configure() {
print("*** Configuring watch defaults settings ***")
WatchWCSessionDelegate().startSession()
}
}
WatchWCSessionDelegate.swift
import Foundation
import WatchConnectivity
/// The WCSession delegate on the watch side
class WatchWCSessionDelegate: NSObject, WCSessionDelegate {
/// Assigns this delegate to WCSession and starts the session
func startSession() {
print("*** Starting WCSession for watch ***")
let session = WCSession.default
session.delegate = self
print("*** Activating watch WCSession ***")
session.activate()
}
/// A delegate function called everytime WCSession is activated
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
if let error = error {
print("*** Watch WCSession activation error: \(error) ***")
} else {
switch activationState {
case .activated:
print("*** WCSession activated for watch ***")
case .notActivated:
print("*** WCSession failed to activate for watch ***")
case .inactive:
print("*** WCSession inactive for watch ***")
@unknown default:
print("*** WCSession activation result: Unknown, for watch ***")
}
}
}
/// A delegate function called everytime WCSession recieves an application context update
func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) {
print("*** WCSession recieved application context on watch ***")
}
}
Per @jnpdx’s suggestion, the delegate was initiated and then removed from scope. By adding the delegate as a property to my singleton, the delegate was able to remain in scope thereby ridding the notification the console was giving me that no delegate exists.