Skip to content

Commit 63310d2

Browse files
committed
feat: Allow brownfield iOS apps to handle storage.
1 parent 566bbb2 commit 63310d2

File tree

7 files changed

+280
-15
lines changed

7 files changed

+280
-15
lines changed

example/ios/AsyncStorageExample.xcodeproj/project.pbxproj

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
2222
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
2323
146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; };
24+
1990B97922399119009E5EA1 /* libRNCAsyncStorage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DC5398C220F2C940035D3A3 /* libRNCAsyncStorage.a */; };
2425
832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; };
2526
ADBDB9381DFEBF1600ED6528 /* libRCTBlob.a in Frameworks */ = {isa = PBXBuildFile; fileRef = ADBDB9271DFEBF0700ED6528 /* libRCTBlob.a */; };
2627
ED297163215061F000B7C4FE /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ED297162215061F000B7C4FE /* JavaScriptCore.framework */; };
@@ -83,6 +84,13 @@
8384
remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192;
8485
remoteInfo = React;
8586
};
87+
1990B95122398FC4009E5EA1 /* PBXContainerItemProxy */ = {
88+
isa = PBXContainerItemProxy;
89+
containerPortal = 3DC5395A220F2C940035D3A3 /* RNCAsyncStorage.xcodeproj */;
90+
proxyType = 1;
91+
remoteGlobalIDString = 58B511DA1A9E6C8500147676;
92+
remoteInfo = RNCAsyncStorage;
93+
};
8694
2D16E6711FA4F8DC00B85C8A /* PBXContainerItemProxy */ = {
8795
isa = PBXContainerItemProxy;
8896
containerPortal = ADBDB91F1DFEBF0600ED6528 /* RCTBlob.xcodeproj */;
@@ -340,6 +348,7 @@
340348
832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */,
341349
00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */,
342350
139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */,
351+
1990B97922399119009E5EA1 /* libRNCAsyncStorage.a in Frameworks */,
343352
);
344353
runOnlyForDeploymentPostprocessing = 0;
345354
};
@@ -555,6 +564,7 @@
555564
buildRules = (
556565
);
557566
dependencies = (
567+
1990B95222398FC4009E5EA1 /* PBXTargetDependency */,
558568
);
559569
name = AsyncStorageExample;
560570
productName = "Hello World";
@@ -936,7 +946,7 @@
936946
);
937947
runOnlyForDeploymentPostprocessing = 0;
938948
shellPath = /bin/sh;
939-
shellScript = "export NODE_BINARY=node\n../../node_modules/react-native/scripts/react-native-xcode.sh";
949+
shellScript = "export NODE_BINARY=node\n../../node_modules/react-native/scripts/react-native-xcode.sh\n";
940950
};
941951
/* End PBXShellScriptBuildPhase section */
942952

@@ -952,6 +962,14 @@
952962
};
953963
/* End PBXSourcesBuildPhase section */
954964

965+
/* Begin PBXTargetDependency section */
966+
1990B95222398FC4009E5EA1 /* PBXTargetDependency */ = {
967+
isa = PBXTargetDependency;
968+
name = RNCAsyncStorage;
969+
targetProxy = 1990B95122398FC4009E5EA1 /* PBXContainerItemProxy */;
970+
};
971+
/* End PBXTargetDependency section */
972+
955973
/* Begin PBXVariantGroup section */
956974
13B07FB11A68108700A75B9A /* LaunchScreen.xib */ = {
957975
isa = PBXVariantGroup;

example/ios/AsyncStorageExample/AppDelegate.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77

88
#import <UIKit/UIKit.h>
99

10-
@interface AppDelegate : UIResponder <UIApplicationDelegate>
10+
#import <RNCAsyncStorage/RNCAsyncStorageDelegate.h>
11+
12+
@interface AppDelegate : UIResponder <UIApplicationDelegate, RNCAsyncStorageDelegate>
1113

1214
@property (nonatomic, strong) UIWindow *window;
1315

example/ios/AsyncStorageExample/AppDelegate.m

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,22 @@
77

88
#import "AppDelegate.h"
99

10+
#import <RNCAsyncStorage/RNCAsyncStorage.h>
1011
#import <React/RCTBundleURLProvider.h>
12+
#import <React/RCTDevMenu.h>
1113
#import <React/RCTRootView.h>
1214

13-
@implementation AppDelegate
15+
@implementation AppDelegate {
16+
NSMutableDictionary<NSString *, NSString *> *_memoryStorage;
17+
}
1418

1519
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
1620
{
21+
[[NSNotificationCenter defaultCenter] addObserver:self
22+
selector:@selector(didLoadJavaScript:)
23+
name:RCTJavaScriptDidLoadNotification
24+
object:nil];
25+
1726
NSURL *jsCodeLocation;
1827

1928
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"example/index" fallbackResource:nil];
@@ -32,4 +41,83 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
3241
return YES;
3342
}
3443

