0

私は Kugel を使用して NSNotifications を使用しており、ウォッチ シミュレーターと iPhone および iPhone シミュレーターの両方でうまく機能しており、UI/状態を更新するメッセージを配信していますが、これらはデバイスでテストするときにウォッチ側で配信できません。

私が信じている問題は、iPhone からの WCSession メッセージに基づいて NSNotifications がトリガーされることです。シミュレーターと iPhone 側ですべてが正常に動作するのは、おそらく、sim が監視アプリを常にアクティブに保ち、iPhone が完全なセッションをサポートしているため、接続と通知が常に配信されるためです。ウォッチでは、ウォッチの状態に基づいて、セッションとおそらく通知の両方が失敗する可能性があります。

時計でのデバッグは非常に遅いです。デバッグ プロセスを開始するだけで 5 ~ 10 分かかります。

電話メッセージが時計で受信され、メッセージに基づいて更新する必要があることを時計アプリに通知するための最良の方法について、誰かが私にいくつかの読書を指摘できますか? それとも、後で確認できる WCSession および NSNotification 情報を記録できる優れたデバッグ コードでしょうか?

私のコードはかなり単純ですが、まだ進行中の作業です....

両側で、セッションを管理するシングルトンを作成します。電話側のコードは次のとおりです。

import WatchConnectivity
import UIKit

// This class manages messaging between the Watch and iPhone

class PhoneSession: NSObject, WCSessionDelegate
{
     static let manager = PhoneSession()

     private var appDelegate: AppDelegate!

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

     private var validSession: WCSession?
         {
            if let session = session where session.reachable
            {
                return session
            }
            return nil
    }


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

        appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
    }


    func isEditing() -> Bool
    {
        if (UIApplication.sharedApplication().applicationState == .Active)
        {
            if (appDelegate.mainView.visible && appDelegate.mainView.currentDay.isToday())
            {
                return false
            }
            return true
        }
        return false
    }

}

extension PhoneSession
{
    func sendEditing()
    {
        if session!.reachable
        {
            sendMessage([Keys.UpdateType : PhoneUpdateType.Editing.rawValue])
        }
    }


    func sendDoneEditing()
    {
        if session!.reachable
        {
            sendMessage([Keys.UpdateType : PhoneUpdateType.DoneEdit.rawValue])
        }
    }


    func sendTable()
    {
        let tableInfo: WatchWorkout = PhoneData().buildWatchTableData()
        let archivedTable: NSData = NSKeyedArchiver.archivedDataWithRootObject(tableInfo)
        if session!.reachable
        {
            sendMessage([Keys.UpdateType : PhoneUpdateType.TableInfo.rawValue, Keys.Workout: archivedTable])
        }
        else
        {
            do
            {
                try updateApplicationContext([Keys.UpdateType : PhoneUpdateType.TableInfo.rawValue, Keys.Workout: archivedTable])
            }
            catch
            {
                print("error sending info: \(error)")
            }
        }
    }


    func sendRowDone(row: Int, done: Bool)
    {
        if session!.reachable
        {
            sendMessage([Keys.UpdateType : PhoneUpdateType.RowDone.rawValue,
                       Keys.RowIndex: row, Keys.Done: done])
        }
        else
        {
            let tableInfo: WatchWorkout = PhoneData().buildWatchTableData()
            let archivedTable: NSData = NSKeyedArchiver.archivedDataWithRootObject(tableInfo)

            do
            {
                try updateApplicationContext(   [Keys.UpdateType : PhoneUpdateType.TableInfo.rawValue,
                                            Keys.Workout: archivedTable])
            }
            catch
            {
                print("error sending info: \(error)")
            }
        }

    }


    func receivedRowDone(info: [String : AnyObject])
    {
        let row: Int = info[Keys.Row] as! Int
        let done: Bool = info[Keys.Done] as! Bool
        PhoneData.manager.updateInfoFromWatch(row, done: done)
    }


    func receivedRowInfo(info: [String : AnyObject])
    {
        let row: Int = info[Keys.Row] as! Int
        let rest: Int = info[Keys.Rest] as! Int
        let reps: Int = info[Keys.Reps] as! Int
        let force: Double = info[Keys.Force] as! Double
        PhoneData.manager.updateSetInfoFromWatch(row, rest: rest, reps: reps, force: force)
}


    func receivedTableDone(info: [String : AnyObject])
    {
        let date: Int = info[Keys.Date] as! Int
        let dones: [Bool] = info[Keys.TableDones] as! [Bool]
        PhoneData.manager.updateDones(dones, forDate: date)
        Kugel.publish(PhoneNotificationKeys.ReloadTable)
    }


