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
This commit is contained in:
Marc Terns
2018-10-12 04:07:39 -07:00
committed by Facebook Github Bot
parent 233b7bcd3c
commit c7ad49a9eb
21 changed files with 557 additions and 53 deletions

View File

@@ -10,8 +10,10 @@
#import <FlipperKit/FlipperClient.h>
#import <FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h>
#import <FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.h>
#import <FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h>
#import <FlipperKitLayoutComponentKitSupport/FlipperKitLayoutComponentKitSupport.h>
#import <SKIOSNetworkPlugin/SKIOSNetworkAdapter.h>
#import <FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h>
#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];

View File

@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14313.18" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14283.14"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
@@ -21,6 +21,20 @@
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="sonarpattern" translatesAutoresizingMaskIntoConstraints="NO" id="B57-dd-H6T">
<rect key="frame" x="0.0" y="20" width="375" height="647"/>
</imageView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="wdV-rw-8hy">
<rect key="frame" x="0.0" y="156" width="375" height="60"/>
<color key="backgroundColor" red="0.89411764709999997" green="0.87450980389999999" blue="0.92941176469999998" alpha="1" colorSpace="custom" customColorSpace="displayP3"/>
<constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="60" id="aj7-LU-dNw"/>
<constraint firstAttribute="height" constant="60" id="k5w-WD-ncV"/>
</constraints>
<state key="normal" title="User Defaults">
<color key="titleColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</state>
<connections>
<action selector="tappedUserDefaults:" destination="2r2-64-LPh" eventType="touchUpInside" id="c9s-hW-3Uc"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="R0E-21-TJB">
<rect key="frame" x="0.0" y="20" width="375" height="60"/>
<color key="backgroundColor" red="0.89411764709999997" green="0.87450980389999999" blue="0.92941176469999998" alpha="1" colorSpace="custom" customColorSpace="displayP3"/>
@@ -53,12 +67,15 @@
<constraints>
<constraint firstItem="xh0-Q4-60g" firstAttribute="trailing" secondItem="fhd-5r-dZW" secondAttribute="trailing" id="0Uf-Hu-a0E"/>
<constraint firstItem="xh0-Q4-60g" firstAttribute="top" secondItem="R0E-21-TJB" secondAttribute="bottom" constant="8" id="1LN-rK-SJg"/>
<constraint firstItem="wdV-rw-8hy" firstAttribute="trailing" secondItem="xh0-Q4-60g" secondAttribute="trailing" id="3RQ-G9-NoU"/>
<constraint firstItem="R0E-21-TJB" firstAttribute="top" secondItem="fhd-5r-dZW" secondAttribute="top" id="4Og-b3-TDg"/>
<constraint firstItem="R0E-21-TJB" firstAttribute="leading" secondItem="fhd-5r-dZW" secondAttribute="leading" id="7mG-cL-FY7"/>
<constraint firstItem="xh0-Q4-60g" firstAttribute="leading" secondItem="fhd-5r-dZW" secondAttribute="leading" id="DPX-l5-Rca"/>
<constraint firstItem="B57-dd-H6T" firstAttribute="leading" secondItem="fhd-5r-dZW" secondAttribute="leading" id="Hhd-in-vfk"/>
<constraint firstItem="fhd-5r-dZW" firstAttribute="trailing" secondItem="B57-dd-H6T" secondAttribute="trailing" id="ODW-CJ-ean"/>
<constraint firstItem="fhd-5r-dZW" firstAttribute="trailing" secondItem="R0E-21-TJB" secondAttribute="trailing" id="QyG-ez-XwS"/>
<constraint firstItem="wdV-rw-8hy" firstAttribute="leading" secondItem="xh0-Q4-60g" secondAttribute="leading" id="ebs-Zh-KRL"/>
<constraint firstItem="wdV-rw-8hy" firstAttribute="top" secondItem="xh0-Q4-60g" secondAttribute="bottom" constant="8" id="g0a-Kf-scc"/>
<constraint firstItem="fhd-5r-dZW" firstAttribute="bottom" secondItem="B57-dd-H6T" secondAttribute="bottom" id="kh1-ra-tzR"/>
<constraint firstItem="B57-dd-H6T" firstAttribute="top" secondItem="fhd-5r-dZW" secondAttribute="top" id="vGA-oo-EGc"/>
</constraints>
@@ -158,8 +175,79 @@
</objects>
<point key="canvasLocation" x="958" y="280"/>
</scene>
<!--User Defaults View Controller-->
<scene sceneID="WTQ-n4-t3v">
<objects>
<viewController storyboardIdentifier="UserDefaultsViewController" useStoryboardIdentifierAsRestorationIdentifier="YES" id="VHD-ba-abm" customClass="UserDefaultsViewController" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="OCa-ry-ipe">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="sonarpattern" translatesAutoresizingMaskIntoConstraints="NO" id="GgH-jO-sEF">
<rect key="frame" x="0.0" y="20" width="375" height="647"/>
</imageView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="qPc-MG-653">
<rect key="frame" x="40" y="366" width="295" height="40"/>
<color key="backgroundColor" red="0.89411764709999997" green="0.87450980389999999" blue="0.92941176469999998" alpha="1" colorSpace="custom" customColorSpace="displayP3"/>
<constraints>
<constraint firstAttribute="height" constant="40" id="BcQ-Iy-Ybl"/>
<constraint firstAttribute="width" constant="295" id="TY0-ba-K8F"/>
</constraints>
<state key="normal" title="Save">
<color key="titleColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</state>
<connections>
<action selector="tappedGithubLitho:" destination="qMe-JX-czP" eventType="touchUpInside" id="wPi-ja-YXo"/>
<action selector="tappedSave:" destination="VHD-ba-abm" eventType="touchUpInside" id="cI3-q0-l2T"/>
</connections>
</button>
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="key (cannot be nil)" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="80Y-f6-kwl">
<rect key="frame" x="39.5" y="328.5" width="296" height="30"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="MFP-cm-uHZ"/>
<constraint firstAttribute="width" constant="296" id="qlF-Cu-akU"/>
</constraints>
<nil key="textColor"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits"/>
</textField>
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="String Value..." textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="0At-MV-CVw">
<rect key="frame" x="40" y="290" width="295" height="30"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="8fO-qk-MZr"/>
<constraint firstAttribute="width" constant="295" id="LFW-DF-pnG"/>
</constraints>
<nil key="textColor"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits"/>
</textField>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="80Y-f6-kwl" firstAttribute="centerX" secondItem="GgH-jO-sEF" secondAttribute="centerX" id="2wS-MR-FU2"/>
<constraint firstItem="gSG-BS-ifN" firstAttribute="trailing" secondItem="GgH-jO-sEF" secondAttribute="trailing" id="9lu-H4-Ixo"/>
<constraint firstItem="80Y-f6-kwl" firstAttribute="centerY" secondItem="GgH-jO-sEF" secondAttribute="centerY" id="CBx-Gd-7cF"/>
<constraint firstItem="qPc-MG-653" firstAttribute="centerX" secondItem="GgH-jO-sEF" secondAttribute="centerX" id="HJN-8X-WMD"/>
<constraint firstItem="GgH-jO-sEF" firstAttribute="leading" secondItem="gSG-BS-ifN" secondAttribute="leading" id="Hva-Hn-bO6"/>
<constraint firstItem="0At-MV-CVw" firstAttribute="centerX" secondItem="GgH-jO-sEF" secondAttribute="centerX" id="Jzw-bv-6qe"/>
<constraint firstItem="80Y-f6-kwl" firstAttribute="top" secondItem="0At-MV-CVw" secondAttribute="bottom" constant="8.5" id="Vcg-1M-vMe"/>
<constraint firstItem="GgH-jO-sEF" firstAttribute="top" secondItem="gSG-BS-ifN" secondAttribute="top" id="ehP-nt-IdN"/>
<constraint firstItem="qPc-MG-653" firstAttribute="top" secondItem="80Y-f6-kwl" secondAttribute="bottom" constant="7.5" id="iTT-7c-IWW"/>
<constraint firstItem="gSG-BS-ifN" firstAttribute="bottom" secondItem="GgH-jO-sEF" secondAttribute="bottom" id="zLE-qV-P6N"/>
</constraints>
<viewLayoutGuide key="safeArea" id="gSG-BS-ifN"/>
</view>
<connections>
<outlet property="keyTextField" destination="80Y-f6-kwl" id="H6X-rN-dKc"/>
<outlet property="valueTextField" destination="0At-MV-CVw" id="QWP-LP-5nH"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="snT-Mu-i3U" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1613.5999999999999" y="278.41079460269867"/>
</scene>
</scenes>
<resources>
<image name="sonarpattern" width="421" height="658"/>
<image name="sonarpattern" width="843" height="1317"/>
</resources>
</document>

View File

@@ -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

View File

@@ -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|

View File

@@ -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 = "<group>"; };
4E10233F216AD7B400160734 /* UserDefaultsViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UserDefaultsViewController.h; sourceTree = "<group>"; };
4E102340216AD7B400160734 /* UserDefaultsViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UserDefaultsViewController.m; sourceTree = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
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 = "<group>";
@@ -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++\"",

View File

@@ -0,0 +1,17 @@
//
// UserDefaultsViewController.h
// Sample
//
// Created by Marc Terns on 10/7/18.
// Copyright © 2018 Facebook. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UserDefaultsViewController : UIViewController
@end
NS_ASSUME_NONNULL_END

View File

@@ -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