├── .gitmodules ├── Example ├── Client │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Default-568h@2x.png │ ├── GroundControl Example.xcodeproj │ │ ├── project.pbxproj │ │ └── project.xcworkspace │ │ │ └── contents.xcworkspacedata │ ├── Info.plist │ ├── Prefix.pch │ ├── ViewController.h │ ├── ViewController.m │ ├── en.lproj │ │ └── ViewController.xib │ └── main.m └── Server │ ├── Gemfile │ ├── Gemfile.lock │ ├── Procfile │ └── config.ru ├── GroundControl.podspec ├── GroundControl.xcworkspace └── contents.xcworkspacedata ├── GroundControl ├── NSUserDefaults+GroundControl.h └── NSUserDefaults+GroundControl.m ├── LICENSE └── README.md /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "AFNetworking"] 2 | path = AFNetworking 3 | url = https://github.com/AFNetworking/AFNetworking.git 4 | -------------------------------------------------------------------------------- /Example/Client/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // AppDelegate.h 2 | // 3 | // Copyright (c) 2012 Mattt Thompson (http://mattt.me/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import 24 | 25 | @class ViewController; 26 | 27 | @interface AppDelegate : UIResponder 28 | 29 | @property (strong, nonatomic) UIWindow *window; 30 | 31 | @property (strong, nonatomic) ViewController *viewController; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /Example/Client/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // AppDelegate.m 2 | // 3 | // Copyright (c) 2012 Mattt Thompson (http://mattt.me/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import "AppDelegate.h" 24 | 25 | #import "ViewController.h" 26 | 27 | #import "NSUserDefaults+GroundControl.h" 28 | 29 | static NSString * const GroundControlDefaultsURLString = @"http://ground-control-demo.herokuapp.com/defaults.plist"; 30 | 31 | @implementation AppDelegate 32 | 33 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 34 | { 35 | [[NSUserDefaults standardUserDefaults] registerDefaultsWithURL:[NSURL URLWithString:GroundControlDefaultsURLString]]; 36 | 37 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 38 | self.viewController = [[ViewController alloc] initWithNibName:@"ViewController" bundle:nil]; 39 | self.window.rootViewController = self.viewController; 40 | [self.window makeKeyAndVisible]; 41 | 42 | return YES; 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /Example/Client/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattt/GroundControl/61853fb69fdc773c7e3ff702fa4e78a591d4f8d0/Example/Client/Default-568h@2x.png -------------------------------------------------------------------------------- /Example/Client/GroundControl Example.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 5086013117F8502500414731 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5086013017F8502500414731 /* Security.framework */; }; 11 | 5086013217F8505700414731 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5086012E17F84FFD00414731 /* SystemConfiguration.framework */; }; 12 | 6C8516501BD6CB050052D6DB /* AFHTTPSessionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 6C85164A1BD6CB050052D6DB /* AFHTTPSessionManager.m */; settings = {ASSET_TAGS = (); }; }; 13 | 6C8516511BD6CB050052D6DB /* AFNetworkReachabilityManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 6C85164D1BD6CB050052D6DB /* AFNetworkReachabilityManager.m */; settings = {ASSET_TAGS = (); }; }; 14 | 6C8516521BD6CB050052D6DB /* AFURLSessionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 6C85164F1BD6CB050052D6DB /* AFURLSessionManager.m */; settings = {ASSET_TAGS = (); }; }; 15 | F88A59B11912C1C0002A429C /* AFURLRequestSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = F88A59B01912C1C0002A429C /* AFURLRequestSerialization.m */; }; 16 | F88A59B31912C3A0002A429C /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F88A59B21912C3A0002A429C /* MobileCoreServices.framework */; }; 17 | F8A56A551812C66B004E454E /* AFURLResponseSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = F8A56A4B1812C66B004E454E /* AFURLResponseSerialization.m */; }; 18 | F8A56A591812C6B1004E454E /* AFSecurityPolicy.m in Sources */ = {isa = PBXBuildFile; fileRef = F8A56A581812C6B1004E454E /* AFSecurityPolicy.m */; }; 19 | F8C0BC5A15F173F500DC38E6 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8C0BC5915F173F500DC38E6 /* UIKit.framework */; }; 20 | F8C0BC5C15F173F500DC38E6 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8C0BC5B15F173F500DC38E6 /* Foundation.framework */; }; 21 | F8C0BC5E15F173F500DC38E6 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8C0BC5D15F173F500DC38E6 /* CoreGraphics.framework */; }; 22 | F8C0BC8915F1743400DC38E6 /* ViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = F8C0BC8215F1743400DC38E6 /* ViewController.xib */; }; 23 | F8C0BC8A15F1743400DC38E6 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = F8C0BC8415F1743400DC38E6 /* AppDelegate.m */; }; 24 | F8C0BC8B15F1743400DC38E6 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F8C0BC8515F1743400DC38E6 /* main.m */; }; 25 | F8C0BC8C15F1743400DC38E6 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F8C0BC8615F1743400DC38E6 /* ViewController.m */; }; 26 | F8C0BC9415F1751600DC38E6 /* NSUserDefaults+GroundControl.m in Sources */ = {isa = PBXBuildFile; fileRef = F8C0BC9315F1751600DC38E6 /* NSUserDefaults+GroundControl.m */; }; 27 | F8CD8EC116B476F100B8B0E4 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F8CD8EC016B476F100B8B0E4 /* Default-568h@2x.png */; }; 28 | /* End PBXBuildFile section */ 29 | 30 | /* Begin PBXFileReference section */ 31 | 5086012E17F84FFD00414731 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; 32 | 5086013017F8502500414731 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; 33 | 6C8516491BD6CB050052D6DB /* AFHTTPSessionManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFHTTPSessionManager.h; sourceTree = ""; }; 34 | 6C85164A1BD6CB050052D6DB /* AFHTTPSessionManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFHTTPSessionManager.m; sourceTree = ""; }; 35 | 6C85164B1BD6CB050052D6DB /* AFNetworking.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFNetworking.h; sourceTree = ""; }; 36 | 6C85164C1BD6CB050052D6DB /* AFNetworkReachabilityManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFNetworkReachabilityManager.h; sourceTree = ""; }; 37 | 6C85164D1BD6CB050052D6DB /* AFNetworkReachabilityManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFNetworkReachabilityManager.m; sourceTree = ""; }; 38 | 6C85164E1BD6CB050052D6DB /* AFURLSessionManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFURLSessionManager.h; sourceTree = ""; }; 39 | 6C85164F1BD6CB050052D6DB /* AFURLSessionManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFURLSessionManager.m; sourceTree = ""; }; 40 | F88A59AF1912C1C0002A429C /* AFURLRequestSerialization.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFURLRequestSerialization.h; sourceTree = ""; }; 41 | F88A59B01912C1C0002A429C /* AFURLRequestSerialization.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFURLRequestSerialization.m; sourceTree = ""; }; 42 | F88A59B21912C3A0002A429C /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; }; 43 | F8A56A4A1812C66B004E454E /* AFURLResponseSerialization.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFURLResponseSerialization.h; sourceTree = ""; }; 44 | F8A56A4B1812C66B004E454E /* AFURLResponseSerialization.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFURLResponseSerialization.m; sourceTree = ""; }; 45 | F8A56A571812C6B1004E454E /* AFSecurityPolicy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFSecurityPolicy.h; sourceTree = ""; }; 46 | F8A56A581812C6B1004E454E /* AFSecurityPolicy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFSecurityPolicy.m; sourceTree = ""; }; 47 | F8C0BC5515F173F500DC38E6 /* GroundControl Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "GroundControl Example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 48 | F8C0BC5915F173F500DC38E6 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 49 | F8C0BC5B15F173F500DC38E6 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 50 | F8C0BC5D15F173F500DC38E6 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 51 | F8C0BC7C15F1743400DC38E6 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 52 | F8C0BC7D15F1743400DC38E6 /* ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 53 | F8C0BC8315F1743400DC38E6 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = ViewController.xib; sourceTree = ""; }; 54 | F8C0BC8415F1743400DC38E6 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 55 | F8C0BC8515F1743400DC38E6 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 56 | F8C0BC8615F1743400DC38E6 /* ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 57 | F8C0BC8E15F174AF00DC38E6 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 58 | F8C0BC9015F174B400DC38E6 /* Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Prefix.pch; sourceTree = ""; }; 59 | F8C0BC9215F1751600DC38E6 /* NSUserDefaults+GroundControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSUserDefaults+GroundControl.h"; sourceTree = ""; }; 60 | F8C0BC9315F1751600DC38E6 /* NSUserDefaults+GroundControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSUserDefaults+GroundControl.m"; sourceTree = ""; }; 61 | F8CD8EC016B476F100B8B0E4 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; 62 | /* End PBXFileReference section */ 63 | 64 | /* Begin PBXFrameworksBuildPhase section */ 65 | F8C0BC5215F173F500DC38E6 /* Frameworks */ = { 66 | isa = PBXFrameworksBuildPhase; 67 | buildActionMask = 2147483647; 68 | files = ( 69 | F88A59B31912C3A0002A429C /* MobileCoreServices.framework in Frameworks */, 70 | 5086013217F8505700414731 /* SystemConfiguration.framework in Frameworks */, 71 | 5086013117F8502500414731 /* Security.framework in Frameworks */, 72 | F8C0BC5A15F173F500DC38E6 /* UIKit.framework in Frameworks */, 73 | F8C0BC5C15F173F500DC38E6 /* Foundation.framework in Frameworks */, 74 | F8C0BC5E15F173F500DC38E6 /* CoreGraphics.framework in Frameworks */, 75 | ); 76 | runOnlyForDeploymentPostprocessing = 0; 77 | }; 78 | /* End PBXFrameworksBuildPhase section */ 79 | 80 | /* Begin PBXGroup section */ 81 | F8A56A3A1812C66B004E454E /* AFNetworking */ = { 82 | isa = PBXGroup; 83 | children = ( 84 | 6C8516491BD6CB050052D6DB /* AFHTTPSessionManager.h */, 85 | 6C85164A1BD6CB050052D6DB /* AFHTTPSessionManager.m */, 86 | 6C85164B1BD6CB050052D6DB /* AFNetworking.h */, 87 | 6C85164C1BD6CB050052D6DB /* AFNetworkReachabilityManager.h */, 88 | 6C85164D1BD6CB050052D6DB /* AFNetworkReachabilityManager.m */, 89 | F8A56A571812C6B1004E454E /* AFSecurityPolicy.h */, 90 | F8A56A581812C6B1004E454E /* AFSecurityPolicy.m */, 91 | F88A59AF1912C1C0002A429C /* AFURLRequestSerialization.h */, 92 | F88A59B01912C1C0002A429C /* AFURLRequestSerialization.m */, 93 | F8A56A4A1812C66B004E454E /* AFURLResponseSerialization.h */, 94 | F8A56A4B1812C66B004E454E /* AFURLResponseSerialization.m */, 95 | 6C85164E1BD6CB050052D6DB /* AFURLSessionManager.h */, 96 | 6C85164F1BD6CB050052D6DB /* AFURLSessionManager.m */, 97 | ); 98 | name = AFNetworking; 99 | path = ../AFNetworking/AFNetworking; 100 | sourceTree = ""; 101 | }; 102 | F8C0BC4A15F173F500DC38E6 = { 103 | isa = PBXGroup; 104 | children = ( 105 | F8CD8EC016B476F100B8B0E4 /* Default-568h@2x.png */, 106 | F8C0BC7B15F1742400DC38E6 /* GroundControl */, 107 | F8C0BC5815F173F500DC38E6 /* Frameworks */, 108 | F8C0BC5615F173F500DC38E6 /* Products */, 109 | F8C0BC9115F1751600DC38E6 /* Vendor */, 110 | ); 111 | sourceTree = ""; 112 | }; 113 | F8C0BC5615F173F500DC38E6 /* Products */ = { 114 | isa = PBXGroup; 115 | children = ( 116 | F8C0BC5515F173F500DC38E6 /* GroundControl Example.app */, 117 | ); 118 | name = Products; 119 | sourceTree = ""; 120 | }; 121 | F8C0BC5815F173F500DC38E6 /* Frameworks */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | F88A59B21912C3A0002A429C /* MobileCoreServices.framework */, 125 | 5086013017F8502500414731 /* Security.framework */, 126 | 5086012E17F84FFD00414731 /* SystemConfiguration.framework */, 127 | F8C0BC5915F173F500DC38E6 /* UIKit.framework */, 128 | F8C0BC5B15F173F500DC38E6 /* Foundation.framework */, 129 | F8C0BC5D15F173F500DC38E6 /* CoreGraphics.framework */, 130 | ); 131 | name = Frameworks; 132 | sourceTree = ""; 133 | }; 134 | F8C0BC7B15F1742400DC38E6 /* GroundControl */ = { 135 | isa = PBXGroup; 136 | children = ( 137 | F8C0BC7C15F1743400DC38E6 /* AppDelegate.h */, 138 | F8C0BC8415F1743400DC38E6 /* AppDelegate.m */, 139 | F8C0BC7D15F1743400DC38E6 /* ViewController.h */, 140 | F8C0BC8615F1743400DC38E6 /* ViewController.m */, 141 | F8C0BC8515F1743400DC38E6 /* main.m */, 142 | F8C0BC9015F174B400DC38E6 /* Prefix.pch */, 143 | F8C0BC8E15F174AF00DC38E6 /* Info.plist */, 144 | F8C0BC7F15F1743400DC38E6 /* en.lproj */, 145 | ); 146 | name = GroundControl; 147 | sourceTree = ""; 148 | }; 149 | F8C0BC7F15F1743400DC38E6 /* en.lproj */ = { 150 | isa = PBXGroup; 151 | children = ( 152 | F8C0BC8215F1743400DC38E6 /* ViewController.xib */, 153 | ); 154 | path = en.lproj; 155 | sourceTree = ""; 156 | }; 157 | F8C0BC9115F1751600DC38E6 /* Vendor */ = { 158 | isa = PBXGroup; 159 | children = ( 160 | F8A56A3A1812C66B004E454E /* AFNetworking */, 161 | F8C0BCB515F1757A00DC38E6 /* GroundControl */, 162 | ); 163 | name = Vendor; 164 | path = ../../GroundControl; 165 | sourceTree = ""; 166 | }; 167 | F8C0BCB515F1757A00DC38E6 /* GroundControl */ = { 168 | isa = PBXGroup; 169 | children = ( 170 | F8C0BC9215F1751600DC38E6 /* NSUserDefaults+GroundControl.h */, 171 | F8C0BC9315F1751600DC38E6 /* NSUserDefaults+GroundControl.m */, 172 | ); 173 | name = GroundControl; 174 | sourceTree = ""; 175 | }; 176 | /* End PBXGroup section */ 177 | 178 | /* Begin PBXNativeTarget section */ 179 | F8C0BC5415F173F500DC38E6 /* GroundControl Example */ = { 180 | isa = PBXNativeTarget; 181 | buildConfigurationList = F8C0BC7315F173F500DC38E6 /* Build configuration list for PBXNativeTarget "GroundControl Example" */; 182 | buildPhases = ( 183 | F8C0BC5115F173F500DC38E6 /* Sources */, 184 | F8C0BC5215F173F500DC38E6 /* Frameworks */, 185 | F8C0BC5315F173F500DC38E6 /* Resources */, 186 | ); 187 | buildRules = ( 188 | ); 189 | dependencies = ( 190 | ); 191 | name = "GroundControl Example"; 192 | productName = "GroundControl Example"; 193 | productReference = F8C0BC5515F173F500DC38E6 /* GroundControl Example.app */; 194 | productType = "com.apple.product-type.application"; 195 | }; 196 | /* End PBXNativeTarget section */ 197 | 198 | /* Begin PBXProject section */ 199 | F8C0BC4C15F173F500DC38E6 /* Project object */ = { 200 | isa = PBXProject; 201 | attributes = { 202 | LastUpgradeCheck = 0510; 203 | ORGANIZATIONNAME = "Mattt Thompson"; 204 | }; 205 | buildConfigurationList = F8C0BC4F15F173F500DC38E6 /* Build configuration list for PBXProject "GroundControl Example" */; 206 | compatibilityVersion = "Xcode 3.2"; 207 | developmentRegion = English; 208 | hasScannedForEncodings = 0; 209 | knownRegions = ( 210 | en, 211 | ); 212 | mainGroup = F8C0BC4A15F173F500DC38E6; 213 | productRefGroup = F8C0BC5615F173F500DC38E6 /* Products */; 214 | projectDirPath = ""; 215 | projectRoot = ""; 216 | targets = ( 217 | F8C0BC5415F173F500DC38E6 /* GroundControl Example */, 218 | ); 219 | }; 220 | /* End PBXProject section */ 221 | 222 | /* Begin PBXResourcesBuildPhase section */ 223 | F8C0BC5315F173F500DC38E6 /* Resources */ = { 224 | isa = PBXResourcesBuildPhase; 225 | buildActionMask = 2147483647; 226 | files = ( 227 | F8C0BC8915F1743400DC38E6 /* ViewController.xib in Resources */, 228 | F8CD8EC116B476F100B8B0E4 /* Default-568h@2x.png in Resources */, 229 | ); 230 | runOnlyForDeploymentPostprocessing = 0; 231 | }; 232 | /* End PBXResourcesBuildPhase section */ 233 | 234 | /* Begin PBXSourcesBuildPhase section */ 235 | F8C0BC5115F173F500DC38E6 /* Sources */ = { 236 | isa = PBXSourcesBuildPhase; 237 | buildActionMask = 2147483647; 238 | files = ( 239 | F88A59B11912C1C0002A429C /* AFURLRequestSerialization.m in Sources */, 240 | 6C8516511BD6CB050052D6DB /* AFNetworkReachabilityManager.m in Sources */, 241 | F8C0BC8A15F1743400DC38E6 /* AppDelegate.m in Sources */, 242 | F8C0BC8B15F1743400DC38E6 /* main.m in Sources */, 243 | F8A56A591812C6B1004E454E /* AFSecurityPolicy.m in Sources */, 244 | 6C8516521BD6CB050052D6DB /* AFURLSessionManager.m in Sources */, 245 | F8C0BC8C15F1743400DC38E6 /* ViewController.m in Sources */, 246 | F8A56A551812C66B004E454E /* AFURLResponseSerialization.m in Sources */, 247 | F8C0BC9415F1751600DC38E6 /* NSUserDefaults+GroundControl.m in Sources */, 248 | 6C8516501BD6CB050052D6DB /* AFHTTPSessionManager.m in Sources */, 249 | ); 250 | runOnlyForDeploymentPostprocessing = 0; 251 | }; 252 | /* End PBXSourcesBuildPhase section */ 253 | 254 | /* Begin PBXVariantGroup section */ 255 | F8C0BC8215F1743400DC38E6 /* ViewController.xib */ = { 256 | isa = PBXVariantGroup; 257 | children = ( 258 | F8C0BC8315F1743400DC38E6 /* en */, 259 | ); 260 | name = ViewController.xib; 261 | sourceTree = ""; 262 | }; 263 | /* End PBXVariantGroup section */ 264 | 265 | /* Begin XCBuildConfiguration section */ 266 | F8C0BC7115F173F500DC38E6 /* Debug */ = { 267 | isa = XCBuildConfiguration; 268 | buildSettings = { 269 | ALWAYS_SEARCH_USER_PATHS = NO; 270 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 271 | CLANG_ENABLE_OBJC_ARC = YES; 272 | CLANG_WARN_BOOL_CONVERSION = YES; 273 | CLANG_WARN_CONSTANT_CONVERSION = YES; 274 | CLANG_WARN_EMPTY_BODY = YES; 275 | CLANG_WARN_ENUM_CONVERSION = YES; 276 | CLANG_WARN_INT_CONVERSION = YES; 277 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 278 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 279 | COPY_PHASE_STRIP = NO; 280 | GCC_C_LANGUAGE_STANDARD = gnu99; 281 | GCC_DYNAMIC_NO_PIC = NO; 282 | GCC_OPTIMIZATION_LEVEL = 0; 283 | GCC_PREPROCESSOR_DEFINITIONS = ( 284 | "DEBUG=1", 285 | "$(inherited)", 286 | ); 287 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 288 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 289 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 290 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 291 | GCC_WARN_UNDECLARED_SELECTOR = YES; 292 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 293 | GCC_WARN_UNUSED_FUNCTION = YES; 294 | GCC_WARN_UNUSED_VARIABLE = YES; 295 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 296 | ONLY_ACTIVE_ARCH = YES; 297 | SDKROOT = iphoneos; 298 | }; 299 | name = Debug; 300 | }; 301 | F8C0BC7215F173F500DC38E6 /* Release */ = { 302 | isa = XCBuildConfiguration; 303 | buildSettings = { 304 | ALWAYS_SEARCH_USER_PATHS = NO; 305 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 306 | CLANG_ENABLE_OBJC_ARC = YES; 307 | CLANG_WARN_BOOL_CONVERSION = YES; 308 | CLANG_WARN_CONSTANT_CONVERSION = YES; 309 | CLANG_WARN_EMPTY_BODY = YES; 310 | CLANG_WARN_ENUM_CONVERSION = YES; 311 | CLANG_WARN_INT_CONVERSION = YES; 312 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 313 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 314 | COPY_PHASE_STRIP = YES; 315 | GCC_C_LANGUAGE_STANDARD = gnu99; 316 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 317 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 318 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 319 | GCC_WARN_UNDECLARED_SELECTOR = YES; 320 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 321 | GCC_WARN_UNUSED_FUNCTION = YES; 322 | GCC_WARN_UNUSED_VARIABLE = YES; 323 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 324 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 325 | SDKROOT = iphoneos; 326 | VALIDATE_PRODUCT = YES; 327 | }; 328 | name = Release; 329 | }; 330 | F8C0BC7415F173F500DC38E6 /* Debug */ = { 331 | isa = XCBuildConfiguration; 332 | buildSettings = { 333 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 334 | GCC_PREFIX_HEADER = Prefix.pch; 335 | INFOPLIST_FILE = Info.plist; 336 | PRODUCT_NAME = "$(TARGET_NAME)"; 337 | WRAPPER_EXTENSION = app; 338 | }; 339 | name = Debug; 340 | }; 341 | F8C0BC7515F173F500DC38E6 /* Release */ = { 342 | isa = XCBuildConfiguration; 343 | buildSettings = { 344 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 345 | GCC_PREFIX_HEADER = Prefix.pch; 346 | INFOPLIST_FILE = Info.plist; 347 | PRODUCT_NAME = "$(TARGET_NAME)"; 348 | WRAPPER_EXTENSION = app; 349 | }; 350 | name = Release; 351 | }; 352 | /* End XCBuildConfiguration section */ 353 | 354 | /* Begin XCConfigurationList section */ 355 | F8C0BC4F15F173F500DC38E6 /* Build configuration list for PBXProject "GroundControl Example" */ = { 356 | isa = XCConfigurationList; 357 | buildConfigurations = ( 358 | F8C0BC7115F173F500DC38E6 /* Debug */, 359 | F8C0BC7215F173F500DC38E6 /* Release */, 360 | ); 361 | defaultConfigurationIsVisible = 0; 362 | defaultConfigurationName = Release; 363 | }; 364 | F8C0BC7315F173F500DC38E6 /* Build configuration list for PBXNativeTarget "GroundControl Example" */ = { 365 | isa = XCConfigurationList; 366 | buildConfigurations = ( 367 | F8C0BC7415F173F500DC38E6 /* Debug */, 368 | F8C0BC7515F173F500DC38E6 /* Release */, 369 | ); 370 | defaultConfigurationIsVisible = 0; 371 | defaultConfigurationName = Release; 372 | }; 373 | /* End XCConfigurationList section */ 374 | }; 375 | rootObject = F8C0BC4C15F173F500DC38E6 /* Project object */; 376 | } 377 | -------------------------------------------------------------------------------- /Example/Client/GroundControl Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/Client/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.mattt.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | NSAppTransportSecurity 32 | 33 | NSAllowsArbitraryLoads 34 | 35 | 36 | UISupportedInterfaceOrientations 37 | 38 | UIInterfaceOrientationPortrait 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Example/Client/Prefix.pch: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #ifndef __IPHONE_4_0 4 | #warning "This project uses features only available in iOS SDK 4.0 and later." 5 | #endif 6 | 7 | #ifdef __OBJC__ 8 | #import 9 | #import 10 | #endif 11 | -------------------------------------------------------------------------------- /Example/Client/ViewController.h: -------------------------------------------------------------------------------- 1 | // ViewController.h 2 | // 3 | // Copyright (c) 2012 Mattt Thompson (http://mattt.me/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import 24 | 25 | @interface ViewController : UIViewController 26 | 27 | @property (weak, nonatomic) IBOutlet UILabel *greetingLabel; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /Example/Client/ViewController.m: -------------------------------------------------------------------------------- 1 | // ViewController.m 2 | // 3 | // Copyright (c) 2012 Mattt Thompson (http://mattt.me/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import "ViewController.h" 24 | 25 | @implementation ViewController 26 | 27 | - (void)updateValues { 28 | self.greetingLabel.text = [[NSUserDefaults standardUserDefaults] objectForKey:@"Greeting"]; 29 | } 30 | 31 | #pragma mark - UIViewController 32 | 33 | - (void)viewDidLoad { 34 | [super viewDidLoad]; 35 | 36 | [[NSNotificationCenter defaultCenter] addObserverForName:NSUserDefaultsDidChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notification) { 37 | NSLog(@"NSUserDefaultsDidChangeNotification: %@", notification); 38 | [self updateValues]; 39 | }]; 40 | 41 | [self updateValues]; 42 | } 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /Example/Client/en.lproj/ViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1296 5 | 12B19 6 | 2549 7 | 1187 8 | 624.00 9 | 10 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 11 | 1498 12 | 13 | 14 | IBProxyObject 15 | IBUILabel 16 | IBUIView 17 | 18 | 19 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 20 | 21 | 22 | PluginDependencyRecalculationVersion 23 | 24 | 25 | 26 | 27 | IBFilesOwner 28 | IBCocoaTouchFramework 29 | 30 | 31 | IBFirstResponder 32 | IBCocoaTouchFramework 33 | 34 | 35 | 36 | 274 37 | 38 | 39 | 40 | 292 41 | {{20, 42}, {280, 46}} 42 | 43 | 44 | 45 | _NS:9 46 | NO 47 | YES 48 | 7 49 | NO 50 | IBCocoaTouchFramework 51 | Greeting 52 | 53 | 1 54 | MCAwIDAAA 55 | 56 | 57 | 0 58 | 10 59 | 1 60 | 61 | 1 62 | 28 63 | 64 | 65 | Helvetica 66 | 28 67 | 16 68 | 69 | 70 | 71 | {{0, 20}, {320, 460}} 72 | 73 | 74 | 75 | 76 | 3 77 | MC43NQA 78 | 79 | 2 80 | 81 | 82 | NO 83 | 84 | IBCocoaTouchFramework 85 | 86 | 87 | 88 | 89 | 90 | 91 | view 92 | 93 | 94 | 95 | 7 96 | 97 | 98 | 99 | greetingLabel 100 | 101 | 102 | 103 | 9 104 | 105 | 106 | 107 | 108 | 109 | 0 110 | 111 | 112 | 113 | 114 | 115 | -1 116 | 117 | 118 | File's Owner 119 | 120 | 121 | -2 122 | 123 | 124 | 125 | 126 | 6 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 8 135 | 136 | 137 | 138 | 139 | 140 | 141 | ViewController 142 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 143 | UIResponder 144 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 145 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 146 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 147 | 148 | 149 | 150 | 151 | 152 | 10 153 | 154 | 155 | 156 | 157 | ViewController 158 | UIViewController 159 | 160 | greetingLabel 161 | UILabel 162 | 163 | 164 | greetingLabel 165 | 166 | greetingLabel 167 | UILabel 168 | 169 | 170 | 171 | IBProjectSource 172 | ./Classes/ViewController.h 173 | 174 | 175 | 176 | 177 | 0 178 | IBCocoaTouchFramework 179 | 180 | com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS 181 | 182 | 183 | YES 184 | 3 185 | 1498 186 | 187 | 188 | -------------------------------------------------------------------------------- /Example/Client/main.m: -------------------------------------------------------------------------------- 1 | // main.m 2 | // 3 | // Copyright (c) 2012 Mattt Thompson (http://mattt.me/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import 24 | 25 | #import "AppDelegate.h" 26 | 27 | int main(int argc, char *argv[]) { 28 | @autoreleasepool { 29 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Example/Server/Gemfile: -------------------------------------------------------------------------------- 1 | source :rubygems 2 | 3 | gem 'sinatra', require: 'sinatra/base' 4 | gem 'plist' 5 | gem 'thin' 6 | -------------------------------------------------------------------------------- /Example/Server/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://rubygems.org/ 3 | specs: 4 | daemons (1.1.9) 5 | eventmachine (0.12.10) 6 | plist (3.1.0) 7 | rack (1.6.12) 8 | rack-protection (1.5.5) 9 | rack 10 | sinatra (1.3.3) 11 | rack (~> 1.3, >= 1.3.6) 12 | rack-protection (~> 1.2) 13 | tilt (~> 1.3, >= 1.3.3) 14 | thin (1.4.1) 15 | daemons (>= 1.0.9) 16 | eventmachine (>= 0.12.6) 17 | rack (>= 1.0.0) 18 | tilt (1.3.3) 19 | 20 | PLATFORMS 21 | ruby 22 | 23 | DEPENDENCIES 24 | plist 25 | sinatra 26 | thin 27 | -------------------------------------------------------------------------------- /Example/Server/Procfile: -------------------------------------------------------------------------------- 1 | web: bundle exec thin start -p $PORT 2 | -------------------------------------------------------------------------------- /Example/Server/config.ru: -------------------------------------------------------------------------------- 1 | require 'bundler' 2 | Bundler.require 3 | 4 | class Defaults < Sinatra::Base 5 | get '/defaults.plist' do 6 | content_type 'application/x-plist' 7 | 8 | { 9 | 'Greeting' => "Hello, World", 10 | 'Price' => 4.20, 11 | 'FeatureXIsLaunched' => true 12 | }.to_plist 13 | end 14 | end 15 | 16 | run Defaults 17 | -------------------------------------------------------------------------------- /GroundControl.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'GroundControl' 3 | s.version = '3.0.0' 4 | s.license = 'MIT' 5 | s.summary = 'Remote configuration for iOS.' 6 | s.homepage = 'https://github.com/mattt/GroundControl' 7 | s.social_media_url = 'https://twitter.com/mattt' 8 | s.authors = { 'Mattt Thompson' => 'm@mattt.me' } 9 | s.source = { :git => 'https://github.com/mattt/GroundControl.git', :tag => '3.0.0' } 10 | s.source_files = 'GroundControl' 11 | s.requires_arc = true 12 | 13 | s.ios.deployment_target = '6.0' 14 | s.osx.deployment_target = '10.8' 15 | 16 | s.dependency 'AFNetworking', '~> 3.0' 17 | end 18 | -------------------------------------------------------------------------------- /GroundControl.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 9 | 10 | 12 | 13 | 14 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /GroundControl/NSUserDefaults+GroundControl.h: -------------------------------------------------------------------------------- 1 | // NSUserDefaults+GroundControl.h 2 | // 3 | // Copyright (c) 2012 Mattt Thompson (http://mattt.me/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import 24 | 25 | @protocol AFURLRequestSerialization; 26 | @protocol AFURLResponseSerialization; 27 | 28 | /** 29 | 30 | */ 31 | @interface NSUserDefaults (GroundControl) 32 | 33 | /** 34 | 35 | */ 36 | @property (nonatomic, strong) id requestSerializer; 37 | 38 | /** 39 | 40 | */ 41 | @property (nonatomic, strong) id responseSerializer; 42 | 43 | /** 44 | 45 | */ 46 | - (void)registerDefaultsWithURL:(NSURL *)url; 47 | 48 | /** 49 | 50 | */ 51 | - (void)registerDefaultsWithURL:(NSURL *)url 52 | success:(void (^)(NSDictionary *defaults))success 53 | failure:(void (^)(NSError *error))failure; 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /GroundControl/NSUserDefaults+GroundControl.m: -------------------------------------------------------------------------------- 1 | // NSUserDefaults+GroundControl.m 2 | // 3 | // Copyright (c) 2012 Mattt Thompson (http://mattt.me/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import "NSUserDefaults+GroundControl.h" 24 | #import "AFHTTPSessionManager.h" 25 | #import "AFURLRequestSerialization.h" 26 | #import "AFURLResponseSerialization.h" 27 | 28 | #import 29 | 30 | @interface NSUserDefaults (_GroundControl) 31 | + (AFHTTPSessionManager *)gc_sharedAFHTTPSessionManager; 32 | @end 33 | 34 | @implementation NSUserDefaults (GroundControl) 35 | 36 | + (AFHTTPSessionManager *)gc_sharedAFHTTPSessionManager { 37 | static AFHTTPSessionManager *_sharedAFHTTPSessionManager = nil; 38 | 39 | static dispatch_once_t onceToken; 40 | dispatch_once(&onceToken, ^{ 41 | _sharedAFHTTPSessionManager = [AFHTTPSessionManager manager]; 42 | }); 43 | 44 | return _sharedAFHTTPSessionManager; 45 | } 46 | 47 | - (id )requestSerializer { 48 | return objc_getAssociatedObject(self, @selector(requestSerializer)); 49 | } 50 | 51 | - (void)setRequestSerializer:(id )requestSerializer { 52 | objc_setAssociatedObject(self, @selector(requestSerializer), requestSerializer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 53 | } 54 | 55 | - (id )responseSerializer { 56 | return objc_getAssociatedObject(self, @selector(responseSerializer)); 57 | } 58 | 59 | - (void)setResponseSerializer:(id )responseSerializer { 60 | objc_setAssociatedObject(self, @selector(responseSerializer), responseSerializer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 61 | } 62 | 63 | #pragma mark - 64 | 65 | - (void)registerDefaultsWithURL:(NSURL *)url { 66 | [self registerDefaultsWithURL:url success:nil failure:nil]; 67 | } 68 | 69 | - (void)registerDefaultsWithURL:(NSURL *)url 70 | success:(void (^)(NSDictionary *defaults))success 71 | failure:(void (^)(NSError *error))failure 72 | { 73 | AFHTTPSessionManager *manager = [[self class] gc_sharedAFHTTPSessionManager]; 74 | manager.responseSerializer = self.responseSerializer ? self.responseSerializer : [AFPropertyListResponseSerializer serializer]; 75 | 76 | [manager GET:url.absoluteString parameters:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nonnull responseObject) { 77 | [self setValuesForKeysWithDictionary:responseObject]; 78 | [self synchronize]; 79 | 80 | if (success) { 81 | success(responseObject); 82 | } 83 | } failure:^(NSURLSessionDataTask * _Nonnull task, NSError * _Nonnull error) { 84 | if (failure) { 85 | failure(error); 86 | } 87 | }]; 88 | } 89 | 90 | @end 91 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Mattt Thompson (http://mattt.me/) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GroundControl 2 | **Remote configuration for iOS** 3 | 4 | > **Note**: This project is no longer being maintained. 5 | 6 | Many developers don't realize that they are allowed to remotely control the behavior of their app (provided that the application isn't downloading any new code). 7 | 8 | GroundControl gives you a dead-simple way to remotely configure your app, allowing you to add things like [feature flags](http://code.flickr.com/blog/2009/12/02/flipping-out/), impromptu [A/B tests](http://en.wikipedia.org/wiki/A/B_testing), or a simple ["message of the day"](http://en.wikipedia.org/wiki/Motd_%28Unix%29). 9 | 10 | It's built on top of [AFNetworking](https://github.com/afnetworking/afnetworking), and provides a single category on `NSUserDefaults` (just add `#import "NSUserDefaults+GroundControl.h"` to the top of any file you want to use it in). 11 | 12 | ## Client Code 13 | 14 | ```objective-c 15 | NSURL *URL = [NSURL URLWithString:@"http://example.com/defaults.plist"]; 16 | [[NSUserDefaults standardUserDefaults] registerDefaultsWithURL:URL]; 17 | ``` 18 | 19 | ...or if you need callbacks for success/failure, and prefer not to listen for a `NSUserDefaultsDidChangeNotification`: 20 | 21 | ```objective-c 22 | NSURL *URL = [NSURL URLWithString:@"http://example.com/defaults.plist"]; 23 | [[NSUserDefaults standardUserDefaults] registerDefaultsWithURL:URL 24 | success:^(NSDictionary *) { 25 | // ... 26 | } failure:^(NSError *) { 27 | // ... 28 | }]; 29 | ``` 30 | 31 | ...or if you need to use an HTTP method other than GET, or need to set any special headers, specify an `NSURLRequest`: 32 | 33 | ```objective-c 34 | NSURL *URL = [NSURL URLWithString:@"http://example.com/defaults.plist"]; 35 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL]; 36 | [[NSUserDefaults standardUserDefaults] registerDefaultsWithURLRequest:request 37 | success:^(NSURLRequest *, NSHTTPURLResponse *, NSDictionary *) { 38 | // ... 39 | } failure:^(NSURLRequest *, NSHTTPURLResponse *, NSError *) { 40 | // ... 41 | }]; 42 | ``` 43 | 44 | ## Server Code 45 | 46 | GroundControl asynchronously downloads and reads a remote plist file. This could be a static file or generated dynamically, like in the following examples (see also the complete Sinatra application found in `example/server`): 47 | 48 | ### Ruby 49 | 50 | ```ruby 51 | require 'sinatra' 52 | require 'plist' 53 | 54 | get '/defaults.plist' do 55 | content_type 'application/x-plist' 56 | 57 | { 58 | 'Greeting' => "Hello, World", 59 | 'Price' => 4.20, 60 | 'FeatureXIsLaunched' => true 61 | }.to_plist 62 | end 63 | ``` 64 | 65 | ### Python 66 | 67 | ```python 68 | from django.http import HttpResponse 69 | import plistlib 70 | 71 | def property_list(request): 72 | plist = { 73 | 'Greeting': "Hello, World", 74 | 'Price': 4.20, 75 | 'FeatureXIsLaunched': True, 76 | 'Status': 1 77 | } 78 | 79 | return HttpResponse(plistlib.writePlistToString(plist)) 80 | ``` 81 | 82 | ### Node.js 83 | 84 | ```javascript 85 | var plist = require('plist'), 86 | express = require('express') 87 | 88 | var host = "127.0.0.1" 89 | var port = 8080 90 | 91 | var app = express() 92 | 93 | app.get("/", function(request, response) { 94 | response.send(plist.build( 95 | { 96 | 'Greeting': "Hello, World", 97 | 'Price': 4.20, 98 | 'FeatureXIsLaunched': true, 99 | 'Status': 1 100 | } 101 | ).toString()) 102 | }) 103 | 104 | app.listen(port, host) 105 | ``` 106 | 107 | ## License 108 | 109 | GroundControl is available under the MIT license. See the LICENSE file for more info. 110 | --------------------------------------------------------------------------------