    func receivedTableComplete()
    {
        Kugel.publish(PhoneNotificationKeys.ReloadTable)
    }


    func receivedStartRest()
    {
        Kugel.publish(PhoneNotificationKeys.StartRest)
    }


    func receivedInfo(info: [String : AnyObject]) -> NSData?
    {
        let messageString: String = info[Keys.UpdateType] as! String
        let updateType: WatchUpdateType = WatchUpdateType.getType(messageString)

        switch (updateType)
        {
        case .RowInfo:
            receivedRowInfo(info)
        case .TableDone:
            receivedTableDone(info)
        case .RowDone:
            receivedRowDone(info)
        case .TableComplete:
            receivedTableComplete()
        case .StartRest:
            receivedStartRest()
        case .RequestUpdate:
            let tableInfo: WatchWorkout = PhoneData().buildWatchTableData()
            let archivedTable: NSData = NSKeyedArchiver.archivedDataWithRootObject(tableInfo)
            return archivedTable
        case .Ignore:
            print("Opps")
        }
        return nil
    }

}


// MARK: Interactive Messaging
extension PhoneSession
{
    // Sender
    func sendMessage(message: [String : AnyObject], replyHandler: (([String : AnyObject]) -> Void)? = nil, errorHandler: ((NSError) -> Void)? = nil)
    {
        validSession!.sendMessage(message,
            replyHandler:
            {
                (returnMessage: [String : AnyObject]) -> Void in
                if let theMessage = returnMessage[Keys.MessageStatus]
                {
                    print("Return Message from Watch: \(theMessage)")
                }
            },
            errorHandler:
            {
                (error) -> Void in
                print("Error Message during transfer to Watch: \(error)")
            }
        )        
}


    func session(session: WCSession, didReceiveMessage message: [String : AnyObject])
    {
        self.receivedInfo(message)
    }


    // Receiver
    func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void)
    {
        let returnMessage = self.receivedInfo(message)
        if (returnMessage != nil)
        {
            if let archivedTable: NSData = returnMessage!
            {
                let replyValues = [Keys.UpdateType : PhoneUpdateType.TableInfo.rawValue, Keys.Workout: archivedTable] // Data to be returned
                replyHandler(replyValues)
            }
        }
    }

}


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

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


    // Receiver
    func session(session: WCSession, didReceiveApplicationContext applicationContext: [String : AnyObject])
    {
        self.receivedInfo(applicationContext)
    }
}

そしてこれは時計側です:

import WatchConnectivity


class WatchSession: NSObject, WCSessionDelegate
{
    static let manager = WatchSession()

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

    private var validSession: WCSession?
    {
        if let session = session where session.reachable
        {
            return session
        }
        return nil
    }

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


extension WatchSession
{

    func sendRowInfo(row:Int, rest: Int, reps: Int, force: Double)
    {
        if session!.reachable
        {
            let message: [String: AnyObject] = [Keys.UpdateType : WatchUpdateType.RowInfo.rawValue,
                                                Keys.Row : row,
                                                Keys.Rest : rest,
                                                Keys.Reps : reps,
                                                Keys.Force : force]
            sendMessage(message)
            print("sent row done to Phone: \(message)")
        }
        else
        {
            sendTableDone()
            print("failed to connect to Phone, sent table done context to Phone")
        }
    }


    func sendRowDone(row:Int, done: Bool)
    {
        if session!.reachable
        {
            let message: [String: AnyObject] = [Keys.UpdateType : WatchUpdateType.RowDone.rawValue,
                                                Keys.Row : row,
                                                Keys.Done : done]
            sendMessage(message)
            print("sent row done to Phone: \(message)")
        }
        else
        {
            sendTableDone()
            print("failed to connect to Phone, sent table done context to Phone")
        }
    }


    func sendTableDone()
    {
        let tableDones: [Bool] = WatchData.manager.watchTableDone()
        let date: Int = WatchData.manager.date()

        do
        {
            try updateApplicationContext(   [Keys.UpdateType : WatchUpdateType.TableDone.rawValue,
                                            Keys.Date : date, Keys.TableDones: tableDones])
        }
        catch _
        {
            print("error trying to send TableDones")
        }
    }


    func sendTableComplete()
    {
        if session!.reachable
        {
            sendMessage([Keys.UpdateType : WatchUpdateType.TableComplete.rawValue])
        }
        else
        {
            let date: Int = WatchData.manager.date()

            do
            {
                try updateApplicationContext(   [Keys.UpdateType : WatchUpdateType.TableComplete.rawValue,
                                                Keys.Date : date])
            }
            catch _
            {
                print("error trying to send TableComplete")
            }
        }
    }


