Setting up your AppGroup to share data between App & Extensions in iOS

AppGroups & some odd behavior in iOS

John Baker
5 min readMay 1, 2024

App Groups in iOS can be a little confusing when getting started. With this article I’m hoping to help shed some light on tips when first getting started, as well as a system that makes sharing data as easy as using single app data.

Stryde | for a better life

I’ve been creating an application that let’s you earn ScreenTime based on your Step count. This has been an interesting process & involves working with 2 very restrictive API’s (FamilyControls & HealthKit). As a user I’m very thankful that Apple takes these precautions to protect sensitive data, but as a developer, it definitely makes my life hard.

Throwing AppGroups in to the mix just added another layer of complexity to an already complex app. But was necessary in order for me to share data with my Shield Configuration (more information here) and share data with my ShieldActionDelegate (article coming soon, dont forget to subscribe).

In this article I’ll share how I set up my App Group to share UserDefaults and local storage between my App & App Extensions. You can also set up App Groups to share data with your own external apps (think Facebook & Messenger), this article will not go through that process.

Getting set up

As a prerequisite make sure you have your main target set up & create an app extension. I’ll be using a shield configuration (mainly just because that’s what my test project already has from my other articles).

Head on over to the Signing & Capabilities section for each target you’d like to add to your app group.

You’ll now have a new section here labeled App Groups. Click the plus to add a new app group.

Be very conscious when creating the app group name. Apple / XCode seem to be very picky about what is allowed here. I have had the best luck following the pattern:

group.[MainTargetBundleId].[descriptor]

So for my app here I’ll create an app group named com.b4k3r.FirebaseTest.sharedData .

Now your project should be properly configured to use your app group, let’s see how we can use this in code to share data between targets.

Accessing Shared Data

To get started with sharing data between targets, I’ve created a new file called SharedData. This file will be built with both targets.

Be sure to only import libraries here that you have assigned to every target. XCode should warn you if you import something not included in one of your targets, but we all know how relying on XCode to properly throw errors has been lately.

class SharedData {
static let defaultsGroup: UserDefaults? = UserDefaults(suiteName: "group.com.b4k3r.FirebaseTest.sharedData")

enum Keys: String {
case isUserPremium = "isUserPremiumKey"

var key: String {
switch self {
default: self.rawValue
}
}
}
}

Let’s break down what’s going on here.

The SharedData class has a static property called defaultsGroup . This is a UserDefaults container. If you’ve worked with UserDefaults before you might be accustomed to using the standard group (UserDefaults.standard). This is the exact same thing, except it points to a specific, shared data, app group location that we set up in the first half of this article.

From here you can just use this SharedData.defaultsGroup exactly like you would with the UserDefaults.standard accessor.

Interested in seeing how to set up custom SwiftUI implementations & UserDefaults wrappers to make using this sharedData even easier? See more about this below a brief advertisement for my app — aka what made this article free instead of paywalled ;)

Stryde | for a better life

Are you someone who spends too much time on a screen & not enough time getting active? Check out my app Stryde mentioned above. It helps limit your ScreenTime & motivate you to get active.

It is available currently on iOS and can be found here

I spent a lot of time on this & even just during testing it has completely transformed my relationship with my phone. Hope you might also be able to get some use out of it.

Stryde — Available on iOS App Store

App can be downloaded here from the Apple app store

Accessing your shared data

Okay now that that’s out of the way. Let’s see how to access this data even easier.

Lets add an accessor property to the original SharedData class with a custom getter and setter.

class SharedData {
static let defaultsGroup: UserDefaults? = UserDefaults(suiteName: "group.com.b4k3r.FirebaseTest.sharedData")

enum Keys: String {
case isUserPremium = "isUserPremiumKey"

var key: String {
switch self {
default: self.rawValue
}
}
}

static var isUserPremium: Bool {
get {
defaultsGroup?.bool(forKey: Keys.isUserPremium.key) ?? false
} set {
defaultsGroup?.set(newValue, forKey: Keys.isUserPremium.key)
}
}
}

This custom getter & setter here do all of the leg work for us.

Now if we want to change the value in our user defaults shared data group, we can simple call SharedData.isUserPremium = true. Much much better than having to call SharedData.defaultsGroup?.set(newValue, forKey: SharedData.Keys.isUserPremium.key) every time we want to set a new value.

Vice versa as well. If we want to read the value we can simply read it from the class like we would with any other property e.g. if SharedData.isUserPremium { showPremiumFeatures() } .

Usage in SwiftUI

SwiftUI has a handy little Macro for accessing User defaults called @AppStorage . But how can we use this in conjunction with our shared data AppGroup?

Simply add this snippet inside your View struct like you would with any other wrapper property.

@AppStorage(SharedData.Keys.isUserPremium.key, store: SharedData.defaultsGroup) var isUserPremium: Bool = false

The note to take here is that we are adding a store argument to the macro in order to access that specific key in our App Group instead of the normal UserDefaults.

If this article helped you please don’t forget to clap, follow, subscribe & most importantly check out Stepper!! But seriously, thanks for reading & hope this helps you get your ShieldConfiguration set up properly.

--

--