Communicating and Persisting Data Between Apps With App Groups

Communicating and persisting data between apps with App Groups

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];

Share datas between two apps with iOS 8 App Groups (using NSUserDefaults)

Check out this thread on the Apple Developer Forums:
https://devforums.apple.com/message/977151#977151

I believe that both instances need to use the group ID (initializing with initWithSuiteName:) or else they are reading/writing to different user defaults sets.

So your Swift code would change to:

var userDefaults = NSUserDefaults(suiteName: "group.com.company.myApp")
userDefaults.setBool(true, forKey: "Bool")
userDefaults.synchronize()

How To Use Shared Container/App Groups To Access Files From Other Targets in Swift

After some trial and error, the answer is as follows:

Instead of passing in the string "user12345", you need to pass in the URL to the file you want to read for the userDefaults.set method as follows:

var userDefaults = UserDefaults(suiteName: "group.com.example.name")!
userDefaults.set(Bundle.main.url(forResource: "testdata", withExtension: ".txt"), forKey: "userId")
userDefaults.synchronize()

Then in the receiver app, you call that object and set the URL:

let userDefaults = UserDefaults(suiteName: "group.com.example.name")
let test = userDefaults?.object(forKey: "userId") as? String
let testURL = URL(fileURLWithPath: test!)

From here you can read in the contents as normal.

Are there any weak points to store all data in App Groups' container area on iOS8?

Naturally it depends on your app but here's my two cents:

Storing all of your information in a shared directory is complete overkill and doesn't sound like its coming from any sound reasoning. If you feel that your widget needs all the information that your app needs then perhaps you need to reconsider the complexity of your widget.

What you are achieving by this is convenience (future you headaches) rather than simplicity (hiding complexity through singular responsibility). The caveat is that as your application grows and becomes more complex you will end up spending a lot more time facilitating information inside of the App Container (a la massive view controller). What you want to strive for is singular responsibility. Instead you now need an object to facilitate the information of two apps into one container.

Shared containers are also different to the App Sandbox as you need to worry about coordinating reads and writes. Thats why typically its best to go ahead with either Core Data or NSUserDefaults (instantiated with the suite name). If your app stores images and other content then you are in for a world of pain. Even Apple recommend User Defaults (since its the only example they give) or CoreData:

After you enable app groups, an app extension and its containing app can both use the NSUserDefaults API to share access to user preferences [...]
To avoid data corruption, you must synchronize data accesses. Use Core Data,
SQLite, or Posix locks to help coordinate data access in a shared
container.

The purpose of a widget is to provide the at-a-minute overview of your app. By minimizing the amount of data the widget has access to you will reduce many headaches as well as make your widget better contained. Remember, with great power comes great responsibility and if you think storing all of your information in one shared container then you have a lot to be accountable for...

Example

I have recently finished building a widget for a news app that displays the top news stories as well as the ability to personalize the content the widget displays. Heres a basic breakdown of the data persistence to give you an idea:

Storage Breakdown

Widget

  • Images its currently displaying
  • Last Modified Date of the JSON Feed (So we know when to tell the widget that new content has been added)

App

  • All Stories, Images and User content etc

Shared Container

  • Users chosen topics
  • List of top stories

When the widget is asked to fetch content it downloads a JSON file containing the top stories for all topics. This JSON is persisted to the shared container. The reason we store all the stories is in case the user changes their favorite topics, the widget can then simply update its selection as all the topics are stored. This also allows the app to update the top stories and the widget to reflect that immediately.

The widget will then extract 3 of the top stories that match the users specified topic tags. These top topics are persisted in a shared user defaults item. Naturally its possible that the widget is opened before the user chooses topics, in this case the widget will automatically choose the first three or so topics.

TL;DR

A metaphor - If a Widget and an App are colleagues in the workplace, the Shared Container is like a computer. How productive would you be working with someone all day, every day, on the same computer?

Possible to share iOS App Groups between an Enterprise team and a standard team?

That's not possible, because app groups are only shared within a single development team.

The com.apple.security.application-groups (available in OS X v10.7.5 and v10.8.3 and later) allows multiple apps produced by a single development team to share access to a special group container.

Source: https://developer.apple.com/library/content/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/EnablingAppSandbox.html#//apple_ref/doc/uid/TP40011195-CH4-SW19

Sharing UserDefaults between extensions

You cannot use UserDefaults.standard to share data between a host app and its app extension. You instead have to create a shared container with UserDefaults(suiteName:) to share data.

Even though an app extension bundle is nested within its containing
app’s bundle, the running app extension and containing app have no
direct access to each other’s containers.

To enable data sharing, use Xcode or the Developer portal to enable
app groups for the containing app and its contained app extensions.
Next, register the app group in the portal and specify the app group
to use in the containing app.

After you enable app groups, an app extension and its containing app
can both use the NSUserDefaults API to share access to user
preferences. To enable this sharing, use the initWithSuiteName: method
to instantiate a new NSUserDefaults object, passing in the identifier
of the shared group.

For more, refer to: https://developer.apple.com/library/content/documentation/General/Conceptual/ExtensibilityPG/ExtensionScenarios.html#//apple_ref/doc/uid/TP40014214-CH21-SW1

How to use App Groups: https://github.com/pgpt10/Today-Widget

Standard or SuiteName?

Use standard one for data that is only for Host App. Use suiteName for data that you want to share between Extension and Host App. Just don't persist the same data in both of them. Avoid data redundancy. Use both of them according to the context.



Related Topics



Leave a reply



Submit