在Xcode项目,为了iOS主应用和Watch应用之间实现数据传递,我创建了一个App Group(group.com.fangjunyu.piglet.watch)。
但是,在调用App Group的过程中,发现iOS应用和Watch应用之间无法使用App Group传递数据。
iOS端代码
在iOS端,我在主视图中调用savePiggyBankDataToAppGroup方法,这个方法会将Swift Data中的数据以字典数组的形式存储在App Group中。
// 将数据保存到 App Group UserDefaults
func savePiggyBankDataToAppGroup() {
let piggyBankData: [[String: Any]] = allPiggyBank.map { bank in
return [
"name": bank.name,
"icon": bank.icon,
"amount": bank.amount,
"targetAmount": bank.targetAmount,
"isPrimary": bank.isPrimary
]
}
print("iOS 数据保存到 App Group UserDefaults中")
print("piggyBankData:\(piggyBankData)")
// 获取 App Group UserDefaults
if let appGroupDefaults = UserDefaults(suiteName: "group.com.fangjunyu.piglet.watch") {
appGroupDefaults.set(piggyBankData, forKey: "piggyBanks")
appGroupDefaults.synchronize() // 强制保存
print("iOS 数据存储到 App Group 完成")
} else {
print("无法访问 App Group UserDefaults")
}
}
func checkStoredData() {
if let appGroupDefaults = UserDefaults(suiteName: "group.com.fangjunyu.piglet.watch") {
let storedData = appGroupDefaults.array(forKey: "piggyBanks")
print("存储的数据:\(storedData ?? [])")
}
}
在Xcode中进行测试,输出的内容为:
iOS 数据保存到 App Group UserDefaults中
piggyBankData:[["icon": "iphone.gen2", "name": "iPhone 16 pro max", "amount": 2000.0, "targetAmount": 9999.0, "isPrimary": true], ["icon": "laptopcomputer", "name": "Mac book pro", "amount": 3000.0, "targetAmount": 18888.0, "isPrimary": false]]
iOS 数据存储到 App Group 完成
应用移入后台,同步 Watch 数据
存储的数据:[{
amount = 2000;
icon = "iphone.gen2";
isPrimary = 1;
name = "iPhone 16 pro max";
targetAmount = 9999;
}, {
amount = 3000;
icon = laptopcomputer;
isPrimary = 0;
name = "Mac book pro";
targetAmount = 18888;
}]
输出的内容表示savePiggyBankDataToAppGroup方法执行成功,并且可以从App Group中读取存储的数据。
Watch端代码
在Watch端创建了一个类,这个类中含有一个加载App Group的方法,这个方法会从App Group中读取数据。
func loadPiggyBankDataFromAppGroup() {
print("进入loadPiggyBankDataFromAppGroup 方法")
if let appGroupDefaults = UserDefaults(suiteName: "group.com.fangjunyu.piglet.watch") {
let allDefaults = appGroupDefaults.dictionaryRepresentation()
print("UserDefaults 中的所有内容:\(allDefaults)") // 打印所有内容
// 尝试读取数据
if let piggyBankData = appGroupDefaults.array(forKey: "piggyBanks") as? [[String: Any]] {
piggyBanks = piggyBankData.compactMap {
if let name = $0["name"] as? String,
let icon = $0["icon"] as? String,
let amount = $0["amount"] as? Double,
let targetAmount = $0["targetAmount"] as? Double,
let isPrimary = $0["isPrimary"] as? Bool {
return PiggyBank(name: name, icon: icon, amount: amount, targetAmount: targetAmount, isPrimary: isPrimary)
}
return nil
}
if !piggyBanks.isEmpty {
print("Watch 端接收数据成功,接收数据为:\(piggyBanks)")
} else {
print("Watch 端接收到的数据为空或无效。")
}
} else {
print("Watch 端解码失败:未能读取piggyBanks数据")
}
} else {
print("无法读取 App Group UserDefaults")
}
}
但是,在Xcode测试的过程中,发现输出的内容为:
从UserDefaults中获取iOS数据
进入loadPiggyBankDataFromAppGroup 方法
Couldn't read values in CFPrefsPlistSource<0x175e3b40> (Domain: group.com.fangjunyu.piglet.watch, User: kCFPreferencesAnyUser, ByHost: Yes, Container: (null), Contents Need Refresh: Yes): Using kCFPreferencesAnyUser with a container is only allowed for System Containers, detaching from cfprefsd
UserDefaults 中的所有内容:["AppleKeyboardsExpanded": 1, "AppleLanguagesSchemaVersion": 4000, "com.apple.content-rating.ExplicitBooksAllowed": 1, "INNextHeartbeatDate": 761823254.908258, "PKKeychainVersionKey": 8, "AKLastIDMSEnvironment": 0, "com.apple.content-rating.TVShowRating": 1000, "AppleLanguages": <__NSArrayM 0x175c3000>(
zh-Hans-CN,
en-CN
)
, "AppleKeyboards": <__NSArrayM 0x175c3120>(
ja_JP-Kana@sw=Kana;hw=Automatic,
en_US@sw=QWERTY;hw=Automatic,
zh_Hans-Pinyin@sw=Pinyin-Simplified;hw=Automatic,
zh_Hans-Pinyin@sw=Pinyin10-Simplified;hw=Automatic,
emoji@sw=Emoji
)
, "com.apple.system.SpellCheckAllowed": 1, "NSLanguages": <__NSSingleObjectArrayI 0x176e4120>(
en-001
)
, "com.apple.content-rating.AppRating": 1000, "AppleMeasurementUnits": Centimeters, "AKLastLocale": zh_CN, "NSInterfaceStyle": macintosh, "PKLogNotificationServiceResponsesKey": 0, "com.apple.content-rating.MovieRating": 1000, "AKLastEmailListRequestDateKey": 2025-02-21 20:12:09 +0000, "AppleLocale": zh_CN, "com.apple.content-rating.ExplicitMusicPodcastsAllowed": 1, "AppleTemperatureUnit": Celsius, "ApplePasscodeKeyboards": <__NSArrayM 0x175c31f0>(
zh_Hans-Pinyin@sw=Pinyin-Simplified;hw=Automatic,
en_US@sw=QWERTY;hw=Automatic,
emoji@sw=Emoji
)
, "AppleMetricUnits": 1]
Watch 端解码失败:未能读取piggyBanks数据
其中有两点,一点是在Watch应用中通过appGroupDefaults.dictionaryRepresentation()方法遍历App Group中的所有数据,并没有从iOS应用中保存的数据。第二点是,输出了一个报错:
Couldn't read values in CFPrefsPlistSource<0x175e3b40> (Domain: group.com.fangjunyu.piglet.watch, User: kCFPreferencesAnyUser, ByHost: Yes, Container: (null), Contents Need Refresh: Yes): Using kCFPreferencesAnyUser with a container is only allowed for System Containers, detaching from cfprefsd
这个报错表示问题可能出现在权限、同步机制或配置不匹配等方面。
检查配置
检查Xcode项目中iOS端和Watch的Signing & Capabilities的内容:


