Swift転職なら=>【LevTech】
↑クリックして拡大
↑クリックして拡大
↑クリックして拡大
↑クリックして拡大

頭痛が減ったので共有です!

rebuild.fmを応援しています!

HOME > WCSession

WCSessionを利用してAppleWatchとデータのやりとり

サンプル画像

Apple公式WCSessinページより引用

WatcOS2からデータやりとりが大きく変わりました。今まで利用できたUserDefaultでのデータ送信は出来なくなり、代わりにWCSessionでの データ送受信にきりかわりました

データの送受信はWCSessionを利用して様々な方法で送受信が可能です。私の中で使いやすいかなと思ったのは、 すぐレスのあるsendMessage,Dictionaryを送りやすいtransferUserInfo,画像を送れるtransferFileです。

ただ、それぞれが少し癖がありsendMessageではデータが大きい場合はエラーがでず沈黙したりするので、 sendMessageDataやtransferUserInfoを 利用したりします。サンプルを作成しましたので残しておきます。

※注意!: 現在(2017年3月7日)の制限は次の変数(WCPayloadSizeLimitApplicationContext, WCPayloadSizeLimitMessage, WCPayloadSizeLimitUserInfo)によると以下になります。ただし、公式ドキュメントには記載されていないので今後変更になる可能性有り。

  • 65,536 bytes (65.5 KB) : sendMessageの制限
  • 65,536 bytes (65.5 KB) : transferUserInfoの制限
  • 262,144 bytes (262.1 KB) : applicationContextの制限

サンプル画像>サンプル画像

参考:wcsession apple公式
参考:Watch Connectivity を使ってiPhone内に保存されているデータを、WatchOS2に持ってくる
参考:NSUserDefaults not working on Xcode beta with Watch OS2
参考:Why isn't iPhone transferring an array to Apple Watch?
参考:How send image and array with text to Apple Watch?
参考:watchOS 2: How to communicate between devices using Watch Connectivity
参考:How to transfer a UIImage using Watch Connectivity
参考:WATCHOS watchOS 2 Tutorial: Transferring images using transferFile (Watch Connectivity #3)
参考:Programmatically save an image to xcassets in Swift [duplicate]
参考:https://forums.developer.apple.com/thread/67606
参考:watchOS 2 Tutorial: Checking Reachability
参考: How big can the payload be when sending data via WatchConnectivity?

やってみた

サンプルとしてAppleWatch側のソースと本体側のソースを公開します。それぞれデータを送り合うボタンを つけていますので、貼り付けて試してみてください。


サンプル画像

以下、iphone側(端末親側)のソース




import UIKit
import WatchConnectivity


class ViewController: UIViewController,WCSessionDelegate {
    var session = WCSession.default();

    override func viewDidLoad() {
        super.viewDidLoad()

        if (WCSession.isSupported()) {
            self.session = WCSession.default()
            self.session.delegate = self
            self.session.activate()
        }
        // Do any additional setup after loading the view, typically from a nib.
        
        let button = UIButton(frame: CGRect(x:0, y:0, width:250, height:50));
        button.backgroundColor = UIColor.gray;
        button.addTarget(self, action: #selector(ViewController.btn_sendMessageClick(_:)), for: .touchUpInside);
        button.setTitle("sendMessage", for: UIControlState.normal);
        self.view.addSubview(button);
        
        
        let button2 = UIButton(frame: CGRect(x:0, y:100, width:250, height:50));
        button2.backgroundColor = UIColor.gray;
        button2.addTarget(self, action: #selector(ViewController.btn_sendTransferUserInfoClick(_:)), for: .touchUpInside);
        button2.setTitle("transferUserInfo", for: UIControlState.normal);
        self.view.addSubview(button2);
        
    }
    
    
    func btn_sendMessageClick(_ sender: UIButton) {
        print("button pressed")
        let contents =  ["body":"test"]
        self.session.sendMessage(contents, replyHandler: { (replyMessage) -> Void in
            print ("receive from apple watch","test");
        }) { (error) -> Void in
            print(error)
        }
    }
    func btn_sendTransferUserInfoClick(_ sender: UIButton) {
        print("button2 pressed")
        let personInfo: Dictionary = ["name": "swift-salaryman", "age": 28] as [String : Any]
        self.session.transferUserInfo(personInfo);
    }
    
    
    public func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Swift.Void){
        print("receiveMessage::\(message)")
    }
    
    public func session(_ session: WCSession, didFinish userInfoTransfer: WCSessionUserInfoTransfer, error: Error?) {
        print("userInfoTransfer::\(userInfoTransfer)")
        
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    
    @available(iOS 9.3, *)
    public func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?){
        
    }
    /** ------------------------- iOS App State For Watch ------------------------ */
    
    /** Called when the session can no longer be used to modify or add any new transfers and, all interactive messages will be cancelled, but delegate callbacks for background transfers can still occur. This will happen when the selected watch is being changed. */
    @available(iOS 9.3, *)
    public func sessionDidBecomeInactive(_ session: WCSession){
    }
    
    
    /** Called when all delegate callbacks for the previously selected watch has occurred. The session can be re-activated for the now selected watch using activateSession. */
    @available(iOS 9.3, *)
    public func sessionDidDeactivate(_ session: WCSession){
    }

}


サンプル画像

アップルウォッチ側のソース


import WatchKit
import Foundation
import WatchConnectivity

class InterfaceController: WKInterfaceController,WCSessionDelegate {

    var session = WCSession.default();
    @IBOutlet weak var sendMessageBtn:WKInterfaceButton?;
    @IBOutlet weak var sendTransferUserInfoBtn:WKInterfaceButton?;
    
    override func awake(withContext context: Any?) {
        super.awake(withContext: context)
        
        if (WCSession.isSupported()) {
            self.session = WCSession.default()
            self.session.delegate = self
            self.session.activate()
        }
    }
    
    override func willActivate() {
        // This method is called when watch view controller is about to be visible to user
        super.willActivate()
    }
    
    override func didDeactivate() {
        // This method is called when watch view controller is no longer visible
        super.didDeactivate()
    }
    @available(watchOSApplicationExtension 2.2, *)
    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
    }

    public func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Swift.Void){
        print("receiveMessage::\(message)")
        
    }
    public func session(_ session: WCSession, didReceiveMessageData messageData: Data, replyHandler: @escaping (Data) -> Void) {
        print("receiveMessageData::\(messageData)")
    }
    public func session(_ session: WCSession, didFinish userInfoTransfer: WCSessionUserInfoTransfer, error: Error?) {
        print("userInfoTransfer::\(userInfoTransfer)")
        
    }
    public func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]){
        print("didReceiveUserInfo::\(userInfo)");
    }
    
    public func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any])
    {
        print("didReceiveApplicationContext::\(applicationContext)");
    }
    
    @IBAction func btn_sendMessageClick(){
        print("btn_sendMessageClick pressed")

        let contents =  ["body":"test"]
        self.session.sendMessage(contents, replyHandler: { (replyMessage) -> Void in
            //iOSからのデータを受信した時の処理
            print ("receive from iphone","test");
        }) { (error) -> Void in
            print(error)
        }
    }
    @IBAction  func btn_transferUserInfoClick(){
        print("btn_transferUserInfoClick pressed")
        let personInfo: Dictionary = ["name": "swift-salaryman", "age": 28] as [String : Any]
        self.session.transferUserInfo(personInfo);
        
    }
}

