Issues setting up WCSessionDelegate using SwiftUI

122 Views Asked by At

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 ***")
        
    }
}
1

There are 1 best solutions below

0
AdrianGutierrez On

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.