iOS端和Watch端都配置了App Group,同时,因为我前几天刚新增了一个Widget(小组件),小组件也是通过App Group进行配置的。目前App Group的对应关系为:

检查Provisioning Profile和Signing Certificate字段都是一致的,因为本身也是我一个人在编写这个项目文件。


我通过查询网络,发现有以下几种可能的解决方案:
1、《NSUserDefaults errors in logs》讨论文章中表示,手机和主机设备重启可以解决这一问题。
2、《Failed to read values in CFPrefsPlistSource iOS 10》讨论文章中表示,在App Group前面添加团队ID可以解决这一问题。
因为重启这个方案比较大,重启后如果可以解决这一问题,但是无法进一步排查问题原因,因此我决定放在最后重启。
我首先是修改iOS和Watch之间的App Group名称,将当前的App Group:
group.com.fangjunyu.piglet.watch
改为团队ID前缀:
group.RRMSD743UQ.com.fangjunyu.piglet.watch
团队ID可以在App Store Connect的“编辑资料”中进行查看。

在iOS应用和Watch应用的App Groups中,新增团队ID前缀的App Group。

在Xcode代码中,也将iOS和Watch的App Group改为新增团队ID前缀的App Group。

运行后,输出的内容仍然是无法读取。
Couldn't read values in CFPrefsPlistSource<0x176939a0> (Domain: group.RRMSD743UQ.com.fangjunyu.piglet.watch, User: kCFPreferencesAnyUser, ByHost: Yes, Container: (null), Contents Need Refresh: Yes): Using kCFPreferencesAnyUser with a container is only allowed for System Containers, detaching from cfprefsd

