├── .gitignore ├── Classes └── Parseport │ ├── ParsePort.h │ └── ParsePort.m ├── LICENSE ├── ParsePort.podspec └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | *.xcworkspace 13 | !default.xcworkspace 14 | xcuserdata 15 | profile 16 | *.moved-aside 17 | DerivedData 18 | .idea/ 19 | -------------------------------------------------------------------------------- /Classes/Parseport/ParsePort.h: -------------------------------------------------------------------------------- 1 | // 2 | // ParsePort.h 3 | // 4 | // The MIT License (MIT) 5 | // 6 | // Copyright (c) 2013 Stevie Graham 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 9 | // this software and associated documentation files (the "Software"), to deal in 10 | // the Software without restriction, including without limitation the rights to 11 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 12 | // the Software, and to permit persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 20 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 21 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 22 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | 26 | #import 27 | 28 | @interface ParsePort : NSObject 29 | +(void)useURL:(NSURL *)url; 30 | @end 31 | 32 | -------------------------------------------------------------------------------- /Classes/Parseport/ParsePort.m: -------------------------------------------------------------------------------- 1 | // 2 | // ParsePort.m 3 | // 4 | // The MIT License (MIT) 5 | // 6 | // Copyright (c) 2013 Stevie Graham 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 9 | // this software and associated documentation files (the "Software"), to deal in 10 | // the Software without restriction, including without limitation the rights to 11 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 12 | // the Software, and to permit persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 20 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 21 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 22 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | #import "ParsePort.h" 26 | #import 27 | 28 | @implementation ParsePort 29 | 30 | +(void)useURL:(NSURL *)url { 31 | Class class = NSClassFromString(@"PF_AFHTTPClient"); 32 | SEL selector = NSSelectorFromString(@"initWithBaseURL:"); 33 | Method method = class_getInstanceMethod(class, selector); 34 | IMP implementation = method_getImplementation(method); 35 | 36 | id (^block)(id, NSURL *) = ^(id _self, NSURL * _) { 37 | return implementation(_self, selector, url); 38 | }; 39 | 40 | IMP initializer = imp_implementationWithBlock(block); 41 | method_setImplementation(method, initializer); 42 | } 43 | @end 44 | 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Stevie Graham 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /ParsePort.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "ParsePort" 3 | s.version = "0.0.1" 4 | s.summary = "ParsePort - leave Parse whenever you want" 5 | s.description = <<-DESC 6 | ParsePort allows you the option to leave Parse in the future by providing a 7 | kill-switch that can be used to force the Parse SDK to communicate with 8 | an arbitrary host nominated at runtime instead of the Parse API. 9 | DESC 10 | 11 | s.homepage = "https://github.com/stevegraham/parseport" 12 | 13 | s.license = 'MIT' 14 | s.license = { :type => 'MIT', :file => 'LICENSE' } 15 | s.author = { "Stevie Graham" => "sjtgraham@mac.com" } 16 | 17 | s.source = { :git => "https://github.com/stevegraham/parseport.git", 18 | :commit => "b4e0ec72ac5a072642197a1796a7bdb97f2a99ba" } 19 | 20 | s.platform = :ios 21 | 22 | s.source_files = 'Classes', 'Classes/**/*.{h,m}' 23 | 24 | s.requires_arc = true 25 | end 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ParsePort 2 | 3 | A killswitch for Parse. Have your app talk to your servers instead when you 4 | outgrow Parse, all without shipping a new binary or changing a line of code. 5 | 6 | ``` 7 | IMPORTANT! This swizzles a private Parse API and could break with any new release of the 8 | Parse SDK. If it does, please open an issue so I know to fix it. 9 | ``` 10 | 11 | Parse is an incredible platform that removes a lot of headache for building 12 | backend services for your apps. As great as Parse is, using it unfortunately 13 | locks you into their platform in perpetuity, i.e. As long as you have users 14 | in the wild with app binaries using the Parse SDK you're stuck with Parse. 15 | 16 | There are a number of reasons for wanting to avoid vendor lock in including: 17 | 18 | - Parse is great today, but there are no guarantees the same team will be there 19 | in the future delivering a service of the same quality. 20 | 21 | - Parse can change their pricing at anytime making it catastrophically 22 | expensive for your app overnight. 23 | 24 | - Parse or its owner Facebook could begin to abuse their priveleged access to 25 | all Parse app metrics to act anti-competitively, e.g. by using the information 26 | unfairly in M&A. For more on this see http://swaggadocio.com/post/60416244109 27 | 28 | ## Usage 29 | 30 | When you first use ParsePort you'll be happy for your app to use Parse, but 31 | ParsePort allows you to build in the flexibility to leave Parse later without 32 | cutting a new app binary. A good strategy is to pair ParsePort with 33 | [GroundControl](https://github.com/mattt/GroundControl) by the prolific 34 | [Mattt Thompson](https://github.com/mattt), 35 | 36 | ### Client code 37 | 38 | In your application delegate `application:didFinishLaunchingWithOptions` method: 39 | 40 | ```obj-c 41 | #import 42 | 43 | NSString * const kParsePortURL = @"parsePortURL" 44 | 45 | NSURL *URL = [NSURL URLWithString:@"http://example.com/defaults.plist"]; 46 | 47 | [[NSUserDefaults standardUserDefaults] registerDefaultsWithURL:URL]; 48 | 49 | NSURL * parsePortURL = [[[NSUserDefaults] standardUserDefaults]] 50 | URLForKey:kParsePortURL]; 51 | 52 | if(parsePortURL) [ParsePort useURL:[NSURL URLWithString:parsePortURL]]; 53 | ``` 54 | 55 | ### Server Code 56 | 57 | The following Sinatra app will force the Parse SDK to talk to your servers 58 | without having to release a new app binary. 59 | 60 | ```ruby 61 | require 'sinatra' 62 | require 'plist' 63 | 64 | get '/defaults.plist' do 65 | content_type 'application/x-plist' 66 | 67 | { parsePortURL: 'http://api.example.com/' }.to_plist 68 | end 69 | ``` 70 | 71 | ### Caveats 72 | 73 | This is only the client portion of a Parse migration. In true lean fashion 74 | I haven't built the server component because I don't need it yet; I'm very 75 | happy with Parse but the peace of mind knowing I have the option of a simpler 76 | migration should the need arise makes me sleep easier at night. 77 | 78 | If the time comes when I need to move apps from Parse, I would start by looking 79 | at [Helios](https://github.com/helios-framework/helios) (again by Mattt), and 80 | use some Rack middleware to make any mappings. 81 | 82 | ### Contact 83 | 84 | [@stevegraham](https://twitter.com/stevegraham) 85 | 86 | 87 | 88 | 89 | --------------------------------------------------------------------------------