BrainExpanded – Login State Caching Issue in iOS Share Extension

As part of the BrainExpanded project, I’m building an iOS app that lets users easily add links, text, and images to the “brain’s memory.” Once in the memory, various agents process the content to make it more accessible and useful (see previous posts for a description). The app’s XCode project has two targets: the main app and a Share Extension. The Share Extension allows users to add content from other apps via the iOS Activity View (previously known as “Share Sheet”).

To integrate with the BrainExpanded service, I needed to send user-submitted entries to a messaging queue. This required authentication, so I turned to Google’s Firebase, which simplifies adding authentication and other backend features to an app. However, I ran into a tricky issue while sharing the user’s login state between the main app and the Share Extension.

The Problem: Keychain Sync and Stale Login State

In XCode, the Share Extension (the entry into the Activity View) and the main app are treated as separate targets. To allow the Share Extension to check the user’s login state stored by the main app, I set up a shared group entitlement for the keychain. This setup allowed both the app and the Share Extension to access the same authentication state.

At first, everything seemed to work fine. The BrainExpanded Share Extension could pick up the login state from the keychain. But then I noticed a strange behavior:

  1. The first time I shared a webpage from Safari, the Share Extension correctly fetched the current login state.
  2. If I switched to the main app and changed the login state (e.g., logged out while logged in), and then returned to Safari to share something else, the Share Extension still used the old login state.

Clearly, this was a caching issue. Firebase wasn’t updating the login state in real time, and I couldn’t find an explicit Firebase API to force a refresh of the keychain data.

The Solution: Let Firebase Manage the Keychain Access Group

After trying several approaches, I found the solution by updating how Firebase determines the access group ID for the keychain. Here’s the key change I made:

override func viewDidLoad() {
    super.viewDidLoad()

    FirebaseApp.configure()

    do {
        var user: User?
        try user = Auth.auth().getStoredUser(forAccessGroup: nil)
        if (user == nil) {
            requireLogin()
            return
        }
    } catch {
        requireLogin()
        return

The key was to let Firebase handle the access group by passing nil to forAccessGroup when calling getStoredUser(). This allowed Firebase to dynamically determine the correct access group ID, ensuring the Share Extension always fetched the up-to-date login state. Many posts and discussions on the web suggested hardcoding the access group ID, but in my case, using nil resolved the caching issue.

Lessons Learned

This issue highlighted the nuances of working with Firebase authentication and iOS extensions. Here are a few takeaways:

  1. Extensions Cache Data: Extensions don’t always refresh their view of shared data, especially when it’s stored in the keychain. Explicit steps may be needed to ensure data stays in sync.
  2. Let the Framework Do the Work: While manually setting the access group ID seemed logical, allowing Firebase to handle it automatically worked better.
  3. Don’t Overlook the “Simple Fix”: Sometimes, the solution isn’t about adding complexity but simplifying and relying on defaults.

Now, the Share Extension and the main app work seamlessly together. If the user’s login state changes in the app, the Share Extension immediately picks it up.


I hope this post helps others who run into similar issues with Firebase, keychain synchronization, or iOS Share Extensions.


Disclaimer: I experimenting with ChatGPT as the generator for this post and the featured image. I did have to make few changes but ChatGPT did a pretty good job 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *