手撸一个Flutter插件实现跨苹果全家桶云同步持久化Key Value数据

news/2024/11/7 20:44:20/

前言

作为一个客户端开发者,后端开发一直是我的弱项。虽然GPT的横空出世,让我对后端的开发有一点眉目。但是现实是,能不触碰就不触碰,因为人的精力是有限,如何在有限的时间里发挥最大的作用一直是我的一个追求。所以回到本次主题,我自己上线的一个产品,目前已经成功上线了iOS以及Mac端,如何在不开发后端的情况下实现同步轻量级数据呢?答案是利用Cloud Kit。Cloud Kit是苹果官方API,用于同步同意iCloud账号下设备的数据,包括Key Value Storage、云dataBase、云document等等。通过翻阅pub类似的库,我发现了有以下几个pub包是可以参考的cloud_kit、icloud_storage,但是他们都不是我想要的,要么是不支持Mac、要么是只能同步存储文件,但是我想要同步的其实只是轻量级持久化数据(也是就是iOS下的UserDefault,基于键值存储)。所以没办法了,肝了一晚上我手撸了一个Flutter插件icloud_kv_storage,完美的支持了跨苹果设备iOS、Mac、iPad等的轻量级Key Value数据同步。
请添加图片描述

开发过程全解析

  • 创建Flutter插件项目,指定支持iOS、Mac平台

如何创建插件虽然已经很简单,这里我还是点一下。

flutter create icloud_kv_storage -t plugin
cd ./icloud_kv_storage//指定platformsflutter create ./ -t plugin --platforms ios
flutter create ./ -t plugin --platforms macos
  • 先设计Flutter Channel接口

由于是肝了一晚上的产品,非常赶,第一版先只支持同步String数据,后续再支持别的基础数据,如int、double、bool等。


///获取真正的Key,我内部Key都加了flutter前缀,如果要拿真实的Key可以使用此方法
String getRealKey(String key) {throw UnimplementedError('getRealKey()) has not been implemented.');}///原生的CallBack 用于实时刷新数据(如果多台苹果设备都在线,支持实时同步void setNativeCallBack({required GetNativeCallBackFuture onCallBack}) {}///保存数据接口,这里用范型方便后续拓展,虽然第一版只支持StringFuture<void> write<T>({required String key,required T value}) {throw UnimplementedError('write({required String key,required String value}) has not been implemented.');}///读取一个Key的数据接口,同理也是范型Future<T?> read<T>({required String key}) {throw UnimplementedError('read({required String key}) has not been implemented.');}///删除一个keyFuture<void> delete({required String key}) {throw UnimplementedError('delete({required String key}) has not been implemented.');}
  • 原生Swift接口的实现

iOS和Mac实际都是共用Foundation框架,所以我们只需要编写同一套Swift实现,即可同时满足iOS以及Mac。

1.首先定义Channel实现协议


enum CKCommandType: String {case DELETE_VALUEcase GET_VALUEcase SAVE_VALUEcase EMPTY = ""
}protocol CKCommandHandlerProtocol {var COMMAND_NAME: CKCommandType { get }func evaluateExecution(command: String) -> Boolfunc handle(command: String, arguments: Dictionary<String, Any>, result: @escaping FlutterResult) -> Void
}

2.实现增删改查对应methodName的协议

查询一个key对应的协议实现


class CKGetValueHandler: CKCommandHandlerProtocol {var COMMAND_NAME: CKCommandType = .GET_VALUEfunc evaluateExecution(command: String) -> Bool {return CKCommandType(rawValue: command) == COMMAND_NAME}func handle(command: String, arguments: Dictionary<String, Any>, result: @escaping FlutterResult) {if (!evaluateExecution(command: command)) {return}if let key = arguments["key"] as? String {let store = NSUbiquitousKeyValueStore.defaultresult(store.object(forKey: key))} else {result(FlutterError.init(code: "Error", message: "Cannot pass key and value parameter", details: nil))}}}

删除一个Key的实现


