swiftwatchkitwatchos-2wcsession

watchOS WCSession 'paired' and 'watchAppAvailable' are unavailable


I am trying to use a singleton to manage WCSession messages, which I found here.

I understand what it is trying to do, but i don't understand why I am getting an error... this is the line I am struggling with:

if let session = session where session.paired && session.watchAppInstalled {

Error: 'watchAppInstalled' is unavailable

Error: 'paired' is unavaiable

Question: How can I make those properties available? New to watchOS and ios in general. Thanks!

Entire code:

import WatchConnectivity

class WatchSessionManager: NSObject, WCSessionDelegate {

    static let sharedManager = WatchSessionManager()
    private override init() {
        super.init()
    }

    private let session: WCSession? = WCSession.isSupported() ? WCSession.defaultSession() : nil

    private var validSession: WCSession? {

        // paired - the user has to have their device paired to the watch
        // watchAppInstalled - the user must have your watch app installed

        // Note: if the device is paired, but your watch app is not installed
        // consider prompting the user to install it for a better experience

        if let session = session where session.paired && session.watchAppInstalled {
            return session
        }
        return nil
    }

    func startSession() {
        session?.delegate = self
        session?.activateSession()
    }
}

// MARK: Application Context
// use when your app needs only the latest information
// if the data was not sent, it will be replaced
extension WatchSessionManager {

    // Sender
    func updateApplicationContext(applicationContext: [String : AnyObject]) throws {
        if let session = validSession {
            do {
                try session.updateApplicationContext(applicationContext)
            } catch let error {
                throw error
            }
        }
    }

    // Receiver
    func session(session: WCSession, didReceiveApplicationContext applicationContext: [String : AnyObject]) {
        // handle receiving application context

        dispatch_async(dispatch_get_main_queue()) {
            // make sure to put on the main queue to update UI!
        }
    }
}

// MARK: User Info
// use when your app needs all the data
// FIFO queue
extension WatchSessionManager {

    // Sender
    func transferUserInfo(userInfo: [String : AnyObject]) -> WCSessionUserInfoTransfer? {
        return validSession?.transferUserInfo(userInfo)
    }

    func session(session: WCSession, didFinishUserInfoTransfer userInfoTransfer: WCSessionUserInfoTransfer, error: NSError?) {
        // implement this on the sender if you need to confirm that
        // the user info did in fact transfer
    }

    // Receiver
    func session(session: WCSession, didReceiveUserInfo userInfo: [String : AnyObject]) {
        // handle receiving user info
        dispatch_async(dispatch_get_main_queue()) {
            // make sure to put on the main queue to update UI!
        }
    }

}

// MARK: Transfer File
extension WatchSessionManager {

    // Sender
    func transferFile(file: NSURL, metadata: [String : AnyObject]) -> WCSessionFileTransfer? {
        return validSession?.transferFile(file, metadata: metadata)
    }

    func session(session: WCSession, didFinishFileTransfer fileTransfer: WCSessionFileTransfer, error: NSError?) {
        // handle filed transfer completion
    }

    // Receiver
    func session(session: WCSession, didReceiveFile file: WCSessionFile) {
        // handle receiving file
        dispatch_async(dispatch_get_main_queue()) {
            // make sure to put on the main queue to update UI!
        }
    }
}


// MARK: Interactive Messaging
extension WatchSessionManager {

    // Live messaging! App has to be reachable
    private var validReachableSession: WCSession? {
        if let session = validSession where session.reachable {
            return session
        }
        return nil
    }

    // Sender
    func sendMessage(message: [String : AnyObject],
        replyHandler: (([String : AnyObject]) -> Void)? = nil,
        errorHandler: ((NSError) -> Void)? = nil)
    {
        validReachableSession?.sendMessage(message, replyHandler: replyHandler, errorHandler: errorHandler)
    }

    func sendMessageData(data: NSData,
        replyHandler: ((NSData) -> Void)? = nil,
        errorHandler: ((NSError) -> Void)? = nil)
    {
        validReachableSession?.sendMessageData(data, replyHandler: replyHandler, errorHandler: errorHandler)
    }

    // Receiver
    func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void) {
        // handle receiving message
        dispatch_async(dispatch_get_main_queue()) {
            // make sure to put on the main queue to update UI!
        }
    }

    func session(session: WCSession, didReceiveMessageData messageData: NSData, replyHandler: (NSData) -> Void) {
        // handle receiving message data
        dispatch_async(dispatch_get_main_queue()) {
            // make sure to put on the main queue to update UI!
        }
    }
}

Solution

  • The problem you are encountering is that you are using the same singleton when compiling using both the iOS SDK and the watchOS SDK. Some of the WCSession properties are only available on one or the other, so your code will have to take that in to consideration. Specifically if you look at the Objective-C header for WCSession you'll see:

    /** Check if iOS device is paired to a watch */
    @property (nonatomic, readonly, getter=isPaired) BOOL paired __WATCHOS_UNAVAILABLE;
    
    /** Check if the user has the Watch app installed */
    @property (nonatomic, readonly, getter=isWatchAppInstalled) BOOL watchAppInstalled __WATCHOS_UNAVAILABLE;
    

    This means if you do want to keep using the singleton, you will have to change this section:

    if let session = session where session.paired && session.watchAppInstalled {
        return session
    }
    return nil
    

    to be something more like (there are other ways to solve this, but this is one solution):

    #if os(iOS)
        if let session = session where session.paired && session.watchAppInstalled {
            return session
        }
        return nil
    #else
        return session
    #endif
    

    This is conditionally compiling different code whether its being compiled for iOS or watchOS. You might have to apply this same trick in other parts of the singleton, but this should at least get you started!