新的App Group
这里,我猜测跟App Group的字段过长有关,我尝试使用新的App Group:
group.RRMSD743UQ.pigletWatch
在测试的过程中,仍然无法读取,同时还发现了新的报错内容。
在iOS端使用检查数据方法后:
func checkStoredData() {
print("group.RRMSD743UQ.pigletWatch")
if let appGroupDefaults = UserDefaults(suiteName: "group.RRMSD743UQ.pigletWatch") {
let storedData = appGroupDefaults.array(forKey: "piggyBanks")
print("存储的数据:\(storedData ?? [])")
}
}
Xcode输出的内容中,包含了存储的数据,但是仍然存在一个Couldn’t read values in CFPrefsPlistSource<0x30273a910> 的报错。
group.RRMSD743UQ.pigletWatch
存储的数据:[{
amount = 3000;
icon = laptopcomputer;
isPrimary = 0;
name = "Mac book pro";
targetAmount = 18888;
}, {
amount = 2000;
icon = "iphone.gen2";
isPrimary = 1;
name = "iPhone 16 pro max";
targetAmount = 9999;
}]
Couldn't read values in CFPrefsPlistSource<0x30273a910> (Domain: group.RRMSD743UQ.pigletWatch, User: kCFPreferencesAnyUser, ByHost: Yes, Container: (null), Contents Need Refresh: Yes): Using kCFPreferencesAnyUser with a container is only allowed for System Containers, detaching from cfprefsd
因此,可以推断iOS 端在尝试读取 App Group 中的数据时出现了问题。
尝试之前的App Group
在修改多个App Group后,仍然无法解决问题。
// 无法传递的 App Group
group.com.fangjunyu.piglet.watch
// 第一次修改
group.RRMSD743UQ.com.fangjunyu.piglet.watch
// 第二次修改
group.RRMSD743UQ.pigletWatch
因此,尝试使用之前iOS主应用和Widget(小组件)的App Group,检查是否能够实现传递。


经过测试发现,即使使用Widget和iOS之间成功传递的App Group,iOS和Watch仍然无法使用App Group进行数据传递。
Couldn't read values in CFPrefsPlistSource<0x3001de910> (Domain: group.com.fangjunyu.piglet, User: kCFPreferencesAnyUser, ByHost: Yes, Container: (null), Contents Need Refresh: Yes): Using kCFPreferencesAnyUser with a container is only allowed for System Containers, detaching from cfprefsd
因此,目前的推断是,问题跟App Group可能没有关系。
新的解决思路
现在的问题还有以下几种测试途径:
一种是iOS和Watch之间的传递方式有问题。既然Widget和iOS可以实现App Group,那么就复制Widget和iOS的传递方式到Watch和iOS之间,如果Watch和iOS可以通过Widget的方式传递,则表示可能是当前的传递方式有问题。
第二种是,App Group无法实现字典数组的数据传递,因此只能尝试其他的数据传递格式。
第三种就是重启Mac和Watch,从而检查是否因某些特殊原因,导致App Group无法使用。
Widget传递方式
iOS和Widget数据传递
iOS端通过调用saveWidgetData将数据存储到App Group中。
func saveWidgetData() {
let userDefaults = UserDefaults(suiteName: "group.com.fangjunyu.piglet")
// 存储存钱罐数据
userDefaults?.set(piggyBank[0].icon, forKey: "piggyBankIcon")
userDefaults?.set(piggyBank[0].name, forKey: "piggyBankName")
userDefaults?.set(piggyBank[0].amount, forKey: "piggyBankAmount")
userDefaults?.set(piggyBank[0].targetAmount, forKey: "piggyBankTargetAmount")
userDefaults?.set(LoopAnimation, forKey: "LoopAnimation")
userDefaults?.set(BackgroundImage, forKey: "background")
}
因此,尝试修改iOS和Watch的传递方法为:
func savePiggyBankDataToAppGroup() {
let userDefaults = UserDefaults(suiteName: "group.com.fangjunyu.piglet")
let piggyBankData: [[String: Any]] = allPiggyBank.map { bank in
return [
"name": bank.name,
"icon": bank.icon,
"amount": bank.amount,
"targetAmount": bank.targetAmount,
"isPrimary": bank.isPrimary
]
}
print("iOS 数据保存到 App Group UserDefaults中")
print("piggyBankData:\(piggyBankData)")
userDefaults?.set(piggyBankData, forKey: "piggyBanks")
}
修改后,在Watch端仍然无法获取到piggyBanks的信息。因此,跟数据传递的方法可能无关。
其他数据形式
既然iOS和Widget可以实现单值的数据传递,因此考虑先测试单个值的UserDefault是否可以连同iOS和Watch,如果证明成立。则侧面的反应了字典数组的形式可能不适应或者配置有误,导致的无法传递数据。
如果单值仍然无法在iOS和Watch之间传递,那么就只能重启设备,以检查问题能否解决。
修改iOS应用savePiggyBankDataToAppGroup的方法,添加一个Watch字段,用于测试iOS和Watch是否可以通过App Group连通。
func savePiggyBankDataToAppGroup() {
let userDefaults = UserDefaults(suiteName: "group.com.fangjunyu.piglet")
let piggyBankData: [[String: Any]] = allPiggyBank.map { bank in
return [
"name": bank.name,
"icon": bank.icon,
"amount": bank.amount,
"targetAmount": bank.targetAmount,
"isPrimary": bank.isPrimary
]
}
print("iOS 数据保存到 App Group UserDefaults中")
print("piggyBankData:\(piggyBankData)")
userDefaults?.set(piggyBankData, forKey: "piggyBanks")
// 存储存钱罐数据
userDefaults?.set("测试Watch是否可以传递", forKey: "Watch")
}
在iOS端保存后,Watch端读取时,显示为nil:
func loadPiggyBankDataFromAppGroup() {
print("进入loadPiggyBankDataFromAppGroup 方法")
print("group.com.fangjunyu.piglet")
if let appGroupDefaults = UserDefaults(suiteName: "group.com.fangjunyu.piglet") {
let watchData = appGroupDefaults.string(forKey: "Watch")
print("测试Watch单值存储的数据:\(watchData)")
}
}
这也就表示,单值也没有实现从iOS和Watch之间的传递数据,因此问题可能不在数据格式上,而是iOS和Watch之间无法使用App Group 通信。
但是,为什么iOS和Widget可以使用App Group,而iOS和Watch之间不可行呢?
重启设备
接下来是最后的验证,如果重启设备可以解决输出的报错,以及实现iOS和Watch之间的连接,那么就表示问题在于设备。
如果重启仍然无法解决这一问题,那么问题可能在于iOS和Watch直接无法通过App Group传递数据。
在重启Mac电脑后,发现之前的 Couldn’t read values in CFPrefsPlistSource<0x3001de910> 报错消失了。

