├── Pod └── Classes │ ├── .gitkeep │ ├── ArtsyAuthentication+Accounts.h │ ├── Artsy+Authentication.h │ ├── ArtsyToken.h │ ├── ArtsyNetworkOperator.h │ ├── ArtsyAuthentication+Accounts.m │ ├── ArtsyAuthentication+Twitter.h │ ├── ArtsyAuthentication+Private.h │ ├── ArtsyAuthentication+Facebook.h │ ├── ArtsyNetworkOperator.m │ ├── ArtsyToken.m │ ├── ArtsyAuthenticationRouter.h │ ├── ArtsyAuthentication.h │ ├── ArtsyAuthentication+Private.m │ ├── ArtsyAuthenticationRouter.m │ ├── ArtsyAuthentication.m │ ├── ArtsyAuthentication+Twitter.m │ └── ArtsyAuthentication+Facebook.m ├── Example ├── Tests │ ├── Tests-Prefix.pch │ ├── en.lproj │ │ └── InfoPlist.strings │ ├── File.swift │ ├── TestingNetworkOperator.h │ ├── Tests-Info.plist │ ├── TestingNetworkOperator.m │ ├── TestingClasses.h │ ├── ArtsyNetworkOperatorTests.m │ ├── ArtsyTokenTests.m │ ├── ArtsyAuthenticationTests.m │ ├── TestingClasses.m │ ├── ArtsyAuthenticationTests+Facebook.m │ ├── ArtsyAuthenticationTests+Twitter.m │ └── ArtsyAuthenticationRouterTests.m ├── Artsy_Authentication │ ├── en.lproj │ │ └── InfoPlist.strings │ ├── ARViewController.h │ ├── ARAppDelegate.h │ ├── ARAppDelegate.m │ ├── main.m │ ├── Artsy_Authentication-Prefix.pch │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── LaunchImage.launchimage │ │ │ └── Contents.json │ ├── Artsy_Authentication-Info.plist │ ├── Base.lproj │ │ ├── Main_iPad.storyboard │ │ └── Main_iPhone.storyboard │ └── ARViewController.m ├── ArtsyAuthenticationExample-Bridging-Header.h ├── Artsy_Authentication.xcodeproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ ├── xcshareddata │ │ └── xcschemes │ │ │ └── Artsy_Authentication-Example.xcscheme │ └── project.pbxproj ├── Artsy_Authentication.xcworkspace │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── contents.xcworkspacedata ├── Makefile ├── Podfile └── Podfile.lock ├── Gemfile ├── .gitignore ├── .travis.yml ├── README.md ├── LICENSE ├── Artsy+Authentication.podspec └── Gemfile.lock /Pod/Classes/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Example/Tests/Tests-Prefix.pch: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Example/Tests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Example/Artsy_Authentication/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem "cocoapods" 4 | gem "cocoapods-keys" 5 | gem "cocoapods-deintegrate" 6 | -------------------------------------------------------------------------------- /Example/Artsy_Authentication/ARViewController.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface ARViewController : UIViewController 4 | 5 | @end 6 | -------------------------------------------------------------------------------- /Example/ArtsyAuthenticationExample-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | -------------------------------------------------------------------------------- /Example/Artsy_Authentication/ARAppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface ARAppDelegate : UIResponder 4 | 5 | @property (strong, nonatomic) UIWindow *window; 6 | 7 | @end 8 | -------------------------------------------------------------------------------- /Example/Tests/File.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // Tests 4 | // 5 | // Created by Orta Therox on 3/7/19. 6 | // Copyright © 2019 Orta Therox. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | -------------------------------------------------------------------------------- /Pod/Classes/ArtsyAuthentication+Accounts.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "ArtsyAuthentication.h" 3 | 4 | @interface ArtsyAuthentication (PrivateAccount) 5 | @property (nonatomic, readonly) ACAccountStore *accountStore; 6 | @end 7 | -------------------------------------------------------------------------------- /Example/Artsy_Authentication.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/Artsy_Authentication/ARAppDelegate.m: -------------------------------------------------------------------------------- 1 | #import "ARAppDelegate.h" 2 | 3 | @implementation ARAppDelegate 4 | 5 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 6 | { 7 | return YES; 8 | } 9 | 10 | @end 11 | -------------------------------------------------------------------------------- /Pod/Classes/Artsy+Authentication.h: -------------------------------------------------------------------------------- 1 | #import "ArtsyToken.h" 2 | #import "ArtsyAuthentication.h" 3 | #if __has_include("ArtsyAuthentication+Facebook.h") 4 | #import "ArtsyAuthentication+Facebook.h" 5 | #endif 6 | #if __has_include("ArtsyAuthentication+Twitter.h") 7 | #import "ArtsyAuthentication+Twitter.h" 8 | #endif 9 | -------------------------------------------------------------------------------- /Example/Artsy_Authentication.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/Artsy_Authentication.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | 22 | # Bundler 23 | .bundle 24 | 25 | Example/Pods/ 26 | -------------------------------------------------------------------------------- /Example/Makefile: -------------------------------------------------------------------------------- 1 | all: install 2 | 3 | install: 4 | pod repo update 5 | pod install 6 | 7 | keys: 8 | bundle exec pod keys set ArtsyTwitterSecret "" Artsy_Authentication 9 | bundle exec pod keys set ArtsyTwitterKey "" 10 | bundle exec pod keys set ArtsyFacebookAppID "" 11 | bundle exec pod keys set ArtsyAPIClientKey "" 12 | bundle exec pod keys set ArtsyAPIClientSecret "" 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | osx_image: xcode10.1 2 | language: objective-c 3 | 4 | before_install: 5 | - bundle install 6 | - cd Example ; make keys ; cd .. 7 | before_script: 8 | - export LANG=en_US.UTF-8 9 | install: 10 | - cd Example ; make install ; cd .. 11 | 12 | xcode_workspace: Example/Artsy_Authentication.xcworkspace 13 | xcode_scheme: Artsy_Authentication-Example 14 | xcode_sdk: iphonesimulator 15 | -------------------------------------------------------------------------------- /Example/Artsy_Authentication/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // Artsy_Authentication 4 | // 5 | // Created by Orta Therox on 12/26/2014. 6 | // Copyright (c) 2014 Orta Therox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "ARAppDelegate.h" 12 | 13 | int main(int argc, char * argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([ARAppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Example/Artsy_Authentication/Artsy_Authentication-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_5_0 10 | #warning "This project uses features only available in iOS SDK 5.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | #import 15 | #import 16 | #endif 17 | -------------------------------------------------------------------------------- /Pod/Classes/ArtsyToken.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | /// Represents a token + expiration combination 4 | 5 | @interface ArtsyToken : NSObject 6 | 7 | - (instancetype)initWithToken:(NSString *)token expirationDate:(NSDate *)expirationDate NS_DESIGNATED_INITIALIZER; 8 | 9 | @property (nonatomic, copy, readonly) NSString *token; 10 | @property (nonatomic, strong, readonly) NSDate *expirationDate; 11 | @property (nonatomic, readonly, getter=isEmpty) BOOL empty; 12 | 13 | - (BOOL)hasExpired; 14 | - (BOOL)hasExpiredWithDate:(NSDate *)date; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /Pod/Classes/ArtsyNetworkOperator.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | typedef void (^ArtsyNetworkSuccessCallback)(NSURLRequest *request, NSHTTPURLResponse *response, id JSON); 4 | typedef void (^ArtsyNetworkFailureCallback)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON); 5 | 6 | @interface ArtsyNetworkOperator : NSObject 7 | 8 | - (NSURLSessionTask *)JSONTaskWithRequest:(NSURLRequest *)request 9 | success:(ArtsyNetworkSuccessCallback)success 10 | failure:(ArtsyNetworkFailureCallback)failure; 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | source 'https://github.com/CocoaPods/Specs.git' 2 | platform :ios, '8.0' 3 | use_frameworks! 4 | 5 | plugin 'cocoapods-keys', 6 | project: 'Artsy_Authentication', 7 | target: 'ArtsyAuthenticationExample', 8 | keys: %w[ 9 | ArtsyAPIClientSecret 10 | ArtsyAPIClientKey 11 | ArtsyFacebookAppID 12 | ArtsyTwitterKey 13 | ArtsyTwitterSecret 14 | ] 15 | 16 | target 'ArtsyAuthenticationExample' do 17 | pod 'Artsy+Authentication', path: '../' 18 | 19 | target 'Tests' do 20 | inherit! :search_paths 21 | 22 | pod 'OHHTTPStubs' 23 | pod 'Quick' 24 | pod 'Nimble' 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /Pod/Classes/ArtsyAuthentication+Accounts.m: -------------------------------------------------------------------------------- 1 | #import "ArtsyAuthentication+Accounts.h" 2 | #import "ArtsyAuthentication+Private.h" 3 | #import 4 | 5 | @implementation ArtsyAuthentication (Private) 6 | 7 | - (ACAccountStore *)accountStore { 8 | ACAccountStore *accountStore = objc_getAssociatedObject(self, ArtsyAccountStoreKey); 9 | 10 | if (!accountStore) { 11 | // This must be around at least as long as we are. 12 | accountStore = [[ACAccountStore alloc] init]; 13 | 14 | objc_setAssociatedObject(self, ArtsyAccountStoreKey, accountStore, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 15 | } 16 | 17 | return accountStore; 18 | } 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /Pod/Classes/ArtsyAuthentication+Twitter.h: -------------------------------------------------------------------------------- 1 | #import "ArtsyAuthentication.h" 2 | #import 3 | 4 | typedef void (^ArtsyTwitterAccountsCallback)(NSArray *accounts, NSError *error); 5 | 6 | @interface ArtsyAuthentication(Twitter) 7 | 8 | - (void)retrieveTwitterAccounts:(ArtsyTwitterAccountsCallback)callback; 9 | - (void)logInWithTwitterAccount:(ACAccount *)accout completion:(ArtsyAuthenticationCallback)callback; 10 | - (void)createNewUserWithTwitter:(ACAccount *)accout email:(NSString *)email name:(NSString *)name completion:(ArtsyAuthenticationCallback)callback; 11 | 12 | @property (nonatomic, copy) NSString *twitterAPIKey; 13 | @property (nonatomic, copy) NSString *twitterAPISecret; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Example/Tests/TestingNetworkOperator.h: -------------------------------------------------------------------------------- 1 | @import Foundation; 2 | @import Artsy_Authentication; 3 | 4 | /// JSONTaskWithRequest:success:failure: calls the appropriate 5 | /// block based on the fail property and *returns nil*. 6 | @interface TestingNetworkOperator : ArtsyNetworkOperator 7 | 8 | /// The presence of a non-nil error value indicate a failure. 9 | /// Otherwise a success. 10 | - (instancetype)initWithResponse:(NSHTTPURLResponse *)response JSON:(id)JSON error:(NSError *)error NS_DESIGNATED_INITIALIZER; 11 | 12 | @property (nonatomic, readonly) NSHTTPURLResponse *response; 13 | @property (nonatomic, readonly) id JSON; 14 | @property (nonatomic, readonly) NSError *error; 15 | 16 | /// Computed based on error != nil 17 | @property (nonatomic, readonly) BOOL fail; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /Pod/Classes/ArtsyAuthentication+Private.h: -------------------------------------------------------------------------------- 1 | #import "ArtsyAuthentication.h" 2 | #import "ArtsyNetworkOperator.h" 3 | 4 | extern NSString* const ArtsyOAuthTokenKey; 5 | extern NSString* const ArtsyOAuthExpiryKey; 6 | extern NSString* const ArtsyXAppTokenKey; 7 | extern const void* ArtsyAccountStoreKey; 8 | 9 | @class ArtsyNetworkOperator; 10 | 11 | @interface ArtsyAuthentication (Private) 12 | 13 | - (void)callback:(ArtsyToken *)token error:(NSError *)error completion:(ArtsyAuthenticationCallback)callback; 14 | - (ArtsyNetworkSuccessCallback)successfulLoginBlock:(ArtsyAuthenticationCallback)callback; 15 | - (ArtsyNetworkFailureCallback)failedLoginBlock:(ArtsyAuthenticationCallback)callback; 16 | 17 | @property (nonatomic, readonly) ArtsyNetworkOperator *networkOperator; 18 | 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Artsy_Authentication 2 | 3 | [![CI Status](http://img.shields.io/travis/artsy/Artsy_Authentication.svg?style=flat)](https://travis-ci.org/artsy/Artsy_Authentication) 4 | 5 | ## Usage 6 | 7 | To run the example project, clone the repo, and run `pod install` from the Example directory first. 8 | 9 | ## Requirements 10 | 11 | ## Installation 12 | 13 | Artsy_Authentication is available through Artsy's [CocoaPods](http://cocoapods.org). To install 14 | it, simply add the following line to your Podfile: 15 | 16 | source 'https://github.com/artsy/Specs.git' 17 | pod "Artsy+Authentication" 18 | 19 | ## Author 20 | 21 | Orta Therox, orta@artsymail.com 22 | 23 | ## License 24 | 25 | Artsy_Authentication is available under the MIT license. See the LICENSE file for more info. 26 | -------------------------------------------------------------------------------- /Example/Tests/Tests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Pod/Classes/ArtsyAuthentication+Facebook.h: -------------------------------------------------------------------------------- 1 | #import "ArtsyAuthentication.h" 2 | 3 | typedef void (^ArtsyAuthenticationFailureCallback)(NSError *error); 4 | 5 | @interface ArtsyAuthentication (Facebook) 6 | 7 | #pragma mark - User Login 8 | 9 | /// Note: You *must* have your main app's Info.plist configured to have 10 | /// the FacebookAppID key set to your app's Facebook ID. If the value 11 | /// cannot be located, callback will be invoked with an NSError having 12 | /// ArtsyErrorNoFacebookAppID code. 13 | - (void)logInWithFacebook:(ArtsyAuthenticationCallback)callback; 14 | 15 | /// For errors with ACErrorDomain domain, check ACErrorCode for possible 16 | /// reasons. 17 | - (void)logInWithFacebook:(NSString *)appID completion:(ArtsyAuthenticationCallback)callback; 18 | 19 | #pragma mark - User Creation 20 | 21 | - (void)createUserWithFacebook:(ArtsyAuthenticationCallback)callback; 22 | 23 | - (void)createUserWithFacebook:(NSString *)appID completion:(ArtsyAuthenticationCallback)callback; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /Example/Tests/TestingNetworkOperator.m: -------------------------------------------------------------------------------- 1 | #import "TestingNetworkOperator.h" 2 | 3 | @implementation TestingNetworkOperator 4 | 5 | - (instancetype)initWithResponse:(NSHTTPURLResponse *)response JSON:(id)JSON error:(NSError *)error { 6 | self = [super init]; 7 | if (self == nil) { return nil; } 8 | 9 | _response = response; 10 | _JSON = JSON; 11 | _error = error; 12 | 13 | return self; 14 | } 15 | 16 | - (BOOL)fail { 17 | return self.error != nil; 18 | } 19 | 20 | - (NSURLSessionTask *)JSONTaskWithRequest:(NSURLRequest *)request success:(void (^)(NSURLRequest *, NSHTTPURLResponse *, id))success failure:(void (^)(NSURLRequest *, NSHTTPURLResponse *, NSError *, id))failure { 21 | 22 | if (self.fail) { 23 | if (failure) { 24 | failure(request, self.response, self.error, self.JSON); 25 | } 26 | } else { 27 | if (success) { 28 | success(request, self.response, self.JSON); 29 | } 30 | } 31 | 32 | return nil; 33 | } 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /Example/Tests/TestingClasses.h: -------------------------------------------------------------------------------- 1 | @import Foundation; 2 | @import Artsy_Authentication; 3 | @import Social; 4 | @import LVTwitterOAuthClient; 5 | #import "TestingNetworkOperator.h" 6 | 7 | extern const void* ArtsyAccountStoreKey; 8 | extern const void* ArtsyTwitterReverseAuthKey; 9 | 10 | @interface ArtsyAuthentication(Test) 11 | 12 | - (SLRequest *)requestForMe:(ACAccount *)facebookAccount; 13 | 14 | @property (nonatomic, strong) ArtsyNetworkOperator *networkOperator; 15 | 16 | @end 17 | 18 | 19 | @interface StubbedAuthenticator : ArtsyAuthentication 20 | @end 21 | 22 | @interface FacebookTestingAccountStore : ACAccountStore 23 | @end 24 | 25 | @interface TwitterTestingAccountStore : ACAccountStore 26 | @end 27 | 28 | @interface TestingResponse : NSHTTPURLResponse 29 | @property (readwrite) NSInteger statusCode; 30 | @end 31 | 32 | @interface FacebookTestingAccount : ACAccount 33 | @end 34 | 35 | @interface TwitterTestingAccount : ACAccount 36 | @end 37 | 38 | @interface TestingTwitterReverseAuth : LVTwitterOAuthClient 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /Example/Artsy_Authentication/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "40x40", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "60x60", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "ipad", 20 | "size" : "29x29", 21 | "scale" : "1x" 22 | }, 23 | { 24 | "idiom" : "ipad", 25 | "size" : "29x29", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "ipad", 30 | "size" : "40x40", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "40x40", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "76x76", 41 | "scale" : "1x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "76x76", 46 | "scale" : "2x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Artsy 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 | -------------------------------------------------------------------------------- /Example/Artsy_Authentication/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "orientation" : "portrait", 20 | "idiom" : "ipad", 21 | "extent" : "full-screen", 22 | "minimum-system-version" : "7.0", 23 | "scale" : "1x" 24 | }, 25 | { 26 | "orientation" : "landscape", 27 | "idiom" : "ipad", 28 | "extent" : "full-screen", 29 | "minimum-system-version" : "7.0", 30 | "scale" : "1x" 31 | }, 32 | { 33 | "orientation" : "portrait", 34 | "idiom" : "ipad", 35 | "extent" : "full-screen", 36 | "minimum-system-version" : "7.0", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "orientation" : "landscape", 41 | "idiom" : "ipad", 42 | "extent" : "full-screen", 43 | "minimum-system-version" : "7.0", 44 | "scale" : "2x" 45 | } 46 | ], 47 | "info" : { 48 | "version" : 1, 49 | "author" : "xcode" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Example/Tests/ArtsyNetworkOperatorTests.m: -------------------------------------------------------------------------------- 1 | @import Quick; 2 | @import Nimble; 3 | @import Artsy_Authentication; 4 | @import OHHTTPStubs; 5 | 6 | QuickSpecBegin(ArtsyNetworkOperatorTests) 7 | 8 | describe(@"a network operator", ^{ 9 | NSURL *url = [NSURL URLWithString:@"https://api.artsy.net/api/hi"]; 10 | NSURLRequest *request = [NSURLRequest requestWithURL:url]; 11 | 12 | it(@"calls through to the network to return JSON", ^{ 13 | ArtsyNetworkOperator *sut = [[ArtsyNetworkOperator alloc] init]; 14 | 15 | [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { 16 | return [request.URL isEqual:url]; 17 | } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { 18 | NSData *data = [NSJSONSerialization dataWithJSONObject:@{@"key": @"value"}options:0 error:nil]; 19 | return [OHHTTPStubsResponse responseWithData:data statusCode:200 headers:@{ @"Content-Type": @"application/json" }]; 20 | }]; 21 | 22 | __block BOOL called = NO; 23 | [sut JSONTaskWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) { 24 | expect(JSON[@"key"]).to( equal(@"value") ); 25 | called = YES; 26 | } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) { 27 | XCTFail(@"Stubbed request failed. Should never happen."); 28 | }]; 29 | 30 | expect(@(called)).toEventually( beTruthy() ); 31 | }); 32 | 33 | afterEach(^{ 34 | [OHHTTPStubs removeAllStubs]; 35 | }); 36 | }); 37 | 38 | QuickSpecEnd 39 | -------------------------------------------------------------------------------- /Pod/Classes/ArtsyNetworkOperator.m: -------------------------------------------------------------------------------- 1 | #import "ArtsyNetworkOperator.h" 2 | #import "ArtsyAuthentication.h" 3 | 4 | @implementation ArtsyNetworkOperator 5 | 6 | - (NSURLSessionTask *)JSONTaskWithRequest:(NSURLRequest *)request 7 | success:(ArtsyNetworkSuccessCallback)success 8 | failure:(ArtsyNetworkFailureCallback)failure { 9 | NSURLSession *session = [NSURLSession sharedSession]; 10 | 11 | NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { 12 | 13 | NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; 14 | id JSON; 15 | NSError *jsonError; 16 | 17 | if (data) { 18 | JSON = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError]; 19 | } 20 | 21 | NSInteger statusCode = httpResponse.statusCode; 22 | 23 | NSDictionary *userInfo; 24 | if (error || jsonError) { 25 | userInfo = @{ NSUnderlyingErrorKey : error ?: jsonError }; 26 | } 27 | NSError *networkError = [NSError errorWithDomain:ArtsyAuthenticationErrorDomain code:statusCode userInfo:userInfo]; 28 | 29 | BOOL validStatusCode = statusCode >= 200 && statusCode < 300; 30 | if (validStatusCode && data && JSON && !error) { 31 | success(request, httpResponse, JSON); 32 | } else { 33 | failure(request, httpResponse, networkError, JSON); 34 | } 35 | }]; 36 | 37 | [task resume]; 38 | 39 | return task; 40 | } 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /Pod/Classes/ArtsyToken.m: -------------------------------------------------------------------------------- 1 | #import "ArtsyToken.h" 2 | 3 | @implementation ArtsyToken 4 | 5 | + (BOOL)supportsSecureCoding 6 | { 7 | return YES; 8 | } 9 | 10 | - (instancetype)initWithToken:(NSString *)token expirationDate:(NSDate *)expirationDate 11 | { 12 | self = [super init]; 13 | if (!self) return nil; 14 | 15 | _token = token; 16 | _expirationDate = expirationDate; 17 | 18 | return self; 19 | } 20 | 21 | - (void)encodeWithCoder:(NSCoder *)encoder 22 | { 23 | [encoder encodeObject:self.token forKey:@"token"]; 24 | [encoder encodeObject:self.expirationDate forKey:@"expiration"]; 25 | } 26 | 27 | - (instancetype)initWithCoder:(NSCoder *)decoder 28 | { 29 | NSString *token = [decoder decodeObjectOfClass:NSString.class forKey:@"token"]; 30 | NSDate *date = [decoder decodeObjectOfClass:NSDate.class forKey:@"expiration"]; 31 | 32 | return [self initWithToken:token expirationDate:date]; 33 | } 34 | 35 | - (BOOL)hasExpired 36 | { 37 | return [self hasExpiredWithDate:[NSDate date]]; 38 | } 39 | 40 | - (BOOL)hasExpiredWithDate:(NSDate *)date 41 | { 42 | return [self.expirationDate compare:date] == NSOrderedAscending; 43 | } 44 | 45 | - (BOOL)isEmpty { 46 | return self.token.length < 1; 47 | } 48 | 49 | - (NSString *)description { 50 | NSString *empty = @"non-empty"; 51 | if (self.empty) { 52 | empty = @"empty"; 53 | } 54 | 55 | return [NSString stringWithFormat:@"Artsy authentication token (%@) %p expires: %@", empty, self, self.expirationDate]; 56 | } 57 | 58 | - (id)copyWithZone:(NSZone *)zone { 59 | return [[ArtsyToken allocWithZone:zone] initWithToken:self.token expirationDate:self.expirationDate]; 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /Artsy+Authentication.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "Artsy+Authentication" 3 | s.version = "1.6.0" 4 | s.summary = "Authentication for Artsy Services." 5 | s.description = "Authentication for Artsy Cocoa libraries. Yawn, boring." 6 | s.homepage = "https://github.com/artsy/Artsy_Authentication" 7 | s.license = 'MIT' 8 | s.author = { "Orta Therox" => "orta@artsymail.com" } 9 | s.source = { :git => "https://github.com/artsy/Artsy_Authentication.git", :tag => "#{s.version}" } 10 | s.social_media_url = 'https://twitter.com/artsyopensource' 11 | 12 | s.ios.deployment_target = '7.0' 13 | s.tvos.deployment_target = '9.0' 14 | 15 | # Twitter/FB/Email 16 | s.subspec "everything" do |ss| 17 | # Does not work with tvOS 18 | ss.tvos.deployment_target = "100.0" 19 | ss.ios.deployment_target = '7.0' 20 | 21 | ss.source_files = 'Pod/Classes' 22 | ss.private_header_files = 'Pod/Classes/*Private.h' 23 | 24 | ss.frameworks = 'Foundation', 'Social', 'Accounts' 25 | ss.dependency 'ISO8601DateFormatter' 26 | ss.dependency 'NSURL+QueryDictionary' 27 | ss.dependency 'LVTwitterOAuthClient' 28 | end 29 | 30 | # Email 31 | s.subspec "email" do |ss| 32 | ss.source_files = 'Pod/Classes' 33 | ss.private_header_files = 'Pod/Classes/*Private.h' 34 | ss.exclude_files = ['Pod/Classes/*Facebook.{h,m}', 'Pod/Classes/*Twitter.{h,m}'] 35 | ss.tvos.exclude_files = ['Pod/Classes/*Facebook.{h,m}', 'Pod/Classes/*Twitter.{h,m}', 'Pod/Classes/*Accounts.{h,m}'] 36 | ss.dependency 'ISO8601DateFormatter' 37 | ss.dependency 'NSURL+QueryDictionary' 38 | ss.frameworks = 'Foundation' 39 | end 40 | 41 | s.default_subspec = "everything" 42 | end 43 | -------------------------------------------------------------------------------- /Example/Artsy_Authentication/Artsy_Authentication-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | net.artsy.artsy.dev 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 | UIMainStoryboardFile 28 | Main_iPhone 29 | UIMainStoryboardFile~ipad 30 | Main_iPad 31 | UIRequiredDeviceCapabilities 32 | 33 | armv7 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationLandscapeLeft 39 | UIInterfaceOrientationLandscapeRight 40 | 41 | UISupportedInterfaceOrientations~ipad 42 | 43 | UIInterfaceOrientationPortrait 44 | UIInterfaceOrientationPortraitUpsideDown 45 | UIInterfaceOrientationLandscapeLeft 46 | UIInterfaceOrientationLandscapeRight 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /Example/Tests/ArtsyTokenTests.m: -------------------------------------------------------------------------------- 1 | @import Quick; 2 | @import Nimble; 3 | @import Artsy_Authentication; 4 | 5 | QuickSpecBegin(ArtsyTokenTests) 6 | 7 | describe(@"a token", ^{ 8 | it(@"successfully initializes", ^{ 9 | NSString *xappToken = @"is_it_exapp_or_zapp"; 10 | NSDate *tokenExpirationDate = [NSDate distantFuture]; 11 | ArtsyToken *sut = [[ArtsyToken alloc] initWithToken:xappToken expirationDate:tokenExpirationDate]; 12 | 13 | expect(sut.token).to( equal(xappToken) ); 14 | expect(sut.expirationDate).to( equal(tokenExpirationDate) ); 15 | }); 16 | 17 | it(@"returns empty correctly", ^{ 18 | ArtsyToken *sut = [[ArtsyToken alloc] initWithToken:@"non_empty_string" expirationDate:[NSDate date]]; 19 | expect(@(sut.empty)).to( equal(@(NO)) ); 20 | 21 | sut = [[ArtsyToken alloc] initWithToken:@"" expirationDate:[NSDate date]]; 22 | expect(@(sut.empty)).to( equal(@(YES)) ); 23 | }); 24 | 25 | it(@"returns hasExpired correctly.", ^{ 26 | ArtsyToken *sut = [[ArtsyToken alloc] initWithToken:@"" expirationDate:[NSDate distantFuture]]; 27 | expect(@(sut.hasExpired)).to( equal(@(NO)) ); 28 | 29 | sut = [[ArtsyToken alloc] initWithToken:@"" expirationDate:[NSDate distantPast]]; 30 | expect(@(sut.hasExpired)).to( equal(@(YES)) ); 31 | }); 32 | 33 | it(@"returns hasExpiredWithDate: correctly.", ^{ 34 | ArtsyToken *sut = [[ArtsyToken alloc] initWithToken:@"" expirationDate:[NSDate distantFuture]]; 35 | expect(@([sut hasExpiredWithDate:[NSDate date]])).to( equal(@(NO)) ); 36 | 37 | sut = [[ArtsyToken alloc] initWithToken:@"" expirationDate:[NSDate distantPast]]; 38 | expect(@([sut hasExpiredWithDate:[NSDate date]])).to( equal(@(YES)) ); 39 | }); 40 | }); 41 | 42 | QuickSpecEnd 43 | -------------------------------------------------------------------------------- /Pod/Classes/ArtsyAuthenticationRouter.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @class ArtsyToken; 4 | 5 | /// Creates NSURLRequests for authentication 6 | 7 | @interface ArtsyAuthenticationRouter : NSObject 8 | 9 | - (instancetype)initWithClientID:(NSString *)clientID clientSecret:(NSString *)clientSecret NS_DESIGNATED_INITIALIZER; 10 | 11 | /// Temporary Xapp token for non-logged-in users 12 | - (NSURLRequest *)requestForXapp; 13 | 14 | /// Typical Artsy artsy log in 15 | - (NSURLRequest *)requestForAuthWithEmail:(NSString *)email password:(NSString *)password; 16 | 17 | /// Details about user 18 | - (NSURLRequest *)requestForUserDetails; 19 | 20 | /// Artsy new user 21 | - (NSURLRequest *)requestForCreateNewUserwithEmail:(NSString *)email name:(NSString *)name password:(NSString *)password; 22 | 23 | /// Log in with Facebook token 24 | - (NSURLRequest *)newFacebookOAuthRequestWithToken:(NSString *)facebookToken; 25 | 26 | /// Create new user with Facebook token, email, and name 27 | - (NSURLRequest *)newCreateUserViaFacebookRequestWithToken:(NSString *)facebookToken email:(NSString *)email name:(NSString *)name; 28 | 29 | /// Log in with twitter token + secret 30 | - (NSURLRequest *)newTwitterOAuthRequestWithToken:(NSString *)token secret:(NSString *)secret; 31 | 32 | /// Create new user with Twitter token, email, and name 33 | - (NSURLRequest *)newCreateUserViaTwitterRequestWithToken:(NSString *)token secret:(NSString *)secret email:(NSString *)email name:(NSString *)name; 34 | 35 | /// Switch to staging environment 36 | @property (nonatomic, assign, readwrite) BOOL staging; 37 | 38 | @property (readonly, nonatomic, copy) NSString *clientID; 39 | @property (readonly, nonatomic, copy) NSString *clientSecret; 40 | 41 | @property (nonatomic, copy) ArtsyToken *xappToken; 42 | @property (nonatomic, copy) ArtsyToken *authToken; 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /Example/Artsy_Authentication/Base.lproj/Main_iPad.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /Pod/Classes/ArtsyAuthentication.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | extern NSString* const ArtsyAuthenticationErrorDomain; 4 | 5 | typedef enum ArtsyErrorCode { 6 | ArtsyErrorNoFacebookAppID = 1, 7 | ArtsyErrorUserDoesNotExist 8 | } ArtsyErrorCode; 9 | 10 | @class ArtsyToken, ArtsyAuthenticationRouter; 11 | 12 | typedef void (^ArtsyAuthenticationCallback)(ArtsyToken *token, NSError *error); 13 | typedef void (^ArtsyAuthenticationWithUserDetailsCallback)(ArtsyToken *token, NSDictionary *userDetails, NSError *error); 14 | 15 | @interface ArtsyAuthentication : NSObject 16 | 17 | /// Create an authentication object 18 | - (instancetype)initWithClientID:(NSString *)clientID 19 | clientSecret:(NSString *)clientSecret NS_DESIGNATED_INITIALIZER; 20 | 21 | /// Gets a week long trial user token, nil if network errors 22 | - (void)getWeekLongXAppTrialToken:(ArtsyAuthenticationCallback)completion; 23 | 24 | /// Gets a 35 year-long authenticated token, nil if network errors 25 | - (void)logInWithEmail:(NSString *)email 26 | password:(NSString *)password 27 | completion:(ArtsyAuthenticationCallback)completion; 28 | 29 | /// Gets a 35 year-long authenticated token, nil if network errors, and in addition fetches the user’s details 30 | - (void)logInAndFetchUserDetailsWithEmail:(NSString *)email 31 | password:(NSString *)password 32 | completion:(ArtsyAuthenticationWithUserDetailsCallback)completion; 33 | 34 | /// Creates a new user, or fails 35 | - (void)createUserWithEmail:(NSString *)email 36 | name:(NSString *)name 37 | password:(NSString *)password 38 | completion:(void (^)(NSDictionary *newUserDictionary, NSError *error))completion; 39 | 40 | /// Clears any user auth token from the router. 41 | - (void)logout; 42 | 43 | /// Use Stubbed data 44 | @property (nonatomic, assign, readwrite) BOOL stubbedData; 45 | 46 | /// Router for generating NSURLRequests 47 | @property (nonatomic, strong, readonly) ArtsyAuthenticationRouter *router; 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - "Artsy+Authentication (1.6.0)": 3 | - "Artsy+Authentication/everything (= 1.6.0)" 4 | - "Artsy+Authentication/everything (1.6.0)": 5 | - ISO8601DateFormatter 6 | - LVTwitterOAuthClient 7 | - "NSURL+QueryDictionary" 8 | - ISO8601DateFormatter (0.8) 9 | - Keys (1.0.1) 10 | - LVTwitterOAuthClient (0.3.0): 11 | - OAuthCore 12 | - Nimble (7.3.4) 13 | - "NSData+Base64 (1.0.0)" 14 | - "NSURL+QueryDictionary (1.2.0)" 15 | - OAuthCore (0.0.1): 16 | - "NSData+Base64" 17 | - OHHTTPStubs (6.1.0): 18 | - OHHTTPStubs/Default (= 6.1.0) 19 | - OHHTTPStubs/Core (6.1.0) 20 | - OHHTTPStubs/Default (6.1.0): 21 | - OHHTTPStubs/Core 22 | - OHHTTPStubs/JSON 23 | - OHHTTPStubs/NSURLSession 24 | - OHHTTPStubs/OHPathHelpers 25 | - OHHTTPStubs/JSON (6.1.0): 26 | - OHHTTPStubs/Core 27 | - OHHTTPStubs/NSURLSession (6.1.0): 28 | - OHHTTPStubs/Core 29 | - OHHTTPStubs/OHPathHelpers (6.1.0) 30 | - Quick (1.3.4) 31 | 32 | DEPENDENCIES: 33 | - "Artsy+Authentication (from `../`)" 34 | - Keys (from `Pods/CocoaPodsKeys`) 35 | - Nimble 36 | - OHHTTPStubs 37 | - Quick 38 | 39 | SPEC REPOS: 40 | https://github.com/cocoapods/specs.git: 41 | - ISO8601DateFormatter 42 | - LVTwitterOAuthClient 43 | - Nimble 44 | - "NSData+Base64" 45 | - "NSURL+QueryDictionary" 46 | - OAuthCore 47 | - OHHTTPStubs 48 | - Quick 49 | 50 | EXTERNAL SOURCES: 51 | "Artsy+Authentication": 52 | :path: "../" 53 | Keys: 54 | :path: Pods/CocoaPodsKeys 55 | 56 | SPEC CHECKSUMS: 57 | "Artsy+Authentication": 0edfc5887e423db8992eee41eddb8b9f54d5a896 58 | ISO8601DateFormatter: 4551b6ce4f83185425f583b0b3feb3c7b59b942c 59 | Keys: a576f4c9c1c641ca913a959a9c62ed3f215a8de9 60 | LVTwitterOAuthClient: 058199e43af92c0b99332332fa26d2229c59f04d 61 | Nimble: 051e3d8912d40138fa5591c78594f95fb172af37 62 | "NSData+Base64": 4e84902c4db907a15673474677e57763ef3903e4 63 | "NSURL+QueryDictionary": bae616404e2adf6409d3d5c02a093cbf44c8a236 64 | OAuthCore: 43dce261db846a1fe20234d95654257e00e78581 65 | OHHTTPStubs: 1e21c7d2c084b8153fc53d48400d8919d2d432d0 66 | Quick: f4f7f063c524394c73ed93ac70983c609805d481 67 | 68 | PODFILE CHECKSUM: 5beac159ab74a19fcfc248de3a2bbc654d6a16bd 69 | 70 | COCOAPODS: 1.6.1 71 | -------------------------------------------------------------------------------- /Pod/Classes/ArtsyAuthentication+Private.m: -------------------------------------------------------------------------------- 1 | #import "ArtsyAuthentication+Private.h" 2 | #import 3 | #import "ArtsyToken.h" 4 | 5 | const void* ArtsyAccountStoreKey = &ArtsyAccountStoreKey; 6 | 7 | @implementation ArtsyAuthentication (Private) 8 | 9 | // Just to silence the compiler warning. 10 | @dynamic networkOperator; 11 | 12 | - (void)callback:(ArtsyToken *)token error:(NSError *)error completion:(ArtsyAuthenticationCallback)callback { 13 | if (callback) { 14 | callback(token, error); 15 | } 16 | } 17 | 18 | - (ArtsyNetworkSuccessCallback)successfulLoginBlock:(ArtsyAuthenticationCallback)callback { 19 | __weak __typeof(self) weakSelf = self; 20 | return ^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) { 21 | __strong __typeof(weakSelf) strongSelf = weakSelf; 22 | NSString *token = JSON[ArtsyOAuthTokenKey]; 23 | NSString *expiryDateString = JSON[ArtsyOAuthExpiryKey]; 24 | ISO8601DateFormatter *dateFormatter = [[ISO8601DateFormatter alloc] init]; 25 | NSDate *expiryDate = [dateFormatter dateFromString:expiryDateString]; 26 | 27 | ArtsyToken *artsyToken = [[ArtsyToken alloc] initWithToken:token expirationDate:expiryDate]; 28 | 29 | [strongSelf callback:artsyToken error:nil completion:callback]; 30 | }; 31 | } 32 | 33 | - (ArtsyNetworkFailureCallback)failedLoginBlock:(ArtsyAuthenticationCallback)callback { 34 | __weak __typeof(self) weakSelf = self; 35 | return ^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) { 36 | __strong __typeof(weakSelf) strongSelf = weakSelf; 37 | // This case handles a 401 from Artsy's server, which means the Facebook account is not associated with a user. 38 | if (response.statusCode == 401) { 39 | NSDictionary *userInfo; 40 | if (error) { 41 | userInfo = @{ NSUnderlyingErrorKey : error }; 42 | } 43 | NSError *artsyError = [NSError errorWithDomain:ArtsyAuthenticationErrorDomain code:ArtsyErrorUserDoesNotExist userInfo:userInfo]; 44 | 45 | [strongSelf callback:nil error:artsyError completion:callback]; 46 | } else { 47 | [strongSelf callback:nil error:error completion:callback]; 48 | } 49 | }; 50 | } 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | CFPropertyList (3.0.0) 5 | RubyInline (3.12.4) 6 | ZenTest (~> 4.3) 7 | ZenTest (4.11.2) 8 | activesupport (4.2.11) 9 | i18n (~> 0.7) 10 | minitest (~> 5.1) 11 | thread_safe (~> 0.3, >= 0.3.4) 12 | tzinfo (~> 1.1) 13 | atomos (0.1.3) 14 | claide (1.0.2) 15 | cocoapods (1.6.1) 16 | activesupport (>= 4.0.2, < 5) 17 | claide (>= 1.0.2, < 2.0) 18 | cocoapods-core (= 1.6.1) 19 | cocoapods-deintegrate (>= 1.0.2, < 2.0) 20 | cocoapods-downloader (>= 1.2.2, < 2.0) 21 | cocoapods-plugins (>= 1.0.0, < 2.0) 22 | cocoapods-search (>= 1.0.0, < 2.0) 23 | cocoapods-stats (>= 1.0.0, < 2.0) 24 | cocoapods-trunk (>= 1.3.1, < 2.0) 25 | cocoapods-try (>= 1.1.0, < 2.0) 26 | colored2 (~> 3.1) 27 | escape (~> 0.0.4) 28 | fourflusher (>= 2.2.0, < 3.0) 29 | gh_inspector (~> 1.0) 30 | molinillo (~> 0.6.6) 31 | nap (~> 1.0) 32 | ruby-macho (~> 1.4) 33 | xcodeproj (>= 1.8.1, < 2.0) 34 | cocoapods-core (1.6.1) 35 | activesupport (>= 4.0.2, < 6) 36 | fuzzy_match (~> 2.0.4) 37 | nap (~> 1.0) 38 | cocoapods-deintegrate (1.0.3) 39 | cocoapods-downloader (1.2.2) 40 | cocoapods-keys (2.1.0) 41 | dotenv 42 | osx_keychain 43 | cocoapods-plugins (1.0.0) 44 | nap 45 | cocoapods-search (1.0.0) 46 | cocoapods-stats (1.1.0) 47 | cocoapods-trunk (1.3.1) 48 | nap (>= 0.8, < 2.0) 49 | netrc (~> 0.11) 50 | cocoapods-try (1.1.0) 51 | colored2 (3.1.2) 52 | concurrent-ruby (1.1.4) 53 | dotenv (2.7.1) 54 | escape (0.0.4) 55 | fourflusher (2.2.0) 56 | fuzzy_match (2.0.4) 57 | gh_inspector (1.1.3) 58 | i18n (0.9.5) 59 | concurrent-ruby (~> 1.0) 60 | minitest (5.11.3) 61 | molinillo (0.6.6) 62 | nanaimo (0.2.6) 63 | nap (1.1.0) 64 | netrc (0.11.0) 65 | osx_keychain (1.0.2) 66 | RubyInline (~> 3) 67 | ruby-macho (1.4.0) 68 | thread_safe (0.3.6) 69 | tzinfo (1.2.10) 70 | thread_safe (~> 0.1) 71 | xcodeproj (1.8.1) 72 | CFPropertyList (>= 2.3.3, < 4.0) 73 | atomos (~> 0.1.3) 74 | claide (>= 1.0.2, < 2.0) 75 | colored2 (~> 3.1) 76 | nanaimo (~> 0.2.6) 77 | 78 | PLATFORMS 79 | ruby 80 | 81 | DEPENDENCIES 82 | cocoapods 83 | cocoapods-deintegrate 84 | cocoapods-keys 85 | 86 | BUNDLED WITH 87 | 1.16.4 88 | -------------------------------------------------------------------------------- /Example/Tests/ArtsyAuthenticationTests.m: -------------------------------------------------------------------------------- 1 | @import Quick; 2 | @import Nimble; 3 | @import Artsy_Authentication; 4 | @import ISO8601DateFormatter; 5 | #import "TestingNetworkOperator.h" 6 | 7 | @interface ArtsyAuthentication(Tests) 8 | 9 | @property (nonatomic, strong) ArtsyNetworkOperator *networkOperator; 10 | 11 | @end 12 | 13 | QuickSpecBegin(ArtsyAuthenticationTests) 14 | 15 | describe(@"an authentication object", ^{ 16 | NSString *clientID = @"Art5y"; 17 | NSString *clientSecret = @"IActuallyLikeWineCoolers"; 18 | ISO8601DateFormatter *dateFormatter = [[ISO8601DateFormatter alloc] init]; 19 | 20 | NSString *tokenString = @"token"; 21 | NSDate *expiryDate = [dateFormatter dateFromString:@"2045-01-21"]; 22 | 23 | __block ArtsyAuthentication *sut; 24 | 25 | beforeEach(^{ 26 | sut = [[ArtsyAuthentication alloc] initWithClientID:clientID clientSecret:clientSecret]; 27 | }); 28 | 29 | it(@"initializes correctly", ^{ 30 | expect(sut.router.clientID).to( equal(clientID) ); 31 | expect(sut.router.clientSecret).to( equal(clientSecret) ); 32 | expect(@(sut.stubbedData)).to( beFalsy() ); 33 | }); 34 | 35 | it(@"successfully sets XApp token upon retrieval", ^{ 36 | 37 | id JSON = @{ 38 | @"expires_in": [dateFormatter stringFromDate:expiryDate], 39 | @"xapp_token": tokenString 40 | }; 41 | sut.networkOperator = [[TestingNetworkOperator alloc] initWithResponse:nil JSON:JSON error:nil]; 42 | 43 | [sut getWeekLongXAppTrialToken:^(ArtsyToken *token, NSError *error) { 44 | expect(token.token).to( equal(tokenString) ); 45 | expect(token.expirationDate).to( equal(expiryDate) ); 46 | }]; 47 | 48 | expect(sut.router.xappToken.token).to( equal(tokenString) ); 49 | expect(sut.router.xappToken.expirationDate).to( equal(expiryDate) ); 50 | }); 51 | 52 | it(@"creates a new user successfully", ^{ 53 | id JSON = @{ 54 | @"expires_in": [dateFormatter stringFromDate:expiryDate], 55 | @"xapp_token": tokenString 56 | }; 57 | sut.networkOperator = [[TestingNetworkOperator alloc] initWithResponse:nil JSON:JSON error:nil]; 58 | 59 | [sut createUserWithEmail:@"" name:@"" password:@"" completion:^(NSDictionary *newUserDictionary, NSError *error) { 60 | expect(@(newUserDictionary.count)).to( beGreaterThan(@(0)) ); 61 | expect(error).to( beNil() ); 62 | }]; 63 | }); 64 | 65 | it(@"logs out successfully", ^{ 66 | sut.router.xappToken = [ArtsyToken new]; 67 | 68 | [sut logout]; 69 | expect(sut.router.authToken).to( beNil() ); 70 | }); 71 | }); 72 | 73 | QuickSpecEnd 74 | -------------------------------------------------------------------------------- /Example/Tests/TestingClasses.m: -------------------------------------------------------------------------------- 1 | #import "TestingClasses.h" 2 | @import Accounts; 3 | 4 | @interface TestingSocialRequest : SLRequest 5 | 6 | @end 7 | 8 | @implementation TestingSocialRequest 9 | 10 | - (instancetype)init { 11 | /// Note that we're *not* calling super to avoid its pesky assertion failures. 12 | return self; 13 | } 14 | 15 | - (void)performRequestWithHandler:(SLRequestHandler)handler { 16 | TestingResponse *response = [[TestingResponse alloc] init]; 17 | response.statusCode = 200; 18 | 19 | id JSON = @{ 20 | @"email" : @"ash@ashfurrow.com", 21 | @"name" : @"Ash Furrow" 22 | }; 23 | NSData *data = [NSJSONSerialization dataWithJSONObject:JSON options:0 error:nil]; 24 | 25 | handler(data, response, nil); 26 | } 27 | 28 | @end 29 | 30 | @implementation StubbedAuthenticator 31 | 32 | - (SLRequest *)requestForMe:(ACAccount *)facebookAccount { 33 | return [[TestingSocialRequest alloc] init]; 34 | } 35 | 36 | @end 37 | 38 | @implementation TwitterTestingAccount 39 | 40 | - (ACAccountCredential *)credential { 41 | return [[ACAccountCredential alloc] initWithOAuthToken:@"twitter_token" tokenSecret:@"twitter_secret"]; 42 | } 43 | 44 | @end 45 | 46 | @implementation FacebookTestingAccount 47 | 48 | - (ACAccountCredential *)credential { 49 | return [[ACAccountCredential alloc] initWithOAuth2Token:@"facebook_token" refreshToken:@"facebook_refresh_token"expiryDate:[NSDate distantFuture]]; 50 | } 51 | 52 | @end 53 | 54 | @implementation FacebookTestingAccountStore 55 | 56 | - (void)requestAccessToAccountsWithType:(ACAccountType *)accountType options:(NSDictionary *)options completion:(ACAccountStoreRequestAccessCompletionHandler)completion { 57 | completion(YES, nil); 58 | } 59 | 60 | - (NSArray *)accountsWithAccountType:(ACAccountType *)accountType { 61 | return @[[FacebookTestingAccount new]]; 62 | } 63 | 64 | @end 65 | 66 | @implementation TwitterTestingAccountStore 67 | 68 | - (void)requestAccessToAccountsWithType:(ACAccountType *)accountType options:(NSDictionary *)options completion:(ACAccountStoreRequestAccessCompletionHandler)completion { 69 | completion(YES, nil); 70 | } 71 | 72 | - (NSArray *)accountsWithAccountType:(ACAccountType *)accountType { 73 | return @[[TwitterTestingAccount new]]; 74 | } 75 | 76 | @end 77 | 78 | @implementation TestingResponse 79 | 80 | @synthesize statusCode; 81 | 82 | @end 83 | 84 | @implementation TestingTwitterReverseAuth 85 | 86 | 87 | - (void)requestTokensForAccount:(ACAccount *)account completionBlock:(void(^)(NSDictionary *credentials, NSError *error))completion { 88 | completion(@{ @"oauth_token" : @"twitter_token", @"oauth_token_secret": @"twitter_secret" }, nil); 89 | } 90 | 91 | @end 92 | -------------------------------------------------------------------------------- /Example/Tests/ArtsyAuthenticationTests+Facebook.m: -------------------------------------------------------------------------------- 1 | @import Quick; 2 | @import Nimble; 3 | @import Accounts; 4 | @import Social; 5 | @import Artsy_Authentication; 6 | @import ISO8601DateFormatter; 7 | #import 8 | #import "TestingNetworkOperator.h" 9 | #import "TestingClasses.h" 10 | 11 | QuickSpecBegin(ArtsyAuthenticationTests_Facebook) 12 | 13 | __block NSString *authToken = @"token"; 14 | __block NSDate *authExpiryDate; 15 | 16 | describe(@"an authentication object", ^{ 17 | NSString *clientID = @"Art5y"; 18 | NSString *clientSecret = @"IActuallyLikeWineCoolers"; 19 | 20 | ISO8601DateFormatter *dateFormatter = [[ISO8601DateFormatter alloc] init]; 21 | authExpiryDate = [dateFormatter dateFromString:@"2045-01-21"]; 22 | 23 | TestingResponse *successfulNetworkResponse = [[TestingResponse alloc] init]; 24 | successfulNetworkResponse.statusCode = 200; 25 | id JSON = @{ 26 | @"access_token" : authToken, 27 | @"expires_in" : [dateFormatter stringFromDate:authExpiryDate] 28 | }; 29 | 30 | TestingResponse *userDoesNotExistNetworkResponse = [[TestingResponse alloc] init]; 31 | userDoesNotExistNetworkResponse.statusCode = 401; 32 | 33 | __block ArtsyAuthentication *sut; 34 | 35 | beforeEach(^{ 36 | sut = [[StubbedAuthenticator alloc] initWithClientID:clientID clientSecret:clientSecret]; 37 | FacebookTestingAccountStore *accountStore = [[FacebookTestingAccountStore alloc] init]; 38 | objc_setAssociatedObject(sut, ArtsyAccountStoreKey, accountStore, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 39 | }); 40 | 41 | it(@"logs in with Facebook", ^{ 42 | sut.networkOperator = [[TestingNetworkOperator alloc] initWithResponse:successfulNetworkResponse JSON:JSON error:nil]; 43 | 44 | __block ArtsyToken *fetchedAuthToken; 45 | [sut logInWithFacebook:@"1234" completion:^(ArtsyToken *token, NSError *error) { 46 | expect(error).to( beNil() ); 47 | fetchedAuthToken = token; 48 | }]; 49 | 50 | expect(fetchedAuthToken.token).to( equal(authToken) ); 51 | expect(fetchedAuthToken.expirationDate).to( equal(authExpiryDate) ); 52 | }); 53 | 54 | it(@"creates a user", ^{ 55 | sut.networkOperator = [[TestingNetworkOperator alloc] initWithResponse:successfulNetworkResponse JSON:JSON error:nil]; 56 | 57 | __block ArtsyToken *fetchedAuthToken; 58 | [sut createUserWithFacebook:@"1234" completion:^(ArtsyToken *token, NSError *error) { 59 | fetchedAuthToken = token; 60 | }]; 61 | 62 | expect(fetchedAuthToken.token).to( equal(authToken) ); 63 | expect(fetchedAuthToken.expirationDate).to( equal(authExpiryDate) ); 64 | }); 65 | 66 | it(@"returns correct error for non-existing users", ^{ 67 | NSError *error = [NSError errorWithDomain:@"Zora's Domain" code:0 userInfo:nil]; 68 | sut.networkOperator = [[TestingNetworkOperator alloc] initWithResponse:userDoesNotExistNetworkResponse JSON:JSON error:error]; 69 | 70 | __block NSError *fetchedError; 71 | [sut logInWithFacebook:@"1234" completion:^(ArtsyToken *token, NSError *error) { 72 | fetchedError = error; 73 | }]; 74 | 75 | expect(@(fetchedError.code)).to( equal(@(ArtsyErrorUserDoesNotExist)) ); 76 | expect(fetchedError.domain).to( equal(ArtsyAuthenticationErrorDomain) ); 77 | expect(fetchedError.userInfo[NSUnderlyingErrorKey]).to( equal(error) ); 78 | }); 79 | }); 80 | 81 | QuickSpecEnd 82 | 83 | -------------------------------------------------------------------------------- /Example/Artsy_Authentication.xcodeproj/xcshareddata/xcschemes/Artsy_Authentication-Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 61 | 63 | 69 | 70 | 71 | 72 | 73 | 74 | 80 | 82 | 88 | 89 | 90 | 91 | 93 | 94 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /Example/Artsy_Authentication/Base.lproj/Main_iPhone.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 28 | 37 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /Example/Tests/ArtsyAuthenticationTests+Twitter.m: -------------------------------------------------------------------------------- 1 | @import Quick; 2 | @import Nimble; 3 | @import Artsy_Authentication; 4 | @import ISO8601DateFormatter; 5 | #import "TestingClasses.h" 6 | #import 7 | 8 | QuickSpecBegin(ArtsyAuthenticationTests_Twitter) 9 | 10 | __block NSString *authToken = @"token"; 11 | __block NSDate *authExpiryDate; 12 | 13 | describe(@"an authentication object", ^{ 14 | NSString *clientID = @"Art5y"; 15 | NSString *clientSecret = @"IActuallyLikeWineCoolers"; 16 | 17 | ISO8601DateFormatter *dateFormatter = [[ISO8601DateFormatter alloc] init]; 18 | authExpiryDate = [dateFormatter dateFromString:@"2045-01-21"]; 19 | 20 | TestingResponse *successfulNetworkResponse = [[TestingResponse alloc] init]; 21 | successfulNetworkResponse.statusCode = 200; 22 | id JSON = @{ 23 | @"access_token" : authToken, 24 | @"expires_in" : [dateFormatter stringFromDate:authExpiryDate] 25 | }; 26 | 27 | TestingResponse *userDoesNotExistNetworkResponse = [[TestingResponse alloc] init]; 28 | userDoesNotExistNetworkResponse.statusCode = 401; 29 | 30 | __block TwitterTestingAccount *account; 31 | __block ArtsyAuthentication *sut; 32 | 33 | beforeEach(^{ 34 | account = [TwitterTestingAccount new]; 35 | sut = [[StubbedAuthenticator alloc] initWithClientID:clientID clientSecret:clientSecret]; 36 | TwitterTestingAccountStore *accountStore = [[TwitterTestingAccountStore alloc] init]; 37 | objc_setAssociatedObject(sut, ArtsyAccountStoreKey, accountStore, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 38 | 39 | TestingTwitterReverseAuth *reverseAuth = [[TestingTwitterReverseAuth alloc] init]; 40 | objc_setAssociatedObject(sut, ArtsyTwitterReverseAuthKey, reverseAuth, OBJC_ASSOCIATION_RETAIN); 41 | }); 42 | 43 | describe(@"with no twitter key and no twitter secret", ^{ 44 | it(@"fails ", ^{ 45 | expectAction(^{ [sut retrieveTwitterAccounts:nil]; }).to( raiseException() ); 46 | expectAction(^{ [sut createNewUserWithTwitter:account email:@"" name:@"" completion:nil]; }).to( raiseException() ); 47 | expectAction(^{ [sut logInWithTwitterAccount:account completion:nil]; }).to( raiseException() ); 48 | }); 49 | }); 50 | 51 | describe(@"with a twitter key and secret", ^{ 52 | 53 | beforeEach(^{ 54 | sut.twitterAPIKey = @""; 55 | sut.twitterAPISecret = @""; 56 | }); 57 | 58 | it (@"retrieves accounts", ^{ 59 | __block NSArray *fetchedAccounts; 60 | 61 | [sut retrieveTwitterAccounts:^(NSArray *accounts, NSError *error) { 62 | expect(error).to( beNil() ); 63 | fetchedAccounts = accounts; 64 | }]; 65 | 66 | expect(@([fetchedAccounts.firstObject isKindOfClass:[account class]])).to( beTruthy() ); 67 | }); 68 | 69 | it(@"logs in with Twitter", ^{ 70 | sut.networkOperator = [[TestingNetworkOperator alloc] initWithResponse:successfulNetworkResponse JSON:JSON error:nil]; 71 | 72 | __block ArtsyToken *fetchedAuthToken; 73 | [sut logInWithTwitterAccount:account completion:^(ArtsyToken *token, NSError *error) { 74 | expect(error).to( beNil() ); 75 | fetchedAuthToken = token; 76 | }]; 77 | 78 | expect(fetchedAuthToken.token).to( equal(authToken) ); 79 | expect(fetchedAuthToken.expirationDate).to( equal(authExpiryDate) ); 80 | }); 81 | 82 | it(@"creates a user with Twitter", ^{ 83 | sut.networkOperator = [[TestingNetworkOperator alloc] initWithResponse:successfulNetworkResponse JSON:JSON error:nil]; 84 | 85 | __block ArtsyToken *fetchedAuthToken; 86 | [sut createNewUserWithTwitter:account email:@"test@example.com" name:@"Mr. Testing" completion:^(ArtsyToken *token, NSError *error) { 87 | fetchedAuthToken = token; 88 | }]; 89 | 90 | expect(fetchedAuthToken.token).toEventually( equal(authToken) ); 91 | expect(fetchedAuthToken.expirationDate).toEventually( equal(authExpiryDate) ); 92 | }); 93 | 94 | it(@"returns correct error for non-existing users", ^{ 95 | NSError *error = [NSError errorWithDomain:@"Zora's Domain" code:0 userInfo:nil]; 96 | sut.networkOperator = [[TestingNetworkOperator alloc] initWithResponse:userDoesNotExistNetworkResponse JSON:JSON error:error]; 97 | 98 | __block NSError *fetchedError; 99 | [sut logInWithTwitterAccount:account completion:^(ArtsyToken *token, NSError *error) { 100 | fetchedError = error; 101 | }]; 102 | 103 | expect(@(fetchedError.code)).to( equal(@(ArtsyErrorUserDoesNotExist)) ); 104 | expect(fetchedError.domain).to( equal(ArtsyAuthenticationErrorDomain) ); 105 | expect(fetchedError.userInfo[NSUnderlyingErrorKey]).to( equal(error) ); 106 | }); 107 | }); 108 | }); 109 | 110 | QuickSpecEnd 111 | -------------------------------------------------------------------------------- /Pod/Classes/ArtsyAuthenticationRouter.m: -------------------------------------------------------------------------------- 1 | #import "ArtsyAuthenticationRouter.h" 2 | #import "ArtsyToken.h" 3 | 4 | #if __has_include() 5 | #import 6 | #endif 7 | 8 | #if __has_include() 9 | #import 10 | #endif 11 | 12 | @interface ArtsyAuthenticationRouter() 13 | 14 | @end 15 | 16 | @implementation ArtsyAuthenticationRouter 17 | 18 | - (instancetype)initWithClientID:(NSString *)clientID clientSecret:(NSString *)clientSecret { 19 | self = [super init]; 20 | if (!self) return nil; 21 | 22 | _clientID = clientID; 23 | _clientSecret = clientSecret; 24 | 25 | return self; 26 | } 27 | 28 | #pragma mark - NSURLRequests for calls 29 | 30 | - (NSURL *)urlWithPath:(NSString *)path { 31 | NSString *base = self.staging ? @"https://stagingapi.artsy.net" : @"https://api.artsy.net"; 32 | return [NSURL URLWithString:path relativeToURL:[NSURL URLWithString:base]]; 33 | } 34 | 35 | - (NSURLRequest *)baseRequestForAddress:(NSURL *)url method:(NSString *)httpMethod { 36 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; 37 | request.HTTPMethod = httpMethod; 38 | 39 | if (self.xappToken) { 40 | [request setValue:self.xappToken.token forHTTPHeaderField:@"X-Xapp-Token"]; 41 | } 42 | if (self.authToken) { 43 | [request setValue:self.authToken.token forHTTPHeaderField:@"X-Access-Token"]; 44 | } 45 | 46 | return [request copy]; 47 | } 48 | 49 | - (NSURLRequest *)baseRequestForAddress:(NSURL *)url { 50 | return [self baseRequestForAddress:url method:@"GET"]; 51 | } 52 | 53 | - (NSURLRequest *)requestForAuthWithEmail:(NSString *)email password:(NSString *)password { 54 | NSDictionary *params = @{ 55 | @"email" : email, 56 | @"password" : password, 57 | @"client_id" : self.clientID, 58 | @"client_secret" : self.clientSecret, 59 | @"grant_type" : @"credentials", 60 | @"scope" : @"offline_access" 61 | }; 62 | 63 | NSURL *url = [[self urlWithPath:@"/oauth2/access_token"] uq_URLByAppendingQueryDictionary:params]; 64 | return [self baseRequestForAddress:url method:@"POST"]; 65 | } 66 | 67 | - (NSURLRequest *)requestForUserDetails; 68 | { 69 | return [self baseRequestForAddress:[self urlWithPath:@"/api/v1/me"]]; 70 | } 71 | 72 | - (NSURLRequest *)requestForCreateNewUserwithEmail:(NSString *)email name:(NSString *)name password:(NSString *)password { 73 | NSDictionary *params = @{ 74 | @"email" : email, 75 | @"password" : password, 76 | @"name" : name 77 | }; 78 | 79 | NSURL *url = [[self urlWithPath:@"/api/v1/user"] uq_URLByAppendingQueryDictionary:params]; 80 | return [self baseRequestForAddress:url method:@"POST"]; 81 | } 82 | 83 | - (NSURLRequest *)newCreateUserViaFacebookRequestWithToken:(NSString *)facebookToken email:(NSString *)email name:(NSString *)name { 84 | NSDictionary *params = @{ 85 | @"provider": @"facebook", 86 | @"oauth_token": facebookToken, 87 | @"email" : email, 88 | @"name" : name 89 | }; 90 | 91 | NSURL *url = [[self urlWithPath:@"/api/v1/user"] uq_URLByAppendingQueryDictionary:params]; 92 | return [self baseRequestForAddress:url method:@"POST"]; 93 | } 94 | 95 | - (NSURLRequest *)newFacebookOAuthRequestWithToken:(NSString *)facebookToken { 96 | NSDictionary *params = @{ 97 | @"oauth_provider" : @"facebook", 98 | @"oauth_token" : facebookToken, 99 | @"client_id" : self.clientID, 100 | @"client_secret" : self.clientSecret, 101 | @"grant_type" : @"oauth_token", 102 | @"scope" : @"offline_access" 103 | }; 104 | 105 | NSURL *url = [[self urlWithPath:@"/oauth2/access_token"] uq_URLByAppendingQueryDictionary:params]; 106 | return [self baseRequestForAddress:url method:@"POST"]; 107 | } 108 | 109 | - (NSURLRequest *)newTwitterOAuthRequestWithToken:(NSString *)token secret:(NSString *)secret { 110 | NSDictionary *params = @{ 111 | @"oauth_provider" : @"twitter", 112 | @"oauth_token" : token, 113 | @"oauth_token_secret" : secret, 114 | @"client_id" : self.clientID, 115 | @"client_secret" : self.clientSecret, 116 | @"grant_type" : @"oauth_token", 117 | @"scope" : @"offline_access" 118 | }; 119 | 120 | NSURL *url = [[self urlWithPath:@"/oauth2/access_token"] uq_URLByAppendingQueryDictionary:params]; 121 | return [self baseRequestForAddress:url method:@"POST"]; 122 | } 123 | 124 | - (NSURLRequest *)newCreateUserViaTwitterRequestWithToken:(NSString *)token secret:(NSString *)secret email:(NSString *)email name:(NSString *)name { 125 | NSDictionary *params = @{ 126 | @"provider": @"twitter", 127 | @"oauth_token": token, 128 | @"oauth_token_secret": secret, 129 | @"email" : email, 130 | @"name" : name 131 | }; 132 | 133 | NSURL *url = [[self urlWithPath:@"/api/v1/user"] uq_URLByAppendingQueryDictionary:params]; 134 | return [self baseRequestForAddress:url method:@"POST"]; 135 | } 136 | 137 | - (NSURLRequest *)requestForXapp { 138 | NSDictionary *params = @{ 139 | @"client_id" : self.clientID, 140 | @"client_secret" : self.clientSecret 141 | }; 142 | 143 | NSURL *url = [[self urlWithPath:@"/api/v1/xapp_token"] uq_URLByAppendingQueryDictionary:params]; 144 | return [self baseRequestForAddress:url]; 145 | } 146 | 147 | @end 148 | 149 | -------------------------------------------------------------------------------- /Example/Artsy_Authentication/ARViewController.m: -------------------------------------------------------------------------------- 1 | #import "ARViewController.h" 2 | 3 | @import Keys; 4 | @import Artsy_Authentication; 5 | 6 | @interface ARViewController () 7 | 8 | @property (nonatomic, strong) ArtsyAuthentication *auth; 9 | @property (nonatomic, strong) Artsy_AuthenticationKeys *keys; 10 | 11 | @end 12 | 13 | @implementation ARViewController 14 | 15 | - (void)viewDidLoad 16 | { 17 | [super viewDidLoad]; 18 | 19 | self.keys = [Artsy_AuthenticationKeys new]; 20 | 21 | ArtsyAuthentication *auth = [[ArtsyAuthentication alloc] initWithClientID:self.keys.artsyAPIClientKey clientSecret:self.keys.artsyAPIClientSecret]; 22 | auth.twitterAPIKey = self.keys.artsyTwitterKey; 23 | auth.twitterAPISecret = self.keys.artsyTwitterSecret; 24 | self.auth = auth; 25 | } 26 | 27 | - (IBAction)artsyAuthTapped:(id)sender 28 | { 29 | [self doArtsy]; 30 | } 31 | 32 | - (IBAction)twitterAuthTapped:(id)sender 33 | { 34 | [self doTwitter]; 35 | } 36 | 37 | - (IBAction)facebookAuthTapped:(id)sender 38 | { 39 | [self doFacebook]; 40 | } 41 | 42 | - (void)doArtsy { 43 | ArtsyAuthentication *auth = self.auth; 44 | 45 | NSLog(@"Getting Xapp token."); 46 | [auth getWeekLongXAppTrialToken:^(ArtsyToken *token, NSError *error) { 47 | NSLog(@"Retrieved Xapp token: %@", token); 48 | 49 | NSString *email = @"starlord@example.com"; 50 | NSString *name = @"Star Lord"; 51 | NSString *password = @"1234Pasd1"; 52 | 53 | [auth logInWithEmail:email password:password completion:^(ArtsyToken *token, NSError *error) { 54 | NSLog(@"Logged in with token %@", token.token); 55 | 56 | }]; 57 | 58 | NSLog(@"Creating new user %@", email); 59 | [auth createUserWithEmail:email name:name password:password completion:^(NSDictionary *newUserDictionary, NSError *error) { 60 | NSLog(@"Created new user: %@", newUserDictionary); 61 | 62 | [auth logInWithEmail:email password:password completion:^(ArtsyToken *token, NSError *error) { 63 | if (error) { 64 | NSLog(@"Error logging in: %@", error); 65 | } 66 | 67 | if (token) { 68 | NSLog(@"Retrieved ArtsyToken: %@", token); 69 | } 70 | }]; 71 | }]; 72 | }]; 73 | } 74 | 75 | - (void)doTwitter { 76 | ArtsyAuthentication *auth = self.auth; 77 | 78 | NSLog(@"Getting Xapp token."); 79 | [auth getWeekLongXAppTrialToken:^(ArtsyToken *token, NSError *error) { 80 | NSLog(@"Retrieved Xapp token: %@", token); 81 | 82 | NSLog(@"Retrieving Twitter accounts."); 83 | [auth retrieveTwitterAccounts:^(NSArray *accounts, NSError *error) { 84 | ACAccount *account = accounts.firstObject; 85 | NSLog(@"Retrieved %@ Twitter accounts. Choosing the first (%@).", @(accounts.count), account.username); 86 | 87 | NSLog(@"Logging in with Twitter."); 88 | [auth logInWithTwitterAccount:account completion:^(ArtsyToken *token, NSError *error) { 89 | if (error) { 90 | if ([error.domain isEqualToString:ArtsyAuthenticationErrorDomain] && error.code == ArtsyErrorUserDoesNotExist) { 91 | NSLog(@"User does not exist. Creating with Twitter token."); 92 | [auth createNewUserWithTwitter:account email:@"ash_example@example.com" name:@"Example Furrow" completion:^(ArtsyToken *token, NSError *error) { 93 | if (error) { 94 | NSLog(@"Error creating user: %@", error); 95 | } else { 96 | NSLog(@"Successfully created Artsy user."); 97 | NSLog(@"Retrieved ArtsyToken: %@", token); 98 | } 99 | }]; 100 | } else { 101 | NSLog(@"Error logging in: %@", error); 102 | } 103 | } 104 | 105 | if (token) { 106 | NSLog(@"Retrieved ArtsyToken: %@", token); 107 | } 108 | }]; 109 | }]; 110 | }]; 111 | } 112 | 113 | - (void)doFacebook { 114 | ArtsyAuthentication *auth = self.auth; 115 | NSString *facebookAppID = self.keys.artsyFacebookAppID; 116 | 117 | NSLog(@"Getting Xapp token."); 118 | [auth getWeekLongXAppTrialToken:^(ArtsyToken *token, NSError *error) { 119 | NSLog(@"Retrieved Xapp token: %@", token); 120 | 121 | NSLog(@"Logging in with Facebook."); 122 | [auth logInWithFacebook:facebookAppID completion:^(ArtsyToken *token, NSError *error) { 123 | if (error) { 124 | if ([error.domain isEqualToString:ArtsyAuthenticationErrorDomain] && error.code == ArtsyErrorUserDoesNotExist) { 125 | NSLog(@"User does not exist. Creating with Facebook token."); 126 | [auth createUserWithFacebook:facebookAppID completion:^(ArtsyToken *token, NSError *error) { 127 | if (error) { 128 | NSLog(@"Error creating user: %@", error); 129 | } else { 130 | NSLog(@"Successfully created Artsy user."); 131 | NSLog(@"Retrieved ArtsyToken: %@", token); 132 | } 133 | }]; 134 | } else { 135 | NSLog(@"Error logging in: %@", error); 136 | } 137 | } 138 | 139 | if (token) { 140 | NSLog(@"Retrieved ArtsyToken: %@", token); 141 | } 142 | }]; 143 | }]; 144 | } 145 | 146 | @end 147 | -------------------------------------------------------------------------------- /Pod/Classes/ArtsyAuthentication.m: -------------------------------------------------------------------------------- 1 | #import "ArtsyAuthentication.h" 2 | #import "ArtsyAuthentication+Private.h" 3 | #import "ArtsyNetworkOperator.h" 4 | #import "ArtsyToken.h" 5 | #import "ArtsyAuthenticationRouter.h" 6 | #import 7 | 8 | NSString* const ArtsyOAuthTokenKey = @"access_token"; 9 | NSString* const ArtsyOAuthExpiryKey = @"expires_in"; 10 | NSString* const ArtsyXAppTokenKey = @"xapp_token"; 11 | 12 | NSString* const ArtsyAuthenticationErrorDomain = @"ArtsyAuthenticationErrorDomain"; 13 | 14 | @interface ArtsyAuthentication() 15 | 16 | @property (nonatomic, strong) ArtsyNetworkOperator *networkOperator; 17 | 18 | @end 19 | 20 | @implementation ArtsyAuthentication 21 | 22 | - (instancetype)initWithClientID:(NSString *)clientID 23 | clientSecret:(NSString *)clientSecret; 24 | { 25 | self = [super init]; 26 | if (!self) return nil; 27 | 28 | _router = [[ArtsyAuthenticationRouter alloc] initWithClientID:clientID clientSecret:clientSecret]; 29 | _networkOperator = [[ArtsyNetworkOperator alloc] init]; 30 | 31 | return self; 32 | } 33 | 34 | #pragma mark - Public API 35 | 36 | - (void)getWeekLongXAppTrialToken:(ArtsyAuthenticationCallback)completion; 37 | { 38 | __weak __typeof(self) weakSelf = self; 39 | 40 | NSURLRequest *request = [self.router requestForXapp]; 41 | [self.networkOperator JSONTaskWithRequest:request success:^(NSURLRequest *_, NSHTTPURLResponse *__, id JSON) { 42 | __strong __typeof(weakSelf) strongSelf = weakSelf; 43 | 44 | NSDate *date = [[[ISO8601DateFormatter alloc] init] dateFromString:JSON[@"expires_in"]]; 45 | ArtsyToken *token = [[ArtsyToken alloc] initWithToken:JSON[ArtsyXAppTokenKey] expirationDate:date]; 46 | 47 | strongSelf.router.xappToken = token; 48 | 49 | completion(token, nil); 50 | } failure:^(NSURLRequest *_, NSHTTPURLResponse *response, NSError *error, id JSON) { 51 | __strong __typeof(weakSelf) strongSelf = weakSelf; 52 | 53 | [strongSelf findErrorsInResponse:response error:&error dict:JSON]; 54 | completion(JSON, error); 55 | }]; 56 | } 57 | 58 | - (void)logInWithEmail:(NSString *)email 59 | password:(NSString *)password 60 | completion:(ArtsyAuthenticationCallback)completion; 61 | { 62 | __weak __typeof(self) weakSelf = self; 63 | 64 | NSURLRequest *request = [self.router requestForAuthWithEmail:email password:password]; 65 | [self.networkOperator JSONTaskWithRequest:request success:^(NSURLRequest *_, NSHTTPURLResponse *__, id JSON) { 66 | __strong __typeof(weakSelf) strongSelf = weakSelf; 67 | 68 | NSDate *date = [[[ISO8601DateFormatter alloc] init] dateFromString:JSON[@"expires_in"]]; 69 | ArtsyToken *token = [[ArtsyToken alloc] initWithToken:JSON[ArtsyOAuthTokenKey] expirationDate:date]; 70 | 71 | [strongSelf callback:token error:nil completion:completion]; 72 | } failure:^(NSURLRequest *_, NSHTTPURLResponse *response, NSError *error, id JSON) { 73 | __strong __typeof(weakSelf) strongSelf = weakSelf; 74 | 75 | [strongSelf findErrorsInResponse:response error:&error dict:JSON]; 76 | [strongSelf callback:nil error:error completion:completion]; 77 | }]; 78 | } 79 | 80 | - (void)logInAndFetchUserDetailsWithEmail:(NSString *)email 81 | password:(NSString *)password 82 | completion:(ArtsyAuthenticationWithUserDetailsCallback)completion; 83 | { 84 | __weak __typeof(self) weakSelf = self; 85 | 86 | [self logInWithEmail:email password:password completion:^(ArtsyToken *token, NSError *error) { 87 | if (error) { 88 | completion(token, nil, error); 89 | } else { 90 | __strong __typeof(weakSelf) strongSelf = weakSelf; 91 | 92 | if (strongSelf) { 93 | NSURLRequest *request = [strongSelf.router requestForUserDetails]; 94 | [strongSelf.networkOperator JSONTaskWithRequest:request success:^(NSURLRequest *_, NSHTTPURLResponse *__, id JSON) { 95 | completion(token, JSON, nil); 96 | } failure:^(NSURLRequest *_, NSHTTPURLResponse *response, NSError *error, id JSON) { 97 | [weakSelf findErrorsInResponse:response error:&error dict:JSON]; 98 | completion(token, JSON, error); 99 | }]; 100 | } 101 | } 102 | }]; 103 | } 104 | 105 | - (void)createUserWithEmail:(NSString *)email 106 | name:(NSString *)name 107 | password:(NSString *)password 108 | completion:(void (^)(NSDictionary *newUserDictionary, NSError *error))completion; 109 | { 110 | __weak __typeof(self) weakSelf = self; 111 | 112 | NSURLRequest *request = [self.router requestForCreateNewUserwithEmail:email name:name password:password]; 113 | [self.networkOperator JSONTaskWithRequest:request success:^(NSURLRequest *_, NSHTTPURLResponse *__, id JSON) { 114 | completion(JSON, nil); 115 | } failure:^(NSURLRequest *_, NSHTTPURLResponse *response, NSError *error, id JSON) { 116 | [weakSelf findErrorsInResponse:response error:&error dict:JSON]; 117 | completion(JSON, error); 118 | }]; 119 | } 120 | 121 | #pragma mark - Request Management 122 | 123 | - (void)findErrorsInResponse:(NSHTTPURLResponse *)response 124 | error:(NSError **)error 125 | dict:(NSDictionary *)dict; 126 | { 127 | if (*error) return; 128 | if (response.statusCode != 500) { 129 | *error = [NSError errorWithDomain:@"net.artsy" code:response.statusCode userInfo:dict]; 130 | } 131 | } 132 | 133 | - (void)logout; 134 | { 135 | self.router.authToken = nil; 136 | } 137 | 138 | @end 139 | -------------------------------------------------------------------------------- /Example/Tests/ArtsyAuthenticationRouterTests.m: -------------------------------------------------------------------------------- 1 | @import Quick; 2 | @import Nimble; 3 | @import Artsy_Authentication; 4 | @import NSURL_QueryDictionary; 5 | 6 | @interface ArtsyAuthenticationRouter(Tests) 7 | 8 | - (NSURL *)urlWithPath:(NSString *)path; 9 | - (NSURLRequest *)baseRequestForAddress:(NSURL *)url; 10 | - (NSURLRequest *)baseRequestForAddress:(NSURL *)url method:(NSString *)httpMethod; 11 | 12 | @end 13 | 14 | QuickSpecBegin(ArtsyAuthenticationRouterSpec) 15 | 16 | describe(@"a router", ^{ 17 | NSString *clientID = @"Art5y"; 18 | NSString *clientSecret = @"IActuallyLikeWineCoolers"; 19 | NSString *xappToken = @"is_it_exapp_or_zapp"; 20 | NSDate *tokenExpirationDate = [NSDate distantFuture]; 21 | 22 | __block ArtsyAuthenticationRouter *sut = nil; 23 | 24 | beforeEach(^{ 25 | sut = [[ArtsyAuthenticationRouter alloc] initWithClientID:clientID clientSecret:clientSecret]; 26 | sut.xappToken = [[ArtsyToken alloc] initWithToken:xappToken expirationDate:tokenExpirationDate]; 27 | }); 28 | 29 | it(@"initializes correctly", ^{ 30 | expect(sut.clientID).to( equal(clientID) ); 31 | expect(sut.clientSecret).to( equal(clientSecret) ); 32 | expect(sut.xappToken.token).to( equal(xappToken) ); 33 | expect(sut.xappToken.expirationDate).to( equal(tokenExpirationDate) ); 34 | }); 35 | 36 | it(@"gives production URLs by default", ^{ 37 | expect([sut urlWithPath:@"/api/hi"].absoluteString).to( equal(@"https://api.artsy.net/api/hi") ); 38 | }); 39 | 40 | it(@"gives staging URLs when staging is YES", ^{ 41 | sut.staging = YES; 42 | expect([sut urlWithPath:@"/api/hi"].absoluteString).to( equal(@"https://stagingapi.artsy.net/api/hi") ); 43 | }); 44 | 45 | it(@"gives GET requests by default", ^{ 46 | NSURL *testURL = [NSURL URLWithString:@"http://artsy.net"]; 47 | expect([sut baseRequestForAddress:testURL].HTTPMethod).to( equal(@"GET") ); 48 | }); 49 | 50 | it(@"gives other requests by default", ^{ 51 | NSURL *testURL = [NSURL URLWithString:@"http://artsy.net"]; 52 | expect([sut baseRequestForAddress:testURL method:@"POST"].HTTPMethod).to( equal(@"POST") ); 53 | }); 54 | 55 | it(@"adds XApp token if it is present", ^{ 56 | NSURL *testURL = [NSURL URLWithString:@"http://artsy.net"]; 57 | NSURLRequest *generatedRequest = [sut baseRequestForAddress:testURL]; 58 | 59 | expect(generatedRequest.allHTTPHeaderFields[@"X-Xapp-Token"]).to( equal(xappToken) ); 60 | }); 61 | 62 | it(@"XApp requests contain clientID/clientSecret", ^{ 63 | NSURL *xappRequestURL = [sut requestForXapp].URL; 64 | NSDictionary *parameters = xappRequestURL.uq_queryDictionary; 65 | 66 | expect(parameters[@"client_id"]).to( equal(clientID) ); 67 | expect(parameters[@"client_secret"]).to( equal(clientSecret) ); 68 | }); 69 | 70 | it(@"generates Twitter OAuth requests", ^{ 71 | NSString *twitterToken = @"costolo"; 72 | NSString *twitterSecret = @"i_miss_ev"; 73 | 74 | NSURLRequest *twitterRequest = [sut newTwitterOAuthRequestWithToken:twitterToken secret:twitterSecret]; 75 | NSDictionary *parameters = twitterRequest.URL.uq_queryDictionary; 76 | 77 | expect(twitterRequest.URL.path).to( equal(@"/oauth2/access_token") ); 78 | expect(parameters[@"oauth_token"]).to( equal(twitterToken) ); 79 | expect(parameters[@"oauth_token_secret"]).to( equal(twitterSecret) ); 80 | expect(parameters[@"oauth_provider"]).to( equal(@"twitter") ); 81 | expect(parameters[@"grant_type"]).to( equal(@"oauth_token") ); 82 | expect(parameters[@"scope"]).to( equal(@"offline_access") ); 83 | 84 | }); 85 | 86 | it(@"generates Facebook OAuth requests", ^{ 87 | NSString *facebookToken = @"token"; 88 | 89 | NSURLRequest *facebookRequest = [sut newFacebookOAuthRequestWithToken:facebookToken]; 90 | NSDictionary *parameters = facebookRequest.URL.uq_queryDictionary; 91 | 92 | expect(facebookRequest.URL.path).to( equal(@"/oauth2/access_token") ); 93 | expect(parameters[@"oauth_token"]).to( equal(facebookToken) ); 94 | expect(parameters[@"oauth_provider"]).to( equal(@"facebook") ); 95 | expect(parameters[@"scope"]).to( equal(@"offline_access") ); 96 | }); 97 | 98 | it(@"creates users with Facebook tokens", ^{ 99 | NSString *name = @"Mark Zuckerberg"; 100 | NSString *email = @"zuck@facebook.com"; 101 | NSString *facebookToken = @"god_mode"; 102 | 103 | NSURLRequest *facebookNewUserRequest = [sut newCreateUserViaFacebookRequestWithToken:facebookToken email:email name:name]; 104 | NSDictionary *parameters = facebookNewUserRequest.URL.uq_queryDictionary; 105 | 106 | expect(facebookNewUserRequest.URL.path).to( equal(@"/api/v1/user") ); 107 | expect(parameters[@"email"]).to( equal(email) ); 108 | expect(parameters[@"name"]).to( equal(name) ); 109 | expect(parameters[@"oauth_token"]).to( equal(facebookToken) ); 110 | expect(parameters[@"provider"]).to( equal(@"facebook") ); 111 | expect(parameters[@"client_id"]).to( beNil() ); 112 | expect(parameters[@"client_secret"]).to( beNil() ); 113 | }); 114 | 115 | it(@"logs in with email", ^{ 116 | NSString *email = @"2cool4school@thedumptersbehindtheschool.com"; 117 | NSString *password = @"glitter"; 118 | NSURLRequest *loginRequest = [sut requestForAuthWithEmail:email password:password]; 119 | NSDictionary *parameters = loginRequest.URL.uq_queryDictionary; 120 | 121 | expect(loginRequest.URL.path).to( equal(@"/oauth2/access_token") ); 122 | expect(parameters[@"email"]).to( equal(email) ); 123 | expect(parameters[@"password"]).to( equal(password) ); 124 | expect(parameters[@"grant_type"]).to( equal(@"credentials") ); 125 | expect(parameters[@"scope"]).to( equal(@"offline_access") ); 126 | }); 127 | }); 128 | 129 | QuickSpecEnd 130 | -------------------------------------------------------------------------------- /Pod/Classes/ArtsyAuthentication+Twitter.m: -------------------------------------------------------------------------------- 1 | #import "ArtsyAuthentication+Twitter.h" 2 | #import "ArtsyAuthentication+Private.h" 3 | #import "ArtsyAuthentication+Accounts.h" 4 | #import "ArtsyAuthenticationRouter.h" 5 | #import "ArtsyNetworkOperator.h" 6 | #import "ArtsyToken.h" 7 | #import 8 | #import 9 | #import 10 | 11 | typedef void (^_ArtsyTwitterAuthenticationCallback)(NSString *oauthToken, NSString *oauthSecret, NSString *twitterUsername, NSError *error); 12 | 13 | const void* ArtsyTwitterReverseAuthKey = &ArtsyTwitterReverseAuthKey; 14 | const void* ArtsyTwitterAPIKeyKey = &ArtsyTwitterAPIKeyKey; 15 | const void* ArtsyTwitterAPISecretKey = &ArtsyTwitterAPISecretKey; 16 | 17 | @interface ArtsyAuthentication(Twitter_Private) 18 | 19 | @property (nonatomic, readonly) LVTwitterOAuthClient *reverseAuth; 20 | 21 | @end 22 | 23 | @implementation ArtsyAuthentication(Twitter_Private) 24 | 25 | #pragma mark - Private Properties 26 | 27 | - (LVTwitterOAuthClient *)reverseAuth { 28 | LVTwitterOAuthClient *reverseAuth = objc_getAssociatedObject(self, ArtsyTwitterReverseAuthKey); 29 | 30 | if (!reverseAuth) { 31 | reverseAuth = [[LVTwitterOAuthClient alloc] initWithConsumerKey:self.twitterAPIKey andConsumerSecret:self.twitterAPISecret]; 32 | 33 | objc_setAssociatedObject(self, ArtsyTwitterReverseAuthKey, reverseAuth, OBJC_ASSOCIATION_RETAIN); 34 | } 35 | 36 | return reverseAuth; 37 | } 38 | 39 | #pragma mark - Private 40 | 41 | - (void)checkForKeys { 42 | NSAssert(self.twitterAPIKey != nil, @"No Twitter API Key."); 43 | NSAssert(self.twitterAPISecret != nil, @"No Twitter API secret."); 44 | } 45 | 46 | - (void)createUserWithTwitterAccount:(ACAccount *)accout email:(NSString *)email name:(NSString *)name credentials:(NSDictionary *)credentials completion:(ArtsyAuthenticationCallback)callback { 47 | __weak __typeof(self) weakSelf = self; 48 | 49 | NSString *oauthToken = credentials[@"oauth_token"]; 50 | NSString *oauthSecret = credentials[@"oauth_token_secret"]; 51 | 52 | NSURLRequest *request = [self.router newCreateUserViaTwitterRequestWithToken:oauthToken secret:oauthSecret email:email name:name]; 53 | 54 | [self.networkOperator JSONTaskWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) { 55 | __strong __typeof(weakSelf) strongSelf = weakSelf; 56 | 57 | [strongSelf logInWithTwitterAccount:accout completion:callback]; 58 | } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) { 59 | __strong __typeof(weakSelf) strongSelf = weakSelf; 60 | [strongSelf callback:nil error:error completion:callback]; 61 | }]; 62 | } 63 | 64 | - (void)logInWithTwitterAccount:(ACAccount *)accout credentials:(NSDictionary *)credentials completion:(ArtsyAuthenticationCallback)callback { 65 | NSString *oauthToken = credentials[@"oauth_token"]; 66 | NSString *oauthSecret = credentials[@"oauth_token_secret"]; 67 | 68 | NSURLRequest *request = [self.router newTwitterOAuthRequestWithToken:oauthToken secret:oauthSecret]; 69 | 70 | [self.networkOperator JSONTaskWithRequest:request success:[self successfulLoginBlock:callback] failure:[self failedLoginBlock:callback]]; 71 | } 72 | 73 | @end 74 | 75 | @implementation ArtsyAuthentication(Twitter) 76 | 77 | #pragma mark - Public API 78 | 79 | - (void)retrieveTwitterAccounts:(ArtsyTwitterAccountsCallback)callback { 80 | [self checkForKeys]; 81 | ACAccountType *accountType = [self.accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter]; 82 | 83 | [self.accountStore requestAccessToAccountsWithType:accountType options:nil completion:^(BOOL granted, NSError *error) { 84 | if (granted) { 85 | NSArray *accounts = [self.accountStore accountsWithAccountType:accountType]; 86 | if (callback) { 87 | callback(accounts, error); 88 | } 89 | } else { 90 | if (callback) { 91 | callback(nil, error); 92 | } 93 | } 94 | }]; 95 | } 96 | 97 | - (void)logInWithTwitterAccount:(ACAccount *)account completion:(ArtsyAuthenticationCallback)callback { 98 | __weak __typeof(self) weakSelf = self; 99 | [self checkForKeys]; 100 | [self.reverseAuth requestTokensForAccount:account completionBlock:^(NSDictionary *oAuthResponse, NSError *error) { 101 | __strong __typeof(weakSelf) strongSelf = weakSelf; 102 | 103 | if (oAuthResponse[kLVOAuthAccessTokenKey] && !error) { 104 | [strongSelf logInWithTwitterAccount:account credentials:oAuthResponse completion:callback]; 105 | } else { 106 | [strongSelf callback:nil error:error completion:callback]; 107 | } 108 | }]; 109 | } 110 | 111 | - (void)createNewUserWithTwitter:(ACAccount *)account email:(NSString *)email name:(NSString *)name completion:(ArtsyAuthenticationCallback)callback { 112 | __weak __typeof(self) weakSelf = self; 113 | [self checkForKeys]; 114 | [self.reverseAuth requestTokensForAccount:account completionBlock:^(NSDictionary *oAuthResponse, NSError *error) { 115 | __strong __typeof(weakSelf) strongSelf = weakSelf; 116 | 117 | if (oAuthResponse[kLVOAuthAccessTokenKey] && !error) { 118 | [strongSelf createUserWithTwitterAccount:account email:email name:name credentials:oAuthResponse completion:callback]; 119 | } else { 120 | [strongSelf callback:nil error:error completion:callback]; 121 | } 122 | }]; 123 | } 124 | 125 | #pragma mark - Properties 126 | 127 | - (NSString *)twitterAPIKey { 128 | return objc_getAssociatedObject(self, ArtsyTwitterAPIKeyKey); 129 | } 130 | 131 | - (void)setTwitterAPIKey:(NSString *)twitterAPIKey { 132 | objc_setAssociatedObject(self, ArtsyTwitterAPIKeyKey, twitterAPIKey, OBJC_ASSOCIATION_COPY); 133 | } 134 | 135 | - (NSString *)twitterAPISecret { 136 | return objc_getAssociatedObject(self, ArtsyTwitterAPISecretKey); 137 | } 138 | 139 | - (void)setTwitterAPISecret:(NSString *)twitterAPISecret { 140 | objc_setAssociatedObject(self, ArtsyTwitterAPISecretKey, twitterAPISecret, OBJC_ASSOCIATION_COPY); 141 | } 142 | 143 | @end 144 | -------------------------------------------------------------------------------- /Pod/Classes/ArtsyAuthentication+Facebook.m: -------------------------------------------------------------------------------- 1 | #import "ArtsyAuthentication+Facebook.h" 2 | #import "ArtsyAuthentication+Private.h" 3 | #import "ArtsyAuthentication+Accounts.h" 4 | #import "ArtsyAuthenticationRouter.h" 5 | #import "ArtsyNetworkOperator.h" 6 | #import "ArtsyToken.h" 7 | #import 8 | #import 9 | #import 10 | 11 | typedef void (^_ArtsyFacebookAuthenticationCallback)(NSString *facebookToken, NSString *email, NSString *name, NSError *error); 12 | 13 | NSString *facebookAppID() { 14 | return [[NSBundle mainBundle] objectForInfoDictionaryKey:@"FacebookAppID"]; 15 | } 16 | 17 | @interface ArtsyAuthentication (Facebook_Private) 18 | @end 19 | 20 | @implementation ArtsyAuthentication (Facebook_Private) 21 | 22 | - (SLRequest *)requestForMe:(ACAccount *)facebookAccount { 23 | NSURL *url = [NSURL URLWithString:@"https://graph.facebook.com/me"]; 24 | SLRequest *request = [SLRequest requestForServiceType:SLServiceTypeFacebook requestMethod:SLRequestMethodGET URL:url parameters:nil]; 25 | request.account = facebookAccount; 26 | 27 | return request; 28 | } 29 | 30 | - (void)retrieveFacebookAccountInformation:(ACAccount *)facebookAccount completion:(_ArtsyFacebookAuthenticationCallback)callback { 31 | SLRequest *request = [self requestForMe:facebookAccount]; 32 | 33 | [request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) { 34 | if (responseData == nil || error || urlResponse.statusCode != 200) { 35 | if (callback) { 36 | callback(nil, nil, nil, error); 37 | } 38 | } else { 39 | NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:nil]; 40 | 41 | NSString *facebookToken = facebookAccount.credential.oauthToken; 42 | NSString *email = responseDictionary[@"email"]; 43 | NSString *name = responseDictionary[@"name"]; 44 | 45 | if (callback) { 46 | callback(facebookToken, email, name, nil); 47 | } 48 | } 49 | }]; 50 | } 51 | 52 | - (void)loginToArtsyWithFacebookToken:(NSString *)facebookToken completion:(ArtsyAuthenticationCallback)callback { 53 | NSURLRequest *request = [self.router newFacebookOAuthRequestWithToken:facebookToken]; 54 | 55 | [self.networkOperator JSONTaskWithRequest:request success:[self successfulLoginBlock:callback] failure:[self failedLoginBlock:callback]]; 56 | } 57 | 58 | - (void)createArtsyUserWithFacebookToken:(NSString *)facebookToken email:(NSString *)email name:(NSString *)name completion:(ArtsyAuthenticationCallback)callback { 59 | __weak __typeof(self) weakSelf = self; 60 | 61 | NSURLRequest *request = [self.router newCreateUserViaFacebookRequestWithToken:facebookToken email:email name:name]; 62 | 63 | [self.networkOperator JSONTaskWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) { 64 | __strong __typeof(weakSelf) strongSelf = weakSelf; 65 | 66 | [strongSelf loginToArtsyWithFacebookToken:facebookToken completion:callback]; 67 | } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) { 68 | __strong __typeof(weakSelf) strongSelf = weakSelf; 69 | 70 | [strongSelf callback:nil error:error completion:callback]; 71 | }]; 72 | } 73 | 74 | - (void)accessFacebookAccount:(NSString *)appID completion:(_ArtsyFacebookAuthenticationCallback)callback { 75 | __weak __typeof(self) weakSelf = self; 76 | 77 | NSArray *permissions = @[@"email"]; 78 | NSDictionary *facebookOptions = @{ACFacebookAppIdKey : appID, 79 | ACFacebookPermissionsKey : permissions}; 80 | 81 | 82 | ACAccountType *accountType = [self.accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook]; 83 | 84 | [self.accountStore requestAccessToAccountsWithType:accountType options:facebookOptions completion:^(BOOL granted, NSError *error) { 85 | __strong __typeof(weakSelf) strongSelf = weakSelf; 86 | 87 | if (granted) { 88 | NSArray *accounts = [self.accountStore accountsWithAccountType:accountType]; 89 | ACAccount *facebookAccount = accounts.lastObject; 90 | 91 | [strongSelf retrieveFacebookAccountInformation:facebookAccount completion:callback]; 92 | } else { 93 | if (callback) { 94 | callback(nil, nil, nil, error); 95 | } 96 | } 97 | }]; 98 | } 99 | 100 | @end 101 | 102 | @implementation ArtsyAuthentication (Facebook) 103 | 104 | - (void)logInWithFacebook:(ArtsyAuthenticationCallback)callback { 105 | NSString *appID = facebookAppID(); 106 | 107 | if (appID) { 108 | [self logInWithFacebook:appID completion:callback]; 109 | } else { 110 | NSError *error = [NSError errorWithDomain:ArtsyAuthenticationErrorDomain code:ArtsyErrorNoFacebookAppID userInfo:nil]; 111 | 112 | [self callback:nil error:error completion:callback]; 113 | } 114 | } 115 | 116 | - (void)logInWithFacebook:(NSString *)appID completion:(ArtsyAuthenticationCallback)callback { 117 | __weak __typeof(self) weakSelf = self; 118 | 119 | [self accessFacebookAccount:appID completion:^(NSString *facebookToken, NSString *email, NSString *name, NSError *error) { 120 | __strong __typeof(weakSelf) strongSelf = weakSelf; 121 | 122 | if (facebookToken && name && email && !error) { 123 | [strongSelf loginToArtsyWithFacebookToken:facebookToken completion:callback]; 124 | } else { 125 | callback(nil, error); 126 | } 127 | }]; 128 | } 129 | 130 | - (void)createUserWithFacebook:(ArtsyAuthenticationCallback)callback { 131 | NSString *appID = facebookAppID(); 132 | 133 | if (appID) { 134 | [self createUserWithFacebook:appID completion:callback]; 135 | } else { 136 | NSError *error = [NSError errorWithDomain:ArtsyAuthenticationErrorDomain code:ArtsyErrorNoFacebookAppID userInfo:nil]; 137 | 138 | [self callback:nil error:error completion:callback]; 139 | } 140 | } 141 | 142 | - (void)createUserWithFacebook:(NSString *)appID completion:(ArtsyAuthenticationCallback)callback { 143 | __weak __typeof(self) weakSelf = self; 144 | 145 | [self accessFacebookAccount:appID completion:^(NSString *facebookToken, NSString *email, NSString *name, NSError *error) { 146 | __strong __typeof(weakSelf) strongSelf = weakSelf; 147 | 148 | if (facebookToken && name && email && !error) { 149 | [strongSelf createArtsyUserWithFacebookToken:facebookToken email:email name:name completion:callback]; 150 | } else { 151 | callback(nil, error); 152 | } 153 | }]; 154 | } 155 | 156 | @end 157 | -------------------------------------------------------------------------------- /Example/Artsy_Authentication.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 3A6B21EC223160EC00BB8853 /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A6B21EB223160EC00BB8853 /* File.swift */; }; 11 | 5E445A961A6FF103009712CA /* TestingNetworkOperator.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E445A951A6FF103009712CA /* TestingNetworkOperator.m */; }; 12 | 5E445A991A6FF4DE009712CA /* ArtsyNetworkOperatorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E445A981A6FF4DE009712CA /* ArtsyNetworkOperatorTests.m */; }; 13 | 5E445A9B1A6FFC9C009712CA /* ArtsyAuthenticationTests+Facebook.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E445A9A1A6FFC9C009712CA /* ArtsyAuthenticationTests+Facebook.m */; }; 14 | 5E445A9D1A7048D0009712CA /* ArtsyAuthenticationTests+Twitter.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E445A9C1A7048D0009712CA /* ArtsyAuthenticationTests+Twitter.m */; }; 15 | 5E82A6581A6FE765002CB6B3 /* ArtsyTokenTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E82A6571A6FE765002CB6B3 /* ArtsyTokenTests.m */; }; 16 | 5E82A65A1A6FEAEC002CB6B3 /* ArtsyAuthenticationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E82A6591A6FEAEC002CB6B3 /* ArtsyAuthenticationTests.m */; }; 17 | 5EF701D81A71446F004C03E9 /* TestingClasses.m in Sources */ = {isa = PBXBuildFile; fileRef = 5EF701D71A71446F004C03E9 /* TestingClasses.m */; }; 18 | 6003F58E195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; 19 | 6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58F195388D20070C39A /* CoreGraphics.framework */; }; 20 | 6003F592195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; 21 | 6003F598195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F596195388D20070C39A /* InfoPlist.strings */; }; 22 | 6003F59A195388D20070C39A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F599195388D20070C39A /* main.m */; }; 23 | 6003F59E195388D20070C39A /* ARAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F59D195388D20070C39A /* ARAppDelegate.m */; }; 24 | 6003F5A1195388D20070C39A /* Main_iPhone.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6003F59F195388D20070C39A /* Main_iPhone.storyboard */; }; 25 | 6003F5A4195388D20070C39A /* Main_iPad.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5A2195388D20070C39A /* Main_iPad.storyboard */; }; 26 | 6003F5A7195388D20070C39A /* ARViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F5A6195388D20070C39A /* ARViewController.m */; }; 27 | 6003F5A9195388D20070C39A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5A8195388D20070C39A /* Images.xcassets */; }; 28 | 6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F5AF195388D20070C39A /* XCTest.framework */; }; 29 | 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; 30 | 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; 31 | 6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5B8195388D20070C39A /* InfoPlist.strings */; }; 32 | 6003F5BC195388D20070C39A /* ArtsyAuthenticationRouterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F5BB195388D20070C39A /* ArtsyAuthenticationRouterTests.m */; }; 33 | 9BB54F99A72CCF3CC0DF43AC /* Pods_ArtsyAuthenticationExample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D56B0245D0F6C8BBD9770879 /* Pods_ArtsyAuthenticationExample.framework */; }; 34 | CA06F40D0E846D369B2F9164 /* Pods_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CDF98ECC59EBC478D28E657 /* Pods_Tests.framework */; }; 35 | /* End PBXBuildFile section */ 36 | 37 | /* Begin PBXContainerItemProxy section */ 38 | 6003F5B3195388D20070C39A /* PBXContainerItemProxy */ = { 39 | isa = PBXContainerItemProxy; 40 | containerPortal = 6003F582195388D10070C39A /* Project object */; 41 | proxyType = 1; 42 | remoteGlobalIDString = 6003F589195388D20070C39A; 43 | remoteInfo = Artsy_Authentication; 44 | }; 45 | /* End PBXContainerItemProxy section */ 46 | 47 | /* Begin PBXFileReference section */ 48 | 0EE7ED2894CD6CCBB70FB624 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; 49 | 2FD6D890C0BA1DF8CF697295 /* Artsy+Authentication.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = "Artsy+Authentication.podspec"; path = "../Artsy+Authentication.podspec"; sourceTree = ""; }; 50 | 3A6B21E8223160BB00BB8853 /* ArtsyAuthenticationExample-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ArtsyAuthenticationExample-Bridging-Header.h"; sourceTree = ""; }; 51 | 3A6B21EB223160EC00BB8853 /* File.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = File.swift; sourceTree = ""; }; 52 | 4CDF98ECC59EBC478D28E657 /* Pods_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 53 | 50808596B20FE4317B82CE4F /* Pods.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 54 | 5E445A941A6FF103009712CA /* TestingNetworkOperator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestingNetworkOperator.h; sourceTree = ""; }; 55 | 5E445A951A6FF103009712CA /* TestingNetworkOperator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestingNetworkOperator.m; sourceTree = ""; }; 56 | 5E445A981A6FF4DE009712CA /* ArtsyNetworkOperatorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ArtsyNetworkOperatorTests.m; sourceTree = ""; }; 57 | 5E445A9A1A6FFC9C009712CA /* ArtsyAuthenticationTests+Facebook.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "ArtsyAuthenticationTests+Facebook.m"; sourceTree = ""; }; 58 | 5E445A9C1A7048D0009712CA /* ArtsyAuthenticationTests+Twitter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "ArtsyAuthenticationTests+Twitter.m"; sourceTree = ""; }; 59 | 5E82A6571A6FE765002CB6B3 /* ArtsyTokenTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ArtsyTokenTests.m; sourceTree = ""; }; 60 | 5E82A6591A6FEAEC002CB6B3 /* ArtsyAuthenticationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ArtsyAuthenticationTests.m; sourceTree = ""; }; 61 | 5EF701D61A71446F004C03E9 /* TestingClasses.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestingClasses.h; sourceTree = ""; }; 62 | 5EF701D71A71446F004C03E9 /* TestingClasses.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestingClasses.m; sourceTree = ""; }; 63 | 5FAFA31DF0D63E2B5A77930D /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; 64 | 6003F58A195388D20070C39A /* ArtsyAuthenticationExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ArtsyAuthenticationExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 65 | 6003F58D195388D20070C39A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 66 | 6003F58F195388D20070C39A /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 67 | 6003F591195388D20070C39A /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 68 | 6003F595195388D20070C39A /* Artsy_Authentication-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Artsy_Authentication-Info.plist"; sourceTree = ""; }; 69 | 6003F597195388D20070C39A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 70 | 6003F599195388D20070C39A /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 71 | 6003F59B195388D20070C39A /* Artsy_Authentication-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Artsy_Authentication-Prefix.pch"; sourceTree = ""; }; 72 | 6003F59C195388D20070C39A /* ARAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ARAppDelegate.h; sourceTree = ""; }; 73 | 6003F59D195388D20070C39A /* ARAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ARAppDelegate.m; sourceTree = ""; }; 74 | 6003F5A0195388D20070C39A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main_iPhone.storyboard; sourceTree = ""; }; 75 | 6003F5A3195388D20070C39A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main_iPad.storyboard; sourceTree = ""; }; 76 | 6003F5A5195388D20070C39A /* ARViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ARViewController.h; sourceTree = ""; }; 77 | 6003F5A6195388D20070C39A /* ARViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ARViewController.m; sourceTree = ""; }; 78 | 6003F5A8195388D20070C39A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 79 | 6003F5AE195388D20070C39A /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 80 | 6003F5AF195388D20070C39A /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 81 | 6003F5B7195388D20070C39A /* Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Tests-Info.plist"; sourceTree = ""; }; 82 | 6003F5B9195388D20070C39A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 83 | 6003F5BB195388D20070C39A /* ArtsyAuthenticationRouterTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ArtsyAuthenticationRouterTests.m; sourceTree = ""; }; 84 | 606FC2411953D9B200FFA9A0 /* Tests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Tests-Prefix.pch"; sourceTree = ""; }; 85 | 6096C0B01A501FF4002B14DC /* Artsy_Authentication.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Artsy_Authentication.framework; path = "Pods/build/Debug-iphoneos/Pods-Tests/Artsy_Authentication.framework"; sourceTree = ""; }; 86 | 652EFE49892D7B2CC9C95AFC /* Pods-ArtsyAuthenticationExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ArtsyAuthenticationExample.release.xcconfig"; path = "Target Support Files/Pods-ArtsyAuthenticationExample/Pods-ArtsyAuthenticationExample.release.xcconfig"; sourceTree = ""; }; 87 | 9E066C36CDB57FA65C5DB65F /* Pods-ArtsyAuthenticationExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ArtsyAuthenticationExample.debug.xcconfig"; path = "Target Support Files/Pods-ArtsyAuthenticationExample/Pods-ArtsyAuthenticationExample.debug.xcconfig"; sourceTree = ""; }; 88 | A243866347F2A745B3FC133C /* Pods-Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tests.debug.xcconfig"; path = "Target Support Files/Pods-Tests/Pods-Tests.debug.xcconfig"; sourceTree = ""; }; 89 | C9ADAE57EED54AB99A8998C1 /* Pods-Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tests.release.xcconfig"; path = "Target Support Files/Pods-Tests/Pods-Tests.release.xcconfig"; sourceTree = ""; }; 90 | D56B0245D0F6C8BBD9770879 /* Pods_ArtsyAuthenticationExample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ArtsyAuthenticationExample.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 91 | /* End PBXFileReference section */ 92 | 93 | /* Begin PBXFrameworksBuildPhase section */ 94 | 6003F587195388D20070C39A /* Frameworks */ = { 95 | isa = PBXFrameworksBuildPhase; 96 | buildActionMask = 2147483647; 97 | files = ( 98 | 6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */, 99 | 6003F592195388D20070C39A /* UIKit.framework in Frameworks */, 100 | 6003F58E195388D20070C39A /* Foundation.framework in Frameworks */, 101 | 9BB54F99A72CCF3CC0DF43AC /* Pods_ArtsyAuthenticationExample.framework in Frameworks */, 102 | ); 103 | runOnlyForDeploymentPostprocessing = 0; 104 | }; 105 | 6003F5AB195388D20070C39A /* Frameworks */ = { 106 | isa = PBXFrameworksBuildPhase; 107 | buildActionMask = 2147483647; 108 | files = ( 109 | 6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */, 110 | 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */, 111 | 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */, 112 | CA06F40D0E846D369B2F9164 /* Pods_Tests.framework in Frameworks */, 113 | ); 114 | runOnlyForDeploymentPostprocessing = 0; 115 | }; 116 | /* End PBXFrameworksBuildPhase section */ 117 | 118 | /* Begin PBXGroup section */ 119 | 5E445A971A6FF112009712CA /* Utilities */ = { 120 | isa = PBXGroup; 121 | children = ( 122 | 5E445A941A6FF103009712CA /* TestingNetworkOperator.h */, 123 | 5E445A951A6FF103009712CA /* TestingNetworkOperator.m */, 124 | 5EF701D61A71446F004C03E9 /* TestingClasses.h */, 125 | 5EF701D71A71446F004C03E9 /* TestingClasses.m */, 126 | ); 127 | name = Utilities; 128 | sourceTree = ""; 129 | }; 130 | 6003F581195388D10070C39A = { 131 | isa = PBXGroup; 132 | children = ( 133 | 6003F593195388D20070C39A /* Artsy_Authentication */, 134 | 60FF7A9C1954A5C5007DD14C /* Podspec Metadata */, 135 | 6003F5B5195388D20070C39A /* Tests */, 136 | 6003F58C195388D20070C39A /* Frameworks */, 137 | 6003F58B195388D20070C39A /* Products */, 138 | 3A6B21E8223160BB00BB8853 /* ArtsyAuthenticationExample-Bridging-Header.h */, 139 | D75F184279C19271F1E8E830 /* Pods */, 140 | ); 141 | sourceTree = ""; 142 | }; 143 | 6003F58B195388D20070C39A /* Products */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | 6003F58A195388D20070C39A /* ArtsyAuthenticationExample.app */, 147 | 6003F5AE195388D20070C39A /* Tests.xctest */, 148 | ); 149 | name = Products; 150 | sourceTree = ""; 151 | }; 152 | 6003F58C195388D20070C39A /* Frameworks */ = { 153 | isa = PBXGroup; 154 | children = ( 155 | 6096C0B01A501FF4002B14DC /* Artsy_Authentication.framework */, 156 | 6003F58D195388D20070C39A /* Foundation.framework */, 157 | 6003F58F195388D20070C39A /* CoreGraphics.framework */, 158 | 6003F591195388D20070C39A /* UIKit.framework */, 159 | 6003F5AF195388D20070C39A /* XCTest.framework */, 160 | 50808596B20FE4317B82CE4F /* Pods.framework */, 161 | D56B0245D0F6C8BBD9770879 /* Pods_ArtsyAuthenticationExample.framework */, 162 | 4CDF98ECC59EBC478D28E657 /* Pods_Tests.framework */, 163 | ); 164 | name = Frameworks; 165 | sourceTree = ""; 166 | }; 167 | 6003F593195388D20070C39A /* Artsy_Authentication */ = { 168 | isa = PBXGroup; 169 | children = ( 170 | 6003F59C195388D20070C39A /* ARAppDelegate.h */, 171 | 6003F59D195388D20070C39A /* ARAppDelegate.m */, 172 | 6003F59F195388D20070C39A /* Main_iPhone.storyboard */, 173 | 6003F5A2195388D20070C39A /* Main_iPad.storyboard */, 174 | 6003F5A5195388D20070C39A /* ARViewController.h */, 175 | 6003F5A6195388D20070C39A /* ARViewController.m */, 176 | 6003F5A8195388D20070C39A /* Images.xcassets */, 177 | 6003F594195388D20070C39A /* Supporting Files */, 178 | ); 179 | path = Artsy_Authentication; 180 | sourceTree = ""; 181 | }; 182 | 6003F594195388D20070C39A /* Supporting Files */ = { 183 | isa = PBXGroup; 184 | children = ( 185 | 6003F595195388D20070C39A /* Artsy_Authentication-Info.plist */, 186 | 6003F596195388D20070C39A /* InfoPlist.strings */, 187 | 6003F599195388D20070C39A /* main.m */, 188 | 6003F59B195388D20070C39A /* Artsy_Authentication-Prefix.pch */, 189 | ); 190 | name = "Supporting Files"; 191 | sourceTree = ""; 192 | }; 193 | 6003F5B5195388D20070C39A /* Tests */ = { 194 | isa = PBXGroup; 195 | children = ( 196 | 5E445A971A6FF112009712CA /* Utilities */, 197 | 6003F5BB195388D20070C39A /* ArtsyAuthenticationRouterTests.m */, 198 | 5E82A6571A6FE765002CB6B3 /* ArtsyTokenTests.m */, 199 | 5E82A6591A6FEAEC002CB6B3 /* ArtsyAuthenticationTests.m */, 200 | 5E445A9A1A6FFC9C009712CA /* ArtsyAuthenticationTests+Facebook.m */, 201 | 5E445A9C1A7048D0009712CA /* ArtsyAuthenticationTests+Twitter.m */, 202 | 5E445A981A6FF4DE009712CA /* ArtsyNetworkOperatorTests.m */, 203 | 6003F5B6195388D20070C39A /* Supporting Files */, 204 | 3A6B21EB223160EC00BB8853 /* File.swift */, 205 | ); 206 | path = Tests; 207 | sourceTree = ""; 208 | }; 209 | 6003F5B6195388D20070C39A /* Supporting Files */ = { 210 | isa = PBXGroup; 211 | children = ( 212 | 6003F5B7195388D20070C39A /* Tests-Info.plist */, 213 | 6003F5B8195388D20070C39A /* InfoPlist.strings */, 214 | 606FC2411953D9B200FFA9A0 /* Tests-Prefix.pch */, 215 | ); 216 | name = "Supporting Files"; 217 | sourceTree = ""; 218 | }; 219 | 60FF7A9C1954A5C5007DD14C /* Podspec Metadata */ = { 220 | isa = PBXGroup; 221 | children = ( 222 | 2FD6D890C0BA1DF8CF697295 /* Artsy+Authentication.podspec */, 223 | 5FAFA31DF0D63E2B5A77930D /* README.md */, 224 | 0EE7ED2894CD6CCBB70FB624 /* LICENSE */, 225 | ); 226 | name = "Podspec Metadata"; 227 | sourceTree = ""; 228 | }; 229 | D75F184279C19271F1E8E830 /* Pods */ = { 230 | isa = PBXGroup; 231 | children = ( 232 | 9E066C36CDB57FA65C5DB65F /* Pods-ArtsyAuthenticationExample.debug.xcconfig */, 233 | 652EFE49892D7B2CC9C95AFC /* Pods-ArtsyAuthenticationExample.release.xcconfig */, 234 | A243866347F2A745B3FC133C /* Pods-Tests.debug.xcconfig */, 235 | C9ADAE57EED54AB99A8998C1 /* Pods-Tests.release.xcconfig */, 236 | ); 237 | name = Pods; 238 | path = Pods; 239 | sourceTree = ""; 240 | }; 241 | /* End PBXGroup section */ 242 | 243 | /* Begin PBXNativeTarget section */ 244 | 6003F589195388D20070C39A /* ArtsyAuthenticationExample */ = { 245 | isa = PBXNativeTarget; 246 | buildConfigurationList = 6003F5BF195388D20070C39A /* Build configuration list for PBXNativeTarget "ArtsyAuthenticationExample" */; 247 | buildPhases = ( 248 | C636AD7B2E8F44029A04AF32 /* [CP] Check Pods Manifest.lock */, 249 | 6003F586195388D20070C39A /* Sources */, 250 | 6003F587195388D20070C39A /* Frameworks */, 251 | 6003F588195388D20070C39A /* Resources */, 252 | A509F6EC7FEAA330FCD3DCE6 /* [CP] Embed Pods Frameworks */, 253 | ); 254 | buildRules = ( 255 | ); 256 | dependencies = ( 257 | ); 258 | name = ArtsyAuthenticationExample; 259 | productName = Artsy_Authentication; 260 | productReference = 6003F58A195388D20070C39A /* ArtsyAuthenticationExample.app */; 261 | productType = "com.apple.product-type.application"; 262 | }; 263 | 6003F5AD195388D20070C39A /* Tests */ = { 264 | isa = PBXNativeTarget; 265 | buildConfigurationList = 6003F5C2195388D20070C39A /* Build configuration list for PBXNativeTarget "Tests" */; 266 | buildPhases = ( 267 | 930AD163847039970CDE1003 /* [CP] Check Pods Manifest.lock */, 268 | 6003F5AA195388D20070C39A /* Sources */, 269 | 6003F5AB195388D20070C39A /* Frameworks */, 270 | 6003F5AC195388D20070C39A /* Resources */, 271 | 69CD0F5F3050926DDC16EDB5 /* [CP] Embed Pods Frameworks */, 272 | ); 273 | buildRules = ( 274 | ); 275 | dependencies = ( 276 | 6003F5B4195388D20070C39A /* PBXTargetDependency */, 277 | ); 278 | name = Tests; 279 | productName = Artsy_AuthenticationTests; 280 | productReference = 6003F5AE195388D20070C39A /* Tests.xctest */; 281 | productType = "com.apple.product-type.bundle.unit-test"; 282 | }; 283 | /* End PBXNativeTarget section */ 284 | 285 | /* Begin PBXProject section */ 286 | 6003F582195388D10070C39A /* Project object */ = { 287 | isa = PBXProject; 288 | attributes = { 289 | CLASSPREFIX = AR; 290 | LastUpgradeCheck = 0510; 291 | ORGANIZATIONNAME = "Orta Therox"; 292 | TargetAttributes = { 293 | 6003F589195388D20070C39A = { 294 | LastSwiftMigration = 1010; 295 | }; 296 | 6003F5AD195388D20070C39A = { 297 | LastSwiftMigration = 1010; 298 | TestTargetID = 6003F589195388D20070C39A; 299 | }; 300 | }; 301 | }; 302 | buildConfigurationList = 6003F585195388D10070C39A /* Build configuration list for PBXProject "Artsy_Authentication" */; 303 | compatibilityVersion = "Xcode 3.2"; 304 | developmentRegion = English; 305 | hasScannedForEncodings = 0; 306 | knownRegions = ( 307 | en, 308 | Base, 309 | ); 310 | mainGroup = 6003F581195388D10070C39A; 311 | productRefGroup = 6003F58B195388D20070C39A /* Products */; 312 | projectDirPath = ""; 313 | projectRoot = ""; 314 | targets = ( 315 | 6003F589195388D20070C39A /* ArtsyAuthenticationExample */, 316 | 6003F5AD195388D20070C39A /* Tests */, 317 | ); 318 | }; 319 | /* End PBXProject section */ 320 | 321 | /* Begin PBXResourcesBuildPhase section */ 322 | 6003F588195388D20070C39A /* Resources */ = { 323 | isa = PBXResourcesBuildPhase; 324 | buildActionMask = 2147483647; 325 | files = ( 326 | 6003F5A4195388D20070C39A /* Main_iPad.storyboard in Resources */, 327 | 6003F5A9195388D20070C39A /* Images.xcassets in Resources */, 328 | 6003F5A1195388D20070C39A /* Main_iPhone.storyboard in Resources */, 329 | 6003F598195388D20070C39A /* InfoPlist.strings in Resources */, 330 | ); 331 | runOnlyForDeploymentPostprocessing = 0; 332 | }; 333 | 6003F5AC195388D20070C39A /* Resources */ = { 334 | isa = PBXResourcesBuildPhase; 335 | buildActionMask = 2147483647; 336 | files = ( 337 | 6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */, 338 | ); 339 | runOnlyForDeploymentPostprocessing = 0; 340 | }; 341 | /* End PBXResourcesBuildPhase section */ 342 | 343 | /* Begin PBXShellScriptBuildPhase section */ 344 | 69CD0F5F3050926DDC16EDB5 /* [CP] Embed Pods Frameworks */ = { 345 | isa = PBXShellScriptBuildPhase; 346 | buildActionMask = 2147483647; 347 | files = ( 348 | ); 349 | inputFileListPaths = ( 350 | ); 351 | inputPaths = ( 352 | "${PODS_ROOT}/Target Support Files/Pods-Tests/Pods-Tests-frameworks.sh", 353 | "${BUILT_PRODUCTS_DIR}/Nimble/Nimble.framework", 354 | "${BUILT_PRODUCTS_DIR}/OHHTTPStubs/OHHTTPStubs.framework", 355 | "${BUILT_PRODUCTS_DIR}/Quick/Quick.framework", 356 | ); 357 | name = "[CP] Embed Pods Frameworks"; 358 | outputFileListPaths = ( 359 | ); 360 | outputPaths = ( 361 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Nimble.framework", 362 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OHHTTPStubs.framework", 363 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Quick.framework", 364 | ); 365 | runOnlyForDeploymentPostprocessing = 0; 366 | shellPath = /bin/sh; 367 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Tests/Pods-Tests-frameworks.sh\"\n"; 368 | showEnvVarsInLog = 0; 369 | }; 370 | 930AD163847039970CDE1003 /* [CP] Check Pods Manifest.lock */ = { 371 | isa = PBXShellScriptBuildPhase; 372 | buildActionMask = 2147483647; 373 | files = ( 374 | ); 375 | inputFileListPaths = ( 376 | ); 377 | inputPaths = ( 378 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 379 | "${PODS_ROOT}/Manifest.lock", 380 | ); 381 | name = "[CP] Check Pods Manifest.lock"; 382 | outputFileListPaths = ( 383 | ); 384 | outputPaths = ( 385 | "$(DERIVED_FILE_DIR)/Pods-Tests-checkManifestLockResult.txt", 386 | ); 387 | runOnlyForDeploymentPostprocessing = 0; 388 | shellPath = /bin/sh; 389 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 390 | showEnvVarsInLog = 0; 391 | }; 392 | A509F6EC7FEAA330FCD3DCE6 /* [CP] Embed Pods Frameworks */ = { 393 | isa = PBXShellScriptBuildPhase; 394 | buildActionMask = 2147483647; 395 | files = ( 396 | ); 397 | inputFileListPaths = ( 398 | ); 399 | inputPaths = ( 400 | "${PODS_ROOT}/Target Support Files/Pods-ArtsyAuthenticationExample/Pods-ArtsyAuthenticationExample-frameworks.sh", 401 | "${BUILT_PRODUCTS_DIR}/Artsy+Authentication/Artsy_Authentication.framework", 402 | "${BUILT_PRODUCTS_DIR}/ISO8601DateFormatter/ISO8601DateFormatter.framework", 403 | "${BUILT_PRODUCTS_DIR}/Keys/Keys.framework", 404 | "${BUILT_PRODUCTS_DIR}/LVTwitterOAuthClient/LVTwitterOAuthClient.framework", 405 | "${BUILT_PRODUCTS_DIR}/NSData+Base64/NSData_Base64.framework", 406 | "${BUILT_PRODUCTS_DIR}/NSURL+QueryDictionary/NSURL_QueryDictionary.framework", 407 | "${BUILT_PRODUCTS_DIR}/OAuthCore/OAuthCore.framework", 408 | ); 409 | name = "[CP] Embed Pods Frameworks"; 410 | outputFileListPaths = ( 411 | ); 412 | outputPaths = ( 413 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Artsy_Authentication.framework", 414 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ISO8601DateFormatter.framework", 415 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Keys.framework", 416 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/LVTwitterOAuthClient.framework", 417 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/NSData_Base64.framework", 418 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/NSURL_QueryDictionary.framework", 419 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OAuthCore.framework", 420 | ); 421 | runOnlyForDeploymentPostprocessing = 0; 422 | shellPath = /bin/sh; 423 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ArtsyAuthenticationExample/Pods-ArtsyAuthenticationExample-frameworks.sh\"\n"; 424 | showEnvVarsInLog = 0; 425 | }; 426 | C636AD7B2E8F44029A04AF32 /* [CP] Check Pods Manifest.lock */ = { 427 | isa = PBXShellScriptBuildPhase; 428 | buildActionMask = 2147483647; 429 | files = ( 430 | ); 431 | inputFileListPaths = ( 432 | ); 433 | inputPaths = ( 434 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 435 | "${PODS_ROOT}/Manifest.lock", 436 | ); 437 | name = "[CP] Check Pods Manifest.lock"; 438 | outputFileListPaths = ( 439 | ); 440 | outputPaths = ( 441 | "$(DERIVED_FILE_DIR)/Pods-ArtsyAuthenticationExample-checkManifestLockResult.txt", 442 | ); 443 | runOnlyForDeploymentPostprocessing = 0; 444 | shellPath = /bin/sh; 445 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 446 | showEnvVarsInLog = 0; 447 | }; 448 | /* End PBXShellScriptBuildPhase section */ 449 | 450 | /* Begin PBXSourcesBuildPhase section */ 451 | 6003F586195388D20070C39A /* Sources */ = { 452 | isa = PBXSourcesBuildPhase; 453 | buildActionMask = 2147483647; 454 | files = ( 455 | 6003F59E195388D20070C39A /* ARAppDelegate.m in Sources */, 456 | 6003F5A7195388D20070C39A /* ARViewController.m in Sources */, 457 | 6003F59A195388D20070C39A /* main.m in Sources */, 458 | ); 459 | runOnlyForDeploymentPostprocessing = 0; 460 | }; 461 | 6003F5AA195388D20070C39A /* Sources */ = { 462 | isa = PBXSourcesBuildPhase; 463 | buildActionMask = 2147483647; 464 | files = ( 465 | 6003F5BC195388D20070C39A /* ArtsyAuthenticationRouterTests.m in Sources */, 466 | 5E445A9D1A7048D0009712CA /* ArtsyAuthenticationTests+Twitter.m in Sources */, 467 | 5E445A991A6FF4DE009712CA /* ArtsyNetworkOperatorTests.m in Sources */, 468 | 5E82A65A1A6FEAEC002CB6B3 /* ArtsyAuthenticationTests.m in Sources */, 469 | 5EF701D81A71446F004C03E9 /* TestingClasses.m in Sources */, 470 | 5E82A6581A6FE765002CB6B3 /* ArtsyTokenTests.m in Sources */, 471 | 5E445A961A6FF103009712CA /* TestingNetworkOperator.m in Sources */, 472 | 3A6B21EC223160EC00BB8853 /* File.swift in Sources */, 473 | 5E445A9B1A6FFC9C009712CA /* ArtsyAuthenticationTests+Facebook.m in Sources */, 474 | ); 475 | runOnlyForDeploymentPostprocessing = 0; 476 | }; 477 | /* End PBXSourcesBuildPhase section */ 478 | 479 | /* Begin PBXTargetDependency section */ 480 | 6003F5B4195388D20070C39A /* PBXTargetDependency */ = { 481 | isa = PBXTargetDependency; 482 | target = 6003F589195388D20070C39A /* ArtsyAuthenticationExample */; 483 | targetProxy = 6003F5B3195388D20070C39A /* PBXContainerItemProxy */; 484 | }; 485 | /* End PBXTargetDependency section */ 486 | 487 | /* Begin PBXVariantGroup section */ 488 | 6003F596195388D20070C39A /* InfoPlist.strings */ = { 489 | isa = PBXVariantGroup; 490 | children = ( 491 | 6003F597195388D20070C39A /* en */, 492 | ); 493 | name = InfoPlist.strings; 494 | sourceTree = ""; 495 | }; 496 | 6003F59F195388D20070C39A /* Main_iPhone.storyboard */ = { 497 | isa = PBXVariantGroup; 498 | children = ( 499 | 6003F5A0195388D20070C39A /* Base */, 500 | ); 501 | name = Main_iPhone.storyboard; 502 | sourceTree = ""; 503 | }; 504 | 6003F5A2195388D20070C39A /* Main_iPad.storyboard */ = { 505 | isa = PBXVariantGroup; 506 | children = ( 507 | 6003F5A3195388D20070C39A /* Base */, 508 | ); 509 | name = Main_iPad.storyboard; 510 | sourceTree = ""; 511 | }; 512 | 6003F5B8195388D20070C39A /* InfoPlist.strings */ = { 513 | isa = PBXVariantGroup; 514 | children = ( 515 | 6003F5B9195388D20070C39A /* en */, 516 | ); 517 | name = InfoPlist.strings; 518 | sourceTree = ""; 519 | }; 520 | /* End PBXVariantGroup section */ 521 | 522 | /* Begin XCBuildConfiguration section */ 523 | 6003F5BD195388D20070C39A /* Debug */ = { 524 | isa = XCBuildConfiguration; 525 | buildSettings = { 526 | ALWAYS_SEARCH_USER_PATHS = NO; 527 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 528 | CLANG_CXX_LIBRARY = "libc++"; 529 | CLANG_ENABLE_MODULES = YES; 530 | CLANG_ENABLE_OBJC_ARC = YES; 531 | CLANG_WARN_BOOL_CONVERSION = YES; 532 | CLANG_WARN_CONSTANT_CONVERSION = YES; 533 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 534 | CLANG_WARN_EMPTY_BODY = YES; 535 | CLANG_WARN_ENUM_CONVERSION = YES; 536 | CLANG_WARN_INT_CONVERSION = YES; 537 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 538 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 539 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 540 | COPY_PHASE_STRIP = NO; 541 | GCC_C_LANGUAGE_STANDARD = gnu99; 542 | GCC_DYNAMIC_NO_PIC = NO; 543 | GCC_OPTIMIZATION_LEVEL = 0; 544 | GCC_PREPROCESSOR_DEFINITIONS = ( 545 | "DEBUG=1", 546 | "$(inherited)", 547 | ); 548 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 549 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 550 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 551 | GCC_WARN_UNDECLARED_SELECTOR = YES; 552 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 553 | GCC_WARN_UNUSED_FUNCTION = YES; 554 | GCC_WARN_UNUSED_VARIABLE = YES; 555 | IPHONEOS_DEPLOYMENT_TARGET = 7.1; 556 | ONLY_ACTIVE_ARCH = YES; 557 | SDKROOT = iphoneos; 558 | TARGETED_DEVICE_FAMILY = "1,2"; 559 | }; 560 | name = Debug; 561 | }; 562 | 6003F5BE195388D20070C39A /* Release */ = { 563 | isa = XCBuildConfiguration; 564 | buildSettings = { 565 | ALWAYS_SEARCH_USER_PATHS = NO; 566 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 567 | CLANG_CXX_LIBRARY = "libc++"; 568 | CLANG_ENABLE_MODULES = YES; 569 | CLANG_ENABLE_OBJC_ARC = YES; 570 | CLANG_WARN_BOOL_CONVERSION = YES; 571 | CLANG_WARN_CONSTANT_CONVERSION = YES; 572 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 573 | CLANG_WARN_EMPTY_BODY = YES; 574 | CLANG_WARN_ENUM_CONVERSION = YES; 575 | CLANG_WARN_INT_CONVERSION = YES; 576 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 577 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 578 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 579 | COPY_PHASE_STRIP = YES; 580 | ENABLE_NS_ASSERTIONS = NO; 581 | GCC_C_LANGUAGE_STANDARD = gnu99; 582 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 583 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 584 | GCC_WARN_UNDECLARED_SELECTOR = YES; 585 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 586 | GCC_WARN_UNUSED_FUNCTION = YES; 587 | GCC_WARN_UNUSED_VARIABLE = YES; 588 | IPHONEOS_DEPLOYMENT_TARGET = 7.1; 589 | SDKROOT = iphoneos; 590 | TARGETED_DEVICE_FAMILY = "1,2"; 591 | VALIDATE_PRODUCT = YES; 592 | }; 593 | name = Release; 594 | }; 595 | 6003F5C0195388D20070C39A /* Debug */ = { 596 | isa = XCBuildConfiguration; 597 | baseConfigurationReference = 9E066C36CDB57FA65C5DB65F /* Pods-ArtsyAuthenticationExample.debug.xcconfig */; 598 | buildSettings = { 599 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 600 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 601 | CLANG_ENABLE_MODULES = YES; 602 | FRAMEWORK_SEARCH_PATHS = ( 603 | "$(inherited)", 604 | "$(PROJECT_DIR)/Pods/build/Debug-iphoneos/Pods-Tests", 605 | ); 606 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 607 | GCC_PREFIX_HEADER = "Artsy_Authentication/Artsy_Authentication-Prefix.pch"; 608 | INFOPLIST_FILE = "Artsy_Authentication/Artsy_Authentication-Info.plist"; 609 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 610 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 611 | PRODUCT_NAME = "$(TARGET_NAME)"; 612 | SWIFT_OBJC_BRIDGING_HEADER = "ArtsyAuthenticationExample-Bridging-Header.h"; 613 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 614 | SWIFT_VERSION = 4.2; 615 | WRAPPER_EXTENSION = app; 616 | }; 617 | name = Debug; 618 | }; 619 | 6003F5C1195388D20070C39A /* Release */ = { 620 | isa = XCBuildConfiguration; 621 | baseConfigurationReference = 652EFE49892D7B2CC9C95AFC /* Pods-ArtsyAuthenticationExample.release.xcconfig */; 622 | buildSettings = { 623 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 624 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 625 | CLANG_ENABLE_MODULES = YES; 626 | FRAMEWORK_SEARCH_PATHS = ( 627 | "$(inherited)", 628 | "$(PROJECT_DIR)/Pods/build/Debug-iphoneos/Pods-Tests", 629 | ); 630 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 631 | GCC_PREFIX_HEADER = "Artsy_Authentication/Artsy_Authentication-Prefix.pch"; 632 | INFOPLIST_FILE = "Artsy_Authentication/Artsy_Authentication-Info.plist"; 633 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 634 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 635 | PRODUCT_NAME = "$(TARGET_NAME)"; 636 | SWIFT_OBJC_BRIDGING_HEADER = "ArtsyAuthenticationExample-Bridging-Header.h"; 637 | SWIFT_VERSION = 4.2; 638 | WRAPPER_EXTENSION = app; 639 | }; 640 | name = Release; 641 | }; 642 | 6003F5C3195388D20070C39A /* Debug */ = { 643 | isa = XCBuildConfiguration; 644 | baseConfigurationReference = A243866347F2A745B3FC133C /* Pods-Tests.debug.xcconfig */; 645 | buildSettings = { 646 | BUNDLE_LOADER = "$(TEST_HOST)"; 647 | CLANG_ENABLE_MODULES = YES; 648 | FRAMEWORK_SEARCH_PATHS = ( 649 | "$(SDKROOT)/Developer/Library/Frameworks", 650 | "$(inherited)", 651 | "$(DEVELOPER_FRAMEWORKS_DIR)", 652 | ); 653 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 654 | GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch"; 655 | GCC_PREPROCESSOR_DEFINITIONS = ( 656 | "DEBUG=1", 657 | "$(inherited)", 658 | ); 659 | INFOPLIST_FILE = "Tests/Tests-Info.plist"; 660 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 661 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 662 | PRODUCT_NAME = "$(TARGET_NAME)"; 663 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 664 | SWIFT_VERSION = 4.2; 665 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ArtsyAuthenticationExample.app/ArtsyAuthenticationExample"; 666 | WRAPPER_EXTENSION = xctest; 667 | }; 668 | name = Debug; 669 | }; 670 | 6003F5C4195388D20070C39A /* Release */ = { 671 | isa = XCBuildConfiguration; 672 | baseConfigurationReference = C9ADAE57EED54AB99A8998C1 /* Pods-Tests.release.xcconfig */; 673 | buildSettings = { 674 | BUNDLE_LOADER = "$(TEST_HOST)"; 675 | CLANG_ENABLE_MODULES = YES; 676 | FRAMEWORK_SEARCH_PATHS = ( 677 | "$(SDKROOT)/Developer/Library/Frameworks", 678 | "$(inherited)", 679 | "$(DEVELOPER_FRAMEWORKS_DIR)", 680 | ); 681 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 682 | GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch"; 683 | INFOPLIST_FILE = "Tests/Tests-Info.plist"; 684 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 685 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 686 | PRODUCT_NAME = "$(TARGET_NAME)"; 687 | SWIFT_VERSION = 4.2; 688 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ArtsyAuthenticationExample.app/ArtsyAuthenticationExample"; 689 | WRAPPER_EXTENSION = xctest; 690 | }; 691 | name = Release; 692 | }; 693 | /* End XCBuildConfiguration section */ 694 | 695 | /* Begin XCConfigurationList section */ 696 | 6003F585195388D10070C39A /* Build configuration list for PBXProject "Artsy_Authentication" */ = { 697 | isa = XCConfigurationList; 698 | buildConfigurations = ( 699 | 6003F5BD195388D20070C39A /* Debug */, 700 | 6003F5BE195388D20070C39A /* Release */, 701 | ); 702 | defaultConfigurationIsVisible = 0; 703 | defaultConfigurationName = Release; 704 | }; 705 | 6003F5BF195388D20070C39A /* Build configuration list for PBXNativeTarget "ArtsyAuthenticationExample" */ = { 706 | isa = XCConfigurationList; 707 | buildConfigurations = ( 708 | 6003F5C0195388D20070C39A /* Debug */, 709 | 6003F5C1195388D20070C39A /* Release */, 710 | ); 711 | defaultConfigurationIsVisible = 0; 712 | defaultConfigurationName = Release; 713 | }; 714 | 6003F5C2195388D20070C39A /* Build configuration list for PBXNativeTarget "Tests" */ = { 715 | isa = XCConfigurationList; 716 | buildConfigurations = ( 717 | 6003F5C3195388D20070C39A /* Debug */, 718 | 6003F5C4195388D20070C39A /* Release */, 719 | ); 720 | defaultConfigurationIsVisible = 0; 721 | defaultConfigurationName = Release; 722 | }; 723 | /* End XCConfigurationList section */ 724 | }; 725 | rootObject = 6003F582195388D10070C39A /* Project object */; 726 | } 727 | --------------------------------------------------------------------------------