44+
- (void)addDevMenuItemsForBridge:(RCTBridge *)bridge
45+
{
46+
_memoryStorage = [NSMutableDictionary dictionary];
47+
48+
__weak AppDelegate *weakSelf = self;
49+
RCTDevMenuItem *delegateToggle = [RCTDevMenuItem
50+
buttonItemWithTitleBlock:^NSString * {
51+
RNCAsyncStorage *asyncStorage = [bridge moduleForClass:[RNCAsyncStorage class]];
52+
return asyncStorage.delegate == nil ? @"Set AsyncStorage Delegate"
53+
: @"Unset AsyncStorage Delegate";
54+
}
55+
handler:^{
56+
RNCAsyncStorage *asyncStorage = [bridge moduleForClass:[RNCAsyncStorage class]];
57+
asyncStorage.delegate = asyncStorage.delegate == nil ? weakSelf : nil;
58+
}];
59+
[bridge.devMenu addItem:delegateToggle];
60+
}
61+
62+
- (void)didLoadJavaScript:(NSNotification *)note
63+
{
64+
RCTBridge *bridge = note.userInfo[@"bridge"];
65+
if (bridge == nil) {
66+
return;
67+
}
68+
69+
[self addDevMenuItemsForBridge:bridge];
70+
}
71+
72+
#pragma mark - RNCAsyncStorageDelegate
73+
74+
- (void)allKeys:(nonnull RNCAsyncStorageResultCallback)completion
75+
{
76+
completion(_memoryStorage.allKeys);
77+
}
78+
79+
- (void)mergeValues:(nonnull NSArray<NSString *> *)values
80+
forKeys:(nonnull NSArray<NSString *> *)keys
81+
completion:(nonnull RNCAsyncStorageResultCallback)block
82+
{
83+
}
84+
85+
- (void)removeAllValues:(nonnull RNCAsyncStorageCompletion)completion
86+
{
87+
[_memoryStorage removeAllObjects];
88+
completion(nil);
89+
}
90+
91+
- (void)removeValuesForKeys:(nonnull NSArray<NSString *> *)keys
92+
completion:(nonnull RNCAsyncStorageResultCallback)completion
93+
{
94+
for (NSString *key in keys) {
95+
[_memoryStorage removeObjectForKey:key];
96+
}
97+
completion(@[]);
98+
}
99+
100+
- (void)setValues:(nonnull NSArray<NSString *> *)values
101+
forKeys:(nonnull NSArray<NSString *> *)keys
102+
completion:(nonnull RNCAsyncStorageResultCallback)completion
103+
{
104+
for (NSUInteger i = 0; i < values.count; ++i) {
105+
NSString *value = values[i];
106+
NSString *key = keys[i];
107+
[_memoryStorage setObject:value forKey:key];
108+
}
109+
completion(@[]);
110+
}
111+
112+
- (void)valuesForKeys:(nonnull NSArray<NSString *> *)keys
113+
completion:(nonnull RNCAsyncStorageResultCallback)completion
114+
{
115+
NSMutableArray *values = [NSMutableArray arrayWithCapacity:keys.count];
116+
for (NSString *key in keys) {
117+
NSString *value = _memoryStorage[key];
118+
[values addObject:value == nil ? [NSNull null] : value];
119+
}
120+
completion(values);
121+
}
122+
35123
@end

ios/RNCAsyncStorage.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#import <React/RCTBridgeModule.h>
99
#import <React/RCTInvalidating.h>
10+
#import <RNCAsyncStorage/RNCAsyncStorageDelegate.h>
1011

1112
/**
1213
* A simple, asynchronous, persistent, key-value storage system designed as a
@@ -21,6 +22,8 @@
2122
*/
2223
@interface RNCAsyncStorage : NSObject <RCTBridgeModule,RCTInvalidating>
2324

25+
@property (nonatomic, weak, nullable) id<RNCAsyncStorageDelegate> delegate;
26+
2427
@property (nonatomic, assign) BOOL clearOnInvalidate;
2528

