iOS Shared UserDefaults for App Extensions With RCTBridgeModule

January 20, 2022
Get/Set key-value from both React Native and Swift/Obj-C

In this blog post, I will share the code for a simple RCTBridgeModule that can write/read key-value from React Native.

The use case for this is to access the data in iOS Share Extension using value set from React Native.

So that the Share Extension can perform API call with sufficient data that was set in React Native, and thus giving user a much better experience similar to other natively built apps. This is a much more complicated implementation and there are much simpler ways to get it done without writing too many native code and that will be for another blog post.

Let's get back and focus on this topic, get and set value in UserDefaults that are accesible in Share Extension. In order to achieve that, we need to use UserDefaults with suiteName that starts with group..

Before you proceed, please make sure that you have already created Share Extension and updated App Groups in the Signing & Capabilities. This is not covered in this post and there are alot of online resources for this topic.



1. First, create a header file in xcode.

I'll name it SharedUserDefaults.h

#ifndef SharedUserDefaults_h
#define SharedUserDefaults_h
#endif

#import <Foundation/Foundation.h>

#if __has_include(<React/RCTAssert.h>)
#import <React/RCTBridgeModule.h>
#else
#import "RCTBridgeModule.h"
#endif

@interface SharedUserDefaults : NSObject <RCTBridgeModule>
@end

2. Then, create the implementation file.

SharedUserDefaults.m

#import <Foundation/Foundation.h>
#import "SharedUserDefaults.h"

@implementation SharedUserDefaults

RCT_EXPORT_MODULE()

+ (BOOL)requiresMainQueueSetup
{
  return YES;
}

RCT_EXPORT_METHOD(set:(NSString *)suiteName
                  key:(NSString *)keyName
                  value:(NSString *)value)
{
  NSUserDefaults *shareExtensionDefaults = [[NSUserDefaults alloc] initWithSuiteName: suiteName];
  [shareExtensionDefaults setObject:value forKey:keyName];
  NSLog(@"val written %@", value);
}

RCT_EXPORT_METHOD(get:(NSString *)suiteName
                  key:(NSString *)keyName
                  resolver: (RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
{
  NSUserDefaults *shareExtensionDefaults = [[NSUserDefaults alloc] initWithSuiteName: suiteName];
  NSString *val = [shareExtensionDefaults stringForKey:keyName];
  if (val == nil) {
    NSError *error = [NSError errorWithDomain:@"org.reactjs.native.example.Bill" code:0 userInfo:@{ @"message": @"Empty" }];
    reject(@"error", @"error description", error);
  } else {
    resolve(val);
  }
}

@end

3. Next, build and run your app.

And you can use the methods from the native module you've just written.

In your React Native project, you can now get and set key-value pair to UserDefaults via the native module.

import { NativeModules } from 'react-native';

const SharedUserDefaults = NativeModules.SharedUserDefaults;

// Usage

// Set
SharedUserDefaults.set("group.org.reactjs.native.example.YourProject", "yourKey", "some string to be stored");

// Get
let val = await SharedUserDefaults.get("group.org.reactjs.native.example.YourProject", "yourKey");

In iOS, the value can be retrieved in ShareViewController.swift or AppDelegate.m.

Swift

var userDefaults = UserDefaults(suiteName: "group.org.reactjs.native.example.YourProject")

// Get value stored earlier
let storedValue = userDefaults?.object(forKey: "yourKey") as? String

 // Set new value
userDefaults?.set("some string to be stored", forKey: "yourKey")

Obj-C

  NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName: @"group.org.reactjs.native.example.YourProject"];

  // Get value stored earlier
  NSString *val = [userDefaults stringForKey:@"yourKey"];

  // Set new value
  [userDefaults setObject:@"some string to be stored" forKey:@"yourKey"];

End

You are now all set!

Now you can set value from React Native and retrieve it in your ShareViewController.swift.

That's it! Hope it helps.

Cheers.