肇鑫的技术博客

业精于勤,荒于嬉

iOS app与watchOS app数据同步

保存的数据库采用realm。手表部分的app具备与iOS端相同的功能。

基础部分

数据保存在用户的Document里。升级app数据不会消失,卸载app数据会消失。

同步算法

以iOS端为核心。watchOS发起同步请求。

首次启动

WCSession的生命周期最为标准。每次WCSessionDelegatesession(_:activationDidCompleteWith:error:)的运行被认为是一次首次启动。

  • iOS:设定需要同步数据库needSyncDatabase = true。在同步数据库完成之前,不会发送新的同步信息。
// MARK: - WatchConnectivity
extension AppDelegate:WCSessionDelegate {
    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
        if transfer?.isTransferring == true { transfer?.cancel() }
        
        needSyncDatabase = true
        transfer = session.transferUserInfo(["needSyncDatabase":needSyncDatabase])
    }
    
    func sessionDidBecomeInactive(_ session: WCSession) {
        
    }
    
    func sessionDidDeactivate(_ session: WCSession) {
        
    }
}
  • watchOS:设定同步数据库,手表端有两种情况。
    • WCSession首次启动时主动同步。
    • 接收到iOS发来的同步请求时同步。
// MARK: -WCSessionDelegate
extension ExtensionDelegate:WCSessionDelegate {
    func shouldSyncDatabase() -> Bool {
        return needSyncDatabase && !isSyncingDatabase
    }
    
    func willSyncDatabase() {
        isSyncingDatabase = true
    }
    
    func didSyncDatabase() {
        needSyncDatabase = false
        isSyncingDatabase = false
    }
    
    func syncDatabase() {
        guard shouldSyncDatabase() else { return }
        willSyncDatabase()
        // sync database
        didSyncDatabase()
    }
    
    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
        syncDatabase()
    }
    
    func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) {
        if let value = userInfo["needSyncDatabase"] as? Bool {
            needSyncDatabase = value
            syncDatabase()
        }
    }
}

运行中

  • iOS
    • 当数据库改变时,发送需要同步的数据。
    • 接收需要同步的数据,存入数据库。

思路

iOS -> watchOS 同步

iOS开发存储策略

程序沙盒

  • 需要相同代码签名和包名
  • Home目录:NSHomeDirectory()
  • 临时文件目录:NSTemporaryDirectory(),在home目录外,但在沙盒里
  • iOS的沙盒内,包含程序自身

程序组容器目录

  • 需要相同开发团队
  • 位置:~/Library/Group Containers/<application-group-id>
  • 调用:FileManagercontainerURL(forSecurityApplicationGroupIdentifier:)方法

使用策略

  • 需要长期保存的数据存在Home目录
  • 需要共享的数据存在程序组容器目录

思考

单独将数据保存在程序组容器目录是否安全?
**安全。**因为只有相同开发团队的人才能访问。而且可以简化开发。
**不安全。**因为一个app的数据有被其它app删除的风险。另外,是否应该限制这部分数据,使只有需要分享的数据才方到这里。
**结论。**我个人更倾向于安全。因为团队内部的人应该被认为是可信的。但是这么做的确会存在过多分享的问题。因此,我认为还是不要把主数据库放在这边,而是将其作为辅助数据库更好。至于说需要额外处理的代码问题。我相信,从长期看,这部分代码必然是必要的。

WKAudioFilePlayer的播放问题

假设手表扩展中存在xishuai.mp3文件。

let url = Bundle.main.url(forResource: "xishuai", withExtension: "mp3")!
let playAsset = WKAudioFileAsset(url: url)
let playItem = WKAudioFilePlayerItem(asset: playAsset)
player = WKAudioFilePlayer(playerItem: playItem)

WKAudioFilePlayer目前在真机下,如果真机没有连接蓝牙耳机,则播放无声音。因此,不能保证作为通知声音的替代。