2629
@property (nonatomic, readonly, getter=isValid) BOOL valid;
@@ -37,5 +40,4 @@
3740
// Add multiple key value pairs to the cache.
3841
- (void)multiSet:(NSArray<NSArray<NSString *> *> *)kvPairs callback:(RCTResponseSenderBlock)callback;
3942

40-
4143
@end

ios/RNCAsyncStorage.m

Lines changed: 109 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,18 @@ static void RCTAppendError(NSDictionary *error, NSMutableArray<NSDictionary *> *
4343
}
4444
}
4545

46+
static NSArray<NSDictionary *> *RCTMakeErrors(NSArray<id<NSObject>> *results) {
47+
NSMutableArray<NSDictionary *> *errors;
48+
for (id object in results) {
49+
if ([object isKindOfClass:[NSError class]]) {
50+
NSError *error = (NSError *)object;
51+
NSDictionary *keyError = RCTMakeError(error.localizedDescription, error, nil);
52+
RCTAppendError(keyError, &errors);
53+
}
54+
}
55+
return errors;
56+
}
57+
4658
static NSString *RCTReadFile(NSString *filePath, NSString *key, NSDictionary **errorOut)
4759
{
4860
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
@@ -329,30 +341,77 @@ - (NSDictionary *)_writeEntry:(NSArray<NSString *> *)entry changedManifest:(BOOL
329341
return errorOut;
330342
}
331343

344+
- (void)_multiGet:(NSArray<NSString *> *)keys
345+
callback:(RCTResponseSenderBlock)callback
346+
getter:(NSString *(^)(NSUInteger i, NSString *key, NSDictionary **errorOut))getValue
347+
{
348+
NSMutableArray<NSDictionary *> *errors;
349+
NSMutableArray<NSArray<NSString *> *> *result = [NSMutableArray arrayWithCapacity:keys.count];
350+
for (NSUInteger i = 0; i < keys.count; ++i) {
351+
NSString *key = keys[i];
352+
id keyError;
353+
id value = getValue(i, key, &keyError);
354+
[result addObject:@[key, RCTNullIfNil(value)]];
355+
RCTAppendError(keyError, &errors);
356+
}
357+
callback(@[RCTNullIfNil(errors), result]);
358+
}
359+
332360
#pragma mark - Exported JS Functions
333361

334362
RCT_EXPORT_METHOD(multiGet:(NSArray<NSString *> *)keys
335363
callback:(RCTResponseSenderBlock)callback)
336364
{
365+
if (self.delegate != nil) {
366+
[self.delegate valuesForKeys:keys completion:^(NSArray<id<NSObject>> *valuesOrErrors) {
367+
[self _multiGet:keys
368+
callback:callback
369+
getter:^NSString *(NSUInteger i, NSString *key, NSDictionary **errorOut) {
370+
id valueOrError = valuesOrErrors[i];
371+
if ([valueOrError isKindOfClass:[NSError class]]) {
372+
NSError *error = (NSError *)valueOrError;
373+
NSDictionary *extraData = @{@"key": RCTNullIfNil(key)};
374+
*errorOut = RCTMakeError(error.localizedDescription, error, extraData);
375+
return nil;
376+
} else {
377+
return [valueOrError isKindOfClass:[NSString class]]
378+
? (NSString *)valueOrError
379+
: nil;
380+
}
381+
}];
382+
}];
383+
return;
384+
}
385+
337386
NSDictionary *errorOut = [self _ensureSetup];
338387
if (errorOut) {
339388
callback(@[@[errorOut], (id)kCFNull]);
340389
return;
341390
}
342-
NSMutableArray<NSDictionary *> *errors;
343-
NSMutableArray<NSArray<NSString *> *> *result = [[NSMutableArray alloc] initWithCapacity:keys.count];
344-
for (NSString *key in keys) {
345-
id keyError;
346-
id value = [self _getValueForKey:key errorOut:&keyError];
347-
[result addObject:@[key, RCTNullIfNil(value)]];
348-
RCTAppendError(keyError, &errors);
349-
}
350-
callback(@[RCTNullIfNil(errors), result]);
391+
[self _multiGet:keys
392+
callback:callback
393+
getter:^(NSUInteger i, NSString *key, NSDictionary **errorOut) {
394+
return [self _getValueForKey:key errorOut:errorOut];
395+
}];
351396
}
352397

353398
RCT_EXPORT_METHOD(multiSet:(NSArray<NSArray<NSString *> *> *)kvPairs
354399
callback:(RCTResponseSenderBlock)callback)
355400
{
401+
if (self.delegate != nil) {
402+
NSMutableArray<NSString *> *keys = [NSMutableArray arrayWithCapacity:kvPairs.count];
403+
NSMutableArray<NSString *> *values = [NSMutableArray arrayWithCapacity:kvPairs.count];
404+
for (NSArray<NSString *> *entry in kvPairs) {
405+
[keys addObject:entry[0]];
406+
[values addObject:entry[1]];
407+
}
408+
[self.delegate setValues:values forKeys:keys completion:^(NSArray<id<NSObject>> *results) {
409+
NSArray<NSDictionary *> *errors = RCTMakeErrors(results);
410+
callback(@[RCTNullIfNil(errors)]);
411+
}];
412+
return;
413+
}
414+
356415
NSDictionary *errorOut = [self _ensureSetup];
357416
if (errorOut) {
358417
callback(@[@[errorOut]]);
@@ -373,6 +432,20 @@ - (NSDictionary *)_writeEntry:(NSArray<NSString *> *)entry changedManifest:(BOOL
373432
RCT_EXPORT_METHOD(multiMerge:(NSArray<NSArray<NSString *> *> *)kvPairs
374433
callback:(RCTResponseSenderBlock)callback)
375434
{
435+
if (self.delegate != nil) {
436+
NSMutableArray<NSString *> *keys = [NSMutableArray arrayWithCapacity:kvPairs.count];
437+
NSMutableArray<NSString *> *values = [NSMutableArray arrayWithCapacity:kvPairs.count];
438+
for (NSArray<NSString *> *entry in kvPairs) {
439+
[keys addObject:entry[0]];
440+
[values addObject:entry[1]];
441+
}
442+
[self.delegate mergeValues:values forKeys:keys completion:^(NSArray<id<NSObject>> *results) {
443+
NSArray<NSDictionary *> *errors = RCTMakeErrors(results);
444+
callback(@[RCTNullIfNil(errors)]);
445+
}];
446+
return;
447+
}
448+
376449
NSDictionary *errorOut = [self _ensureSetup];
377450
if (errorOut) {
378451
callback(@[@[errorOut]]);
@@ -407,8 +480,16 @@ - (NSDictionary *)_writeEntry:(NSArray<NSString *> *)entry changedManifest:(BOOL
407480
}
408481

409482
RCT_EXPORT_METHOD(multiRemove:(NSArray<NSString *> *)keys
410-
callback:(RCTResponseSenderBlock)callback)
483+
callback:(RCTResponseSenderBlock)callback)
411484
{
485+
if (self.delegate != nil) {
486+
[self.delegate removeValuesForKeys:keys completion:^(NSArray<id<NSObject>> *results) {
487+
NSArray<NSDictionary *> *errors = RCTMakeErrors(results);
488+
callback(@[RCTNullIfNil(errors)]);
489+
}];
490+
return;
491+
}
492+
412493
NSDictionary *errorOut = [self _ensureSetup];
413494
if (errorOut) {
414495
callback(@[@[errorOut]]);
@@ -439,6 +520,17 @@ - (NSDictionary *)_writeEntry:(NSArray<NSString *> *)entry changedManifest:(BOOL
439520

440521
RCT_EXPORT_METHOD(clear:(RCTResponseSenderBlock)callback)
441522
{
523+
if (self.delegate != nil) {
524+
[self.delegate removeAllValues:^(NSError *error) {
525+
NSDictionary *result = nil;
526+
if (error != nil) {
527+
result = RCTMakeError(error.localizedDescription, error, nil);
528+
}
529+
callback(@[RCTNullIfNil(result)]);
530+
}];
531+
return;
532+
}
533+
442534
[_manifest removeAllObjects];
443535
[RCTGetCache() removeAllObjects];
444536
NSDictionary *error = RCTDeleteStorageDirectory();
@@ -447,6 +539,13 @@ - (NSDictionary *)_writeEntry:(NSArray<NSString *> *)entry changedManifest:(BOOL
447539

448540
RCT_EXPORT_METHOD(getAllKeys:(RCTResponseSenderBlock)callback)
449541
{
542+
if (self.delegate != nil) {
543+
[self.delegate allKeys:^(NSArray<id<NSObject>> *keys) {
544+
callback(@[(id)kCFNull, keys]);
545+
}];
546+
return;
547+
}
548+
450549
NSDictionary *errorOut = [self _ensureSetup];
451550
if (errorOut) {
452551
callback(@[errorOut, (id)kCFNull]);

0 commit comments

Comments
 (0)