在iOS 8共享扩展和主应用程序之间共享数据

65

最近,我正在制作一个简单的iOS 8分享扩展程序,以了解系统的工作方式。正如苹果在其应用扩展编程指南中所述

默认情况下,您的承载应用程序及其扩展程序之间没有直接访问对方容器的权限。

这意味着扩展程序和承载应用程序不共享数据。但是在同一页中,苹果提出了一种解决方案:

如果您希望您的承载应用程序及其扩展程序能够共享数据,请使用Xcode或开发人员门户网站为应用程序及其扩展启用应用组。然后,在门户网站中注册应用组并指定要在承载应用程序中使用的应用组。

然后就可以使用NSUserDefaults在承载应用程序和扩展程序之间共享数据。这正是我想要做的。但出于某些原因,它无法正常工作。

在同一页中,苹果建议使用标准设置:

var defaults = NSUserDefaults.standardUserDefaults()

在一次 WWDC 演示 (217) 中,他们建议使用一个通用的包:

var defaults = NSUserDefaults(suiteName: kDefaultsPackage)

此外,我为包含应用程序目标和扩展目标启用了 App Groups,并使用相同的 App Group 名称:XCode Target capabilities, App Groups

但是所有这些设置都是无用的。 我无法从扩展中检索存储在包含应用程序中的数据。 就像两个目标使用完全不同的 NSUserDefaults 存储一样。

因此,

  1. 是否有解决此方法的方案?
  2. 如何在包含应用程序和共享扩展之间共享简单数据? 该数据仅为 API 的用户凭据。

3
可能是使用应用组在应用之间进行通信的重复问题。 - GuramK
1
这可能会对您有所帮助: https://dev59.com/bWAg5IYBdhLWcg3wDHfS但我仍然有类似这样的问题: https://dev59.com/UIDba4cB1Zd3GeqPDWC4 - heheBear
8个回答

50

您应该像这样使用NSUserDefaults:

保存数据:

objc

NSUserDefaults *shared = [[NSUserDefaults alloc] initWithSuiteName:@"group.yougroup"];
[shared setObject:object forKey:@"yourkey"];
[shared synchronize];

快速

let defaults = UserDefaults(suiteName: "group.yourgroup")
defaults?.set(5.9, forKey: "yourKey")

读取数据:

objc

NSUserDefaults *shared = [[NSUserDefaults alloc] initWithSuiteName:@"group.yougroup"];
id value = [shared valueForKey:@"yourkey"];
NSLog(@"%@",value);

快速

let defaults = UserDefaults(suiteName: "group.yourgroup")
let x = defaults?.double(forKey: "yourKey")
print(x)

这将运行得很好!


11
请更新您的帖子,使用Swift语言或至少添加Swift语言。 - Nicholas
5
看到 Objective-C 和 Swift 并存非常不错,这有助于理解它们的区别。考虑到现在有大量的 Objective-C 代码示例而很少有 Swift 的,了解如何将其转换是很好的。 - Lightbulb1
1
实际上,在Swift中,.synchronize()是多余的。 - arbel03

15

以下是我是如何完成的:

  1. 打开您的主应用程序目标 > 能力 > 应用程序组设置为打开
  2. 添加一个新的应用程序组,并确保它被勾选(例如,group.com.seligmanventures.LightAlarmFree)
  3. 打开您的手表目标(带有能力选项卡的那个)> 应用程序组设置为打开
  4. 添加一个新的应用程序组,并确保它被勾选(例如,group.com.seligmanventures.LightAlarmFree - 但必须与上面的组名称相同)
  5. 将数据保存到该组中,方法如下:

    var defaults = NSUserDefaults(suiteName: "group.com.seligmanventures.LightAlarmFree")
    defaults?.setObject("It worked!", forKey: "alarmTime")
    defaults?.synchronize()
    
  6. 按以下方式从该组检索数据:

    var defaults = NSUserDefaults(suiteName: "group.com.seligmanventures.LightAlarmFree") 
    defaults?.synchronize()
    
    // Check for null value before setting
    if let restoredValue = defaults!.stringForKey("alarmTime") {
        myLabel.setText(restoredValue)
    }
    else {
        myLabel.setText("Cannot find value")
    }
    

你知道defaults.suiteName是否像defaults.standard一样永久存储吗? - VDog
语法更新 var defaults = UserDefaults(suiteName: "group.com.seligman") defaults?.set(sharedTxt, forKey: "shared") - Ronen Rabinovici

7
如果您有
group.yourappgroup

使用

var defaults = NSUserDefaults(suiteName: "yourappgroup")

这对我有用