class CKDeleteValueHandler: CKCommandHandlerProtocol {var COMMAND_NAME: CKCommandType = .DELETE_VALUEfunc evaluateExecution(command: String) -> Bool {return CKCommandType(rawValue: command) == COMMAND_NAME}func handle(command: String, arguments: Dictionary<String, Any>, result: @escaping FlutterResult) {if (!evaluateExecution(command: command)) {return}if let key = arguments["key"] as? String {let store = NSUbiquitousKeyValueStore.defaultstore.removeObject(forKey: key)result(true)} else {result(FlutterError.init(code: "Error", message: "Cannot pass key parameter", details: nil))}}}

写入保存一个Key的实现


class CKSaveValueHandler: CKCommandHandlerProtocol {var COMMAND_NAME: CKCommandType = .SAVE_VALUEfunc evaluateExecution(command: String) -> Bool {return CKCommandType(rawValue: command) == COMMAND_NAME}func handle(command: String, arguments: Dictionary<String, Any>, result: @escaping FlutterResult) {if (!evaluateExecution(command: command)) {return}if let key = arguments["key"] as? String, let value = arguments["value"] as? String{let store = NSUbiquitousKeyValueStore.defaultstore.set(value, forKey: key)result(true)} else {result(FlutterError.init(code: "Error", message: "Cannot pass key and value parameter", details: nil))}}}
  • 多设备实时刷新的实现

这里其实实现一下原生的通知监听,返回到Flutter就行,代码如下。

let keyValueStore = NSUbiquitousKeyValueStore.default// 监听数据变化NotificationCenter.default.addObserver(self, selector: #selector(keyValueStoreDidChange), name: NSUbiquitousKeyValueStore.didChangeExternallyNotification, object: keyValueStore)keyValueStore.synchronize()@objc func keyValueStoreDidChange(notification: Notification) {// 处理数据变化if let userInfo = notification.userInfo as? [String: Any],let reasonForChange = userInfo[NSUbiquitousKeyValueStoreChangeReasonKey] as? NSNumber {var reason = -1reason = reasonForChange.intValueif (reason == NSUbiquitousKeyValueStoreServerChange || reason == NSUbiquitousKeyValueStoreInitialSyncChange) {guard let changedKeys = userInfo[NSUbiquitousKeyValueStoreChangedKeysKey] as? [String] else {return}let store = NSUbiquitousKeyValueStore.defaultfor key in changedKeys {let value = store.object(forKey: key)self.channel?.invokeMethod("icloud_key_update", arguments: [key:value])}}}}
  • Plugin Channel通讯的处理

上面一个一个的协议实现完毕,我们只需要扔到Flutter Plugin handle的协议方法里就可。代码如下


public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {let callArguments: Dictionary<String, Any> = call.arguments as! Dictionary<String, Any>CKGetValueHandler().handle(command: call.method, arguments: callArguments, result: result)CKSaveValueHandler().handle(command: call.method, arguments: callArguments, result: result)CKDeleteValueHandler().handle(command: call.method, arguments: callArguments, result: result)}

就这样一个支持跨苹果全家桶同步持久化Key Value数据的Flutter插件就完全了,是不是很简单呢?

用法

首先我们要在iOS或Mac项目开启iCloud Key Value Storge 服务

在这里插入图片描述

然后导入icloud_kv_storage享受它吧~

flutter pub add icloud_kv_storagedependencies:icloud_kv_storage: ^0.0.1

简单写法如下

import 'package:icloud_kv_storage/icloud_kv_storage.dart';var iCloudStorage = CKKVStorage();

Update A Key

void _incrementCounter() {setState(() {_counter++;iCloudStorage.writeString(key: key, value: _counter.toString());});}

Read A Key

iCloudStorage.getString('k_storage_count').then((value) {if (value != null) {setState(() {_counter = int.parse(value);});}});

Delete A Key

void _clearCounter() {setState(() {_counter = 0;iCloudStorage.delete(key);});}

实时刷新CallBack

