iPhone 与 Watch 通讯
WCSession类可以让手机和手表进行通讯,要完成其通讯,iOS和iWatch都要创建会话对象,当 iOS和iWatch会话对象都是在激活状态时,手机应用和手表应用就能马上进入通讯。当手机应用和手表应用会话只有一个是活动的,活动的会话会发送和传递文件,但是这些是在后台发送,WCSession是ios9出来的。
一、简介
重要
会话对象一定要活动的,保证能够传送和收到数据。在激活会话时,可调用 isSupported
方法,保证当前设备支持Watch Connectivity 框架。
二、配置和激活会话
配置和激活会话,让默认的会话对象设置代理和调用对象 activateSession
方法,如Listing 1.所示 ,手机应用和手表应用一定要设置其各自的会话对象,活跃的会话会给手表应用和手机应用建立连接。
Listing 1
配置和激活会话
if ([WCSession isSupported])
{
WCSession* session = [WCSession defaultSession];
session.delegate = self;
[session activateSession];
}
为了支持多个手表应用和一个手机应用配对,手机应用和手表应用必须要实现会话代理方法,请实现 session:activationDidCompleteWithState:error:方法,
让会话知道app 支持多个手表通讯。在手机应用中,实现 sessionDidBecomeInactive:
和 sessionDidDeactivate:
会话代理方法,去管理不同的手表应用状态。
重要
1.如果不实现异步活动和状态的代理的方法时,手机应用会退出支持多个手表连接。当用户手机和新的手表进行配对,退出对手机应用造成严重后果。当出现新的配对,你的手机应用 会话将不再有活跃,当随后手机应用又退到后台,系统会杀死手机应用。当第二次登陆手机应用,手机应用会连接新的手表。
2.当会话是激活的时候,手机应用和手表应用才能相互传数据。激活的前提是 activationState设置为WCSessionActivationStateActivated,在传送数据前,手机应用要检查手机和手表是否配对(paired )和手表是否安装该应用watchAppInstalled。当会话是活跃的,大多数会话的属性是要检查是否有效,在其他情况,会话的属性可能没有定义。activationState
属性经常是有效的和包含当前会话的活动状态。
三、支持和多个手表应用进行通讯
手机iOS 9.3系统或者更晚系统可能要与手表watchOS 2.2或者更晚系统进行配对。对于手机应用,应该支持异步活动会话,但是这样是必须要求的。在手机应用中,手机应用支持异步活动会话和会话状态(活跃和不活跃),可以实现如下的会话代理方法:
session:activationDidCompleteWithState:error:
sessionDidBecomeInactive:
sessionDidDeactivate:
Figure 1 展示当手机换一个新的手机配对。当自动配对成功,一个手机应用只能和一个苹果表应用进行通讯,手机应用会话是活跃状态,但是手机应用将从不太活跃转换成不活跃状态,在这两个状态的短暂时间里,可以传递已经收到的数据,当数据被传递后,会话状态是不活跃的,在这个时间时,手机应用一定要再次调用 activateSession
一次去和新的手表进行连接。
Figure 1
Activation state changes when switching to another Apple Watch
Figure 1.png
手机应用可以用 watchDirectoryURL
属性去存储运行在手表上数据,在大多数情况,该数据和手机的一样的,然而,可以用字典去存储数据,偏好设置和其他的数据文件,手机应用与手机正确的交互,如果这样做,用会话的不太活跃和不活跃两个状态去更新手机应用
四.与相应的应用通讯
当手表应用和手机应用会话的属性activationState被置成
WCSessionActivationStateActivated时,两个应用可以进行相互通讯。
在传数据前,手机应用要确保手表应用被安装。
开始通讯时,要按照如下步骤:
-
调用 updateApplicationContext:error:
方法和最新状态的应用通讯。当应用被调用时,它用信息去更新自己的会话状态。如,手机应用支持后台刷新,可以用它来刷新对应手机的应用,这个方法重写之前的数据字典,调用此方法当手机应用在最新的数据。 -
调用 sendMessage:replyHandler:errorHandler:
或者 sendMessageData:replyHandler:errorHandler方法发送数据给对应应用。这些方法是要马上与手机应用和手表应用进行通讯的。会话的
reachable属性一定要 YES; -
调用transferUserInfo:
方法在后台中传送一个字典,传送数据是顺序传送和当应用挂掉也会继续传送。 -
调用 transferFile:metadata:方法传送文件夹在后台,调此方法,可以传送好多数据。
-
在手机应用中,调用 transferCurrentComplicationUserInfo:
方法去发送关于complication的数据。用此方法,会与手表应用中complication有冲击。
当发送信息给对应的应用,后台的信息是顺序传送的。接收到的信息也是在会话代理里收到。发送数据用sendMessage:replyHandler:errorHandler:
, sendMessageData:replyHandler:errorHandler:
, 和 transferCurrentComplicationUserInfo:
方法,这些方法有高的优先级和马上传送数据,所有的信息都是异步线程按照顺序接收信息的。
注意
-
注意后台传送数据不是马上的,系统是尽快传送数据的,不是立刻马上的,当电量不足时,可以延迟传送数据。因此,发送大量的数据花的时间,接受端也要花相同的时间去接收数据。
-
当发送数据,发送应用需要的数据。传送是数据是无线的需要消耗电的,数据改变时才发送数据,而不是每次都要发送。
-
当传送数据失败,准备处理错误和提供处理办法。错误会发生如果设备内存不足时,如果数据的全面或者通讯出现错误。检查错误并提供合理的解决办法。
五、属性和方法
@interface WCSession : NSObject
//检查是会话是否可用
+ (BOOL)isSupported;
//拿到系统默认的会话
+ (WCSession *)defaultSession;
//会话初始化,请用上面系统默认的会话
- (instancetype)init;
//在传送数据前一定要设置代理
@property (nonatomic, weak, nullable) id <WCSessionDelegate> delegate;
/** 调用此方法前,要得到系统中的session,并设置代理 */
- (void)activateSession;
//得到当前用户状态 此方法是9.3出来的,慎用
@property (nonatomic, readonly) WCSessionActivationState activationState ;
//会话是否要发送数据 ios10.0 出来的,慎用
@property (nonatomic, readonly) BOOL hasContentPending;
/** ------------------------- 手机应用状态 ---------------------------
关于手表应用的状态和只有在手机应用中可以用,信息包括设备状态,手表状态和手机应用信息.
*/
/**判断手机是否配对 */
@property (nonatomic, readonly, getter=isPaired) BOOL paired ;
/** 判断手表是否安装该应用 */
@property (nonatomic, readonly, getter=isWatchAppInstalled) BOOL watchAppInstalled
/** 判断手表是否有complication功能 */
@property (nonatomic, readonly, getter=isComplicationEnabled) BOOL complicationEnabled;
/** 当系统开始传递complicationUserInfo之前,调用transferCurrentComplicationUserInfo的次数,如果该属性是0,complicationUserInfo将会是传送数据。当complication不能用时,该属性也是0;*/
@property (nonatomic, readonly) NSUInteger remainingComplicationUserInfoTransfers __IOS_AVAILABLE(10.0) __WATCHOS_UNAVAILABLE;
/** 用此属性去记录配对的手表。当配对的手表改变,该属性也会改变,在下次使用应用时,如果手表没有装该应用或者没有配对,该属性为nil; */
@property (nonatomic, readonly, nullable) NSURL *watchDirectoryURL __WATCHOS_UNAVAILABLE;
/** -------------------------- 通讯数据 ---------------------------*/
//通讯数据只能在两个活跃的会话有效和要求两个手机应用和手表应用已经连接
// 两个应用连接是否能传送数据
@property (nonatomic, readonly, getter=isReachable) BOOL reachable;
/** 手机和手表的通讯要求至少已配对,此属性可以决定手机应用是否需要开锁,如果此属性设置为NO, 可能因为手机设备已经重启和需要开锁,在此情况下,手表可以建议用户要手机开启*/
@property (nonatomic, readonly) BOOL iOSDeviceNeedsUnlockAfterRebootForReachability __IOS_UNAVAILABLE __WATCHOS_AVAILABLE(2.0);
/**可用此方法发送信息给相对应的应用,当发送完信息时,可以在replyHandler回调中得到接收方给发送方的回复,如果信息不能发送或者没有收到的,回调会抛出错误信息,信息只有应用运行时才会被 发送。如果信息没有发送完,应用就已经退出时,发送信息失败,如果对应应用没有运行,将要启动去接收信息(只有手机应用才能重启去接收信息),信息的格式只能是字典形式的*/
- (void)sendMessage:(NSDictionary<NSString *, id> *)message replyHandler:(nullable void (^)(NSDictionary<NSString *, id> *replyMessage))replyHandler errorHandler:(nullable void (^)(NSError *error))errorHandler;
/**可以发送NSData数据,以NSData格式改送,不要需要转成字典 */
- (void)sendMessageData:(NSData *)data replyHandler:(nullable void (^)(NSData *replyMessageData))replyHandler errorHandler:(nullable void (^)(NSError *error))errorHandler;
/** --------------------------- 后台传送数据 ---------------------------
当应用已经退出时,在后台中传送数据,对应的应用不需要运行去接受数据,系统将要在把数据传递给接受方
*/
/**设置applicationContext方式去传递应用最新的状态,当更新applicationContext后,在应用退出时,系统会初始传送数据。如果applicationContext已经发送,对应的应用在下次登陆时,在回调中拿到状态。如果应用没有内容 ,可能更新一个空的字典,applicationContext只能接收字典 */
@property (nonatomic, readonly, copy) NSDictionary<NSString *, id> *applicationContext;
- (BOOL)updateApplicationContext:(NSDictionary<NSString *, id> *)applicationContext error:(NSError **)error;
/**此属性得到是 对方应用的ApplicationContext*/
@property (nonatomic, readonly, copy) NSDictionary<NSString *, id> *receivedApplicationContext;
/** 系统将在合适的时候将用户的信息传给对应应用上,当应用已经退出时,用户的信息还是可以被传递。如果信息传递成功,对应的应用将在代理回调得到信息,用户的信息只能用字典格式。
*/
- (WCSessionUserInfoTransfer *)transferUserInfo:(NSDictionary<NSString *, id> *)userInfo;
- (WCSessionUserInfoTransfer *)transferCurrentComplicationUserInfo:(NSDictionary<NSString *, id> *)userInfo __WATCHOS_UNAVAILABLE;
//返回传送的用户信息,包括正在传送的(如没有取消的,失败的,或者对方应用没有接收的)
@property (nonatomic, readonly, copy) NSArray<WCSessionUserInfoTransfer *> *outstandingUserInfoTransfers;
//系统将文件放在队列中和在合适的时候将数据传递过去,如果应用已经退出,文件还是可以传送。如果文件传递成功,对应的应用将在代理中得到文件,metadata只能是字典
- (WCSessionFileTransfer *)transferFile:(NSURL *)file metadata:(nullable NSDictionary<NSString *, id> *)metadata;
//返回传送的文件,包括正在传送的(如没有取消的,失败的,或者对方应用没有接收的)
@property (nonatomic, readonly, copy) NSArray<WCSessionFileTransfer *> *outstandingFileTransfers;
@end
```
```
/** ----------------------------- 会话代理 -----------------------------*/
//当收到数据和会话状态改变时,会调用其代理方法,所有的代理将被调用在队列中,该队列不是主队列,如果需要的话,客户端将分配队列任务
@protocol WCSessionDelegate <NSObject>
//当会话完成活动时调用该方法,如果会话是断开的状态,error将展示更细致的信息。
- (void)session:(WCSession *)session activationDidCompleteWithState:(WCSessionActivationState)activationState error:(nullable NSError *)error __IOS_AVAILABLE(9.3) __WATCHOS_AVAILABLE(2.2);
/** -------------------------手表应用状态,在手机应用中使用 ------------------------ */
/**当会话不再用于修改或者增加新的传输文件,所有的交互信息将被取消,该方法被调用,但是代理回调还是可以在后台传输文件。当手表会话状态被改变 */
- (void)sessionDidBecomeInactive:(WCSession *)session __IOS_AVAILABLE(9.3) __WATCHOS_UNAVAILABLE;
/** 当手表的会话已经没有活动时,会回调此方法,当手表应用使用activateSession可以让会话再次活动。*/
- (void)sessionDidDeactivate:(WCSession *)session __IOS_AVAILABLE(9.3) __WATCHOS_UNAVAILABLE;
@optional
//当手表的会话状态进行改变,会走此方法
- (void)sessionWatchStateDidChange:(WCSession *)session __WATCHOS_UNAVAILABLE;
/** ------------------------- 通讯信息 ------------------------- */
/**当手机和手表应用的连接状态改变时,将调用此方法,接受方应该检查连接属性去检查代理回调*/
- (void)sessionReachabilityDidChange:(WCSession *)session;
/**在接收信息端调用此方法,如果收到信息时,马上调用此方法*/
- (void)session:(WCSession *)session didReceiveMessage:(NSDictionary<NSString *, id> *)message;
//当接收到信息时,并在回调中回复发送方信息,如果收到信息时,马上调用此方法
- (void)session:(WCSession *)session didReceiveMessage:(NSDictionary<NSString *, id> *)message replyHandler:(void(^)(NSDictionary<NSString *, id> *replyMessage))replyHandler;
/** 当接收到NSData时,就会调用 方法*/
- (void)session:(WCSession *)session didReceiveMessageData:(NSData *)messageData;
/* 当接收到NSData并回复发送者信息时,会调用此方法. */
- (void)session:(WCSession *)session didReceiveMessageData:(NSData *)messageData replyHandler:(void(^)(NSData *replyMessageData))replyHandler;
/** -------------------------- 后台传送数据------------------------- */
/** 当授受到ApplicationContext时,代理会被调用 */
- (void)session:(WCSession *)session didReceiveApplicationContext:(NSDictionary<NSString *, id> *)applicationContext;
/**
当授受到对应传递过来的用户信息成功或者失败会调用此方法,
如果当用户信息已经完成而发送用户信息端没有运行,在发送用户信息端下次登陆时,该方法会被再次调用 。
*/
- (void)session:(WCSession * __nonnull)session didFinishUserInfoTransfer:(WCSessionUserInfoTransfer *)userInfoTransfer error:(nullable NSError *)error;
//接受端调用此方法,当收到对应应用的用户信息,如果接收方没有运行,系统将重新启动该应用
- (void)session:(WCSession *)session didReceiveUserInfo:(NSDictionary<NSString *, id> *)userInfo;
//当文件发送完成时,发送端会调用此方法,当文件已经发送完成但是发送端没有运行,该方法将在下次登陆时被调用
- (void)session:(WCSession *)session didFinishFileTransfer:(WCSessionFileTransfer *)fileTransfer error:(nullable NSError *)error;
/** 接收数据端,调用此代理方法,如果接收端没有运行,如果收到数据时会被启动,当文件被传送时,文件将被放在Documents/Inbox/ folder中,接收才一定要将文件移到其他的位置,当该方法完成时,系统将会移除所有内容
*/
- (void)session:(WCSession *)session didReceiveFile:(WCSessionFile *)file;
@end
```
本人小白,不足之前,请指出,感恩!