但是测试Watch端时,仍然无法读取App Groups中的数据,还是输出:
Couldn't read values in CFPrefsPlistSource<0x175be9a0> (Domain: group.com.fangjunyu.piglet, User: kCFPreferencesAnyUser, ByHost: Yes, Container: (null), Contents Need Refresh: Yes): Using kCFPreferencesAnyUser with a container is only allowed for System Containers, detaching from cfprefsd
然后尝试重启Watch端。
重新安装应用,发现Watch端仍然无法从App Groups中读取数据。
卸载iOS和Watch端应用后,重新安装应用,问题仍然无法解决。
总结
经过很长的测试,仍然未能解决这一问题。
有趣的是,我在搜索解决文章时,发现存在同样问题的文章《App group shared user defaults》。
在这个文章中有一个留言提到“这个现代系统将 watchOS 与 iOS 一起分发,但它们不能属于同一个应用程序“组”,无法共享“状态”或过去可以共享的任何数据位。”

另一篇stackoverflow文章《iPhone and Apple Watch not sharing App Group》的高赞回答表示:
随着 watchOS 2 和原生 watchOS 应用的引入,Apple Watch 应用不再仅仅是其 iOS 版本的扩展,因此它们不再共享 App Groups。
从 watchOS 2 开始,你无法再使用 App Groups 来共享数据。你必须使用 WatchConnectivity 框架来在 iOS 和 watchOS 应用之间发送数据。

因此,iOS和Watch应用之间已经无法通过App Group实现连接,这貌似已经是一个不可否认的事实。那么Watch应用应该如何实现iOS的同步呢?
一种是使用WatchConnectivity框架发送数据,但昨天刚写过一篇WatchConnectivity文章《iOS和Apple Watch数据交换的框架WatchConnectivit》,在使用WatchConnectivity过程中必须保持iOS和Watch应用在前台的状态,才能实现数据传递。
如果想要实现本地同步的情况,可能需要使用CloudKit,但是我的iOS应用是通过SwiftData同步到Cloud中的,Watch好像还不支持SwiftData,这样也无法实现从CloudKit同步数据。
因此,如果想要实现App Group类似的同步数据方法,将iOS的数据同步到Watch上,可能还需要仔细的研究一下。
参考文章
1、Failed to read values in CFPrefsPlistSource iOS 10:https://stackoverflow.com/questions/38275395/failed-to-read-values-in-cfprefsplistsource-ios-10/77923029#77923029
2、NSUserDefaults errors in logs:https://stackoverflow.com/questions/39876042/nsuserdefaults-errors-in-logs
3、App group shared user defaults:https://developer.apple.com/forums/thread/710966
4、iPhone and Apple Watch not sharing App Group:https://stackoverflow.com/questions/46140731/iphone-and-apple-watch-not-sharing-app-group
5、iOS和Apple Watch数据交换的框架WatchConnectivit:https://fangjunyu.com/2025/02/21/ios%e5%92%8capple-watch%e6%95%b0%e6%8d%ae%e4%ba%a4%e6%8d%a2%e7%9a%84%e6%a1%86%e6%9e%b6watchconnectivit/