From c7ad49a9eb67f6bf85e51a3b9024e898ccb766b6 Mon Sep 17 00:00:00 2001 From: Marc Terns Date: Fri, 12 Oct 2018 04:07:39 -0700 Subject: [PATCH] Integrating NSUserDefaults plugin to iOS (#291) Summary: I have a few details left, but its almost done. This PR addresses #145 - The NSUserDefaults plugin uses the SharedPreferences Desktop Part since we can reuse all of it. - The NSUserDefaults plugin uses swizzling in order to be notified of what specific event changed at runtime. - Added Test harness in both Sample Swift and Sample apps for iOS in order to test the plugin. - Updated the documentation in `docs/shared-preferences-plugin.md` and` README.md` I am open to suggestions since the desktop sharedPreferences version doesn't support deletion of preferences. Most likely I would have to modify the UI, and for that matter, I might as well build a user defaults desktop version I wanted to add xiphirx in this MR since he developed the shared preferences plugin for Android and Desktop. I don't see a way to remove preferences from the flipper desktop app so I was wondering if you would be OK with me adding that. Pull Request resolved: https://github.com/facebook/flipper/pull/291 Reviewed By: passy Differential Revision: D10334685 Pulled By: priteshrnandgaonkar fbshipit-source-id: d798c01a46df7ddecf713924799f046b560ea922 --- README.md | 1 + docs/getting-started.md | 6 +- docs/shared-preferences-plugin.md | 20 +++- iOS/FlipperKit.podspec | 10 ++ .../FKUserDefaultsPlugin.h | 20 ++++ .../FKUserDefaultsPlugin.m | 80 ++++++++++++++++ .../FKUserDefaultsSwizzleUtility.h | 18 ++++ .../FKUserDefaultsSwizzleUtility.m | 76 +++++++++++++++ iOS/Sample/AppDelegate.mm | 5 +- iOS/Sample/MainStoryBoard.storyboard | 94 ++++++++++++++++++- iOS/Sample/MainViewController.m | 8 ++ iOS/Sample/Podfile | 2 + iOS/Sample/Sample.xcodeproj/project.pbxproj | 22 +++-- iOS/Sample/UserDefaultsViewController.h | 17 ++++ iOS/Sample/UserDefaultsViewController.m | 30 ++++++ iOS/SampleSwift/Podfile | 2 + iOS/SampleSwift/Podfile.lock | 31 +++--- .../SampleSwift.xcodeproj/project.pbxproj | 48 +++++----- iOS/SampleSwift/SampleSwift/AppDelegate.swift | 1 + .../SampleSwift/MainStoryBoard.storyboard | 93 +++++++++++++++++- .../UserDefaultsViewController.swift | 26 +++++ 21 files changed, 557 insertions(+), 53 deletions(-) create mode 100644 iOS/Plugins/FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h create mode 100644 iOS/Plugins/FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.m create mode 100644 iOS/Plugins/FlipperKitUserDefaultsPlugin/FKUserDefaultsSwizzleUtility.h create mode 100644 iOS/Plugins/FlipperKitUserDefaultsPlugin/FKUserDefaultsSwizzleUtility.m create mode 100644 iOS/Sample/UserDefaultsViewController.h create mode 100644 iOS/Sample/UserDefaultsViewController.m create mode 100644 iOS/SampleSwift/SampleSwift/UserDefaultsViewController.swift diff --git a/README.md b/README.md index 9f9df8b96..e74161213 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,7 @@ This repository includes all parts of Flipper. This includes: * Logs (`/src/device-plugins/logs`) * Layout inspector (`/src/plugins/layout`) * Network inspector (`/src/plugins/network`) + * Shared Preferences/NSUserDefaults inspector (`/src/plugins/shared_preferences`) * website and documentation (`/website` / `/docs`) # Getting started diff --git a/docs/getting-started.md b/docs/getting-started.md index 165b20aa1..e2d7966af 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -97,11 +97,12 @@ swift_version = "4.1" flipperkit_version = '0.8.1' target 'MyApp' do - + platform :ios, '9.0' pod 'FlipperKit', '~>'+flipperkit_version # Layout and network plugins are not yet supported for swift projects pod 'FlipperKit/FlipperKitLayoutComponentKitSupport', '~>' + flipperkit_version pod 'FlipperKit/SKIOSNetworkPlugin', '~>' + flipperkit_version + pod 'FlipperKit/FlipperKitUserDefaultsPlugin', '~>' + flipperkit_version post_install do |installer| @@ -120,6 +121,7 @@ and install the dependencies by running `pod install`. When you open the Xcode w ```objective-c #import +#import @implementation AppDelegate @@ -130,6 +132,8 @@ and install the dependencies by running `pod install`. When you open the Xcode w SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults]; [client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode: application withDescriptorMapper: layoutDescriptorMapper]]; + [client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]]; + [client start]; #endif ... diff --git a/docs/shared-preferences-plugin.md b/docs/shared-preferences-plugin.md index ddf566516..3c50ece5e 100644 --- a/docs/shared-preferences-plugin.md +++ b/docs/shared-preferences-plugin.md @@ -9,7 +9,7 @@ Easily inspect and modify the data contained within your app's shared preference ## Setup -Note: this plugin is only available for Android. +This plugin is available for both Android and iOS. ### Android @@ -20,6 +20,24 @@ client.addPlugin( new SharedPreferencesFlipperPlugin(context, "my_shared_preference_file")); ``` +### iOS + +#### Swift + +```swift +import FlipperKit + +client?.add(FKUserDefaultsPlugin.init(suiteName: "your_suitename")) +``` + +#### Objective-c + +```objc +#import + +[client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:@"your_suitename"]]; +``` + ## Usage All changes to the given shared preference file will automatically appear in Flipper. You may also edit the values in Flipper and have them synced to your device. This can be done by clicking on the value of the specific key you wish to edit, editing the value and then pressing enter. diff --git a/iOS/FlipperKit.podspec b/iOS/FlipperKit.podspec index 18821eb32..edbae8723 100644 --- a/iOS/FlipperKit.podspec +++ b/iOS/FlipperKit.podspec @@ -63,6 +63,7 @@ Pod::Spec.new do |spec| 'iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKRequestInfo.h', 'iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKResponseInfo.h', 'iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.h', + 'iOS/Plugins/FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h', 'iOS/FBDefines/FBMacros.h', 'iOS/FlipperKit/**/{FlipperStateUpdateListener,FlipperClient,FlipperPlugin,FlipperConnection,FlipperResponder,SKMacros}.h' header_search_paths = "\"$(PODS_ROOT)/FlipperKit/iOS/FlipperKit\" \"$(PODS_ROOT)\"/Headers/Private/FlipperKit/** \"$(PODS_ROOT)/boost-for-react-native\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/PeerTalkSonar\"" @@ -131,4 +132,13 @@ Pod::Spec.new do |spec| ss.source_files = "iOS/Plugins/FlipperKitNetworkPlugin/SKIOSNetworkPlugin/**/*.{h,cpp,m,mm}" ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)\"/Headers/Private/FlipperKit/**" } end + + spec.subspec "FlipperKitUserDefaultsPlugin" do |ss| + ss.header_dir = "FlipperKitUserDefaultsPlugin" + ss.dependency 'FlipperKit/Core' + ss.compiler_flags = folly_compiler_flags + ss.public_header_files = 'iOS/Plugins/FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h' + ss.source_files = "iOS/Plugins/FlipperKitUserDefaultsPlugin/**/*.{h,m}" + ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)\"/Headers/Private/FlipperKit/**" } + end end diff --git a/iOS/Plugins/FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h b/iOS/Plugins/FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h new file mode 100644 index 000000000..b0154595b --- /dev/null +++ b/iOS/Plugins/FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h @@ -0,0 +1,20 @@ +// +// FKUserDefaultsPlugin.h +// Sample +// +// Created by Marc Terns on 9/30/18. +// Copyright © 2018 Facebook. All rights reserved. +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface FKUserDefaultsPlugin : NSObject + +- (instancetype)initWithSuiteName:(nullable NSString *)suiteName; + +@end + +NS_ASSUME_NONNULL_END diff --git a/iOS/Plugins/FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.m b/iOS/Plugins/FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.m new file mode 100644 index 000000000..7275f7f6c --- /dev/null +++ b/iOS/Plugins/FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.m @@ -0,0 +1,80 @@ +// +// FKUserDefaultsPlugin.m +// Sample +// +// Created by Marc Terns on 9/30/18. +// Copyright © 2018 Facebook. All rights reserved. +// + +#import "FKUserDefaultsPlugin.h" +#import +#import +#import "FKUserDefaultsSwizzleUtility.h" + +@interface FKUserDefaultsPlugin () +@property (nonatomic, strong) id flipperConnection; +@property (nonatomic, strong) NSUserDefaults *userDefaults; +@property (nonatomic, copy) NSString *key; +@property (nonatomic, copy) NSString *suiteName; +@end + +@implementation FKUserDefaultsPlugin + +- (instancetype)initWithSuiteName:(NSString *)suiteName { + if (self = [super init]) { + _userDefaults = [NSUserDefaults standardUserDefaults]; + _suiteName = suiteName; + __weak typeof(self) weakSelf = self; + [FKUserDefaultsSwizzleUtility swizzleSelector:@selector(setObject:forKey:) class:[NSUserDefaults class] block:^(NSInvocation * _Nonnull invocation) { + __unsafe_unretained id firstArg = nil; + __unsafe_unretained id secondArg = nil; + [invocation getArgument:&firstArg atIndex:2]; + [invocation getArgument:&secondArg atIndex:3]; + [invocation invoke]; + [weakSelf userDefaultsChangedWithValue:firstArg key:secondArg]; + }]; + } + return self; + +} + +- (void)didConnect:(id)connection { + self.flipperConnection = connection; + [connection receive:@"getSharedPreferences" withBlock:^(NSDictionary *params, id responder) { + [responder success:[self.userDefaults dictionaryRepresentation]]; + }]; + + [connection receive:@"setSharedPreference" withBlock:^(NSDictionary *params , id responder) { + NSString *preferenceName = params[@"preferenceName"]; + [self.userDefaults setObject:params[@"preferenceValue"] forKey:preferenceName]; + [responder success:[self.userDefaults dictionaryRepresentation]]; + }]; +} + +- (void)didDisconnect { + self.flipperConnection = nil; +} + +- (NSString *)identifier { + return @"Preferences"; +} + +#pragma mark - Private methods + +- (void)userDefaultsChangedWithValue:(id)value key:(NSString *)key { + NSTimeInterval interval = [[NSDate date] timeIntervalSince1970] * 1000; + NSString *intervalStr = [NSString stringWithFormat:@"%f", interval]; + NSMutableDictionary *params = [@{@"name":key, + @"time":intervalStr + } mutableCopy]; + + if (!value) { + [params setObject:@"YES" forKey:@"deleted"]; + } else { + [params setObject:value forKey:@"value"]; + } + + [self.flipperConnection send:@"sharedPreferencesChange" withParams:[params copy]]; +} + +@end diff --git a/iOS/Plugins/FlipperKitUserDefaultsPlugin/FKUserDefaultsSwizzleUtility.h b/iOS/Plugins/FlipperKitUserDefaultsPlugin/FKUserDefaultsSwizzleUtility.h new file mode 100644 index 000000000..984fe43b1 --- /dev/null +++ b/iOS/Plugins/FlipperKitUserDefaultsPlugin/FKUserDefaultsSwizzleUtility.h @@ -0,0 +1,18 @@ +// +// FKUserDefaultsSwizzleUtility.h +// FlipperKit +// +// Created by Marc Terns on 10/6/18. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface FKUserDefaultsSwizzleUtility : NSObject + ++ (void)swizzleSelector:(SEL)selector class:(Class)aClass block:(void(^)(NSInvocation *invocation))block; + +@end + +NS_ASSUME_NONNULL_END diff --git a/iOS/Plugins/FlipperKitUserDefaultsPlugin/FKUserDefaultsSwizzleUtility.m b/iOS/Plugins/FlipperKitUserDefaultsPlugin/FKUserDefaultsSwizzleUtility.m new file mode 100644 index 000000000..8e5611871 --- /dev/null +++ b/iOS/Plugins/FlipperKitUserDefaultsPlugin/FKUserDefaultsSwizzleUtility.m @@ -0,0 +1,76 @@ +// +// FKUserDefaultsSwizzleUtility.m +// FlipperKit +// +// Created by Marc Terns on 10/6/18. +// + +#import "FKUserDefaultsSwizzleUtility.h" +#import + +@interface FKUserDefaultsSwizzleUtility () +@property (nonatomic, strong) NSMutableSet *swizzledClasses; +@property (nonatomic, strong) NSMutableDictionary *swizzledBlocks; +@property (nonatomic) IMP forwardingIMP; +@end + +@implementation FKUserDefaultsSwizzleUtility + +- (instancetype)init { + if (self = [super init]) { + _swizzledClasses = [NSMutableSet set]; + _swizzledBlocks = [NSMutableDictionary dictionary]; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wundeclared-selector" + _forwardingIMP = class_getMethodImplementation([NSObject class], @selector(flipperKitThisMethodShouldNotExist)); +#pragma clang diagnostic pop + } + return self; +} + ++ (instancetype)sharedInstance { + static FKUserDefaultsSwizzleUtility *sharedInstance = nil; + static dispatch_once_t onceToken = 0; + dispatch_once(&onceToken, ^{ + sharedInstance = [[self alloc] init]; + }); + return sharedInstance; +} + ++ (void)swizzleSelector:(SEL)selector class:(Class)aClass block:(void (^)(NSInvocation * _Nonnull))block { + [[self sharedInstance] swizzleSelector:selector class:aClass block:block]; +} + +- (void)swizzleSelector:(SEL)selector class:(Class)aClass block:(void (^)(NSInvocation * _Nonnull))block { + if (![self.swizzledClasses containsObject:aClass]) { + SEL fwdSel = @selector(forwardInvocation:); + Method m = class_getInstanceMethod(aClass, fwdSel); + __block IMP orig; + __weak typeof(self) weakSelf = self; + IMP imp = imp_implementationWithBlock(^(id self, NSInvocation *invocation) { + NSString * selStr = NSStringFromSelector([invocation selector]); + void (^block)(NSInvocation *) = weakSelf.swizzledBlocks[aClass][selStr]; + if (block != nil) { + NSString *originalStr = [@"comfacebookFlipperKit_" stringByAppendingString:selStr]; + [invocation setSelector:NSSelectorFromString(originalStr)]; + block(invocation); + } else { + ((void (*)(id, SEL, NSInvocation *))orig)(self, fwdSel, invocation); + } + }); + orig = method_setImplementation(m, imp); + [self.swizzledClasses addObject:aClass]; + } + NSMutableDictionary *classDict = self.swizzledBlocks[aClass]; + if (classDict == nil) { + classDict = [NSMutableDictionary dictionary]; + self.swizzledBlocks[(id)aClass] = classDict; + } + classDict[NSStringFromSelector(selector)] = block; + Method m = class_getInstanceMethod(aClass, selector); + NSString *newSelStr = [@"comfacebookFlipperKit_" stringByAppendingString:NSStringFromSelector(selector)]; + SEL newSel = NSSelectorFromString(newSelStr); + class_addMethod(aClass, newSel, method_getImplementation(m), method_getTypeEncoding(m)); + method_setImplementation(m, self.forwardingIMP); +} +@end diff --git a/iOS/Sample/AppDelegate.mm b/iOS/Sample/AppDelegate.mm index 7ab81f8bc..18b59a63f 100644 --- a/iOS/Sample/AppDelegate.mm +++ b/iOS/Sample/AppDelegate.mm @@ -10,8 +10,10 @@ #import #import #import +#import #import #import +#import #import "MainViewController.h" #import "RootViewController.h" @@ -27,13 +29,14 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { _window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; - FlipperClient *client = [FlipperClient sharedClient]; SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults]; [FlipperKitLayoutComponentKitSupport setUpWithDescriptorMapper: layoutDescriptorMapper]; [client addPlugin: [[FlipperKitLayoutPlugin alloc] initWithRootNode: application withDescriptorMapper: layoutDescriptorMapper]]; + + [client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]]; [[FlipperClient sharedClient] addPlugin: [[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]]; [client start]; diff --git a/iOS/Sample/MainStoryBoard.storyboard b/iOS/Sample/MainStoryBoard.storyboard index 352bad524..41a5307dd 100644 --- a/iOS/Sample/MainStoryBoard.storyboard +++ b/iOS/Sample/MainStoryBoard.storyboard @@ -1,11 +1,11 @@ - + - + @@ -21,6 +21,20 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + diff --git a/iOS/Sample/MainViewController.m b/iOS/Sample/MainViewController.m index ac31bf779..27de034fd 100644 --- a/iOS/Sample/MainViewController.m +++ b/iOS/Sample/MainViewController.m @@ -10,6 +10,7 @@ #import "NetworkViewController.h" #import "RootViewController.h" +#import "UserDefaultsViewController.h" @interface MainViewController () @@ -35,4 +36,11 @@ [self.navigationController pushViewController:networkViewController animated:true]; } +- (IBAction)tappedUserDefaults:(id)sender { + UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryBoard" bundle:nil]; + UserDefaultsViewController *userDefaultsViewController = [storyboard instantiateViewControllerWithIdentifier:@"UserDefaultsViewController"]; + + [self.navigationController pushViewController:userDefaultsViewController animated:true]; +} + @end diff --git a/iOS/Sample/Podfile b/iOS/Sample/Podfile index 665a29ea0..3204256c5 100644 --- a/iOS/Sample/Podfile +++ b/iOS/Sample/Podfile @@ -4,9 +4,11 @@ source 'https://github.com/CocoaPods/Specs' swift_version = "4.1" target 'Sample' do + platform :ios, '9.0' pod 'FlipperKit', :path => '../../FlipperKit.podspec' pod 'FlipperKit/FlipperKitLayoutComponentKitSupport', :path => '../../FlipperKit.podspec' pod 'FlipperKit/SKIOSNetworkPlugin', :path => '../../FlipperKit.podspec' + pod 'FlipperKit/FlipperKitUserDefaultsPlugin', :path => '../../FlipperKit.podspec' pod 'Flipper', :path => '../../Flipper.podspec' post_install do |installer| diff --git a/iOS/Sample/Sample.xcodeproj/project.pbxproj b/iOS/Sample/Sample.xcodeproj/project.pbxproj index a6e895626..930c53e4b 100644 --- a/iOS/Sample/Sample.xcodeproj/project.pbxproj +++ b/iOS/Sample/Sample.xcodeproj/project.pbxproj @@ -7,7 +7,8 @@ objects = { /* Begin PBXBuildFile section */ - 070E4F377782060039511A32 /* libPods-Sample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 55478E5C767F9CC35112E1C9 /* libPods-Sample.a */; }; + 0D0B1CF0E859D91C55CC453B /* libPods-Sample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F97F4E8C3E28D8BFAAEE60F4 /* libPods-Sample.a */; }; + 4E102341216AD7B400160734 /* UserDefaultsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E102340216AD7B400160734 /* UserDefaultsViewController.m */; }; 53D59DB320ABA18400207065 /* NetworkViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 53D59DAA20ABA18300207065 /* NetworkViewController.m */; }; 53D59DB420ABA18400207065 /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 53D59DAB20ABA18300207065 /* AppDelegate.mm */; }; 53D59DB520ABA18400207065 /* MainViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 53D59DAD20ABA18300207065 /* MainViewController.m */; }; @@ -19,6 +20,8 @@ /* Begin PBXFileReference section */ 081A9FC23643CD21C7D61AA1 /* Pods-Sample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sample.release.xcconfig"; path = "Pods/Target Support Files/Pods-Sample/Pods-Sample.release.xcconfig"; sourceTree = ""; }; + 4E10233F216AD7B400160734 /* UserDefaultsViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UserDefaultsViewController.h; sourceTree = ""; }; + 4E102340216AD7B400160734 /* UserDefaultsViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UserDefaultsViewController.m; sourceTree = ""; }; 53D59DAA20ABA18300207065 /* NetworkViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NetworkViewController.m; sourceTree = SOURCE_ROOT; }; 53D59DAB20ABA18300207065 /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AppDelegate.mm; sourceTree = SOURCE_ROOT; }; 53D59DAC20ABA18300207065 /* NetworkViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NetworkViewController.h; sourceTree = SOURCE_ROOT; }; @@ -32,8 +35,8 @@ 53E0DE4120ABA0E3005682E1 /* Sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 53E0DE5220ABA0E4005682E1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = SOURCE_ROOT; }; 53E0DE5320ABA0E4005682E1 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; - 55478E5C767F9CC35112E1C9 /* libPods-Sample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Sample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; BDF8FF7C018FDB3437209993 /* Pods-Sample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sample.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Sample/Pods-Sample.debug.xcconfig"; sourceTree = ""; }; + F97F4E8C3E28D8BFAAEE60F4 /* libPods-Sample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Sample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -41,7 +44,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 070E4F377782060039511A32 /* libPods-Sample.a in Frameworks */, + 0D0B1CF0E859D91C55CC453B /* libPods-Sample.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -59,6 +62,8 @@ 53D59DAD20ABA18300207065 /* MainViewController.m */, 53D59DAC20ABA18300207065 /* NetworkViewController.h */, 53D59DAA20ABA18300207065 /* NetworkViewController.m */, + 4E10233F216AD7B400160734 /* UserDefaultsViewController.h */, + 4E102340216AD7B400160734 /* UserDefaultsViewController.m */, 53D59DAE20ABA18300207065 /* RootViewController.h */, 53D59DAF20ABA18300207065 /* RootViewController.mm */, 53E0DE5220ABA0E4005682E1 /* Info.plist */, @@ -97,7 +102,7 @@ C89232DD95E032B5B6FA95A1 /* Frameworks */ = { isa = PBXGroup; children = ( - 55478E5C767F9CC35112E1C9 /* libPods-Sample.a */, + F97F4E8C3E28D8BFAAEE60F4 /* libPods-Sample.a */, ); name = Frameworks; sourceTree = ""; @@ -194,6 +199,7 @@ buildActionMask = 2147483647; files = ( 53E0DE5420ABA0E4005682E1 /* main.m in Sources */, + 4E102341216AD7B400160734 /* UserDefaultsViewController.m in Sources */, 53D59DB320ABA18400207065 /* NetworkViewController.m in Sources */, 53D59DB420ABA18400207065 /* AppDelegate.mm in Sources */, 53D59DB520ABA18400207065 /* MainViewController.m in Sources */, @@ -342,7 +348,7 @@ "\"${PODS_ROOT}/Headers/Public/glog\"", ); INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 11.3; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -361,7 +367,7 @@ "-l\"DoubleConversion\"", "-l\"Folly\"", "-l\"PeerTalk\"", - "-l\"Flipper\"", + "-l\"Flipper\"", "-l\"FlipperKit\"", "-l\"Yoga\"", "-l\"c++\"", @@ -406,7 +412,7 @@ "\"${PODS_ROOT}/Headers/Public/glog\"", ); INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 11.3; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -425,7 +431,7 @@ "-l\"DoubleConversion\"", "-l\"Folly\"", "-l\"PeerTalk\"", - "-l\"Flipper\"", + "-l\"Flipper\"", "-l\"FlipperKit\"", "-l\"Yoga\"", "-l\"c++\"", diff --git a/iOS/Sample/UserDefaultsViewController.h b/iOS/Sample/UserDefaultsViewController.h new file mode 100644 index 000000000..09e607817 --- /dev/null +++ b/iOS/Sample/UserDefaultsViewController.h @@ -0,0 +1,17 @@ +// +// UserDefaultsViewController.h +// Sample +// +// Created by Marc Terns on 10/7/18. +// Copyright © 2018 Facebook. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface UserDefaultsViewController : UIViewController + +@end + +NS_ASSUME_NONNULL_END diff --git a/iOS/Sample/UserDefaultsViewController.m b/iOS/Sample/UserDefaultsViewController.m new file mode 100644 index 000000000..21795dfd0 --- /dev/null +++ b/iOS/Sample/UserDefaultsViewController.m @@ -0,0 +1,30 @@ +// +// UserDefaultsViewController.m +// Sample +// +// Created by Marc Terns on 10/7/18. +// Copyright © 2018 Facebook. All rights reserved. +// + +#import "UserDefaultsViewController.h" + +@interface UserDefaultsViewController () +@property (weak, nonatomic) IBOutlet UITextField *valueTextField; +@property (weak, nonatomic) IBOutlet UITextField *keyTextField; +@property (nonatomic, strong) NSUserDefaults *userDefaults; +@end + +@implementation UserDefaultsViewController + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + if (self = [super initWithCoder:aDecoder]) { + _userDefaults = [[NSUserDefaults alloc] initWithSuiteName:nil]; + } + return self; +} + +- (IBAction)tappedSave:(id)sender { + [self.userDefaults setObject:self.valueTextField.text forKey:self.keyTextField.text]; +} + +@end diff --git a/iOS/SampleSwift/Podfile b/iOS/SampleSwift/Podfile index 8ec5098a5..9d27340c6 100644 --- a/iOS/SampleSwift/Podfile +++ b/iOS/SampleSwift/Podfile @@ -4,9 +4,11 @@ source 'https://github.com/CocoaPods/Specs' swift_version = "4.1" target 'SampleSwift' do + platform :ios, '9.0' pod 'Flipper', :path => '../../Flipper.podspec' pod 'FlipperKit', :path => '../../FlipperKit.podspec' pod 'FlipperKit/SKIOSNetworkPlugin', :path => '../../FlipperKit.podspec' + pod 'FlipperKit/FlipperKitUserDefaultsPlugin', :path => '../../FlipperKit.podspec' # Layout and network plugins are not yet supported for swift projects #pod 'SonarKit/FlipperKitLayoutComponentKitSupport', :path => '../../SonarKit.podspec' diff --git a/iOS/SampleSwift/Podfile.lock b/iOS/SampleSwift/Podfile.lock index 282949302..f2f40c527 100644 --- a/iOS/SampleSwift/Podfile.lock +++ b/iOS/SampleSwift/Podfile.lock @@ -3,26 +3,28 @@ PODS: - CocoaAsyncSocket (7.6.3) - CocoaLibEvent (1.0.0) - DoubleConversion (1.1.5) - - Flipper (0.7.2): + - Flipper (0.8.1): - Folly (~> 1.1) - RSocket (~> 0.10) - - FlipperKit (0.7.2): - - FlipperKit/Core (= 0.7.2) - - FlipperKit/Core (0.7.2): + - FlipperKit (0.8.1): + - FlipperKit/Core (= 0.8.1) + - FlipperKit/Core (0.8.1): - CocoaAsyncSocket (~> 7.6) - - Flipper (~> 0.7.2) + - Flipper (~> 0.8.1) - FlipperKit/CppBridge - FlipperKit/FBCxxUtils - FlipperKit/FBDefines - Folly (~> 1.1) - OpenSSL-Static (= 1.0.2.c1) - PeerTalk (~> 0.0.2) - - FlipperKit/CppBridge (0.7.2) - - FlipperKit/FBCxxUtils (0.7.2) - - FlipperKit/FBDefines (0.7.2) - - FlipperKit/FlipperKitNetworkPlugin (0.7.2): + - FlipperKit/CppBridge (0.8.1) + - FlipperKit/FBCxxUtils (0.8.1) + - FlipperKit/FBDefines (0.8.1) + - FlipperKit/FlipperKitNetworkPlugin (0.8.1): - FlipperKit/Core - - FlipperKit/SKIOSNetworkPlugin (0.7.2): + - FlipperKit/FlipperKitUserDefaultsPlugin (0.8.1): + - FlipperKit/Core + - FlipperKit/SKIOSNetworkPlugin (0.8.1): - FlipperKit/Core - FlipperKit/FlipperKitNetworkPlugin - Folly (1.1.0): @@ -40,6 +42,7 @@ PODS: DEPENDENCIES: - Flipper (from `../../Flipper.podspec`) - FlipperKit (from `../../FlipperKit.podspec`) + - FlipperKit/FlipperKitUserDefaultsPlugin (from `../../FlipperKit.podspec`) - FlipperKit/SKIOSNetworkPlugin (from `../../FlipperKit.podspec`) SPEC REPOS: @@ -50,7 +53,7 @@ SPEC REPOS: - DoubleConversion - glog - OpenSSL-Static - https://github.com/facebook/Sonar.git: + https://github.com/facebook/Sonar: - Folly - PeerTalk - RSocket @@ -66,14 +69,14 @@ SPEC CHECKSUMS: CocoaAsyncSocket: eafaa68a7e0ec99ead0a7b35015e0bf25d2c8987 CocoaLibEvent: 2fab71b8bd46dd33ddb959f7928ec5909f838e3f DoubleConversion: e22e0762848812a87afd67ffda3998d9ef29170c - Flipper: 495fbd327298fbc55b3ff7cb399eae52c1580494 - FlipperKit: a3c03ff44340fa38ff476f6269484270533c5749 + Flipper: 847a9ba40de727f6bb02a1b365674538f051ecf2 + FlipperKit: 93c4d1c38f10ef2900f646ddb65c29bdf4975cfd Folly: dd92f69322d8535b9df98d7c91e442b0fce2fff2 glog: 1de0bb937dccdc981596d3b5825ebfb765017ded OpenSSL-Static: bd17e34564a8591ad76b740318683a6caa19a13e PeerTalk: 77481b0a8136f226b90ccf828d6061f70139ffde RSocket: 4fdb7e562db30a2d4fceddefdc601749ffc9ebe2 -PODFILE CHECKSUM: 74eec891e5ca29c4d3817bc4c2c8ed86c6b9958c +PODFILE CHECKSUM: 3eeb7fcb7e93b388b33cfa6730390259dd6292f7 COCOAPODS: 1.5.3 diff --git a/iOS/SampleSwift/SampleSwift.xcodeproj/project.pbxproj b/iOS/SampleSwift/SampleSwift.xcodeproj/project.pbxproj index db9c612b4..d6a3a1612 100644 --- a/iOS/SampleSwift/SampleSwift.xcodeproj/project.pbxproj +++ b/iOS/SampleSwift/SampleSwift.xcodeproj/project.pbxproj @@ -7,8 +7,9 @@ objects = { /* Begin PBXBuildFile section */ + 322121E8134CB1102F388691 /* libPods-SampleSwift.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6233B2BCBACBB2E8D715CC04 /* libPods-SampleSwift.a */; }; + 4E102343216ADB5900160734 /* UserDefaultsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E102342216ADB5900160734 /* UserDefaultsViewController.swift */; }; 532FF2DF211316ED00FC5A10 /* NetworkViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 532FF2DE211316ED00FC5A10 /* NetworkViewController.swift */; }; - 629899EF3F77DD9CC9462399 /* libPods-SampleSwift.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 54D075F591C612F4A46C4C50 /* libPods-SampleSwift.a */; }; A19C402720E20023004BF1F7 /* Icons.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A19C402620E20023004BF1F7 /* Icons.xcassets */; }; A1EC522D20DED61B007C6977 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1EC522C20DED61B007C6977 /* AppDelegate.swift */; }; A1EC522F20DED61B007C6977 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1EC522E20DED61B007C6977 /* ViewController.swift */; }; @@ -17,10 +18,11 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 4E102342216ADB5900160734 /* UserDefaultsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsViewController.swift; sourceTree = ""; }; 532FF2DE211316ED00FC5A10 /* NetworkViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkViewController.swift; sourceTree = ""; }; - 54D075F591C612F4A46C4C50 /* libPods-SampleSwift.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-SampleSwift.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 59BC319BD0C305C87CCBB954 /* Pods-SampleSwift.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SampleSwift.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SampleSwift/Pods-SampleSwift.debug.xcconfig"; sourceTree = ""; }; - 76A1EA0FA19A4343BF5468B9 /* Pods-SampleSwift.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SampleSwift.release.xcconfig"; path = "Pods/Target Support Files/Pods-SampleSwift/Pods-SampleSwift.release.xcconfig"; sourceTree = ""; }; + 6233B2BCBACBB2E8D715CC04 /* libPods-SampleSwift.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-SampleSwift.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 82933F6ACB19D9FAA4A0B0DC /* Pods-SampleSwift.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SampleSwift.release.xcconfig"; path = "Pods/Target Support Files/Pods-SampleSwift/Pods-SampleSwift.release.xcconfig"; sourceTree = ""; }; + 871A353CDDB4CD0F4FE9A564 /* Pods-SampleSwift.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SampleSwift.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SampleSwift/Pods-SampleSwift.debug.xcconfig"; sourceTree = ""; }; A19C402620E20023004BF1F7 /* Icons.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Icons.xcassets; sourceTree = SOURCE_ROOT; }; A1EC522920DED61B007C6977 /* SampleSwift.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SampleSwift.app; sourceTree = BUILT_PRODUCTS_DIR; }; A1EC522C20DED61B007C6977 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -35,20 +37,28 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 629899EF3F77DD9CC9462399 /* libPods-SampleSwift.a in Frameworks */, + 322121E8134CB1102F388691 /* libPods-SampleSwift.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 690EC1830793DEC228432979 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 6233B2BCBACBB2E8D715CC04 /* libPods-SampleSwift.a */, + ); + name = Frameworks; + sourceTree = ""; + }; A1EC522020DED61B007C6977 = { isa = PBXGroup; children = ( A1EC522B20DED61B007C6977 /* SampleSwift */, A1EC522A20DED61B007C6977 /* Products */, - D64C1609A23F7F455EB46170 /* Pods */, - FCE001267EA01B99C44EBA11 /* Frameworks */, + B8EB61E9CA5FC85959AD5F3E /* Pods */, + 690EC1830793DEC228432979 /* Frameworks */, ); sourceTree = ""; }; @@ -70,27 +80,20 @@ A1EC523520DED61C007C6977 /* LaunchScreen.storyboard */, A1EC523820DED61C007C6977 /* Info.plist */, 532FF2DE211316ED00FC5A10 /* NetworkViewController.swift */, + 4E102342216ADB5900160734 /* UserDefaultsViewController.swift */, ); path = SampleSwift; sourceTree = ""; }; - D64C1609A23F7F455EB46170 /* Pods */ = { + B8EB61E9CA5FC85959AD5F3E /* Pods */ = { isa = PBXGroup; children = ( - 59BC319BD0C305C87CCBB954 /* Pods-SampleSwift.debug.xcconfig */, - 76A1EA0FA19A4343BF5468B9 /* Pods-SampleSwift.release.xcconfig */, + 871A353CDDB4CD0F4FE9A564 /* Pods-SampleSwift.debug.xcconfig */, + 82933F6ACB19D9FAA4A0B0DC /* Pods-SampleSwift.release.xcconfig */, ); name = Pods; sourceTree = ""; }; - FCE001267EA01B99C44EBA11 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 54D075F591C612F4A46C4C50 /* libPods-SampleSwift.a */, - ); - name = Frameworks; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -98,7 +101,7 @@ isa = PBXNativeTarget; buildConfigurationList = A1EC523B20DED61C007C6977 /* Build configuration list for PBXNativeTarget "SampleSwift" */; buildPhases = ( - FEB6C3EADB1D3B088347CC11 /* [CP] Check Pods Manifest.lock */, + D9A91DD7C5294D8508D0A168 /* [CP] Check Pods Manifest.lock */, A1EC522520DED61B007C6977 /* Sources */, A1EC522620DED61B007C6977 /* Frameworks */, A1EC522720DED61B007C6977 /* Resources */, @@ -159,7 +162,7 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - FEB6C3EADB1D3B088347CC11 /* [CP] Check Pods Manifest.lock */ = { + D9A91DD7C5294D8508D0A168 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -186,6 +189,7 @@ files = ( A1EC522F20DED61B007C6977 /* ViewController.swift in Sources */, 532FF2DF211316ED00FC5A10 /* NetworkViewController.swift in Sources */, + 4E102343216ADB5900160734 /* UserDefaultsViewController.swift in Sources */, A1EC522D20DED61B007C6977 /* AppDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -320,7 +324,7 @@ }; A1EC523C20DED61C007C6977 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 59BC319BD0C305C87CCBB954 /* Pods-SampleSwift.debug.xcconfig */; + baseConfigurationReference = 871A353CDDB4CD0F4FE9A564 /* Pods-SampleSwift.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; @@ -384,7 +388,7 @@ }; A1EC523D20DED61C007C6977 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 76A1EA0FA19A4343BF5468B9 /* Pods-SampleSwift.release.xcconfig */; + baseConfigurationReference = 82933F6ACB19D9FAA4A0B0DC /* Pods-SampleSwift.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; diff --git a/iOS/SampleSwift/SampleSwift/AppDelegate.swift b/iOS/SampleSwift/SampleSwift/AppDelegate.swift index fa6dc1dff..2968f171c 100644 --- a/iOS/SampleSwift/SampleSwift/AppDelegate.swift +++ b/iOS/SampleSwift/SampleSwift/AppDelegate.swift @@ -19,6 +19,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // client.addPlugin(FlipperKitLayoutPlugin(rootNode: application, descriptorMapper: layoutDescriptorMapper)) client?.add(FlipperKitNetworkPlugin(networkAdapter: SKIOSNetworkAdapter())) + client?.add(FKUserDefaultsPlugin.init(suiteName: nil)) client?.start() let storyboard = UIStoryboard(name: "MainStoryBoard", bundle: nil) diff --git a/iOS/SampleSwift/SampleSwift/MainStoryBoard.storyboard b/iOS/SampleSwift/SampleSwift/MainStoryBoard.storyboard index e0b427f91..f2508e204 100644 --- a/iOS/SampleSwift/SampleSwift/MainStoryBoard.storyboard +++ b/iOS/SampleSwift/SampleSwift/MainStoryBoard.storyboard @@ -1,11 +1,11 @@ - + - + @@ -45,17 +45,34 @@ + + + + @@ -156,8 +173,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + diff --git a/iOS/SampleSwift/SampleSwift/UserDefaultsViewController.swift b/iOS/SampleSwift/SampleSwift/UserDefaultsViewController.swift new file mode 100644 index 000000000..12599ba88 --- /dev/null +++ b/iOS/SampleSwift/SampleSwift/UserDefaultsViewController.swift @@ -0,0 +1,26 @@ +// +// UserDefaultsViewController.swift +// SampleSwift +// +// Created by Marc Terns on 10/7/18. +// Copyright © 2018 Noah Gilmore. All rights reserved. +// + +import UIKit + +class UserDefaultsViewController: UIViewController { + + @IBOutlet weak var keyTextField: UITextField! + @IBOutlet weak var valueTextField: UITextField! + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + } + + @IBAction func tappedSave(_ sender: Any) { + UserDefaults.standard.set(self.valueTextField.text ?? "", forKey: self.keyTextField.text ?? "") + } + +}