Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
stuartkhall committed Jun 12, 2014
0 parents commit 5b0f025
Show file tree
Hide file tree
Showing 63 changed files with 4,616 additions and 0 deletions.
27 changes: 27 additions & 0 deletions .gitignore
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/

12 changes: 12 additions & 0 deletions Classes/ABX.h
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"
35 changes: 35 additions & 0 deletions Classes/Classes/ABXApiClient.h
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
227 changes: 227 additions & 0 deletions Classes/Classes/ABXApiClient.m
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
89 changes: 89 additions & 0 deletions Classes/Classes/ABXKeychain.h
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
Loading

0 comments on commit 5b0f025

Please sign in to comment.