iCloudStorage.onCloudKitKVUpdateCallBack(onCallBack: (kvMap) {print('receive icloud_key_update map $kvMap');//if receive remove key will rec {flutter.k_storage_count: null}//if receive update key will rec {flutter.k_storage_count: 1}//because have prefix flutter. so need use my method to get real key.var key = iCloudStorage.getRealKey('k_storage_count');if (kvMap.containsKey(key)) {String? value = kvMap[key];setState(() {if (value != null) {_counter =int.parse(kvMap[iCloudStorage.getRealKey('k_storage_count')]);} else {_counter = 0;}});}},);

下载地址

Github

icloud_kv_storage

flutter pub

icloud_kv_storage

结尾

由于是1.0版本,非常肝。目前只支持同步String类型的数据,后续其他基础类型数据也会更新上。如果这个库对你有用,那就Star一个吧,感谢🙏


http://www.ppmy.cn/news/730907.html

相关文章

在Windows系统删除苹果全家桶软件的顺序

移除和重新安装适用于 Windows 7 及更高版本的 iTunes 及相关软件组件 - Apple 支持 (中国) 移除和重新安装适用于 Windows 7 及更高版本的 iTunes 及相关软件组件 移除 iTunes 及相关组件 如果您在卸载这些组件方面需要协助&#xff0c;请按照以下步骤操作&#xff1a; W…

苹果全家桶,十四块屏幕间的爱情

所有动画片段事先制作好并存储在各个设备上&#xff0c;然后同步启动、按序播放&#xff0c;于是有了这个在14部苹果终端穿梭际遇的爱情故事。 苹果这个创意视频&#xff0c;难道是告诉我们&#xff1a;爱情坎坷&#xff0c;多买苹果&#xff1f; 查看视频链接

程序员真的可以轻松来一套苹果全家桶吗

本周发文章的时候突发奇想&#xff0c;于是起了个题目&#xff0c;如何快速来一套苹果全家桶&#xff0c;发完了以后就在想一套苹果全家桶到底想要多少钱&#xff1f; 那我们就来算一算&#xff0c;看是不是真的能通过跳槽快速来一套苹果全家桶。 在这里我们只统计程序员最常用…

苹果全家桶最值得入手的排序是什么?

必入&#xff1a;iphone &#xff0c;MacBook Pro&#xff0c;airpods pro。 苹果手机是苹果生态的核心。在人人都有一部手机甚至多部手机的时代能少得了iPhone吗&#xff1f;iphone 必入。 macbook&#xff1a;是程序员&#xff0c;视频编辑&#xff0c;平面设计师&#xff…

如何快速来一套苹果全家桶

答案很简单&#xff0c;那就是赶紧刷好LeetCode跳个槽加个薪&#xff0c;电子产品什么的不在话下&#xff0c;哈哈。 好了&#xff0c;让我们继续刷LeetCode吧。 如果给你一个找规律的题目1,2,3,5,? 下一个是多少&#xff1f; 大家在读书时肯定遇到过这样找规律的题目&#xf…

苹果年夜饭“全家桶”来了,给你不一样的新年味

最近几天被苹果的贺岁电影《一个桶》刷了屏&#xff0c;人们对家的期盼和对过年的期待也是越来越浓。进入春节倒计时&#xff0c;苹果特别推出了年夜饭“全家桶”&#xff0c;一部iPhone、一部iPad Pro或者一台全新上市的Homepod&#xff0c;都可以给你的幸福年增加一份不一样的…

【宇麦科技】苹果全家桶如何联动群晖NAS,让你的“苹果”更香

使用群晖 NAS 的同学 不少也是 iPhone 的用户 这一期准备了一份小攻略 让您的设备更有价值&#xff01; iPhone 使用手机&#xff0c;换新机是必然的&#xff0c;同时肯定避免不了迁移文件。每次迁移最怕的就是新手机变成了这样 出现这种情况&#xff0c;不如用 Synology P…

多架构Docker镜像制作

文章目录 安装buildx安装binfmtDockerfileAMD64构建镜像导出镜像 ARM64构建镜像导出镜像 安装buildx 从https://github.com/docker/buildx/releases网站下载二进制文件到本地并重命名为docker-buildx&#xff0c;移动到 docker 的插件目录 ~/.docker/cli-plugins。 增加可执行…