気づき:payload is too large (WCErrorCodePayloadTooLarge)エラー

sendMessageで大きめのデータを送ってしまうとpayload is too largeが発生。UserDefaultのデータをAppleWatch側に送りたくて、transferUserInfoでuserdefaultのデータを Dictionaryにして送信するとなぜか受信側でうけとれない。リファレンスをみても制限が記載されていないので気付かず、、、ちなみにuserdefault をそのまま全て送るとキーの数が137程度ありそれが原因なのか、必要な量だけにしぼってキーを20程度に抑えると通信成功。これはsendMessageDataでも 発生していて、何もエラーないのに受信ができなくなる。わかりずらい、、、

気づき: 大きな画像をやりとりする場合はtransferFileを利用する

データサイズが大きい場合では私の環境ではtransferFileを利用しないとAppleWatch側に届かなかった(小さいアイコンサイズ程度の データであればtransferUserInfoでも届くが、transferUserInfoもキーが多くサイズが大きいと届かない)。transferUserInfo、sendMessageData,ApplicationContext を利用するもエラー(エラーがでれば良いですが反応がない場合があってわかりずらい)。TransferFileを利用して解決。ネットワーク上の画像を利用して UIImageにして画像をiPhone端末が側に保存、そのデータをAppleWatchに送信する形で対処しました。

気づき;AppleWatchとデバイスとの接続があいまい

WatchOS1の時もでしたが、シュミレータを利用してデバイス間の接続テストをしていると、接続の時間がかなりかかったりする場合が乱発。。。。sendMessageの レスポンスが1秒もかからなかったとおもっていたら、次の日チェックしたら数分かかって、一度接続すると1秒程度に短縮されたりと曖昧なレスポンスがかえってきて、 バグなのか不明。色々調査してもどうやらシュミレーターだとよく起こるらしい。本格的な開発をするならAppleWatch端末が必要。

まとめ

デバイス間の送受信で沈黙されるのが開発で手間でした。解決して欲しい。

↓こんな記事もありますよ!

UISwitchで切り替えスイッチコントロール

スイッチによるONOFFを実装するためのコントロールの説明です。

WatchKitでタイマーアプリを作成する。

簡単なアプリを作成しながらAppleWatchを学んでいきます。前回の記事で大まかな動きは理解できましたので、 つぎはAppleWatchとアプリとの連携をタイマー機能を利用して検証していきます

willsetとdidsetでプロパティを監視。意味あるのか???

クラスのプロパティにはWillsetとdidsetという監視用の関数があると聞いたのですが、用途は分かるにしても メリットとしてはどういうものなのかなと、うーん、と考えていました。調べていると海外のサイトでは同じように疑問にもつ質問があがっていました。 getとsetで同じことができるのでは?との私と同じスタンスです。解決されていましたので、順に説明していきます。
このエントリーをはてなブックマークに追加
右側のFacebookのLikeをクリック頂けると記事更新の際に通知されますので宜しければご利用下さい!