我遇到了错误:"在调用中标签不正确(已有'suiteName:',应为'user:')"。 - Jason G
对,这个方法对我也没有用。Xcode 6 显示了适当的异常:“将自己的 bundle 标识符用作 NSUserDefaults 套件名称是没有意义的,也不会起作用。请在 _NSUserDefaults_Log_Nonsensical_Suites 上中断以找到此内容。” 然而通过添加前缀 group. 就可以工作了。 - udondan
@naturalc NSUserDefaults(suiteName:) 返回一个可选的NSUserDefaults。你应该使用可选绑定来确保它是有效的。尝试在末尾放置!。 - Abu Saad Papa
@Pops:这是NSUserDefaults的Swift注释中的一个错误。实际上,参数应该是可选的,而不是初始化本身。我已经报告了这个问题。 - nschum

5

看起来只有在使用组名作为套件名的情况下,它才能正常运行于NSUserDefaults。

文档说NSUserDefaults.standartUserDefaults()也应该可以工作,但事实并非如此,这可能是一个错误。


你能添加一些设置NSUserDefaults和传递数据的示例代码吗?谢谢。 - Ste Prescott
你必须将组添加到两个目标吗? - Ste Prescott
我相信UserDefaults.standard在幕后使用Bundle标识符。由于您的主应用程序和扩展具有不同的Bundle ID,因此标准用户默认设置将无法工作。 - Justin Vallely

3

在我的场景中,我正在父iOS应用程序和WatchKit之间共享数据。我正在使用Xcode 6.3.1、iOS Deployment Target 8.3

var defaults = NSUserDefaults(suiteName: "group.yourappgroup.example")

在您的viewDidLoad中,请确保同步:
    override func viewDidLoad() {
    super.viewDidLoad()

    defaults?.synchronize()

}

以下是一个发送文本的按钮示例,当然您可以传递任何内容:

 @IBAction func btnSend(sender: UIButton) {
    var aNumber : Int = 0;
    aNumber = aNumber + 1

    //Pass anything with this line
    defaults?.setObject("\(aNumber)", forKey: "userKey")
    defaults?.synchronize()

}

然后在另一侧确保应用程序组匹配:

 var defaults = NSUserDefaults(suiteName: "group.yourappgroup.example")

然后同步并计算:(在这种情况下,“lblNumber”是IBOutlet标签)
defaults?.synchronize()
var tempVar = defaults!.stringForKey("userKey")!;
lblNumber.setText(tempVar);

如果您想在这一侧设置某些内容并将其同步回来,那么只需执行相同的操作并进行同步,只需确保在另一侧调用的stringForKey与此相同:

defaults?.setObject("sending sample text", forKey: "sampleKey")
defaults?.synchronize()

希望这有意义。

2
我翻译了swift foogry的答案,它有效!保存数据:
let shared = NSUserDefaults(suiteName: "nameOfCreatedGroup")
shared("Saved String 1", forKey: "Key1")
shared("Saved String 2", forKey: "Key2")

读取数据:

let shared = NSUserDefaults(suiteName: "nameOfCreatedGroup")!
valueToRead1 = shared("Key1") as? String
valueToRead2 = shared("Key2") as? String
println(valueToRead1)   // Saved String 1
println(valueToRead2)   // Saved String 2

2
您可以按照以下步骤共享数据:
1)选择您的项目 -> 选择功能选项卡 -> 启用应用组 -> 点击“+” -> 在“group.”后粘贴您的包标识符。

Enable AppGroups in project capabilities tab

选择您的扩展 -> 选择功能选项卡 -> 启用应用程序组 -> 点击“+” -> 将您的包标识粘贴到“group.”之后。

Enable AppGroups in Extension capabilities tab

将以下代码放入您想要分享数据的主应用程序中:
NSUserDefaults * appGroupData = [[NSUserDefaults alloc]initWithSuiteName:@"group.com.appname"];
NSData * data = [NSKeyedArchiver archivedDataWithRootObject:[self allData]]; // Get my array which I need to share
[appGroupData setObject:data forKey:@"Data"];
[appGroupData synchronize];

4) 您可能在扩展中获得对象:
NSUserDefaults * appGroupData = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.appname"];
NSData * data = [appGroupData dataForKey:@"Data"];
NSArray * arrReceivedData = [NSKeyedUnarchiver unarchiveObjectWithData:data];

无法将代码放置在“源代码标签”中,因此答案显示的格式不正确。 - Anjali jariwala

0

你应该像以下这样使用NSUserDefaults,并确保你已经在你的临时配置文件中启用了应用程序组,并且应用程序组必须配置为绿色符号,并且它应该添加到你的临时配置文件和BundleID中。

NSUserDefaults *sharedUserDefault = [[NSUserDefaults alloc] initWithSuiteName:@"group.yougroup"];
[sharedUserDefault setObject:object forKey:@"yourkey"];
[sharedUserDefault synchronize];

NSUserDefaults *sharedUserDefault = [[NSUserDefaults alloc] initWithSuiteName:@"group.yougroup"];
sharedUserDefault value = [sharedUserDefault valueForKey:@"yourkey"];

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接