应用程序与应用程序组之间的数据通信和持久化

昨天 iOS8发布了一个关于应用程序组的新 API。在此之前,在应用程序之间分享数据和进行交流是一件很麻烦的事情,而我相信这正是应用程序组想要纠正的。

在我的应用程序中,我已经启用了应用程序组,并添加了一个新的组,但是我就是找不到任何关于如何使用它的文档。文档和 API 引用仅说明如何添加组。

那么应用程序组的真正目的是什么呢? 有没有关于如何使用它的文档?

57996 次浏览

Application groups, according to my interpretation of the existing documentation, are primarily targeted for extensions, more specifically, for widgets. Widgets are their own application bundle that coexist with your app. Since they are a separate application and therefore have their own sandbox, you will need to use App Groups to share files.

After some header grep'ing, I think I found the API needed, but was actually put in as part of iOS 7.

NSFileManager has a method on it containerURLForSecurityApplicationGroupIdentifier: where you can pass in the identifier you created when turning on App Groups for your apps:

NSURL *containerURL = [[NSFileManager defaultManager]
containerURLForSecurityApplicationGroupIdentifier:@"group.com.company.app"];

Another benefit to App Groups is the ability to share a NSUserDefaults database. This also works for App Extensions (notification center widgets, custom keyboards, etc).

Initialize your NSUserDefaults object like this in all applications in the app group and they will share the database:

Objective-C:

[[NSUserDefaults alloc] initWithSuiteName:@"<group identifier>"];

Swift:

NSUserDefaults(suiteName: "<group identifier>")

Keep in mind everything from the [NSUserDefaults standardUserDefaults] database for each application will not carry over into this database.

The documentation gives a correct example as well (As of Beta 3).

And don't forget to synchronize the database:

[yourDefaults synchronize];

Sharing NSUserDefaults data between multiple apps

In order to have shared defaults between an app and an extension or between 2 apps you have to add an App Group in your settings using the following steps:

  1. In the Project Navigator click on the *.xcodeproj file (should be at the top).
  2. To the right of the Project Navigator look for Project and Targets. Under targets click on your primary target (should be the first thing under Targets).
  3. Towards the top, click on the Capabilities tab.
  4. In the App Groups section click the switch to the right to turn App Groups ON.
  5. Click on the + button and add an App Group named group.com.company.myApp.
  6. Go to the same place in your other apps and this group should now be available to select. Turn this group on for each app that will be using this shared data.

Note: If you go to the Apple Developer Portal (the Apple website that shows all of your Certificates, Identifiers, Devices and Provisioning Profiles) and go to Identifiers > App Groups you should see this new App Group.

To store data:

var userDefaults = NSUserDefaults(suiteName: "group.com.company.myApp")!
userDefaults.setObject("user12345", forKey: "userId")
userDefaults.synchronize()

To retrieve data:

var userDefaults = NSUserDefaults(suiteName: "group.com.company.myApp")
if let testUserId = userDefaults?.objectForKey("userId") as? String {
print("User Id: \(testUserId)")
}

One important trap I tapped into today is the following:

In many projects I saw a single app target and with different bundle identifiers set for each configuration of that target. Here things get messy. What the developers intended was to create a debug app for the debug config and a production app for the release target.

If you do so both apps will share the same NSUserDefaults when they are set up like so

var userDefaults = NSUserDefaults(suiteName: "group.com.company.myApp")
userDefaults!.setObject("user12345", forKey: "userId")
userDefaults!.synchronize()

This causes problems in many places:

  1. Imagine you set YES for a key when a special app-intro-screen has been shown to the user. The other app will now also read YES and don't show the intro.
  2. Yes some apps also store oAuth tokens in their user defaults. Anyways... Depending on the implementation, the app will recognize that there's a token and start retrieving data using the wrong token. The chance is high that this will fail with strange errors.

The solution to this problem in general is to prefix the defaults keys with the current configuration built. You can detect the configuration easily at runtime by setting different bundle identifiers for your configurations. Then just read the bundle identifier from NSBundle.mainBundle(). If you have the same bundle identifiers you need to set different preprocessor macros like

#ifdef DEBUG
NSString* configuration = @"debug";
#elif RELEASE
NSString* configuration = @"release";
#endif

In Swift it will look almost the same:

#if DEBUG
let configuration = "debug"
#elseif RELEASE
let configuration = "release"
#endif

To store

let shared: NSUserDefaults = NSUserDefaults(suiteName: "group.abcapp")!
shared.setObject("abc.png", forKey: "favEmoji")
shared.synchronize()

iOS App Group

App Group allows you to share data(UserDefaults, Files, CoreData(manage model graph), POSIX locks) between different processes(applications, extensions...) from the same development team(account). It creates a shared container whit id which shoyld start from group. for saving/caching data which you are allowed to access thought url and IPC

To use App Group with UserDefaults

  1. Add App Group capability with the same id for ALL targets(app, extension...) which you will get an access from enter image description here

After creation you are able to check it on Apple Developer. Certificates, IDs & Profiles -> Identifiers -> ALL Groups

enter image description here

  1. Write from Application1
let defaults = UserDefaults(suiteName: "group.goforit")
defaults?.setValue("Hello World!", forKey: "key1")
  1. Read from Application2
let defaults = UserDefaults(suiteName: "group.goforit")
let result = defaults?.value(forKey: "key1") //Hello World!

shared container root location single URL.

let rootURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.goforit")


//file:///Users/alex/Library/Developer/CoreSimulator/Devices/1AF41817-FE2E-485A-A592-12C39C0B0141/data/Containers/Shared/AppGroup/DC14D43F-2C2C-4771-83BE-64A9F54BD2E1/

[iOS App Extension]