65

Recently, I've been making a simple iOS 8 share extension to understand how the system works. As Apple states in its App Extension Programming Guide:

By default, your containing app and its extensions have no direct access to each other’s containers.

Which means the extension and the containing app do not share data. But in the same page Apple brings a solution:

If you want your containing app and its extensions to be able to share data, use Xcode or the Developer portal to enable app groups for the app and its extensions. Next, register the app group in the portal and specify the app group to use in the containing app.

Then it becomes possible to use NSUserDefaults to share data between the containing app and the extension. This is exactly what I would like to do. But for some reason, it does not work.

In the same page, Apple suggests the standard defaults:

var defaults = NSUserDefaults.standardUserDefaults()

In a WWDC presentation (217), they suggest a common package:

var defaults = NSUserDefaults(suiteName: kDefaultsPackage)

Also, I enabled App Groups for both the containing app target and the extension target, with the same App Group name: XCode Target capabilities, App Groups

But all this setup is for nothing. I cannot retrieve the data I stored in the containing app, from the extension. It is like two targets are using completely different NSUserDefaults storages.

So,

  1. Is there a solution for this method?
  2. How can I share simple data between the containing app and the share extension? The data is just user credentials for an API.
mfaani
  • 33,269
  • 19
  • 164
  • 293
Oguz Bilgener
  • 755
  • 1
  • 10
  • 17
  • 3
    possible duplicate of [Communicating between apps with App Groups](http://stackoverflow.com/questions/24015506/communicating-between-apps-with-app-groups) – GuramK Jun 09 '14 at 12:53
  • 1
    This may help you: http://stackoverflow.com/q/24015506/1586692 But I still have problem like this: http://stackoverflow.com/q/24067975/1586692 – heheBear Jun 30 '14 at 06:44

8 Answers8

50

You should use NSUserDefaults like this:

Save data:

objc

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

swift

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

Read data:

objc

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

swift

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

This will work fine!

Mike
  • 13
  • 3
foogry
  • 835
  • 7
  • 17
  • 11
    swift language was asked please update your post using or at least adding swift language – Nicholas Jan 18 '15 at 14:07
  • 5
    Its nice to see objective c along side swift. It helps understand the difference. Considering theres tonns of code examples of ObjC and not many of swift at the moment its good to understand how to convert it. – Lightbulb1 Mar 20 '15 at 12:08
  • 1
    Actually in Swift the .synchronize() is redundant – arbel03 Jul 24 '16 at 15:21
15

Here is how I did it:

  1. Open your main app target > Capabilities > App Groups set to on
  2. Add a new app group and make sure it is ticked (e.g. group.com.seligmanventures.LightAlarmFree)
  3. Open your watch target (the one with Capabilities tab) > App Groups set to on
  4. Add a new app group and make sure it is ticked (e.g. group.com.seligmanventures.LightAlarmFree - but must be the same name as group above)
  5. Save data to the group as follows:

    var defaults = NSUserDefaults(suiteName: "group.com.seligmanventures.LightAlarmFree")
    defaults?.setObject("It worked!", forKey: "alarmTime")
    defaults?.synchronize()
    
  6. Retrieve data from the group as follows:

    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")
    }
    
Charlie S
  • 4,366
  • 6
  • 59
  • 97
7

If you have

group.yourappgroup

use

var defaults = NSUserDefaults(suiteName: "yourappgroup")

This works for me

Berni
  • 449
  • 4
  • 5
  • i get the error Incorrect argument label in call (have 'suiteName:', expected 'user:') – Jason G Feb 03 '15 at 17:44
  • Right, neither this does work for me. Xcode 6 shows the appropriate exception: `Using your own bundle identifier as an NSUserDefaults suite name does not make sense and will not work. Break on _NSUserDefaults_Log_Nonsensical_Suites to find this`. With the prefix `group.` though it works. – udondan Feb 13 '15 at 12:19
  • @naturalc NSUserDefaults(suiteName:) returns an optional NSUserDefaults. You should use optional binding to make sure it's valid. Try putting the ! at the end. – Abu Saad Papa Feb 17 '15 at 11:03
  • @Pops: That's a bug in the Swift annotations for `NSUserDefaults`. Actually the argument should be optional, not the init itself. I have already reported it. – nschum Mar 17 '15 at 14:48
5

So apparently it works, only when the group name is used as the suite name for NSUserDefaults.

The documentation says NSUserDefaults.standartUserDefaults() should also work but it does not, and this is probably a bug.

Oguz Bilgener
  • 755
  • 1
  • 10
  • 17
3

In my scenario I'm sharing data between the parent iOS app and WatchKit. I'm using Xcode 6.3.1, iOS Deployment Target 8.3

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

In your viewDidLoad make sure you synchronize:

    override func viewDidLoad() {
    super.viewDidLoad()

    defaults?.synchronize()

}

Example of a button sending text but of course you can pass whatever:

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

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

}

Then on the other side make sure app group matches:

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

Then synchronize and cal: (In this case "lblNumber" is an IBOutlet label)

defaults?.synchronize()
var tempVar = defaults!.stringForKey("userKey")!;
lblNumber.setText(tempVar);

Then if you wanted to set something on this side and sync it back then just do the same thing and synchronize and just make sure the stringForKey that you call on the other side is the same:

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

Hope this makes sense

Richmond
  • 235
  • 3
  • 4
2

I translated in swift foogry's answer and it works!!

Save data:

let shared = NSUserDefaults(suiteName: "nameOfCreatedGroup")
shared("Saved String 1", forKey: "Key1")
shared("Saved String 2", forKey: "Key2")

Read data:

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
Matte.Car
  • 2,007
  • 4
  • 23
  • 41
2

You may share data by Following below steps:

1) Select your project -> Select Capabilities tab -> Enable App Groups -> Click on '+' -> paste your bundle Id after 'group.'

Enable AppGroups in project capabilities tab

2) Select your Extension -> Select Capabilities tab -> Enable App Groups -> Click on '+' -> paste your bundle Id after 'group.'

Enable AppGroups in Extension capabilities tab

3) Place below code in your main app for which data you want to share:

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) You may get object in extension:

NSUserDefaults * appGroupData = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.appname"];
NSData * data = [appGroupData dataForKey:@"Data"];
NSArray * arrReceivedData = [NSKeyedUnarchiver unarchiveObjectWithData:data];
Aamir
  • 16,329
  • 10
  • 59
  • 65
Anjali jariwala
  • 410
  • 5
  • 15
0

You should use NSUserDefaults like this following and make sure you must have enabled app group in your provisional profile and app group must configure as a green symbol and it should add to your provisional profile & 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"];
Aleksander Azizi
  • 9,829
  • 9
  • 59
  • 87
Hitesh Vaghela
  • 1,635
  • 1
  • 11
  • 11