    func sendRest() -> Bool
    {
        var sent: Bool = false
        if session!.reachable
        {
            sendMessage([Keys.UpdateType : WatchUpdateType.StartRest.rawValue])
            sent = true
        }
        return sent
    }


    func requestUpdate() -> Bool
    {
        var sent: Bool = false
        if session!.reachable
        {
            print("requesting update reply")
            sendMessage([Keys.UpdateType : WatchUpdateType.RequestUpdate.rawValue])
            sent = true
        }
        return sent
    }


    func receivedUpdateReply(info: [String : AnyObject])
    {

    }


    func receiveRowDone(info: [String : AnyObject])
    {
        let row: Int = info[Keys.RowIndex] as! Int
        let done: Bool = info[Keys.Done] as! Bool

        WatchData.manager.updateWatchTable(row, done: done)
        Kugel.publish(WatchNotificationKeys.UpdateRow)
    }


    func receivedTable(archivedTable: NSData)
    {
        let workout: WatchWorkout = NSKeyedUnarchiver.unarchiveObjectWithData(archivedTable) as! WatchWorkout
        WatchData.manager.updateWatchWorkout(workout)

        Kugel.publish(WatchNotificationKeys.ReloadTable)
    }


    func receivedStartEditStatus()
    {
        Kugel.publish(WatchNotificationKeys.StartEdit)
    }


    func receivedDoneEditStatus()
    {
        WatchData.manager.retrieveWorkout()
        Kugel.publish(WatchNotificationKeys.DoneEdit)
    }


    func receivedStopRest()
    {
        Kugel.publish(WatchNotificationKeys.StopRest)
    }


    func receivedInfo(info: [String : AnyObject])
    {
        let messageString: String = info[Keys.UpdateType] as! String
        let updateType: PhoneUpdateType = PhoneUpdateType.getType(messageString)

        switch (updateType)
        {
            case .TableInfo:
                receivedTable(info[Keys.Workout] as! NSData)
            case .Editing:
                receivedStartEditStatus()
            case .DoneEdit:
                receivedDoneEditStatus()
            case .RowDone:
                receiveRowDone(info)
            case .StopRest:
                receivedStopRest()
            case .Ignore:
                print("Opps")
        }
    }

    func receivedReply(info: [String : AnyObject])
    {
        if let replyString: String = info[Keys.ReplyType] as? String
        {
            let replyType: ReplyType = ReplyType.getType(replyString)

            switch (replyType)
            {
            case .Table:
                print("received Reply Table")
                receivedTable(info[Keys.Workout] as! NSData)
            case .NoData:
                print("Opps ... nodata in reply")
            case .Ignore:
                print("Opps replyType message error")
            }
        }
    }
}



// MARK: Interactive Messaging
extension WatchSession
{

    // Sender    
    func sendMessage(message: [String : AnyObject], replyHandler: (([String : AnyObject]) -> Void)? = nil, errorHandler: ((NSError) -> Void)? = nil)
    {
        validSession!.sendMessage(message,
            replyHandler:
            {
                (replyMessage: [String : AnyObject]) -> Void in
                     if let typeMessage: String = replyMessage[Keys.ReplyType] as? String
                    {
                        self.receivedReply(replyMessage)
                        print("Return Message from Phone: \(typeMessage)")
                    }
            },
            errorHandler:
            {
                (error) -> Void in
                    print("Error Message during transfer to Phone: \(error)")
            }
        )
    }


    // Receiver

    func session(session: WCSession, didReceiveMessage message: [String : AnyObject])
    {
        self.receivedInfo(message)
    }


    func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void)
    {
        self.receivedInfo(message)

        let replyValues = [Keys.MessageStatus : "Watch received message"] // Data to be returned
        replyHandler(replyValues)
    }

}


// MARK: Application Context

extension WatchSession
{

    // 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
        receivedInfo(applicationContext)
    }
}

iPhone 側の AppDelegate と時計側の ExtensionDelegate にシングルトンを作成します。電話側は次のとおりです。

var phoneSession: PhoneSession!

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool
{
    self.phoneSession = PhoneSession()
    PhoneSession.manager.startSession()

メッセージを送信するときの基本的なロジックは、相手側が到達可能かどうか、到達可能である場合は sendMessage が使用され、到達可能でない場合は sendApplicationContext が使用されることです。

電話側でメッセージが受信されると、アプリがフォアグラウンドにあるかバックグラウンドにあるかを確認する追加のロジックがあり、フォアグラウンドの場合はメイン スレッドに通知をプッシュし、バックグラウンドの場合は情報だけが更新されます。私の理解では、メッセージはバックグラウンドで受信されないため、時計側では常にメインスレッドにプッシュされます。

4

0 に答える 0