-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 5b0f025
Showing
63 changed files
with
4,616 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
# OS X | ||
.DS_Store | ||
|
||
# Xcode | ||
build/ | ||
*.pbxuser | ||
!default.pbxuser | ||
*.mode1v3 | ||
!default.mode1v3 | ||
*.mode2v3 | ||
!default.mode2v3 | ||
*.perspectivev3 | ||
!default.perspectivev3 | ||
xcuserdata | ||
*.xccheckout | ||
profile | ||
*.moved-aside | ||
DerivedData | ||
*.hmap | ||
*.ipa | ||
|
||
# We recommend against adding the Pods directory to your .gitignore. However | ||
# you should judge for yourself, the pros and cons are mentioned at: | ||
# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control? | ||
# | ||
# Pods/ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
#import "ABXApiClient.h" | ||
|
||
#import "ABXFaq.h" | ||
#import "ABXNotification.h" | ||
#import "ABXVersion.h" | ||
#import "ABXIssue.h" | ||
|
||
#import "ABXFAQsViewController.h" | ||
#import "ABXVersionsViewController.h" | ||
#import "ABXFeedbackViewController.h" | ||
|
||
#import "ABXNotificationView.h" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
// | ||
// ABXApiClient.h | ||
// | ||
// Created by Stuart Hall on 21/05/2014. | ||
// Copyright (c) 2014 Appbot. All rights reserved. | ||
// | ||
|
||
#import <Foundation/Foundation.h> | ||
|
||
// Error codes | ||
typedef enum { | ||
ABXResponseCodeSuccess, // Request completed successfully | ||
ABXResponseCodeErrorAuth, // Check your bundle identifier and API key | ||
ABXResponseCodeErrorExpired, // Account requires payment | ||
ABXResponseCodeErrorDecoding, // Error decoding the JSON data | ||
ABXResponseCodeErrorEncoding, // Error encoding the post/put request | ||
ABXResponseCodeErrorNotFound, // Not found | ||
ABXResponseCodeErrorUnknown // Unknown error | ||
} ABXResponseCode; | ||
|
||
typedef void (^ABXRequestCompletion)(ABXResponseCode responseCode, NSInteger httpCode, NSError *error, id JSON); | ||
|
||
@interface ABXApiClient : NSObject | ||
|
||
+ (ABXApiClient*)instance; | ||
|
||
- (void)setApiKey:(NSString *)apiKey; | ||
|
||
- (NSURLSessionDataTask*)GET:(NSString*)path params:(NSDictionary*)params complete:(ABXRequestCompletion)complete; | ||
|
||
- (NSURLSessionDataTask*)POST:(NSString*)path params:(NSDictionary*)params complete:(ABXRequestCompletion)complete; | ||
|
||
- (NSURLSessionDataTask*)PUT:(NSString*)path params:(NSDictionary*)params complete:(ABXRequestCompletion)complete; | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,227 @@ | ||
// | ||
// ABXApiClient.m | ||
// | ||
// Created by Stuart Hall on 21/05/2014. | ||
// Copyright (c) 2014 Appbot. All rights reserved. | ||
// | ||
|
||
#import "ABXApiClient.h" | ||
|
||
#import "NSDictionary+ABXQueryString.h" | ||
#import "NSDictionary+ABXNSNullAsNull.h" | ||
|
||
@interface ABXApiClient () | ||
|
||
@property (nonatomic, strong) NSURLSession *session; | ||
@property (nonatomic, strong) NSOperationQueue *queue; | ||
|
||
@property (nonatomic, copy) NSString *apiKey; | ||
|
||
@end | ||
|
||
@implementation ABXApiClient | ||
|
||
static NSString *kAppbotUrl = @"https://api.appbot.co/v1"; | ||
|
||
+ (ABXApiClient*)instance | ||
{ | ||
static dispatch_once_t onceToken; | ||
static ABXApiClient *client = nil; | ||
dispatch_once(&onceToken, ^{ | ||
client = [[ABXApiClient alloc] init]; | ||
}); | ||
return client; | ||
} | ||
|
||
#pragma mark - Init | ||
|
||
- (id)init | ||
{ | ||
self = [super init]; | ||
if (self) { | ||
// Setup the request queue | ||
self.queue = [[NSOperationQueue alloc] init]; | ||
_queue.maxConcurrentOperationCount = NSOperationQueueDefaultMaxConcurrentOperationCount; | ||
|
||
// Setup our session | ||
self.session = [NSURLSession sessionWithConfiguration:nil delegate:nil delegateQueue:_queue]; | ||
} | ||
return self; | ||
} | ||
|
||
#pragma mark - Requests | ||
|
||
- (NSURLSessionDataTask*)GET:(NSString*)path params:(NSDictionary*)params complete:(ABXRequestCompletion)complete | ||
{ | ||
NSDictionary *parameters = [self combineDefaultParamsWith:params]; | ||
|
||
// Create our URL | ||
NSURL *url = [[NSURL URLWithString:kAppbotUrl] URLByAppendingPathComponent:path]; | ||
NSString *query = [parameters queryStringValue]; | ||
url = [NSURL URLWithString:[[url absoluteString] stringByAppendingFormat:url.query ? @"&%@" : @"?%@", query]]; | ||
|
||
// Create the request | ||
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; | ||
request.allowsCellularAccess = YES; | ||
request.HTTPMethod = @"GET"; | ||
return [self performRequest:request complete:complete]; | ||
} | ||
|
||
- (NSURLSessionDataTask*)POST:(NSString*)path params:(NSDictionary*)params complete:(ABXRequestCompletion)complete | ||
{ | ||
return [self httpBodyRequest:@"POST" path:path params:params complete:complete]; | ||
} | ||
|
||
- (NSURLSessionDataTask*)PUT:(NSString*)path params:(NSDictionary*)params complete:(ABXRequestCompletion)complete | ||
{ | ||
return [self httpBodyRequest:@"PUT" path:path params:params complete:complete]; | ||
} | ||
|
||
- (NSURLSessionDataTask*)httpBodyRequest:(NSString*)method path:(NSString*)path params:(NSDictionary*)params complete:(ABXRequestCompletion)complete | ||
{ | ||
NSDictionary *parameters = [self combineDefaultParamsWith:params]; | ||
|
||
// Create our URL | ||
NSURL *url = [[NSURL URLWithString:kAppbotUrl] URLByAppendingPathComponent:path]; | ||
NSString *charset = (__bridge NSString *)CFStringConvertEncodingToIANACharSetName(CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding)); | ||
|
||
// Create the request | ||
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; | ||
[request setValue:[NSString stringWithFormat:@"application/json; charset=%@", charset] forHTTPHeaderField:@"Content-Type"]; | ||
NSError *error = nil; | ||
[request setHTTPBody:[NSJSONSerialization dataWithJSONObject:parameters options:0 error:&error]]; | ||
if (error) { | ||
// Error setting the HTTP body | ||
if (complete) { | ||
complete(ABXResponseCodeErrorEncoding, -1, error, nil); | ||
} | ||
return nil; | ||
} | ||
else { | ||
request.allowsCellularAccess = YES; | ||
request.HTTPMethod = method; | ||
return [self performRequest:request complete:complete]; | ||
} | ||
} | ||
|
||
- (NSURLSessionDataTask*)performRequest:(NSURLRequest*)request complete:(ABXRequestCompletion)complete | ||
{ | ||
// https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/TransitionGuide/SupportingEarlieriOS.html#//apple_ref/doc/uid/TP40013174-CH14-SW1 | ||
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1) { | ||
// iOS 6.1 and below | ||
[NSURLConnection sendAsynchronousRequest:request | ||
queue:self.queue | ||
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) { | ||
[self handleResponse:response data:data error:error complete:complete]; | ||
}]; | ||
return nil; | ||
} | ||
else { | ||
// iOS 7 | ||
NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request | ||
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { | ||
[self handleResponse:response data:data error:error complete:complete]; }]; | ||
[task resume]; | ||
return task; | ||
} | ||
} | ||
|
||
- (void)handleResponse:(NSURLResponse*)response data:(NSData*)data error:(NSError*)error complete:(ABXRequestCompletion)complete | ||
{ | ||
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response; | ||
NSInteger httpCode = [httpResponse statusCode]; | ||
if (httpCode >= 200 && httpCode < 300) { | ||
[self handleRequestSuccess:httpCode data:data complete:complete]; | ||
} | ||
else { | ||
[self handleRequestFailure:httpCode error:error complete:complete]; | ||
} | ||
} | ||
|
||
- (void)handleRequestSuccess:(NSInteger)httpCode data:(NSData*)data complete:(ABXRequestCompletion)complete | ||
{ | ||
NSError *jsonError = nil; | ||
NSDictionary *json = nil; | ||
|
||
if (data != nil && data.length > 0) { | ||
json = [NSJSONSerialization JSONObjectWithData:data | ||
options:0 | ||
error:&jsonError]; | ||
} | ||
|
||
if (jsonError) { | ||
// JSON error | ||
if (complete) { | ||
dispatch_async(dispatch_get_main_queue(), ^{ | ||
complete(ABXResponseCodeErrorDecoding, httpCode, jsonError, nil); | ||
}); | ||
} | ||
} | ||
else { | ||
// Success! | ||
if (complete) { | ||
dispatch_async(dispatch_get_main_queue(), ^{ | ||
complete(ABXResponseCodeSuccess, httpCode, nil, json); | ||
}); | ||
} | ||
} | ||
} | ||
|
||
- (void)handleRequestFailure:(NSInteger)httpCode error:(NSError*)error complete:(ABXRequestCompletion)complete | ||
{ | ||
// Work out which error code | ||
ABXResponseCode responseCode = ABXResponseCodeErrorUnknown; | ||
switch (httpCode) { | ||
case 401: | ||
responseCode = ABXResponseCodeErrorAuth; | ||
break; | ||
|
||
case 402: | ||
responseCode = ABXResponseCodeErrorExpired; | ||
break; | ||
} | ||
|
||
if (complete) { | ||
dispatch_async(dispatch_get_main_queue(), ^{ | ||
complete(responseCode, httpCode, error, nil); | ||
}); | ||
} | ||
} | ||
|
||
#pragma mark - Key | ||
|
||
- (void)validateApiKey | ||
{ | ||
// The API key must always be set | ||
assert(_apiKey); | ||
if (_apiKey == nil || _apiKey.length == 0) { | ||
NSException* myException = [NSException | ||
exceptionWithName:@"InvalidKeyException" | ||
reason:@"Key is not valid." | ||
userInfo:nil]; | ||
@throw myException; | ||
} | ||
} | ||
|
||
#pragma mark - Params | ||
|
||
- (NSDictionary*)combineDefaultParamsWith:(NSDictionary*)params | ||
{ | ||
[self validateApiKey]; | ||
|
||
NSDictionary *defaultParams = @{ @"bundle_identifier" : [[NSBundle mainBundle] bundleIdentifier], | ||
@"key" : _apiKey }; | ||
if (params == nil) { | ||
// If there are no other params just use they key and bundle | ||
return defaultParams; | ||
} | ||
else { | ||
// Append the default values | ||
NSMutableDictionary *mutableParams = [params mutableCopy]; | ||
[mutableParams addEntriesFromDictionary:defaultParams]; | ||
return mutableParams; | ||
} | ||
} | ||
|
||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
// | ||
// Altered Version of https://github.com/nicklockwood/FXKeychain | ||
// Altered to not conflict with any existing uses of the library | ||
// | ||
// FXKeychain.h | ||
// | ||
// Version 1.5 beta | ||
// | ||
// Created by Nick Lockwood on 29/12/2012. | ||
// Copyright 2012 Charcoal Design | ||
// | ||
// Distributed under the permissive zlib License | ||
// Get the latest version from here: | ||
// | ||
// https://github.com/nicklockwood/FXKeychain | ||
// | ||
// This software is provided 'as-is', without any express or implied | ||
// warranty. In no event will the authors be held liable for any damages | ||
// arising from the use of this software. | ||
// | ||
// Permission is granted to anyone to use this software for any purpose, | ||
// including commercial applications, and to alter it and redistribute it | ||
// freely, subject to the following restrictions: | ||
// | ||
// 1. The origin of this software must not be misrepresented; you must not | ||
// claim that you wrote the original software. If you use this software | ||
// in a product, an acknowledgment in the product documentation would be | ||
// appreciated but is not required. | ||
// | ||
// 2. Altered source versions must be plainly marked as such, and must not be | ||
// misrepresented as being the original software. | ||
// | ||
// 3. This notice may not be removed or altered from any source distribution. | ||
// | ||
|
||
#import <Foundation/Foundation.h> | ||
|
||
#import <Security/Security.h> | ||
|
||
|
||
#pragma GCC diagnostic push | ||
#pragma GCC diagnostic ignored "-Wobjc-missing-property-synthesis" | ||
|
||
|
||
#ifndef APPBOTKEYCHAIN_USE_NSCODING | ||
#if TARGET_OS_IPHONE | ||
#define APPBOTKEYCHAIN_USE_NSCODING 1 | ||
#else | ||
#define APPBOTKEYCHAIN_USE_NSCODING 0 | ||
#endif | ||
#endif | ||
|
||
|
||
typedef NS_ENUM(NSInteger, ABXKeychainAccess) | ||
{ | ||
ABXKeychainAccessibleWhenUnlocked = 0, | ||
ABXKeychainAccessibleAfterFirstUnlock, | ||
ABXKeychainAccessibleAlways, | ||
ABXKeychainAccessibleWhenUnlockedThisDeviceOnly, | ||
ABXKeychainAccessibleAfterFirstUnlockThisDeviceOnly, | ||
ABXKeychainAccessibleAlwaysThisDeviceOnly | ||
}; | ||
|
||
|
||
@interface ABXKeychain : NSObject | ||
|
||
+ (instancetype)defaultKeychain; | ||
|
||
@property (nonatomic, readonly) NSString *service; | ||
@property (nonatomic, readonly) NSString *accessGroup; | ||
@property (nonatomic, assign) ABXKeychainAccess accessibility; | ||
|
||
- (id)initWithService:(NSString *)service | ||
accessGroup:(NSString *)accessGroup | ||
accessibility:(ABXKeychainAccess)accessibility; | ||
|
||
- (id)initWithService:(NSString *)service | ||
accessGroup:(NSString *)accessGroup; | ||
|
||
- (BOOL)setObject:(id)object forKey:(id)key; | ||
- (BOOL)setObject:(id)object forKeyedSubscript:(id)key; | ||
- (BOOL)removeObjectForKey:(id)key; | ||
- (id)objectForKey:(id)key; | ||
- (id)objectForKeyedSubscript:(id)key; | ||
|
||
@end | ||
|
||
|
||
#pragma GCC diagnostic pop |
Oops, something went wrong.