From c676978abc78a158b6fff6d6f00ac5b03fbdd973 Mon Sep 17 00:00:00 2001 From: Ricardo Pereira Date: Mon, 28 Sep 2015 10:42:36 +0100 Subject: [PATCH 01/22] Realtime tests: init RTC1, Added .gitignore --- ablySpec/RealtimeClient.swift | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 ablySpec/RealtimeClient.swift diff --git a/ablySpec/RealtimeClient.swift b/ablySpec/RealtimeClient.swift new file mode 100644 index 000000000..890c9343f --- /dev/null +++ b/ablySpec/RealtimeClient.swift @@ -0,0 +1,9 @@ +// +// RealtimeClient.swift +// ably +// +// Created by Ricardo Pereira on 26/09/2015. +// Copyright (c) 2015 Ably. All rights reserved. +// + +import Foundation From 04bdbf779d8ca2efadfe02d569da6b78a161fb0e Mon Sep 17 00:00:00 2001 From: Ricardo Pereira Date: Mon, 28 Sep 2015 10:42:40 +0100 Subject: [PATCH 02/22] Realtime tests: init RTC1, Added .gitignore --- .gitignore | 50 ++++++++++++ ably.xcodeproj/project.pbxproj | 4 + .../xcshareddata/xcschemes/ably.xcscheme | 10 +-- ablySpec/RealtimeClient.swift | 78 ++++++++++++++++++- ablySpec/RestClient.swift | 28 +++---- ablySpec/TestUtilities.swift | 7 +- 6 files changed, 156 insertions(+), 21 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..135d23186 --- /dev/null +++ b/.gitignore @@ -0,0 +1,50 @@ +# Created by https://www.gitignore.io/api/objective-c + +### Objective-C ### +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## Build generated +build/ +DerivedData + +## Various settings +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata + +## Other +*.xccheckout +*.moved-aside +*.xcuserstate +*.xcscmblueprint + +## Obj-C/Swift specific +*.hmap +*.ipa + +# CocoaPods +# +# 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-check-the-pods-directory-into-source-control +# +Pods/ + +# Carthage +# +# Add this line if you want to avoid checking in source code from Carthage dependencies. +# Carthage/Checkouts + +Carthage/Build + +### Objective-C Patch ### +*.xcscmblueprint + diff --git a/ably.xcodeproj/project.pbxproj b/ably.xcodeproj/project.pbxproj index 8447b40d2..2bcaec071 100644 --- a/ably.xcodeproj/project.pbxproj +++ b/ably.xcodeproj/project.pbxproj @@ -104,6 +104,7 @@ 96E408441A38939E00087F77 /* ARTProtocolMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 96E408421A38939E00087F77 /* ARTProtocolMessage.m */; }; 96E408471A3895E800087F77 /* ARTWebSocketTransport.h in Headers */ = {isa = PBXBuildFile; fileRef = 96E408451A3895E800087F77 /* ARTWebSocketTransport.h */; }; 96E408481A3895E800087F77 /* ARTWebSocketTransport.m in Sources */ = {isa = PBXBuildFile; fileRef = 96E408461A3895E800087F77 /* ARTWebSocketTransport.m */; }; + D72304701BB72CED00F1ABDA /* RealtimeClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = D723046F1BB72CED00F1ABDA /* RealtimeClient.swift */; }; FC78549C1BCD5688539CCBE4 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FDA6E099E4BC04FC80F845C0 /* libPods.a */; }; /* End PBXBuildFile section */ @@ -254,6 +255,7 @@ 96E408451A3895E800087F77 /* ARTWebSocketTransport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTWebSocketTransport.h; sourceTree = ""; }; 96E408461A3895E800087F77 /* ARTWebSocketTransport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTWebSocketTransport.m; sourceTree = ""; }; CDE13941D61BC4A0690896F6 /* Pods-ablySpec.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ablySpec.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ablySpec/Pods-ablySpec.debug.xcconfig"; sourceTree = ""; }; + D723046F1BB72CED00F1ABDA /* RealtimeClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealtimeClient.swift; sourceTree = ""; }; DF8DF755839D59574B7B795F /* Pods-ablySpec.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ablySpec.release.xcconfig"; path = "Pods/Target Support Files/Pods-ablySpec/Pods-ablySpec.release.xcconfig"; sourceTree = ""; }; FDA6E099E4BC04FC80F845C0 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -332,6 +334,7 @@ 856AAC951B6E30C800B07119 /* ablySpec-Bridging-Header.h */, 856AAC981B6E312F00B07119 /* RestClient.swift */, 853ED7C31B7A1A3C006F1C6F /* RestClient.stats.swift */, + D723046F1BB72CED00F1ABDA /* RealtimeClient.swift */, 851674EE1B7BA5CD00D35169 /* Stats.swift */, ); path = ablySpec; @@ -743,6 +746,7 @@ 856AAC971B6E30C800B07119 /* TestUtilities.swift in Sources */, 853ED7C41B7A1A3C006F1C6F /* RestClient.stats.swift in Sources */, 851674EF1B7BA5CD00D35169 /* Stats.swift in Sources */, + D72304701BB72CED00F1ABDA /* RealtimeClient.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ably.xcodeproj/xcshareddata/xcschemes/ably.xcscheme b/ably.xcodeproj/xcshareddata/xcschemes/ably.xcscheme index 238cae894..72613936f 100644 --- a/ably.xcodeproj/xcshareddata/xcschemes/ably.xcscheme +++ b/ably.xcodeproj/xcshareddata/xcschemes/ably.xcscheme @@ -1,7 +1,7 @@ + version = "1.8"> @@ -37,10 +37,10 @@ + shouldUseLaunchSchemeArgsEnv = "YES" + buildConfiguration = "Debug"> @@ -66,11 +66,11 @@ PublishTes } func getTestToken() -> String { - let options = AblyTests.setupOptions(AblyTests.jsonRestOptions) + let options = AblyTests.commonAppSetup() options.authOptions.useTokenAuth = true options.authOptions.clientId = "testToken" let client = ARTRest(options: options) @@ -53,7 +53,7 @@ class RestClient: QuickSpec { // RSC1 context("initializer") { it("should accept an API key") { - let options = AblyTests.setupOptions(AblyTests.jsonRestOptions) + let options = AblyTests.commonAppSetup() let client = ARTRest(key: "\(options.authOptions.keyName):\(options.authOptions.keySecret)") let publishTask = publishTestMessage(client) @@ -66,7 +66,7 @@ class RestClient: QuickSpec { } it("should result in error status when provided a bad key") { - let options = AblyTests.setupOptions(AblyTests.jsonRestOptions) + let options = AblyTests.commonAppSetup() let client = ARTRest(key: "badName:\(options.authOptions.keySecret)") let publishTask = publishTestMessage(client, failOnError: false) @@ -75,8 +75,8 @@ class RestClient: QuickSpec { expect(publishTask.status?.errorInfo.code).toEventually(equal(40005)) } - it("should accept an options object") { - let options = AblyTests.setupOptions(AblyTests.jsonRestOptions) + fit("should accept an options object") { + let options = AblyTests.commonAppSetup() let client = ARTRest(options: options) let publishTask = publishTestMessage(client) @@ -85,7 +85,7 @@ class RestClient: QuickSpec { } it("should accept an options object with token authentication") { - let options = AblyTests.setupOptions(AblyTests.jsonRestOptions) + let options = AblyTests.commonAppSetup() options.authOptions.useTokenAuth = true options.authOptions.token = getTestToken() let client = ARTRest(options: options) @@ -97,7 +97,7 @@ class RestClient: QuickSpec { } it("should result in error status when provided a bad token") { - let options = AblyTests.setupOptions(AblyTests.jsonRestOptions) + let options = AblyTests.commonAppSetup() options.authOptions.useTokenAuth = true options.authOptions.token = "invalid_token" let client = ARTRest(options: options) @@ -114,7 +114,7 @@ class RestClient: QuickSpec { // RSC2 it("should output to the system log and the log level should be Warn") { let logTime = NSDate() - let client = ARTRest(options: AblyTests.setupOptions(AblyTests.jsonRestOptions)) + let client = ARTRest(options: AblyTests.commonAppSetup()) client.logger.warn("This is a warning") let logs = querySyslog(forLogsAfter: logTime) @@ -126,7 +126,7 @@ class RestClient: QuickSpec { // RSC3 it("should have a mutable log level") { let logTime = NSDate() - let client = ARTRest(options: AblyTests.setupOptions(AblyTests.jsonRestOptions)) + let client = ARTRest(options: AblyTests.commonAppSetup()) client.logger.logLevel = .Error client.logger.warn("This is a warning") @@ -146,7 +146,7 @@ class RestClient: QuickSpec { } } - let options = AblyTests.setupOptions(AblyTests.jsonRestOptions) + let options = AblyTests.commonAppSetup() options.loggerClass = MyLogger.self let client = ARTRest(options: options) @@ -162,7 +162,7 @@ class RestClient: QuickSpec { it("should accept an options object with an environment set") { // reset the default host in order to force ARTClientOptions to compute it ARTClientOptions.getDefaultRestHost("rest.ably.io", modify: true) - let options = AblyTests.setupOptions(AblyTests.jsonRestOptions) + let options = AblyTests.commonAppSetup() let newOptions = ARTClientOptions() newOptions.authOptions.keyName = options.authOptions.keyName newOptions.authOptions.keySecret = options.authOptions.keySecret @@ -177,9 +177,9 @@ class RestClient: QuickSpec { // RSC5 it("should provide access to the AuthOptions object passed in ClientOptions") { - let options = AblyTests.setupOptions(AblyTests.jsonRestOptions) + let options = AblyTests.commonAppSetup() let client = ARTRest(options: options) - + let authOptions = client.auth().getAuthOptions() expect(authOptions).to(beIdenticalTo(options.authOptions)) @@ -188,7 +188,7 @@ class RestClient: QuickSpec { // RSC16 context("time") { it("should return server time") { - let options = AblyTests.setupOptions(AblyTests.jsonRestOptions) + let options = AblyTests.commonAppSetup() let client = ARTRest(options: options) var time: NSDate? diff --git a/ablySpec/TestUtilities.swift b/ablySpec/TestUtilities.swift index a8126bc85..eee0c0cd2 100644 --- a/ablySpec/TestUtilities.swift +++ b/ablySpec/TestUtilities.swift @@ -86,6 +86,11 @@ class AblyTests { return options } + + class func commonAppSetup() -> ARTClientOptions { + return AblyTests.setupOptions(AblyTests.jsonRestOptions) + } + } func querySyslog(forLogsAfter startingTime: NSDate? = nil) -> GeneratorOf { @@ -106,4 +111,4 @@ func querySyslog(forLogsAfter startingTime: NSDate? = nil) -> GeneratorOf Date: Tue, 29 Sep 2015 04:12:00 +0100 Subject: [PATCH 03/22] Fixed Logger: . Only one instance of logger (ARTRest.logger) --- ably-ios/ARTAuth.h | 21 ++- ably-ios/ARTAuth.m | 24 +-- ably-ios/ARTClientOptions.h | 2 - ably-ios/ARTClientOptions.m | 7 +- ably-ios/ARTLog.m | 1 + ably-ios/ARTRealtime.h | 49 ++++-- ably-ios/ARTRealtime.m | 146 +++++++++++------- ably-ios/ARTRest.h | 30 ++-- ably-ios/ARTRest.m | 22 +-- ably-ios/ARTWebSocketTransport.h | 6 +- ably-ios/ARTWebSocketTransport.m | 39 +++-- ably.xcodeproj/project.pbxproj | 13 +- .../xcshareddata/xcschemes/ably.xcscheme | 16 +- ablySpec/RealtimeClient.swift | 27 +++- ablySpec/RestClient.swift | 7 +- ablySpec/TestUtilities.swift | 1 + 16 files changed, 250 insertions(+), 161 deletions(-) diff --git a/ably-ios/ARTAuth.h b/ably-ios/ARTAuth.h index 0b4f47a8a..f2285a1dd 100644 --- a/ably-ios/ARTAuth.h +++ b/ably-ios/ARTAuth.h @@ -11,8 +11,11 @@ #import @class ARTRest; - @class ARTLog; + + +#pragma mark - ARTTokenDetails + @interface ARTTokenDetails : NSObject @property (readonly, strong, nonatomic) NSString *token; @@ -27,6 +30,9 @@ @end + +#pragma mark - ARTAuthTokenParams + @interface ARTAuthTokenParams : NSObject @property (readonly, strong, nonatomic) NSString *keyName; @@ -37,12 +43,12 @@ @property (readonly, strong, nonatomic) NSString *nonce; @property (readonly, strong, nonatomic) NSString *mac; - - (instancetype)init UNAVAILABLE_ATTRIBUTE; - (instancetype)initWithId:(NSString *)id ttl:(int64_t)ttl capability:(NSString *)capability clientId:(NSString *)clientId timestamp:(int64_t)timestamp nonce:(NSString *)nonce mac:(NSString *)mac; -(NSDictionary *) asDictionary; + @end typedef id(^ARTAuthCb)(void(^continuation)(ARTStatus *,ARTTokenDetails *)); @@ -52,9 +58,11 @@ typedef NS_ENUM(NSUInteger, ARTAuthMethod) { ARTAuthMethodToken }; + +#pragma mark - ARTAuthOptions + @interface ARTAuthOptions : NSObject -@property (nonatomic, weak) ARTLog * logger; @property (readwrite, strong, nonatomic) ARTAuthCb authCallback; @property (readwrite, strong, nonatomic) ARTSignedTokenRequestCb signedTokenRequestCallback; @property (readwrite, strong, nonatomic) ARTAuthTokenParams *tokenParams; @@ -71,7 +79,6 @@ typedef NS_ENUM(NSUInteger, ARTAuthMethod) { @property (readwrite, assign, nonatomic) BOOL useTokenAuth; @property (readwrite, assign, nonatomic) ARTTokenDetails * tokenDetails; - - (instancetype)init; - (instancetype)initWithKey:(NSString *)key; @@ -82,6 +89,9 @@ typedef NS_ENUM(NSUInteger, ARTAuthMethod) { @end + +#pragma mark - ARTAuth + @interface ARTAuth : NSObject - (instancetype)initWithRest:(ARTRest *) rest options:(ARTAuthOptions *) options; @@ -96,5 +106,4 @@ typedef NS_ENUM(NSUInteger, ARTAuthMethod) { - (void)attemptTokenFetch:(void (^)()) cb; - (bool)canRequestToken; -+ (ARTSignedTokenRequestCb)defaultSignedTokenRequestCallback:(ARTAuthOptions *)authOptions rest:(ARTRest *)rest; -@end \ No newline at end of file +@end diff --git a/ably-ios/ARTAuth.m b/ably-ios/ARTAuth.m index 2e46ccaa3..4684cd2c9 100644 --- a/ably-ios/ARTAuth.m +++ b/ably-ios/ARTAuth.m @@ -44,9 +44,10 @@ @interface ARTAuth () @property (readwrite, strong, nonatomic) id tokenRequest; @property (readwrite, strong, nonatomic) ARTAuthOptions *options; -+ (ARTSignedTokenRequestCb)defaultSignedTokenRequestCallback:(ARTAuthOptions *)authOptions rest:(ARTRest *)rest; ++ (ARTSignedTokenRequestCb)defaultSignedTokenRequestCallback:(ARTAuthOptions *)authOptions forRest:(ARTRest *)rest withLogger:(ARTLog *)logger; + (NSString *)random; + (NSArray *)checkValidKey:(NSString *) key; + @end @implementation ARTTokenDetails @@ -227,6 +228,7 @@ - (instancetype)initWithRest:(ARTRest *) rest options:(ARTAuthOptions *) options _tokenRequest = nil; _options = options; self.logger = rest.logger; + if (options.keyName != nil) { [self.logger debug:@"ARTAuth: setting up auth method Basic"]; _basicCredentials = [NSString stringWithFormat:@"Basic %@", @@ -311,7 +313,8 @@ -(void) prepConnection { [self.logger debug:@"ARTAuth: signed token request."]; - ARTSignedTokenRequestCb strCb = (self.options.signedTokenRequestCallback ? self.options.signedTokenRequestCallback : [ARTAuth defaultSignedTokenRequestCallback:self.options rest:self.rest]); + ARTSignedTokenRequestCb strCb = (self.options.signedTokenRequestCallback ? self.options.signedTokenRequestCallback : [ARTAuth defaultSignedTokenRequestCallback:self.options forRest:self.rest withLogger:self.logger]); + __weak ARTAuth * weakSelf = self; _authTokenCb = ^(void(^authCb)(ARTStatus *,ARTTokenDetails *)) { ARTIndirectCancellable *ic = [[ARTIndirectCancellable alloc] init]; @@ -322,7 +325,7 @@ -(void) prepConnection { s.options.tokenParams = params; } id c = strCb(params,^( ARTAuthTokenParams *params) { - [self.logger debug:[NSString stringWithFormat:@"ARTAuth tokenRequest strCb got %@", [params asDictionary]]]; + [weakSelf.logger debug:[NSString stringWithFormat:@"ARTAuth tokenRequest strCb got %@", [params asDictionary]]]; ARTAuth * s = weakSelf; if(s) { [s.rest token:params tokenCb:^(ARTStatus * status, ARTTokenDetails * tokenDetails) { @@ -334,7 +337,7 @@ -(void) prepConnection { s.options.tokenDetails = tokenDetails; } else { - [self.logger error:@"ARTAuth became nil during token request. Can't assign token"]; + [weakSelf.logger error:@"ARTAuth became nil during token request. Can't assign token"]; } authCb(status, tokenDetails); }]; @@ -463,14 +466,15 @@ + (NSString *)random { return [NSString stringWithFormat:@"%08lu%08lu", (long)r1, (long)r2]; } -+ (ARTSignedTokenRequestCb)defaultSignedTokenRequestCallback:(ARTAuthOptions *)authOptions rest:(ARTRest *)rest { ++ (ARTSignedTokenRequestCb)defaultSignedTokenRequestCallback:(ARTAuthOptions *)authOptions forRest:(ARTRest *)rest withLogger:(ARTLog *)logger { __weak ARTRest *weakRest = rest; - [authOptions.logger verbose:@"ARTAUTH creating signed token request callback"]; + [logger verbose:@"ARTAUTH creating signed token request callback"]; + return ^id(ARTAuthTokenParams *params, void(^cb)(ARTAuthTokenParams *)) { - [authOptions.logger verbose:@"ARTAUTH signed token request callback called"]; + [logger verbose:@"ARTAUTH signed token request callback called"]; + NSString *keySecret = authOptions.keySecret; BOOL queryTime = authOptions.queryTime; - if (authOptions.keyName != nil && params.keyName && ![params.keyName isEqualToString:authOptions.keyName]) { [NSException raise:@"ARTAuthParams keyName is not equal to ARTAuthOptions keyName" format:@"'%@' != '%@'", params.keyName, authOptions.keyName]; @@ -485,7 +489,7 @@ + (ARTSignedTokenRequestCb)defaultSignedTokenRequestCallback:(ARTAuthOptions *)a void (^timeCb)(void(^)(int64_t)) = nil; if (!params.timestamp) { if (queryTime) { - [authOptions.logger debug:@"ARTAuth: query time is being used"]; + [logger debug:@"ARTAuth: query time is being used"]; timeCb = ^(void(^cb)(int64_t)) { ARTRest *strongRest = weakRest; if (strongRest) { @@ -502,7 +506,7 @@ + (ARTSignedTokenRequestCb)defaultSignedTokenRequestCallback:(ARTAuthOptions *)a }; } else { timeCb = ^(void(^cb)(int64_t)) { - [authOptions.logger debug:@"ARTAuth: client time is being used"]; + [logger debug:@"ARTAuth: client time is being used"]; cb((int64_t)([[NSDate date] timeIntervalSince1970] *1000.0 )); }; } diff --git a/ably-ios/ARTClientOptions.h b/ably-ios/ARTClientOptions.h index 401c4dffc..7edb06925 100644 --- a/ably-ios/ARTClientOptions.h +++ b/ably-ios/ARTClientOptions.h @@ -28,8 +28,6 @@ @property (readwrite, copy, nonatomic) NSString *recover; @property (readonly, strong, nonatomic) NSURL *restUrl; -@property (nonatomic, assign) Class loggerClass; - - (instancetype)init; - (instancetype)initWithKey:(NSString *)key; diff --git a/ably-ios/ARTClientOptions.m b/ably-ios/ARTClientOptions.m index e3bfe3f87..76c9d30cd 100644 --- a/ably-ios/ARTClientOptions.m +++ b/ably-ios/ARTClientOptions.m @@ -9,8 +9,11 @@ #import "ARTClientOptions.h" #import "ARTClientOptions+Private.h" #import "ARTDefault.h" + @interface ARTClientOptions () + @property (readwrite, strong, nonatomic) NSString *realtimeHost; + - (instancetype)initDefaults; @end @@ -33,9 +36,6 @@ +(NSString *) getDefaultRealtimeHost:(NSString *) replacement modify:(bool) modi return realtimeHost; } - - - - (instancetype)init { self = [super init]; if (self) { @@ -126,7 +126,6 @@ - (instancetype)clone { options.connectionSerial = self.connectionSerial; options.resumeKey = self.resumeKey; options.environment = self.environment; - options.loggerClass = self.loggerClass; return options; } diff --git a/ably-ios/ARTLog.m b/ably-ios/ARTLog.m index 5c68feaa4..5fefea868 100644 --- a/ably-ios/ARTLog.m +++ b/ably-ios/ARTLog.m @@ -31,6 +31,7 @@ @implementation ARTLog - (instancetype)init { if (self = [super init]) { + // Default self->_logLevel = ARTLogLevelWarn; } return self; diff --git a/ably-ios/ARTRealtime.h b/ably-ios/ARTRealtime.h index f25194f36..2a6a80bca 100644 --- a/ably-ios/ARTRealtime.h +++ b/ably-ios/ARTRealtime.h @@ -18,10 +18,14 @@ #define ART_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +@class ARTPresence; @class ARTPresenceMap; @class ARTRealtimeChannelPresenceSubscription; @class ARTEventEmitter; + +#pragma mark - Enumerations + typedef NS_ENUM(NSUInteger, ARTRealtimeChannelState) { ARTRealtimeChannelInitialised, ARTRealtimeChannelAttaching, @@ -43,13 +47,18 @@ typedef NS_ENUM(NSUInteger, ARTRealtimeConnectionState) { ARTRealtimeFailed }; + +#pragma mark - Protocols + @protocol ARTSubscription - (void)unsubscribe; @end -@class ARTPresence; + +#pragma mark - ARTRealtimeChannel + @interface ARTRealtimeChannel : NSObject - (void)publish:(id)payload withName:(NSString *)name cb:(ARTStatusCallback)cb; @@ -58,14 +67,11 @@ typedef NS_ENUM(NSUInteger, ARTRealtimeConnectionState) { - (id)history:(ARTPaginatedResultCallback)callback; - (id)historyWithParams:(NSDictionary *)queryParams cb:(ARTPaginatedResultCallback)callback; - - typedef void (^ARTRealtimeChannelMessageCb)(ARTMessage *); - (id)subscribe:(ARTRealtimeChannelMessageCb)cb; - (id)subscribeToName:(NSString *)name cb:(ARTRealtimeChannelMessageCb)cb; - (id)subscribeToNames:(NSArray *)names cb:(ARTRealtimeChannelMessageCb)cb; - typedef void (^ARTRealtimeChannelStateCb)(ARTRealtimeChannelState, ARTStatus *); - (id)subscribeToStateChanges:(ARTRealtimeChannelStateCb)cb; @@ -79,7 +85,11 @@ typedef void (^ARTRealtimeChannelStateCb)(ARTRealtimeChannelState, ARTStatus *); @end + +#pragma mark - ARTPresence + @interface ARTPresence : NSObject + - (instancetype) initWithChannel:(ARTRealtimeChannel *) channel; - (id)get:(ARTPaginatedResultCallback)callback; - (id)getWithParams:(NSDictionary *) queryParams cb:(ARTPaginatedResultCallback)callback; @@ -105,20 +115,33 @@ typedef void (^ARTRealtimeChannelPresenceCb)(ARTPresenceMessage *); @end +#pragma mark - ARTRealtime + @interface ARTRealtime : NSObject - (instancetype)init UNAVAILABLE_ATTRIBUTE; -- (instancetype)initWithKey:(NSString *) key; -- (instancetype)initWithOptions:(ARTClientOptions *) options; -- (void) close; -- (BOOL) connect; +/** +Instance the Ably library using a key only. This is simply a convenience constructor for the simplest case of instancing the library with a key for basic authentication and no other options. +:param key; String key (obtained from application dashboard) +*/ +- (instancetype)initWithKey:(NSString *)key; + +/** +Instance the Ably library with the given options. +:param options: see {@link io.ably.types.ClientOptions} for options +*/ +- (instancetype)initWithOptions:(ARTClientOptions *)options; +- (instancetype)initWithLogger:(ARTLog *)logger andOptions:(ARTClientOptions *)options; + +- (void)close; +- (BOOL)connect; - (ARTRealtimeConnectionState)state; - (NSString *)connectionId; - (NSString *)connectionKey; - (NSString *)recoveryKey; -- (ARTAuth *) auth; +- (ARTAuth *)auth; - (NSDictionary *) channels; - (id)time:(void(^)(ARTStatus *status, NSDate *time))cb; @@ -132,14 +155,16 @@ typedef void (^ARTRealtimePingCb)(ARTStatus *); - (ARTRealtimeChannel *)channel:(NSString *)channelName; - (ARTRealtimeChannel *)channel:(NSString *)channelName cipherParams:(ARTCipherParams *)cipherParams; - - -@property (nonatomic, weak) ARTLog * logger; @property (readonly, strong, nonatomic) ARTEventEmitter *eventEmitter; +@property (readonly, getter=getLogger) ARTLog *logger; @end + +#pragma mark - ARTEventEmitter + @interface ARTEventEmitter : NSObject + -(instancetype) initWithRealtime:(ARTRealtime *) realtime; typedef void (^ARTRealtimeConnectionStateCb)(ARTRealtimeConnectionState); diff --git a/ably-ios/ARTRealtime.m b/ably-ios/ARTRealtime.m index a216ec654..de8470b32 100644 --- a/ably-ios/ARTRealtime.m +++ b/ably-ios/ARTRealtime.m @@ -32,6 +32,7 @@ - (ARTStatusCallback)cb; @end + @interface ARTRealtimeChannelSubscription : NSObject @property (readonly, weak, nonatomic) ARTRealtimeChannel *channel; @@ -43,6 +44,7 @@ - (void)unsubscribe; @end + @interface ARTRealtimeChannelPresenceSubscription : NSObject @property (readonly, strong, nonatomic) NSMutableSet * excludedActions; @@ -56,6 +58,7 @@ - (void)unsubscribe; @end + @interface ARTRealtimeChannelStateSubscription : NSObject @property (readonly, weak, nonatomic) ARTRealtimeChannel *channel; @@ -67,6 +70,7 @@ - (void)unsubscribe; @end + @interface ARTRealtimeConnectionStateSubscription : NSObject @property (readonly, weak, nonatomic) ARTRealtime *realtime; @@ -78,16 +82,23 @@ - (void)unsubscribe; @end + @interface ARTPresence () + @property (nonatomic, weak) ARTLog * logger; @property (readonly, weak, nonatomic) ARTRealtimeChannel *channel; + @end @interface ARTEventEmitter () + @property (readonly, weak, nonatomic) ARTRealtime * realtime; + @end +#pragma mark - ARTRealtimeChannel interface + @interface ARTRealtimeChannel () @property (nonatomic, weak) ARTLog * logger; @@ -133,6 +144,9 @@ - (void)broadcastPresence:(ARTPresenceMessage *)pm; @end + +#pragma mark - ARTRealtime interface + @interface ARTRealtime () @property (readwrite, strong, nonatomic) ARTRest *rest; @@ -201,9 +215,6 @@ - (void)failQueuedMessages:(ARTStatus *)error; - (void)ack:(int64_t)serial count:(int64_t)count; - (void)nack:(int64_t)serial count:(int64_t)count; - - - // util - (id)createTransport; - (CFRunLoopTimerRef)startTimer:(void(^)())onTimeout interval:(NSTimeInterval)interval; @@ -213,6 +224,9 @@ - (void)unsubscribeState:(ARTRealtimeConnectionStateSubscription *)subscription; @end + +#pragma mark - ARTQueuedMessage + @implementation ARTQueuedMessage - (instancetype)initWithProtocolMessage:(ARTProtocolMessage *)msg cb:(ARTStatusCallback)cb { @@ -247,6 +261,9 @@ - (ARTStatusCallback)cb { @end + +#pragma mark - ARTRealtimeChannelSubscription + @implementation ARTRealtimeChannelSubscription - (instancetype)initWithChannel:(ARTRealtimeChannel *)channel cb:(ARTRealtimeChannelMessageCb)cb { @@ -264,6 +281,9 @@ - (void)unsubscribe { @end + +#pragma mark - ARTRealtimeChannelPresenceSubscription + @implementation ARTRealtimeChannelPresenceSubscription - (instancetype)initWithChannel:(ARTRealtimeChannel *)channel cb:(ARTRealtimeChannelPresenceCb)cb { @@ -298,6 +318,9 @@ - (void)unsubscribe { @end + +#pragma mark - ARTRealtimeChannelStateSubscription + @implementation ARTRealtimeChannelStateSubscription - (instancetype)initWithChannel:(ARTRealtimeChannel *)channel cb:(ARTRealtimeChannelStateCb)cb { @@ -315,6 +338,9 @@ - (void)unsubscribe { @end + +#pragma mark - ARTRealtimeConnectionStateSubscription + @implementation ARTRealtimeConnectionStateSubscription - (instancetype)initWithRealtime:(ARTRealtime *)realtime cb:(ARTRealtimeConnectionStateCb)cb { @@ -332,6 +358,9 @@ - (void)unsubscribe { @end + +#pragma mark - ARTRealtimeChannel + @implementation ARTRealtimeChannel - (instancetype)initWithRealtime:(ARTRealtime *)realtime name:(NSString *)name cipherParams:(ARTCipherParams *)cipherParams { @@ -393,7 +422,6 @@ - (void)publishMessages:(NSArray *)messages cb:(ARTStatusCallback)cb { [self publishProtocolMessage:msg cb:cb]; } - - (void) requestContinueSync { [self.logger info:@"ARTRealtime requesting to continue sync operation after reconnect"]; @@ -404,6 +432,7 @@ - (void) requestContinueSync { [self.realtime send:msg cb:^(ARTStatus *status) {}]; } + - (void)publishPresence:(ARTPresenceMessage *)msg cb:(ARTStatusCallback)cb { if (!msg.clientId) { msg.clientId = self.clientId; @@ -466,11 +495,13 @@ - (void)publishProtocolMessage:(ARTProtocolMessage *)pm cb:(ARTStatusCallback)cb - (ARTPresenceMap *) presenceMap { return _presenceMap; } + -(void) throwOnDisconnectedOrFailed { if(self.realtime.state == ARTRealtimeFailed || self.realtime.state == ARTRealtimeDisconnected) { [NSException raise:@"realtime cannot perform action in disconnected or failed state" format:@"state: %d", (int)self.realtime.state]; } } + - (id)history:(ARTPaginatedResultCallback)callback { [self throwOnDisconnectedOrFailed]; return [self.restChannel history:callback]; @@ -484,8 +515,6 @@ -(void) throwOnDisconnectedOrFailed { return [self.restChannel historyWithParams:queryParams cb:callback]; } - - - (id)subscribe:(ARTRealtimeChannelMessageCb)cb { // Empty string used for blanket subscriptions return [self subscribeToName:@"" cb:cb]; @@ -775,7 +804,6 @@ - (BOOL)detach { return true; } - - (void)sendQueuedMessages { NSArray *qms = self.queuedMessages; self.queuedMessages = [NSMutableArray array]; @@ -795,8 +823,51 @@ - (void)failQueuedMessages:(ARTStatus *)status { @end + +#pragma mark - ARTRealtime + @implementation ARTRealtime +- (instancetype)initWithOptions:(ARTClientOptions *)options { + return [self initWithLogger:[[ARTLog alloc] init] andOptions:options]; +} + +- (instancetype)initWithKey:(NSString *)key { + return [self initWithOptions:[ARTClientOptions optionsWithKey:key]]; +} + +- (instancetype)initWithLogger:(ARTLog *)logger andOptions:(ARTClientOptions *)options { + self = [super init]; + if (self) { + _rest = [[ARTRest alloc] initWithLogger:logger andOptions:options]; + _eventEmitter = [[ARTEventEmitter alloc] initWithRealtime:self]; + _allChannels = [NSMutableDictionary dictionary]; + _transport = nil; + self.state = ARTRealtimeInitialized; + _connectTimeout = NULL; + _suspendTimeout = NULL; + _retryTimeout = NULL; + _connectionId = nil; + _msgSerial = 0; + _queuedMessages = [NSMutableArray array]; + _pendingMessages = [NSMutableArray array]; + _pendingMessageStartSerial = 0; + _clientId = options.clientId; + _options = [options clone]; + _stateSubscriptions = [NSMutableArray array]; + _errorReason = [[ARTErrorInfo alloc] init]; + + if (options.autoConnect) { + [self connect]; + } + } + return self; +} + +- (ARTLog *)getLogger { + return _rest.logger; +} + - (ARTErrorInfo *)connectionErrorReason { return self.errorReason; } @@ -827,43 +898,6 @@ - (ARTAuth *) auth { return self.rest.auth; } -- (instancetype)initWithKey:(NSString *) key { - return [self initWithOptions:[ARTClientOptions optionsWithKey:key]]; -} - -- (instancetype)initWithOptions:(ARTClientOptions *) options { - ARTRealtime * r = [[ARTRealtime alloc] initWithoutRest:options]; - r.rest = [[ARTRest alloc] initWithOptions:options]; - r.logger = r.logger; - if(options.autoConnect) { - [r connect]; - } - return r; -} - --(instancetype) initWithoutRest:(ARTClientOptions *) options { - self = [super init]; - if (self) { - _eventEmitter = [[ARTEventEmitter alloc] initWithRealtime:self]; - _allChannels = [NSMutableDictionary dictionary]; - _transport = nil; - self.state = ARTRealtimeInitialized; - _connectTimeout = NULL; - _suspendTimeout = NULL; - _retryTimeout = NULL; - _connectionId = nil; - _msgSerial = 0; - _queuedMessages = [NSMutableArray array]; - _pendingMessages = [NSMutableArray array]; - _pendingMessageStartSerial = 0; - _clientId = options.clientId; - _options = [options clone]; - _stateSubscriptions = [NSMutableArray array]; - _errorReason = [[ARTErrorInfo alloc] init]; - } - return self; -} - - (NSDictionary *) channels { return _allChannels; } @@ -1141,7 +1175,6 @@ - (void)onHeartbeat:(ARTProtocolMessage *)message { } } - - (void)onConnected:(ARTProtocolMessage *)message { self.connectionId = message.connectionId; switch (self.state) { @@ -1180,8 +1213,6 @@ - (void)onDisconnected:(ARTProtocolMessage *)message { } } - - - (void)onError:(ARTProtocolMessage *)message { // TODO work out which states this can be received in @@ -1204,20 +1235,19 @@ - (void)onNack:(ARTProtocolMessage *)message { [self nack:message.msgSerial count:message.count]; } - - - - (void)onChannelMessage:(ARTProtocolMessage *)message { // TODO work out which states this can be received in ARTRealtimeChannel *channel = [self.allChannels objectForKey:message.channel]; [channel onChannelMessage:message]; } - - (void)onConnectTimerFired { switch (self.state) { case ARTRealtimeConnecting: [self.logger warn:@"ARTRealtime connecting timer fired."]; + + NSLog(@"ARTRealtime connecting timer fired."); + [self transition:ARTRealtimeFailed]; break; default: @@ -1226,11 +1256,11 @@ - (void)onConnectTimerFired { } } --(void) onCloseTimerFired { +- (void)onCloseTimerFired { [self transition:ARTRealtimeClosed]; } --(void) onPingTimerFired { +- (void)onPingTimerFired { if(self.pingCb) { self.pingCb([ARTStatus state:ARTStatusConnectionFailed]); self.pingCb = nil; @@ -1240,6 +1270,7 @@ -(void) onPingTimerFired { - (void)onSuspended { [self transition:ARTRealtimeSuspended]; } + - (void)onSuspendTimerFired { switch (self.state) { case ARTRealtimeConnected: @@ -1504,7 +1535,6 @@ - (void)realtimeTransportTooBig:(id)transport { [self transition:ARTRealtimeFailed]; } - +(NSString *) protocolStr:(ARTProtocolMessageAction ) action { switch(action) { case ARTProtocolMessageHeartbeat: @@ -1576,6 +1606,7 @@ +(NSString *) ARTRealtimeStateToStr:(ARTRealtimeConnectionState) state @end +#pragma mark - ARTPresence @implementation ARTPresence @@ -1701,8 +1732,12 @@ - (void)unsubscribe:(ARTRealtimeChannelPresenceSubscription *)subscription { ARTRealtimeChannelPresenceSubscription *s = (ARTRealtimeChannelPresenceSubscription *) subscription; [self.channel.presenceSubscriptions removeObject:s]; } + @end + +#pragma mark - ARTEventEmitter + @implementation ARTEventEmitter -(instancetype) initWithRealtime:(ARTRealtime *) realtime { @@ -1720,5 +1755,4 @@ -(instancetype) initWithRealtime:(ARTRealtime *) realtime { return subscription; } - -@end \ No newline at end of file +@end diff --git a/ably-ios/ARTRest.h b/ably-ios/ARTRest.h index 06406a066..355b7e31d 100644 --- a/ably-ios/ARTRest.h +++ b/ably-ios/ARTRest.h @@ -19,6 +19,9 @@ @class ARTAuthTokenParams; @class ARTRestPresence; + +# pragma mark - ARTRestChannel + @interface ARTRestChannel : NSObject - (id)publish:(id)payload withName:(NSString *)name cb:(ARTStatusCallback)cb; @@ -28,36 +31,41 @@ - (id)historyWithParams:(NSDictionary *)queryParams cb:(ARTPaginatedResultCallback)callback; @property (readonly, strong, nonatomic) ARTRestPresence *presence; + @end + +# pragma mark - ARTRestPresence + @interface ARTRestPresence : NSObject + - (instancetype) initWithChannel:(ARTRestChannel *) channel; - (id)get:(ARTPaginatedResultCallback)callback; - (id)getWithParams:(NSDictionary *)queryParams cb:(ARTPaginatedResultCallback)callback; - (id)history:(ARTPaginatedResultCallback)callback; - (id)historyWithParams:(NSDictionary *)queryParams cb:(ARTPaginatedResultCallback)callback; + @end -@interface ARTRest : NSObject -{ -} +# pragma mark - ARTRest + +@interface ARTRest : NSObject -@property (nonatomic, strong) ARTLog * logger; - (instancetype)init UNAVAILABLE_ATTRIBUTE; --(instancetype) initWithOptions:(ARTClientOptions *) options; --(instancetype) initWithKey:(NSString *) key; -- (id) token:(ARTAuthTokenParams *) keyName tokenCb:(void (^)(ARTStatus * status, ARTTokenDetails *)) cb; +- (instancetype)initWithOptions:(ARTClientOptions *)options; +- (instancetype)initWithLogger:(ARTLog *)logger andOptions:(ARTClientOptions *)options; +- (instancetype)initWithKey:(NSString *)key; -- (id)time:(void(^)(ARTStatus * status, NSDate *time))cb; +- (id)token:(ARTAuthTokenParams *)keyName tokenCb:(void (^)(ARTStatus * status, ARTTokenDetails *)) cb; +- (id)time:(void(^)(ARTStatus *status, NSDate *time))cb; - (id)stats:(ARTStatsQuery *)query callback:(ARTPaginatedResultCallback)callback; - (id)internetIsUp:(void (^)(bool isUp)) cb; - (ARTRestChannel *)channel:(NSString *)channelName; - (ARTRestChannel *)channel:(NSString *)channelName cipherParams:(ARTCipherParams *)cipherParams; +- (ARTAuth *)auth; --(ARTAuth *) auth; - - +@property (nonatomic, strong, readonly) ARTLog *logger; @end diff --git a/ably-ios/ARTRest.m b/ably-ios/ARTRest.m index d26290bee..59cf83d59 100644 --- a/ably-ios/ARTRest.m +++ b/ably-ios/ARTRest.m @@ -70,7 +70,6 @@ @implementation ARTRestChannel - (instancetype)initWithRest:(ARTRest *)rest name:(NSString *)name cipherParams:(ARTCipherParams *)cipherParams { self = [super init]; if (self) { - self.logger = rest.logger; _presence = [[ARTRestPresence alloc] initWithChannel:self]; [self.logger debug:[NSString stringWithFormat:@"ARTRestChannel: instantiating under %@", name]]; @@ -153,16 +152,19 @@ + (instancetype)channelWithRest:(ARTRest *)rest name:(NSString *)name cipherPara }]; } +@end -@end +#pragma mark - ARTRest @implementation ARTRest --(instancetype) initWithOptions:(ARTClientOptions *) options { +- (instancetype)initWithLogger:(ARTLog *)logger andOptions:(ARTClientOptions *)options { self = [super init]; - if(self) { + if (self) { + _logger = logger; _options = options; + self.baseUrl = [options restUrl]; [self setup]; _auth = [[ARTAuth alloc] initWithRest:self options:options.authOptions]; @@ -170,15 +172,16 @@ -(instancetype) initWithOptions:(ARTClientOptions *) options { return self; } --(instancetype) initWithKey:(NSString *) key { +- (instancetype)initWithOptions:(ARTClientOptions *)options { + return [self initWithLogger:[[ARTLog alloc] init] andOptions:options]; +} + +- (instancetype)initWithKey:(NSString *) key { return [self initWithOptions:[ARTClientOptions optionsWithKey:key]]; } -- (void) setup { +- (void)setup { _http = [[ARTHttp alloc] init]; - - Class loggerClass = self->_options.loggerClass ?: [ARTLog class]; - self->_logger = [[loggerClass alloc] init]; _channels = [NSMutableDictionary dictionary]; id defaultEncoder = [[ARTJsonEncoder alloc] init]; @@ -188,7 +191,6 @@ - (void) setup { _defaultEncoding = [defaultEncoder mimeType]; _fallbackCount = 0; - } - (id) token:(ARTAuthTokenParams *) params tokenCb:(void (^)(ARTStatus *status, ARTTokenDetails *)) cb { diff --git a/ably-ios/ARTWebSocketTransport.h b/ably-ios/ARTWebSocketTransport.h index c41b1394b..c88273e3a 100644 --- a/ably-ios/ARTWebSocketTransport.h +++ b/ably-ios/ARTWebSocketTransport.h @@ -17,11 +17,9 @@ @interface ARTWebSocketTransport : NSObject - -@property (nonatomic, weak) ARTLog * logger; -@property (readwrite, weak, nonatomic) id delegate; - - (instancetype)init UNAVAILABLE_ATTRIBUTE; - (instancetype)initWithRest:(ARTRest *)rest options:(ARTClientOptions *)options; +@property (readwrite, weak, nonatomic) id delegate; + @end diff --git a/ably-ios/ARTWebSocketTransport.m b/ably-ios/ARTWebSocketTransport.m index f5c989b8c..5659b88ea 100644 --- a/ably-ios/ARTWebSocketTransport.m +++ b/ably-ios/ARTWebSocketTransport.m @@ -13,6 +13,7 @@ #import "ARTRest.h" #import "ARTRest+Private.h" #import "ARTLog.h" + enum { ARTWsNeverConnected = -1, ARTWsBuggyClose = -2, @@ -36,7 +37,7 @@ @interface ARTWebSocketTransport () @property (readwrite, strong, nonatomic) SRWebSocket *websocket; @property (readwrite, assign, nonatomic) BOOL closing; @property (readwrite, strong, nonatomic) id encoder; - +@property (readwrite, strong, nonatomic) ARTLog *logger; @end @@ -53,7 +54,7 @@ - (instancetype)initWithRest:(ARTRest *)rest options:(ARTClientOptions *)options __weak ARTWebSocketTransport *wSelf = self; __weak ARTRest *wRest = rest; - + _logger = rest.logger; BOOL echoMessages = options.echoMessages; NSString *clientId = options.clientId; @@ -84,15 +85,15 @@ - (instancetype)initWithRest:(ARTRest *)rest options:(ARTClientOptions *)options if(options.recover) { NSArray * parts = [options.recover componentsSeparatedByString:@":"]; - if([parts count] ==2) { + if([parts count] == 2) { NSString * conId = [parts objectAtIndex:0]; NSString * key = [parts objectAtIndex:1]; - [self.logger info:[NSString stringWithFormat:@"attempting recovery of connection %@", conId]]; + [wRest.logger info:[NSString stringWithFormat:@"attempting recovery of connection %@", conId]]; queryParams[@"recover"] = conId; queryParams[@"connection_serial"] = key; } else { - [self.logger error:[NSString stringWithFormat:@"recovery string is malformed, ignoring: '%@'", options.recover]]; + [wRest.logger error:[NSString stringWithFormat:@"recovery string is malformed, ignoring: '%@'", options.recover]]; } } else if(options.resumeKey != nil) { @@ -105,8 +106,8 @@ - (instancetype)initWithRest:(ARTRest *)rest options:(ARTClientOptions *)options NSString *queryString = [sRest formatQueryParams:queryParams]; NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"wss://%@:%d/?%@", realtimeHost, realtimePort, queryString]]; - - [self.logger debug:[NSString stringWithFormat:@"Websocket url: %@", url]]; + + [wRest.logger debug:[NSString stringWithFormat:@"ARTWebSocketTransport: url: %@", url]]; sSelf.websocket = [[SRWebSocket alloc] initWithURL:url]; sSelf.websocket.delegate = sSelf; [sSelf.websocket setDelegateDispatchQueue:sSelf.q]; @@ -127,6 +128,7 @@ - (void)send:(ARTProtocolMessage *)msg { } - (void)connect { + [self.logger debug:[NSString stringWithFormat:@"ARTWebSocketTransport: websocket connect"]]; [self.websocket open]; } @@ -153,9 +155,13 @@ - (void)abort:(ARTStatus *)reason { self.websocket.delegate = nil; } + +#pragma mark - SRWebSocketDelegate + - (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message { ARTWebSocketTransport * __weak weakSelf = self; - + [self.logger debug:[NSString stringWithFormat:@"ARTWebSocketTransport: websocket did receive message %@", message]]; + CFRunLoopPerformBlock(self.rl, kCFRunLoopDefaultMode, ^{ NSData *data = nil; if ([message isKindOfClass:[NSString class]]) { @@ -170,8 +176,7 @@ - (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message { } ARTWebSocketTransport *s = weakSelf; - if(s) - { + if (s) { ARTProtocolMessage *pm = [s.encoder decodeProtocolMessage:data]; [s.delegate realtimeTransport:s didReceiveMessage:pm]; } @@ -181,10 +186,11 @@ - (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message { - (void)webSocketDidOpen:(SRWebSocket *)webSocket { ARTWebSocketTransport * __weak weakSelf = self; + [self.logger debug:[NSString stringWithFormat:@"ARTWebSocketTransport: websocket did open"]]; + CFRunLoopPerformBlock(self.rl, kCFRunLoopDefaultMode, ^{ ARTWebSocketTransport *s = weakSelf; - if(s) - { + if (s) { [s.delegate realtimeTransportAvailable:s]; } }); @@ -193,12 +199,11 @@ - (void)webSocketDidOpen:(SRWebSocket *)webSocket { - (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error { ARTWebSocketTransport * __weak weakSelf = self; + [self.logger error:[NSString stringWithFormat:@"ARTWebSocketTransport: websocket did fail with error %@", error]]; + CFRunLoopPerformBlock(self.rl, kCFRunLoopDefaultMode, ^{ ARTWebSocketTransport *s = weakSelf; - if(error) { - [self.logger error:[NSString stringWithFormat:@"ARTWebSocketTransport: websocket did fail with error %@", error]]; - } - if(s) { + if (s) { [s.delegate realtimeTransportFailed:s]; } }); @@ -207,6 +212,8 @@ - (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error { - (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean { ARTWebSocketTransport * __weak weakSelf = self; + [self.logger debug:[NSString stringWithFormat:@"ARTWebSocketTransport: websocket did close with reason %@", reason]]; + CFRunLoopPerformBlock(self.rl, kCFRunLoopDefaultMode, ^{ ARTWebSocketTransport *s = weakSelf; if(!s) diff --git a/ably.xcodeproj/project.pbxproj b/ably.xcodeproj/project.pbxproj index 2bcaec071..e98b88969 100644 --- a/ably.xcodeproj/project.pbxproj +++ b/ably.xcodeproj/project.pbxproj @@ -116,6 +116,7 @@ isEditable = 1; outputFiles = ( ); + script = ""; }; /* End PBXBuildRule section */ @@ -370,6 +371,12 @@ 96BF61341A35B2AB004CF2B3 /* Supporting Files */, 96BF61511A35B39C004CF2B3 /* ARTRest.h */, 96BF61521A35B39C004CF2B3 /* ARTRest.m */, + 96A507BB1A3791490077CDF8 /* ARTRealtime.h */, + 96A507BC1A3791490077CDF8 /* ARTRealtime.m */, + 96E408451A3895E800087F77 /* ARTWebSocketTransport.h */, + 96E408461A3895E800087F77 /* ARTWebSocketTransport.m */, + 961343D61A42E0B7006DC822 /* ARTClientOptions.h */, + 961343D71A42E0B7006DC822 /* ARTClientOptions.m */, 96BF61551A35B40E004CF2B3 /* ARTStatus.h */, 1C55427C1B148306003068DB /* ARTStatus.m */, 96BF61561A35B52C004CF2B3 /* ARTHttp.h */, @@ -395,17 +402,11 @@ 96A507AC1A3780F60077CDF8 /* ARTJsonEncoder.m */, 96A507B31A37881C0077CDF8 /* ARTNSDate+ARTUtil.h */, 96A507B41A37881C0077CDF8 /* ARTNSDate+ARTUtil.m */, - 96A507BB1A3791490077CDF8 /* ARTRealtime.h */, - 96A507BC1A3791490077CDF8 /* ARTRealtime.m */, 96E4083D1A3892C700087F77 /* ARTRealtimeTransport.h */, 96E408411A38939E00087F77 /* ARTProtocolMessage.h */, 96E408421A38939E00087F77 /* ARTProtocolMessage.m */, - 96E408451A3895E800087F77 /* ARTWebSocketTransport.h */, - 96E408461A3895E800087F77 /* ARTWebSocketTransport.m */, 967A431F1A39AEAF00E4CE23 /* ARTNSArray+ARTFunctional.h */, 967A43201A39AEAF00E4CE23 /* ARTNSArray+ARTFunctional.m */, - 961343D61A42E0B7006DC822 /* ARTClientOptions.h */, - 961343D71A42E0B7006DC822 /* ARTClientOptions.m */, 961343E61A432E7C006DC822 /* ARTPayload.h */, 961343E71A432E7C006DC822 /* ARTPayload.m */, 960D07911A45F1D800ED8C8C /* ARTCrypto.h */, diff --git a/ably.xcodeproj/xcshareddata/xcschemes/ably.xcscheme b/ably.xcodeproj/xcshareddata/xcschemes/ably.xcscheme index 72613936f..8e9e9b2e1 100644 --- a/ably.xcodeproj/xcshareddata/xcschemes/ably.xcscheme +++ b/ably.xcodeproj/xcshareddata/xcschemes/ably.xcscheme @@ -21,11 +21,11 @@ + buildForAnalyzing = "NO"> - - - - Date: Tue, 29 Sep 2015 17:05:53 +0100 Subject: [PATCH 04/22] RTC1e: done. (Fixed environment property of ARTClientOptions... _keep it simple_) --- ably-ios/ARTAuth.m | 2 +- ably-ios/ARTClientOptions+Private.h | 2 - ably-ios/ARTClientOptions.h | 17 +++---- ably-ios/ARTClientOptions.m | 77 ++++------------------------- ably-ios/ARTDefault.h | 7 ++- ably-ios/ARTDefault.m | 13 ++++- ably-ios/ARTHttp.m | 2 +- ably-ios/ARTRealtime.m | 13 ++--- ably-ios/ARTRest.m | 19 ++++--- ably-ios/ARTWebSocketTransport.m | 2 +- ably.xcodeproj/project.pbxproj | 2 +- ablySpec/RealtimeClient.swift | 37 +++++--------- ablySpec/RestClient.stats.swift | 4 +- ablySpec/RestClient.swift | 2 - ablySpec/TestUtilities.swift | 17 +++---- 15 files changed, 79 insertions(+), 137 deletions(-) diff --git a/ably-ios/ARTAuth.m b/ably-ios/ARTAuth.m index 4684cd2c9..8199d4c85 100644 --- a/ably-ios/ARTAuth.m +++ b/ably-ios/ARTAuth.m @@ -343,7 +343,7 @@ -(void) prepConnection { }]; } else { - [self.logger error:@"ARTAuth has no ARTRest to use to request a token"]; + [weakSelf.logger error:@"ARTAuth has no ARTRest to use to request a token"]; } }); ic.cancellable = c; diff --git a/ably-ios/ARTClientOptions+Private.h b/ably-ios/ARTClientOptions+Private.h index f525b383f..31da3bffe 100644 --- a/ably-ios/ARTClientOptions+Private.h +++ b/ably-ios/ARTClientOptions+Private.h @@ -9,7 +9,5 @@ @interface ARTClientOptions (Private) { } -+ (NSString *)getDefaultRestHost:(NSString *) replacement modify:(bool) modify; -+ (NSString *)getDefaultRealtimeHost:(NSString *) replacement modify:(bool) modify; @end \ No newline at end of file diff --git a/ably-ios/ARTClientOptions.h b/ably-ios/ARTClientOptions.h index 7edb06925..fe01bd4de 100644 --- a/ably-ios/ARTClientOptions.h +++ b/ably-ios/ARTClientOptions.h @@ -14,32 +14,29 @@ @property (readwrite, strong, nonatomic) ARTAuthOptions *authOptions; @property (readwrite, strong, nonatomic) NSString *clientId; -@property ( strong, nonatomic) NSString *restHost; + +@property (readonly, getter=getRestHost) NSString *restHost; +@property (readonly, getter=getRealtimeHost) NSString *realtimeHost; + @property (readwrite, assign, nonatomic) int restPort; @property (readwrite, assign, nonatomic) int realtimePort; +@property (readwrite, strong, nonatomic) NSString *environment; @property (readwrite, assign, nonatomic) BOOL queueMessages; @property (readwrite, assign, nonatomic) BOOL echoMessages; @property (readwrite, assign, nonatomic) BOOL binary; @property (readwrite, assign, nonatomic) BOOL autoConnect; -@property (readwrite, strong, nonatomic) NSString *environment; @property (readwrite, assign, nonatomic) int64_t connectionSerial; @property (readwrite, copy, nonatomic) NSString *resumeKey; @property (readwrite, copy, nonatomic) NSString *recover; -@property (readonly, strong, nonatomic) NSURL *restUrl; - (instancetype)init; - (instancetype)initWithKey:(NSString *)key; +- (bool)isFallbackPermitted; -// realtime requires a rest host so we explictly set both together when using realtime. -- (void) setRealtimeHost:(NSString *)realtimeHost withRestHost:(NSString *) restHost; -- (NSString *) realtimeHost; --(bool) isFallbackPermitted; - -+ (NSURL *) restUrl:(NSString *) host port:(int) port; + (instancetype)options; + (instancetype)optionsWithKey:(NSString *)key; -- (instancetype)clone; ++ (NSURL*)restUrl:(NSString *)host port:(int)port; @end diff --git a/ably-ios/ARTClientOptions.m b/ably-ios/ARTClientOptions.m index 76c9d30cd..2d6d53447 100644 --- a/ably-ios/ARTClientOptions.m +++ b/ably-ios/ARTClientOptions.m @@ -12,30 +12,12 @@ @interface ARTClientOptions () -@property (readwrite, strong, nonatomic) NSString *realtimeHost; - - (instancetype)initDefaults; @end @implementation ARTClientOptions -+(NSString *) getDefaultRestHost:(NSString *) replacement modify:(bool) modify { - static NSString * restHost =@"rest.ably.io"; - if (modify) { - restHost = replacement; - } - return restHost; -} - -+(NSString *) getDefaultRealtimeHost:(NSString *) replacement modify:(bool) modify { - static NSString * realtimeHost =@"realtime.ably.io"; - if (modify) { - realtimeHost = replacement; - } - return realtimeHost; -} - - (instancetype)init { self = [super init]; if (self) { @@ -61,22 +43,16 @@ - (instancetype)initWithKey:(NSString *)key { return self; } --(NSString *) restHost { - return _environment ?[NSString stringWithFormat:@"%@-%@", _environment, _restHost] : _restHost; -} - --(NSString * ) defaultRestHost { - return [ARTClientOptions getDefaultRestHost:@"" modify:false]; +- (NSString*)getRestHost { + return _environment ? [NSString stringWithFormat:@"%@-%@", _environment, [ARTDefault restHost]] : [ARTDefault restHost]; } --(NSString *) defaultRealtimeHost { - return [ARTClientOptions getDefaultRealtimeHost:@"" modify:false]; +- (NSString*)getRealtimeHost { + return _environment ? [NSString stringWithFormat:@"%@-%@", _environment, [ARTDefault realtimeHost]] : [ARTDefault realtimeHost]; } - (instancetype)initDefaults { _clientId = nil; - self.restHost = [self defaultRestHost]; - _realtimeHost = [self defaultRealtimeHost]; _restPort = [ARTDefault TLSPort]; _realtimePort = [ARTDefault TLSPort]; _queueMessages = YES; @@ -98,49 +74,14 @@ + (instancetype)optionsWithKey:(NSString *)key { return [[ARTClientOptions alloc] initWithKey:key]; } -+(NSURL *) restUrl:(NSString *) host port:(int) port { - NSString *s = [NSString stringWithFormat:@"https://%@:%d", host, port]; - return [NSURL URLWithString:s]; -} -- (NSURL *)restUrl { - return [ARTClientOptions restUrl:self.restHost port:self.restPort]; -} - -- (instancetype)clone { - ARTClientOptions *options = [[ARTClientOptions alloc] init]; - options.authOptions = [self.authOptions clone]; - if (!options.authOptions) { - return nil; - } - - options.clientId = self.clientId; - options.restHost = self.restHost; - options.realtimeHost = self.realtimeHost; - options.restPort = self.restPort; - options.realtimePort = self.realtimePort; - options.queueMessages = self.queueMessages; - options.echoMessages = self.echoMessages; - options.recover = self.recover; - options.binary = self.binary; - options.autoConnect = self.autoConnect; - options.connectionSerial = self.connectionSerial; - options.resumeKey = self.resumeKey; - options.environment = self.environment; - - return options; -} - --(void) setRealtimeHost:(NSString *)realtimeHost withRestHost:(NSString *) restHost { - self.realtimeHost = realtimeHost; - self.restHost = restHost; -} - -- (NSString *)realtimeHost { - return _environment ?[NSString stringWithFormat:@"%@-%@", _environment, _realtimeHost] : _realtimeHost; ++ (NSURL*)restUrl:(NSString *)host port:(int)port { + NSString *urlStr = [NSString stringWithFormat:@"https://%@:%d", host, port]; + return [NSURL URLWithString:urlStr]; } - (bool)isFallbackPermitted { - return [self.restHost isEqualToString:[self defaultRestHost]]; + // FIXME: self.restHost is immutable! + return [self.restHost isEqualToString:[ARTDefault restHost]]; } @end diff --git a/ably-ios/ARTDefault.h b/ably-ios/ARTDefault.h index c86a6b10e..8d1f2e269 100644 --- a/ably-ios/ARTDefault.h +++ b/ably-ios/ARTDefault.h @@ -9,12 +9,15 @@ #import @interface ARTDefault : NSObject { - + } -+ (NSArray *)fallbackHosts; ++ (NSArray*)fallbackHosts; ++ (NSString*)restHost; ++ (NSString*)realtimeHost; + (int)TLSPort; + (NSTimeInterval)connectTimeout; + (NSTimeInterval)disconnectTimeout; + (NSTimeInterval)suspendTimeout; + @end diff --git a/ably-ios/ARTDefault.m b/ably-ios/ARTDefault.m index 717a1d761..ffd847c1b 100644 --- a/ably-ios/ARTDefault.m +++ b/ably-ios/ARTDefault.m @@ -10,10 +10,21 @@ @implementation ARTDefault -+(NSArray *)fallbackHosts { +NSString *const DefaultRestHost = @"rest.ably.io"; +NSString *const DefaultRealtimeHost = @"realtime.ably.io"; + ++ (NSArray*)fallbackHosts { return @[@"A.ably-realtime.com", @"B.ably-realtime.com", @"C.ably-realtime.com", @"D.ably-realtime.com", @"E.ably-realtime.com"]; } ++ (NSString*)restHost { + return DefaultRestHost; +} + ++ (NSString*)realtimeHost { + return DefaultRealtimeHost; +} + + (int)TLSPort { return 443; } diff --git a/ably-ios/ARTHttp.m b/ably-ios/ARTHttp.m index 4146ef50e..8c520f7aa 100644 --- a/ably-ios/ARTHttp.m +++ b/ably-ios/ARTHttp.m @@ -13,7 +13,7 @@ @interface ARTHttp () -@property (readonly, strong, nonatomic) NSURL *baseUrl; +@property (readonly, copy, nonatomic) NSURL *baseUrl; @property (readonly, strong, nonatomic) NSURLSession *urlSession; @end diff --git a/ably-ios/ARTRealtime.m b/ably-ios/ARTRealtime.m index de8470b32..5de42be3a 100644 --- a/ably-ios/ARTRealtime.m +++ b/ably-ios/ARTRealtime.m @@ -172,8 +172,8 @@ @interface ARTRealtime () @property (readonly, strong, nonatomic) NSMutableArray *stateSubscriptions; @property (nonatomic, copy) ARTRealtimePingCb pingCb; -@property (readonly, strong, nonatomic) ARTClientOptions *options; -@property (readwrite, strong, nonatomic) ARTErrorInfo * errorReason; +@property (readonly, weak, nonatomic) ARTClientOptions *options; +@property (readwrite, strong, nonatomic) ARTErrorInfo *errorReason; - (void)transition:(ARTRealtimeConnectionState)state; @@ -194,7 +194,6 @@ - (void)cancelRetryTimer; - (void)cancelPingTimer; - (void)cancelCloseTimer; - // Timer events - (void)onConnectTimerFired; - (void)onSuspendTimerFired; @@ -839,6 +838,8 @@ - (instancetype)initWithKey:(NSString *)key { - (instancetype)initWithLogger:(ARTLog *)logger andOptions:(ARTClientOptions *)options { self = [super init]; if (self) { + NSAssert(options, @"ARTRealtime: No options provided"); + _rest = [[ARTRest alloc] initWithLogger:logger andOptions:options]; _eventEmitter = [[ARTEventEmitter alloc] initWithRealtime:self]; _allChannels = [NSMutableDictionary dictionary]; @@ -853,7 +854,7 @@ - (instancetype)initWithLogger:(ARTLog *)logger andOptions:(ARTClientOptions *)o _pendingMessages = [NSMutableArray array]; _pendingMessageStartSerial = 0; _clientId = options.clientId; - _options = [options clone]; + _options = options; _stateSubscriptions = [NSMutableArray array]; _errorReason = [[ARTErrorInfo alloc] init]; @@ -894,11 +895,11 @@ - (NSString *)recoveryKey { } } -- (ARTAuth *) auth { +- (ARTAuth *)auth { return self.rest.auth; } -- (NSDictionary *) channels { +- (NSDictionary *)channels { return _allChannels; } diff --git a/ably-ios/ARTRest.m b/ably-ios/ARTRest.m index 59cf83d59..32bf3e879 100644 --- a/ably-ios/ARTRest.m +++ b/ably-ios/ARTRest.m @@ -50,13 +50,13 @@ + (instancetype)channelWithRest:(ARTRest *)rest name:(NSString *)name cipherPara @interface ARTRest () @property (readonly, strong, nonatomic) ARTHttp *http; -@property (readonly, strong, nonatomic) ARTClientOptions * options; +@property (readonly, weak, nonatomic) ARTClientOptions *options; @property (readonly, strong, nonatomic) NSMutableDictionary *channels; @property ( strong, nonatomic) ARTAuth *auth; @property (readonly, strong, nonatomic) NSDictionary *encoders; @property (readonly, strong, nonatomic) NSString *defaultEncoding; @property (readwrite, assign, nonatomic) int fallbackCount; -@property (readwrite, strong, nonatomic) NSURL *baseUrl; +@property (readwrite, copy, nonatomic) NSURL *baseUrl; - (id)makeRequestWithMethod:(NSString *)method relUrl:(NSString *)relUrl headers:(NSDictionary *)headers body:(NSData *)body authenticated:(ARTAuthentication)authenticated cb:(ARTHttpCb)cb; @@ -162,10 +162,17 @@ @implementation ARTRest - (instancetype)initWithLogger:(ARTLog *)logger andOptions:(ARTClientOptions *)options { self = [super init]; if (self) { - _logger = logger; + NSAssert(options, @"ARTRest: No options provided"); _options = options; - self.baseUrl = [options restUrl]; + if (logger) { + _logger = logger; + } + else { + _logger = [[ARTLog alloc] init]; + } + + self.baseUrl = [ARTClientOptions restUrl:self.options.restHost port:self.options.restPort]; [self setup]; _auth = [[ARTAuth alloc] initWithRest:self options:options.authOptions]; } @@ -329,14 +336,14 @@ -(bool) isAnErrorStatus:(int) status { } NSString * nextFallbackHost = [theFb popFallbackHost]; if(nextFallbackHost != nil) { - self.baseUrl =[ARTClientOptions restUrl:nextFallbackHost port:self.options.restPort]; + self.baseUrl = [ARTClientOptions restUrl:nextFallbackHost port:self.options.restPort]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ [s makeRequestWithMethod:method relUrl:relUrl headers:headers body:body authenticated:authenticated fb:theFb cb:cb]; }); } else { [self.logger warn:@"ARTRest has no more fallback hosts to attempt. Giving up."]; - self.baseUrl = [self.options restUrl]; + self.baseUrl = [ARTClientOptions restUrl:self.options.restHost port:self.options.restPort]; cb(response); return; } diff --git a/ably-ios/ARTWebSocketTransport.m b/ably-ios/ARTWebSocketTransport.m index 5659b88ea..f0abeafd1 100644 --- a/ably-ios/ARTWebSocketTransport.m +++ b/ably-ios/ARTWebSocketTransport.m @@ -37,7 +37,7 @@ @interface ARTWebSocketTransport () @property (readwrite, strong, nonatomic) SRWebSocket *websocket; @property (readwrite, assign, nonatomic) BOOL closing; @property (readwrite, strong, nonatomic) id encoder; -@property (readwrite, strong, nonatomic) ARTLog *logger; +@property (readonly, strong, nonatomic) ARTLog *logger; @end diff --git a/ably.xcodeproj/project.pbxproj b/ably.xcodeproj/project.pbxproj index e98b88969..fc5ed5b6a 100644 --- a/ably.xcodeproj/project.pbxproj +++ b/ably.xcodeproj/project.pbxproj @@ -376,6 +376,7 @@ 96E408451A3895E800087F77 /* ARTWebSocketTransport.h */, 96E408461A3895E800087F77 /* ARTWebSocketTransport.m */, 961343D61A42E0B7006DC822 /* ARTClientOptions.h */, + 1C70CB871B020D7F003D295A /* ARTClientOptions+Private.h */, 961343D71A42E0B7006DC822 /* ARTClientOptions.m */, 96BF61551A35B40E004CF2B3 /* ARTStatus.h */, 1C55427C1B148306003068DB /* ARTStatus.m */, @@ -418,7 +419,6 @@ 1C6C18A11ADFDAB100AB79E4 /* ARTLog.h */, 1C6C18A21ADFDAB100AB79E4 /* ARTLog.m */, 1C8065041AE7C8FA00D49357 /* ARTPayload+Private.h */, - 1C70CB871B020D7F003D295A /* ARTClientOptions+Private.h */, 1CEFC60D1B0F9EF500D84463 /* ARTTokenDetails+Private.h */, 1C2B0FFB1B136A6D00E3633C /* ARTPresenceMap.h */, 1C2B0FFC1B136A6D00E3633C /* ARTPresenceMap.m */, diff --git a/ablySpec/RealtimeClient.swift b/ablySpec/RealtimeClient.swift index bb71ae3ae..2c0f03abb 100644 --- a/ablySpec/RealtimeClient.swift +++ b/ablySpec/RealtimeClient.swift @@ -20,11 +20,16 @@ class RealtimeClient: QuickSpec { let options = AblyTests.commonAppSetup() //Same as Rest let client = ARTRealtime(options: options) + let expectation = self.expectationWithDescription("async") + client.eventEmitter.on { state in if state != .Connecting { expect(state).to(equal(ARTRealtimeConnectionState.Connected)) + expectation.fulfill() } } + + self.waitForExpectationsWithTimeout(10.0, handler: nil) } //RTC1a @@ -53,7 +58,7 @@ class RealtimeClient: QuickSpec { // realtimeHost string, when set, will modify the realtime endpoint host used by this client library //Default: realtime.ably.io - let realtimeHost = options.realtimeHost() + let realtimeHost = options.realtimeHost // TODO: try to swizzle } @@ -62,33 +67,17 @@ class RealtimeClient: QuickSpec { fit("should modify both the REST and realtime endpoint if environment string is assigned") { let options = AblyTests.commonAppSetup() - let logger = ARTLog() - logger.logLevel = .Verbose - - let expectation = self.expectationWithDescription("async") + let oldRestHost = options.restHost + let oldRealtimeHost = options.realtimeHost // Change REST and realtime endpoint hosts options.environment = "test" - //options.realtimePort = 1111 - - //sandbox-rest.ably.io - //sandbox-realtime.ably.io - - let client = ARTRealtime(logger: logger, andOptions: options) - - // FIXME: environment is not working - // Result: test-test-sandbox-realtime - var testState: ARTRealtimeConnectionState = .Connecting - - client.eventEmitter.on { state in - if state != .Connecting { - expect(state).to(equal(ARTRealtimeConnectionState.Connected)) - expectation.fulfill() - } - } - - self.waitForExpectationsWithTimeout(10.0, handler: nil) + expect(options.restHost).to(equal("test-rest.ably.io")) + expect(options.realtimeHost).to(equal("test-realtime.ably.io")) + // Extra care + expect(oldRestHost).to(equal("sandbox-rest.ably.io")) + expect(oldRealtimeHost).to(equal("sandbox-realtime.ably.io")) } } } diff --git a/ablySpec/RestClient.stats.swift b/ablySpec/RestClient.stats.swift index fa359bf51..12eb63703 100644 --- a/ablySpec/RestClient.stats.swift +++ b/ablySpec/RestClient.stats.swift @@ -13,12 +13,12 @@ import SwiftyJSON import Foundation private func postTestStats(stats: JSON) -> ARTClientOptions { - let options = AblyTests.setupOptions(AblyTests.jsonRestOptions); + let options = AblyTests.commonAppSetup() let key = ("\(options.authOptions.keyName):\(options.authOptions.keySecret)" as NSString) .dataUsingEncoding(NSUTF8StringEncoding)! .base64EncodedStringWithOptions(NSDataBase64EncodingOptions(0)) - let request = NSMutableURLRequest(URL: NSURL(string: "https://\(restHost)/stats")!) + let request = NSMutableURLRequest(URL: NSURL(string: "https://\(options.restHost)/stats")!) request.HTTPMethod = "POST" request.HTTPBody = stats.rawData() request.setValue("application/json", forHTTPHeaderField: "Content-Type") diff --git a/ablySpec/RestClient.swift b/ablySpec/RestClient.swift index d7a157163..723abf114 100644 --- a/ablySpec/RestClient.swift +++ b/ablySpec/RestClient.swift @@ -161,8 +161,6 @@ class RestClient: QuickSpec { // RSC11 context("endpoint") { it("should accept an options object with an environment set") { - // reset the default host in order to force ARTClientOptions to compute it - ARTClientOptions.getDefaultRestHost("rest.ably.io", modify: true) let options = AblyTests.commonAppSetup() let newOptions = ARTClientOptions() newOptions.authOptions.keyName = options.authOptions.keyName diff --git a/ablySpec/TestUtilities.swift b/ablySpec/TestUtilities.swift index 40580b60c..24da62727 100644 --- a/ablySpec/TestUtilities.swift +++ b/ablySpec/TestUtilities.swift @@ -16,8 +16,8 @@ import Quick class Configuration : QuickConfiguration { override class func configure(configuration: Quick.Configuration!) { configuration.beforeEach { - ARTClientOptions.getDefaultRestHost("sandbox-rest.ably.io", modify: true) - ARTClientOptions.getDefaultRealtimeHost("sandbox-realtime.ably.io", modify: true) + //ARTClientOptions.getDefaultRestHost("sandbox-rest.ably.io", modify: true) + //ARTClientOptions.getDefaultRealtimeHost("sandbox-realtime.ably.io", modify: true) } } } @@ -29,8 +29,6 @@ func pathForTestResource(resourcePath: String) -> String { let appSetupJson = JSON(data: NSData(contentsOfFile: pathForTestResource("ably-common/test-resources/test-app-setup.json"))!, options: .MutableContainers) -let restHost = "sandbox-rest.ably.io" - let testTimeout: NSTimeInterval = 30 class AblyTests { @@ -38,7 +36,7 @@ class AblyTests { class var jsonRestOptions: ARTClientOptions { get { let options = ARTClientOptions() - options.restHost = restHost + options.environment = "sandbox" options.binary = false return options } @@ -78,11 +76,10 @@ class AblyTests { let appId = response["appId"] let id = key["id"] - let appOptions = options.clone() - appOptions.authOptions.keyName = "\(appId).\(id)" - appOptions.authOptions.keySecret = key["value"].stringValue - appOptions.authOptions.capability = key["capability"].stringValue - return appOptions + options.authOptions.keyName = "\(appId).\(id)" + options.authOptions.keySecret = key["value"].stringValue + options.authOptions.capability = key["capability"].stringValue + return options } return options From 2a85493a8479092b2f8ae021e577f34eefa63e99 Mon Sep 17 00:00:00 2001 From: Ricardo Pereira Date: Tue, 29 Sep 2015 17:32:30 +0100 Subject: [PATCH 05/22] Check if custom logger is assigned correctly --- ablySpec/RealtimeClient.swift | 2 +- ablySpec/RestClient.swift | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ablySpec/RealtimeClient.swift b/ablySpec/RealtimeClient.swift index 2c0f03abb..bdbb9f844 100644 --- a/ablySpec/RealtimeClient.swift +++ b/ablySpec/RealtimeClient.swift @@ -64,7 +64,7 @@ class RealtimeClient: QuickSpec { } //RTC1e - fit("should modify both the REST and realtime endpoint if environment string is assigned") { + it("should modify both the REST and realtime endpoint if environment string is assigned") { let options = AblyTests.commonAppSetup() let oldRestHost = options.restHost diff --git a/ablySpec/RestClient.swift b/ablySpec/RestClient.swift index 723abf114..53b4be7bb 100644 --- a/ablySpec/RestClient.swift +++ b/ablySpec/RestClient.swift @@ -152,9 +152,11 @@ class RestClient: QuickSpec { let client = ARTRest(logger: customLogger, andOptions: options) client.logger.warn("This is a warning") - + expect(Log.interceptedLog.0).to(equal("This is a warning")) expect(Log.interceptedLog.1).to(equal(ARTLogLevel.Warn)) + + expect(client.logger.logLevel).to(equal(customLogger.logLevel)) } } From d8077ee74a6559b75b8e44160e1f067e72395aa2 Mon Sep 17 00:00:00 2001 From: Ricardo Pereira Date: Tue, 29 Sep 2015 18:38:59 +0100 Subject: [PATCH 06/22] ARTStatus cosmetics (Yavor Georgiev) Rename the status property to state Fix the member names of ARTState --- ably-ios/ARTAuth.m | 4 +-- ably-ios/ARTCrypto.m | 20 ++++++------ ably-ios/ARTHttpPaginatedResult.m | 8 ++--- ably-ios/ARTJsonEncoder.m | 6 ++-- ably-ios/ARTPayload.m | 52 +++++++++++++++---------------- ably-ios/ARTRealtime.m | 49 ++++++++++++++--------------- ably-ios/ARTRest.m | 18 +++++------ ably-ios/ARTStatus.h | 28 ++++++++--------- ably-ios/ARTStatus.m | 8 ++--- ablySpec/RestClient.stats.swift | 4 +-- ablySpec/RestClient.swift | 16 +++++----- 11 files changed, 104 insertions(+), 109 deletions(-) diff --git a/ably-ios/ARTAuth.m b/ably-ios/ARTAuth.m index 8199d4c85..24bd8082d 100644 --- a/ably-ios/ARTAuth.m +++ b/ably-ios/ARTAuth.m @@ -443,7 +443,7 @@ - (ARTAuthMethod) getAuthMethod { [self.tokenCbs addObject:c]; if (!self.tokenRequest) { self.tokenRequest = self.authTokenCb(^(ARTStatus * status, ARTTokenDetails *token) { - if(status.status != ARTStatusOk) { + if(status.state != ARTStateOk) { [self.logger error:@"ARTAuth: error fetching token"]; cb(nil); return; @@ -494,7 +494,7 @@ + (ARTSignedTokenRequestCb)defaultSignedTokenRequestCallback:(ARTAuthOptions *)a ARTRest *strongRest = weakRest; if (strongRest) { [strongRest time:^(ARTStatus * status, NSDate *time) { - if (status.status == ARTStatusOk) { + if (status.state == ARTStateOk) { cb((int64_t)([time timeIntervalSince1970] *1000.0)); } else { cb(0); diff --git a/ably-ios/ARTCrypto.m b/ably-ios/ARTCrypto.m index aca30d150..0d8dfe0fc 100644 --- a/ably-ios/ARTCrypto.m +++ b/ably-ios/ARTCrypto.m @@ -132,7 +132,7 @@ - (ARTStatus *)encrypt:(NSData *)plaintext output:(NSData *__autoreleasing *)out if (!buf) { [self.logger error:@"ARTCrypto error encrypting"]; - return [ARTStatus state:ARTStatusError]; + return [ARTStatus state:ARTStateError]; } // Copy the iv first @@ -154,14 +154,14 @@ - (ARTStatus *)encrypt:(NSData *)plaintext output:(NSData *__autoreleasing *)out if (status) { [self.logger error:[NSString stringWithFormat:@"ARTCrypto error encrypting. Status is %d", status]]; free(ciphertextBuf); - return [ARTStatus state: ARTStatusError]; + return [ARTStatus state: ARTStateError]; } ciphertext = [NSData dataWithBytesNoCopy:buf length:(bytesWritten + self.blockLength) freeWhenDone:YES]; if (nil == ciphertext) { [self.logger error:@"ARTCrypto error encrypting. cipher text is nil"]; free(buf); - return [ARTStatus state:ARTStatusError]; + return [ARTStatus state:ARTStateError]; } // Finally update the iv. This should be the last *blockSize* bytes of the cipher text @@ -175,13 +175,13 @@ - (ARTStatus *)encrypt:(NSData *)plaintext output:(NSData *__autoreleasing *)out *output = ciphertext; - return [ARTStatus state:ARTStatusOk]; + return [ARTStatus state:ARTStateOk]; } - (ARTStatus *)decrypt:(NSData *)ciphertext output:(NSData *__autoreleasing *)output { // The first *blockLength* bytes are the iv if ([ciphertext length] < self.blockLength) { - return [ARTStatus state: ARTStatusInvalidArgs];; + return [ARTStatus state: ARTStateInvalidArgs];; } NSData *ivData = [ciphertext subdataWithRange:NSMakeRange(0, self.blockLength)]; @@ -202,7 +202,7 @@ - (ARTStatus *)decrypt:(NSData *)ciphertext output:(NSData *__autoreleasing *)ou if (!buf) { [self.logger error:@"ARTCrypto error decrypting."]; - return [ARTStatus state:ARTStatusError]; + return [ARTStatus state:ARTStateError]; } // Decrypt without padding because CCCrypt does not return an error code @@ -212,7 +212,7 @@ - (ARTStatus *)decrypt:(NSData *)ciphertext output:(NSData *__autoreleasing *)ou if (status) { [self.logger error:[NSString stringWithFormat:@"ARTCrypto error decrypting. Status is %d", status]]; free(buf); - return [ARTStatus state:ARTStatusError]; + return [ARTStatus state:ARTStateError]; } // Check that the decrypted value is padded correctly and determine the unpadded length @@ -220,13 +220,13 @@ - (ARTStatus *)decrypt:(NSData *)ciphertext output:(NSData *__autoreleasing *)ou int paddingLength = cbuf[bytesWritten - 1]; if (0 == paddingLength || paddingLength > bytesWritten) { free(buf); - return [ARTStatus state:ARTStatusCryptoBadPadding]; + return [ARTStatus state:ARTStateCryptoBadPadding]; } for (size_t i=(bytesWritten - 1); i>(bytesWritten - paddingLength); --i) { if (paddingLength != cbuf[i-1]) { free(buf); - return [ARTStatus state:ARTStatusCryptoBadPadding]; + return [ARTStatus state:ARTStateCryptoBadPadding]; } } @@ -240,7 +240,7 @@ - (ARTStatus *)decrypt:(NSData *)ciphertext output:(NSData *__autoreleasing *)ou *output = plaintext; - return [ARTStatus state:ARTStatusOk]; + return [ARTStatus state:ARTStateOk]; } - (NSString *)cipherName { diff --git a/ably-ios/ARTHttpPaginatedResult.m b/ably-ios/ARTHttpPaginatedResult.m index 3f89f7bd5..35bdac0bb 100644 --- a/ably-ios/ARTHttpPaginatedResult.m +++ b/ably-ios/ARTHttpPaginatedResult.m @@ -59,15 +59,15 @@ - (void)next:(ARTPaginatedResultCallback)callback { if (!response) { ARTErrorInfo * info = [[ARTErrorInfo alloc] init]; [info setCode:40000 message:@"ARTHttpPaginatedResult got no response"]; - [ARTStatus state:ARTStatusError info:info]; - callback([ARTStatus state:ARTStatusError info:info], nil); + [ARTStatus state:ARTStateError info:info]; + callback([ARTStatus state:ARTStateError info:info], nil); return; } if (response.status < 200 || response.status >= 300) { ARTErrorInfo * info = [[ARTErrorInfo alloc] init]; [info setCode:40000 message:[NSString stringWithFormat:@"ARTHttpPaginatedResult response.status invalid: %d", response.status]]; - callback([ARTStatus state:ARTStatusError info:info], nil); + callback([ARTStatus state:ARTStateError info:info], nil); return; } @@ -87,7 +87,7 @@ - (void)next:(ARTPaginatedResultCallback)callback { relFirst:firstRelRequest relCurrent:currentRelRequest relNext:nextRelRequest responseProcessor:responseProcessor]; - callback([ARTStatus state:ARTStatusOk], result); + callback([ARTStatus state:ARTStateOk], result); }]; } diff --git a/ably-ios/ARTJsonEncoder.m b/ably-ios/ARTJsonEncoder.m index 518cb877c..29fbdd720 100644 --- a/ably-ios/ARTJsonEncoder.m +++ b/ably-ios/ARTJsonEncoder.m @@ -543,7 +543,7 @@ - (ARTPayload *)payloadFromDictionary:(NSDictionary *)input { ARTPayload *payload = [ARTPayload payloadWithPayload:data encoding:encoding]; ARTPayload *decoded = nil; ARTStatus *status = [[ARTBase64PayloadEncoder instance] decode:payload output:&decoded]; - if (status.status != ARTStatusOk) { + if (status.state != ARTStateOk) { [self.logger error:[NSString stringWithFormat:@"ARTJsonEncoder failed to decode payload %@", payload]]; } return decoded; @@ -552,10 +552,10 @@ - (ARTPayload *)payloadFromDictionary:(NSDictionary *)input { - (void)writePayload:(ARTPayload *)payload toDictionary:(NSMutableDictionary *)output { ARTPayload *encoded = nil; ARTStatus *status = [[ARTBase64PayloadEncoder instance] encode:payload output:&encoded]; - if(status.status != ARTStatusOk) { + if(status.state != ARTStateOk) { [self.logger error:@"ARTJsonEncoder failed to encode payload"]; } - NSAssert(status.status == ARTStatusOk, @"Error encoding payload"); + NSAssert(status.state == ARTStateOk, @"Error encoding payload"); NSAssert([payload.payload isKindOfClass:[NSString class]], @"Only string payloads are accepted"); if (encoded.encoding.length) { diff --git a/ably-ios/ARTPayload.m b/ably-ios/ARTPayload.m index 99fc36480..9eb6017db 100644 --- a/ably-ios/ARTPayload.m +++ b/ably-ios/ARTPayload.m @@ -176,15 +176,15 @@ - (ARTStatus *)encode:(ARTPayload *)payload output:(ARTPayload *__autoreleasing NSString *encoded = [((NSData *)payload.payload) base64EncodedStringWithOptions:0]; if (encoded) { *output = [ARTPayload payloadWithPayload:encoded encoding:[payload.encoding artAddEncoding:[ARTBase64PayloadEncoder getName]]]; - return [ARTStatus state:ARTStatusOk]; + return [ARTStatus state:ARTStateOk]; } else { // Set the output to be the original payload *output = payload; - return [ARTStatus state:ARTStatusError]; + return [ARTStatus state:ARTStateError]; } } *output = payload; - return [ARTStatus state:ARTStatusOk]; + return [ARTStatus state:ARTStateOk]; } - (ARTStatus *)decode:(ARTPayload *)payload output:(ARTPayload *__autoreleasing *)output { @@ -192,14 +192,14 @@ - (ARTStatus *)decode:(ARTPayload *)payload output:(ARTPayload *__autoreleasing NSData *decoded = [[NSData alloc] initWithBase64EncodedString:payload.payload options:0]; if (decoded) { *output = [ARTPayload payloadWithPayload:decoded encoding:[payload.encoding artRemoveLastEncoding]]; - return [ARTStatus state:ARTStatusOk]; + return [ARTStatus state:ARTStateOk]; } // Set the output to be the original payload *output = payload; - return [ARTStatus state:ARTStatusError]; + return [ARTStatus state:ARTStateError]; } *output = payload; - return [ARTStatus state:ARTStatusOk]; + return [ARTStatus state:ARTStateOk]; } @end @@ -228,12 +228,12 @@ - (ARTStatus *)decode:(ARTPayload *)payload output:(ARTPayload *__autoreleasing NSString *decoded = [[NSString alloc] initWithData:payload.payload encoding:NSUTF8StringEncoding]; if (decoded) { *output = [ARTPayload payloadWithPayload:decoded encoding:[payload.encoding artRemoveLastEncoding]]; - return [ARTStatus state:ARTStatusOk]; + return [ARTStatus state:ARTStateOk]; } } - return [ARTStatus state:ARTStatusError]; + return [ARTStatus state:ARTStateError]; } - return [ARTStatus state:ARTStatusOk]; + return [ARTStatus state:ARTStateOk]; } - (ARTStatus *)encode:(ARTPayload *)payload output:(ARTPayload *__autoreleasing *)output { @@ -242,11 +242,11 @@ - (ARTStatus *)encode:(ARTPayload *)payload output:(ARTPayload *__autoreleasing NSData *encoded = [((NSString *)payload.payload) dataUsingEncoding:NSUTF8StringEncoding]; if (encoded) { *output = [ARTPayload payloadWithPayload:encoded encoding:[payload.encoding artAddEncoding:[ARTUtf8PayloadEncoder getName]]]; - return ARTStatusOk; + return ARTStateOk; } - return [ARTStatus state:ARTStatusError]; + return [ARTStatus state:ARTStateError]; } - return [ARTStatus state:ARTStatusOk]; + return [ARTStatus state:ARTStateOk]; } @end @@ -279,16 +279,16 @@ - (ARTStatus *)encode:(ARTPayload *)payload output:(ARTPayload *__autoreleasing if (encoded) { *output = [ARTPayload payloadWithPayload:encoded encoding:[payload.encoding artAddEncoding:[ARTJsonPayloadEncoder getName]]]; - return [ARTStatus state:ARTStatusOk]; + return [ARTStatus state:ARTStateOk]; } else { - return [ARTStatus state:ARTStatusError]; + return [ARTStatus state:ARTStateError]; } } // otherwise do nothing besides confirm payload is nsdata or nsstring else if(!([payload.payload isKindOfClass:[NSData class]] || [payload.payload isKindOfClass:[NSString class]])) { [NSException raise:@"ARTPayload must be either NSDictionary, NSArray, NSData or NSString" format:@"%@", [payload.payload class]]; } - return [ARTStatus state:ARTStatusOk]; + return [ARTStatus state:ARTStateOk]; } @@ -303,12 +303,12 @@ - (ARTStatus *)decode:(ARTPayload *)payload output:(ARTPayload *__autoreleasing if (decoded) { *output = [ARTPayload payloadWithPayload:decoded encoding:[payload.encoding artRemoveLastEncoding]]; - return [ARTStatus state:ARTStatusOk]; + return [ARTStatus state:ARTStateOk]; } } - return [ARTStatus state:ARTStatusError]; + return [ARTStatus state:ARTStateError]; } - return [ARTStatus state:ARTStatusOk]; + return [ARTStatus state:ARTStateOk]; } @end @@ -357,14 +357,14 @@ - (ARTStatus *)decode:(ARTPayload *)payload output:(ARTPayload *__autoreleasing if ([payload.payload isKindOfClass:[NSData class]] && [cipherName isEqualToString:[self name]]) { NSData *decrypted = nil; ARTStatus * status = [self.cipher decrypt:payload.payload output:&decrypted]; - if (status.status == ARTStatusOk) { + if (status.state == ARTStateOk) { *output = [ARTPayload payloadWithPayload:decrypted encoding:[payload.encoding artRemoveLastEncoding]]; [self.logger debug:@"cipher payload decoded successfully"]; } return status; } - return [ARTStatus state:ARTStatusOk]; + return [ARTStatus state:ARTStateOk]; } - (ARTStatus *)encode:(ARTPayload *)payload output:(ARTPayload *__autoreleasing *)output { @@ -372,13 +372,13 @@ - (ARTStatus *)encode:(ARTPayload *)payload output:(ARTPayload *__autoreleasing if ([payload.payload isKindOfClass:[NSData class]]) { NSData *encrypted = nil; ARTStatus *status = [self.cipher encrypt:payload.payload output:&encrypted]; - if (status.status == ARTStatusOk) { + if (status.state == ARTStateOk) { NSString *cipherName = [self name]; *output = [ARTPayload payloadWithPayload:encrypted encoding:[payload.encoding artAddEncoding:cipherName]]; } return status; } - return [ARTStatus state:ARTStatusOk]; + return [ARTStatus state:ARTStateOk]; } @end @@ -402,12 +402,12 @@ -(NSString *) name { } - (ARTStatus *)encode:(ARTPayload *)payload output:(ARTPayload *__autoreleasing *)output { - ARTStatus *status = ARTStatusOk; + ARTStatus *status = ARTStateOk; *output = payload; for (id enc in self.encoders) { status = [enc encode:*output output:output]; - if (status.status != ARTStatusOk) { + if (status.state != ARTStateOk) { break; } } @@ -416,13 +416,13 @@ - (ARTStatus *)encode:(ARTPayload *)payload output:(ARTPayload *__autoreleasing } - (ARTStatus *)decode:(ARTPayload *)payload output:(ARTPayload *__autoreleasing *)output { - ARTStatus *status = [ARTStatus state:ARTStatusOk]; + ARTStatus *status = [ARTStatus state:ARTStateOk]; *output = payload; int count=0; for (id enc in self.encoders.reverseObjectEnumerator) { status = [enc decode:*output output:output]; - if (status.status != ARTStatusOk) { + if (status.state != ARTStateOk) { ARTPayload * p = *output; [self.logger error:[NSString stringWithFormat:@"ARTPayload: error in ARTPayloadEncoderChain decoding with encoder %d. Remaining decoding jobs are %@", count, p.encoding]]; break; diff --git a/ably-ios/ARTRealtime.m b/ably-ios/ARTRealtime.m index 5de42be3a..6e70349cf 100644 --- a/ably-ios/ARTRealtime.m +++ b/ably-ios/ARTRealtime.m @@ -408,7 +408,7 @@ - (void)publishMessages:(NSArray *)messages cb:(ARTStatusCallback)cb { messages = [messages artMap:^id(ARTMessage *message) { ARTPayload *encodedPayload = nil; ARTStatus * status = [self.payloadEncoder encode:message.payload output:&encodedPayload]; - if (status.status != ARTStatusOk) { + if (status.state != ARTStateOk) { [self.logger error:[NSString stringWithFormat:@"ARTRealtime: error decoding payload, status: %tu", status]]; } return [message messageWithPayload:encodedPayload]; @@ -437,7 +437,7 @@ - (void)publishPresence:(ARTPresenceMessage *)msg cb:(ARTStatusCallback)cb { msg.clientId = self.clientId; } if(!msg.clientId) { - cb([ARTStatus state:ARTStatusNoClientId]); + cb([ARTStatus state:ARTStateNoClientId]); return; } _lastPresenceAction = msg.action; @@ -445,7 +445,7 @@ - (void)publishPresence:(ARTPresenceMessage *)msg cb:(ARTStatusCallback)cb { if (msg.payload && self.payloadEncoder) { ARTPayload *encodedPayload = nil; ARTStatus * status = [self.payloadEncoder encode:msg.payload output:&encodedPayload]; - if (status.status != ARTStatusOk) { + if (status.state != ARTStateOk) { [self.logger warn:[NSString stringWithFormat:@"bad status encoding presence message %d",(int) status]]; } msg.payload = encodedPayload; @@ -475,7 +475,7 @@ - (void)publishProtocolMessage:(ARTProtocolMessage *)pm cb:(ARTStatusCallback)cb case ARTRealtimeChannelFailed: { if (cb) { - ARTStatus * status = [ARTStatus state:ARTStatusError]; + ARTStatus *status = [ARTStatus state:ARTStateError]; [status.errorInfo setCode:90001 message:@"invalid channel state"]; cb(status); } @@ -643,18 +643,18 @@ - (void)setAttached:(ARTProtocolMessage *)message { for (ARTPresenceMessage *pm in message.presence) { [self.presenceDict setObject:pm forKey:pm.clientId]; } - [self transition:ARTRealtimeChannelAttached status:ARTStatusOk]; + [self transition:ARTRealtimeChannelAttached status:ARTStateOk]; } - (void)setDetached:(ARTProtocolMessage *)message { self.attachSerial = nil; - ARTStatus *reason = [ARTStatus state:ARTStatusNotAttached info:message.error]; + ARTStatus *reason = [ARTStatus state:ARTStateNotAttached info:message.error]; [self detachChannel:reason]; } - (void)releaseChannel { - [self detachChannel:ARTStatusOk]; + [self detachChannel:ARTStateOk]; [self.realtime.allChannels removeObjectForKey:self.name]; } @@ -747,8 +747,8 @@ - (void)broadcastPresence:(ARTPresenceMessage *)pm { } - (void)onError:(ARTProtocolMessage *)msg { - [self failQueuedMessages:[ARTStatus state:ARTStatusError info: msg.error]]; - [self transition:ARTRealtimeChannelFailed status:[ARTStatus state:ARTStatusError info: msg.error]]; + [self failQueuedMessages:[ARTStatus state:ARTStateError info: msg.error]]; + [self transition:ARTRealtimeChannelFailed status:[ARTStatus state:ARTStateError info: msg.error]]; } @@ -774,7 +774,7 @@ - (BOOL)attach { // TODO should queueEvents be forced? [self.realtime send:attachMessage cb:nil]; - [self transition:ARTRealtimeChannelAttaching status:ARTStatusOk]; + [self transition:ARTRealtimeChannelAttaching status:ARTStateOk]; return true; } @@ -799,7 +799,7 @@ - (BOOL)detach { detachMessage.channel = self.name; [self.realtime send:detachMessage cb:nil]; - [self transition:ARTRealtimeChannelDetaching status:ARTStatusOk]; + [self transition:ARTRealtimeChannelDetaching status:ARTStateOk]; return true; } @@ -814,7 +814,6 @@ - (void)sendQueuedMessages { - (void)failQueuedMessages:(ARTStatus *)status { NSArray *qms = self.queuedMessages; self.queuedMessages = [NSMutableArray array]; - for (ARTQueuedMessage *qm in qms) { qm.cb(status); } @@ -1017,7 +1016,7 @@ - (void)transition:(ARTRealtimeConnectionState)state { ARTRealtimeChannel *channel = [self.allChannels objectForKey:channelName]; ARTErrorInfo * info = [[ARTErrorInfo alloc] init]; [info setCode:80000 message:@"resume connection failed"]; - [channel detachChannel:[ARTStatus state:ARTStatusConnectionDisconnected info:info]]; + [channel detachChannel:[ARTStatus state:ARTStateConnectionDisconnected info:info]]; } } self.options.resumeKey = nil; @@ -1041,12 +1040,12 @@ - (void)transition:(ARTRealtimeConnectionState)state { self.transport = nil; case ARTRealtimeFailed: // reasonFailed doesn't need to be a property on self - [self.transport abort:[ARTStatus state:ARTStatusConnectionFailed]]; + [self.transport abort:[ARTStatus state:ARTStateConnectionFailed]]; self.transport.delegate = nil; self.transport = nil; break; case ARTRealtimeDisconnected: - [self.transport abort:[ARTStatus state:ARTStatusConnectionDisconnected]]; + [self.transport abort:[ARTStatus state:ARTStateConnectionDisconnected]]; self.transport.delegate = nil; self.transport = nil; case ARTRealtimeInitialized: @@ -1167,10 +1166,10 @@ - (void)onHeartbeat:(ARTProtocolMessage *)message { [self cancelPingTimer]; if(self.state != ARTRealtimeConnected) { [self.logger warn:[NSString stringWithFormat:@"ARTRealtime received a ping when in state %@", [ARTRealtime ARTRealtimeStateToStr:self.state]]]; - self.pingCb([ARTStatus state:ARTStatusError]); + self.pingCb([ARTStatus state:ARTStateError]); } else { - self.pingCb([ARTStatus state:ARTStatusOk]); + self.pingCb([ARTStatus state:ARTStateOk]); } self.pingCb = nil; } @@ -1263,7 +1262,7 @@ - (void)onCloseTimerFired { - (void)onPingTimerFired { if(self.pingCb) { - self.pingCb([ARTStatus state:ARTStatusConnectionFailed]); + self.pingCb([ARTStatus state:ARTStateConnectionFailed]); self.pingCb = nil; } } @@ -1322,7 +1321,7 @@ - (NSTimeInterval)retryInterval { } - (ARTStatus *)defaultError { - return [ARTStatus state:ARTStatusError]; + return [ARTStatus state:ARTStateError]; } - (BOOL)isActive { @@ -1358,7 +1357,7 @@ - (void)send:(ARTProtocolMessage *)msg cb:(ARTStatusCallback)cb { } else { // TODO review error code if (cb) { - cb([ARTStatus state:ARTStatusError]); + cb([ARTStatus state:ARTStateError]); } } } @@ -1412,11 +1411,11 @@ - (void)ack:(int64_t)serial count:(int64_t)count { } for (ARTQueuedMessage *msg in nackMessages) { - msg.cb([ARTStatus state:ARTStatusError]); + msg.cb([ARTStatus state:ARTStateError]); } for (ARTQueuedMessage *msg in ackMessages) { - msg.cb([ARTStatus state:ARTStatusOk]); + msg.cb([ARTStatus state:ARTStateOk]); } } @@ -1436,7 +1435,7 @@ - (void)nack:(int64_t)serial count:(int64_t)count { self.pendingMessageStartSerial = serial; for (ARTQueuedMessage *msg in nackMessages) { - msg.cb([ARTStatus state:ARTStatusError]); + msg.cb([ARTStatus state:ARTStateError]); } } @@ -1668,7 +1667,7 @@ - (void)updateClient:(NSString *) clientId data:(id) data cb:(ARTStatusCallback) msg.action = ARTPresenceMessageUpdate; msg.clientId = clientId; if(!msg.clientId) { - cb([ARTStatus state:ARTStatusNoClientId]); + cb([ARTStatus state:ARTStateNoClientId]); return; } if(data) { @@ -1700,7 +1699,7 @@ - (void) leaveClient:(NSString *) clientId data:(id) data cb:(ARTStatusCallback) msg.clientId = clientId; msg.connectionId = self.channel.realtime.connectionId; if(!msg.clientId) { - cb([ARTStatus state:ARTStatusNoClientId]); + cb([ARTStatus state:ARTStateNoClientId]); return; } [self.channel publishPresence:msg cb:cb]; diff --git a/ably-ios/ARTRest.m b/ably-ios/ARTRest.m index 32bf3e879..bddb3b725 100644 --- a/ably-ios/ARTRest.m +++ b/ably-ios/ARTRest.m @@ -95,7 +95,7 @@ + (instancetype)channelWithRest:(ARTRest *)rest name:(NSString *)name cipherPara ARTPayload * p = [ARTPayload payloadWithPayload:[messages objectAtIndex:i] encoding:self.rest.defaultEncoding]; ARTStatus * status = [self.payloadEncoder encode:p output:&encodedPayload]; - if (status.status != ARTStatusOk) { + if (status.state != ARTStateOk) { [self.logger warn:[NSString stringWithFormat:@"ARTRest publishMessages could not encode message %d", i]]; } [encodedMessages addObject:encodedPayload.payload]; @@ -112,7 +112,7 @@ + (instancetype)channelWithRest:(ARTRest *)rest name:(NSString *)name cipherPara NSDictionary *headers = @{@"Content-Type":defaultEncoding}; NSString *path = [NSString stringWithFormat:@"%@/messages", self.basePath]; return [self.rest post:path headers:headers body:encodedMessage authenticated:ARTAuthenticationOn cb:^(ARTHttpResponse *response) { - ARTStatus *status = [ARTStatus state:(response.status >= 200 && response.status < 300 ? ARTStatusOk : ARTStatusError) info:response.error]; + ARTStatus *status = [ARTStatus state:(response.status >= 200 && response.status < 300 ? ARTStateOk : ARTStateError) info:response.error]; cb(status); }]; } @@ -204,7 +204,7 @@ - (void)setup { [self.logger debug:@"ARTRest is requesting a fresh token"]; if(![self.auth canRequestToken]) { - cb([ARTStatus state:ARTStatusError], nil); + cb([ARTStatus state:ARTStateError], nil); id c = nil; return c; } @@ -221,20 +221,20 @@ - (void)setup { NSDictionary *headers = @{@"Content-Type":self.defaultEncoding}; return [self post:keyPath headers:headers body:dictData authenticated:ARTAuthenticationUseBasic cb:^(ARTHttpResponse *response) { if(!response.body) { - cb([ARTStatus state:ARTStatusError info:response.error], nil); + cb([ARTStatus state:ARTStateError info:response.error], nil); return; } NSString * str = [[NSString alloc] initWithData:response.body encoding:NSUTF8StringEncoding]; [self.logger verbose:[NSString stringWithFormat:@"ARTRest token is %@", str]]; if(response.status == 201) { ARTTokenDetails * token =[self.defaultEncoder decodeAccessToken:response.body]; - cb(ARTStatusOk, token); + cb(ARTStateOk, token); } else { ARTErrorInfo * e = [self.defaultEncoder decodeError:response.body]; [self.logger error:[NSString stringWithFormat:@"ARTRest: requestToken Error code: %d, Status %d, Message %@", e.code, e.statusCode, e.message]]; - cb([ARTStatus state:ARTStatusError info:e], nil); + cb([ARTStatus state:ARTStateError info:e], nil); } }]; } @@ -253,9 +253,9 @@ -(ARTAuth *) auth { date = [self.defaultEncoder decodeTime:response.body]; } if (date) { - cb([ARTStatus state:ARTStatusOk], date); + cb([ARTStatus state:ARTStateOk], date); } else { - cb([ARTStatus state:ARTStatusError info:response.error], nil); + cb([ARTStatus state:ARTStateError info:response.error], nil); } }]; } @@ -424,7 +424,7 @@ @implementation ARTRest (Private) NSDictionary *headers = @{@"Content-Type":self.defaultEncoding}; NSData * statsData = [NSJSONSerialization dataWithJSONObject:stats options:0 error:nil]; return [self post:@"/stats" headers:headers body:statsData authenticated:ARTAuthenticationOn cb:^(ARTHttpResponse *response) { - cb([ARTStatus state:ARTStatusOk info:response.error]); + cb([ARTStatus state:ARTStateOk info:response.error]); }]; } diff --git a/ably-ios/ARTStatus.h b/ably-ios/ARTStatus.h index 22c999fa8..1089e0e4d 100644 --- a/ably-ios/ARTStatus.h +++ b/ably-ios/ARTStatus.h @@ -9,19 +9,19 @@ #import typedef NS_ENUM(NSUInteger, ARTState) { - ARTStatusOk = 0, - ARTStatusConnectionClosedByClient, - ARTStatusConnectionDisconnected, - ARTStatusConnectionSuspended, - ARTStatusConnectionFailed, - ARTStatusAccessRefused, - ARTStatusNeverConnected, - ARTStatusConnectionTimedOut, - ARTStatusNotAttached, - ARTStatusInvalidArgs, - ARTStatusCryptoBadPadding, - ARTStatusNoClientId, - ARTStatusError = 99999 + ARTStateOk = 0, + ARTStateConnectionClosedByClient, + ARTStateConnectionDisconnected, + ARTStateConnectionSuspended, + ARTStateConnectionFailed, + ARTStateAccessRefused, + ARTStateNeverConnected, + ARTStateConnectionTimedOut, + ARTStateNotAttached, + ARTStateInvalidArgs, + ARTStateCryptoBadPadding, + ARTStateNoClientId, + ARTStateError = 99999 }; @interface ARTErrorInfo : NSObject @@ -37,7 +37,7 @@ typedef NS_ENUM(NSUInteger, ARTState) { } @property (readonly, strong, nonatomic) ARTErrorInfo * errorInfo; -@property (nonatomic, assign) ARTState status; +@property (nonatomic, assign) ARTState state; +(ARTStatus *) state:(ARTState) state; +(ARTStatus *) state:(ARTState) state info:(ARTErrorInfo *) info; diff --git a/ably-ios/ARTStatus.m b/ably-ios/ARTStatus.m index b0f0cc822..383e14e4a 100644 --- a/ably-ios/ARTStatus.m +++ b/ably-ios/ARTStatus.m @@ -30,19 +30,15 @@ @implementation ARTStatus -(instancetype) init { self = [super init]; if(self) { - _status = ARTStatusOk; + _state = ARTStateOk; _errorInfo =[[ARTErrorInfo alloc] init]; } return self; } --(void) setStatus:(ARTState)status { - _status = status; -} - +(ARTStatus *) state:(ARTState) state { ARTStatus *s = [[ARTStatus alloc] init]; - s.status= state; + s.state = state; return s; } diff --git a/ablySpec/RestClient.stats.swift b/ablySpec/RestClient.stats.swift index 12eb63703..7993137cd 100644 --- a/ablySpec/RestClient.stats.swift +++ b/ablySpec/RestClient.stats.swift @@ -62,7 +62,7 @@ private func queryStats(client: ARTRest, query: ARTStatsQuery) -> ARTPaginatedRe CFRunLoopRunInMode(kCFRunLoopDefaultMode, CFTimeInterval(0.1), Boolean(0)) } - if status!.status != .StatusOk { + if status!.state != .Ok { XCTFail(status!.errorInfo.message) } @@ -81,7 +81,7 @@ private func getPage(paginator: (ARTPaginatedResultCallback!) -> Void) -> ARTPag CFRunLoopRunInMode(kCFRunLoopDefaultMode, CFTimeInterval(0.1), Boolean(0)) } - if status!.status != .StatusOk { + if status!.state != .Ok { XCTFail(status!.errorInfo.message) } diff --git a/ablySpec/RestClient.swift b/ablySpec/RestClient.swift index 53b4be7bb..e8004be92 100644 --- a/ablySpec/RestClient.swift +++ b/ablySpec/RestClient.swift @@ -17,8 +17,8 @@ class PublishTestMessage { init(client: ARTRest, failOnError: Bool) { client.channel("test").publish("message") { status in self.status = status - if failOnError && status.status != .StatusOk { - XCTFail("Got status \(status.status.rawValue) with error '\(status.errorInfo?.message)'") + if failOnError && status.state != .Ok { + XCTFail("Got status \(status.state.rawValue) with error '\(status.errorInfo?.message)'") } } } @@ -58,7 +58,7 @@ class RestClient: QuickSpec { let publishTask = publishTestMessage(client) - expect(publishTask.status?.status).toEventually(equal(ARTState.StatusOk), timeout: testTimeout) + expect(publishTask.status?.state).toEventually(equal(ARTState.Ok), timeout: testTimeout) } it("should throw when provided an invalid key") { @@ -71,7 +71,7 @@ class RestClient: QuickSpec { let publishTask = publishTestMessage(client, failOnError: false) - expect(publishTask.status?.status).toEventually(equal(ARTState.StatusError), timeout: testTimeout) + expect(publishTask.status?.state).toEventually(equal(ARTState.Error), timeout: testTimeout) expect(publishTask.status?.errorInfo.code).toEventually(equal(40005)) } @@ -81,7 +81,7 @@ class RestClient: QuickSpec { let publishTask = publishTestMessage(client) - expect(publishTask.status?.status).toEventually(equal(ARTState.StatusOk), timeout: testTimeout) + expect(publishTask.status?.state).toEventually(equal(ARTState.Ok), timeout: testTimeout) } it("should accept an options object with token authentication") { @@ -93,7 +93,7 @@ class RestClient: QuickSpec { let publishTask = publishTestMessage(client) expect(client.auth().getAuthMethod()).to(equal(ARTAuthMethod.Token)) - expect(publishTask.status?.status).toEventually(equal(ARTState.StatusOk), timeout: testTimeout) + expect(publishTask.status?.state).toEventually(equal(ARTState.Ok), timeout: testTimeout) } it("should result in error status when provided a bad token") { @@ -105,7 +105,7 @@ class RestClient: QuickSpec { let publishTask = publishTestMessage(client, failOnError: false) expect(client.auth().getAuthMethod()).to(equal(ARTAuthMethod.Token)) - expect(publishTask.status?.status).toEventually(equal(ARTState.StatusError), timeout: testTimeout) + expect(publishTask.status?.state).toEventually(equal(ARTState.Error), timeout: testTimeout) expect(publishTask.status?.errorInfo.code).toEventually(equal(40005)) } } @@ -172,7 +172,7 @@ class RestClient: QuickSpec { let publishTask = publishTestMessage(client) - expect(publishTask.status?.status).toEventually(equal(ARTState.StatusOk), timeout: testTimeout) + expect(publishTask.status?.state).toEventually(equal(ARTState.Ok), timeout: testTimeout) } } From 3880ba69c5dac1986d79548d98dfc5447c7426a4 Mon Sep 17 00:00:00 2001 From: Ricardo Pereira Date: Wed, 30 Sep 2015 00:07:15 +0100 Subject: [PATCH 07/22] ARTLog now has varargs methods (Yavor Georgiev) --- ably-ios/ARTAuth.m | 7 +++--- ably-ios/ARTCrypto.m | 6 ++--- ably-ios/ARTHttp.m | 16 ++++++------- ably-ios/ARTJsonEncoder.m | 26 ++++++++++---------- ably-ios/ARTLog.h | 10 ++++---- ably-ios/ARTLog.m | 41 ++++++++++++++++++++++++-------- ably-ios/ARTPayload.m | 2 +- ably-ios/ARTRealtime.m | 18 +++++++------- ably-ios/ARTRest.m | 16 ++++++------- ably-ios/ARTWebSocketTransport.m | 16 ++++++------- ablySpec/RestClient.swift | 6 ++--- 11 files changed, 92 insertions(+), 72 deletions(-) diff --git a/ably-ios/ARTAuth.m b/ably-ios/ARTAuth.m index 24bd8082d..aa9123899 100644 --- a/ably-ios/ARTAuth.m +++ b/ably-ios/ARTAuth.m @@ -298,7 +298,8 @@ -(void) prepConnection { _tokenCbs = [NSMutableArray array]; if (self.options.token) { - [self.logger debug:[NSString stringWithFormat:@"ARTAuth:using provided authToken %@", self.options.token]]; + [self.logger debug:@"ARTAuth:using provided authToken %@", self.options.token]; + _token = [[ARTTokenDetails alloc] initWithId:self.options.token expires:self.options.tokenDetails.expires issued:self.options.tokenDetails.issued @@ -325,7 +326,7 @@ -(void) prepConnection { s.options.tokenParams = params; } id c = strCb(params,^( ARTAuthTokenParams *params) { - [weakSelf.logger debug:[NSString stringWithFormat:@"ARTAuth tokenRequest strCb got %@", [params asDictionary]]]; + [weakSelf.logger debug:@"ARTAuth tokenRequest strCb got %@", [params asDictionary]]; ARTAuth * s = weakSelf; if(s) { [s.rest token:params tokenCb:^(ARTStatus * status, ARTTokenDetails * tokenDetails) { @@ -431,7 +432,7 @@ - (ARTAuthMethod) getAuthMethod { } } else { - [self.logger debug:[NSString stringWithFormat:@"ARTAuth token expired %f milliseconds ago. Expiry: %lld", ([[NSDate date] timeIntervalSince1970]*1000)- self.token.expires, self.token.expires]]; + [self.logger debug:@"ARTAuth token expired %f milliseconds ago. Expiry: %lld", ([[NSDate date] timeIntervalSince1970]*1000)- self.token.expires, self.token.expires]; } self.token = nil; } diff --git a/ably-ios/ARTCrypto.m b/ably-ios/ARTCrypto.m index 0d8dfe0fc..1b1c9db6e 100644 --- a/ably-ios/ARTCrypto.m +++ b/ably-ios/ARTCrypto.m @@ -74,7 +74,7 @@ + (instancetype)cipherParamsWithAlgorithm:(NSString *)algorithm keySpec:(NSData - (BOOL)ccAlgorithm:(CCAlgorithm *)algorithm { if (NSOrderedSame == [self.algorithm compare:@"AES" options:NSCaseInsensitiveSearch]) { if ([self.ivSpec.iv length] != 16) { - [self.logger error:[NSString stringWithFormat:@"ArtCrypto Error iv length is not 16: %d", (int)[self.ivSpec.iv length]]]; + [self.logger error:@"ArtCrypto Error iv length is not 16: %d", (int)[self.ivSpec.iv length]]; return NO; } *algorithm = kCCAlgorithmAES128; @@ -152,7 +152,7 @@ - (ARTStatus *)encrypt:(NSData *)plaintext output:(NSData *__autoreleasing *)out CCCryptorStatus status = CCCrypt(kCCEncrypt, self.algorithm, kCCOptionPKCS7Padding, key, keyLen, iv, dataIn, dataInLen, ciphertextBuf, ciphertextBufLen, &bytesWritten); if (status) { - [self.logger error:[NSString stringWithFormat:@"ARTCrypto error encrypting. Status is %d", status]]; + [self.logger error:@"ARTCrypto error encrypting. Status is %d", status]; free(ciphertextBuf); return [ARTStatus state: ARTStateError]; } @@ -210,7 +210,7 @@ - (ARTStatus *)decrypt:(NSData *)ciphertext output:(NSData *__autoreleasing *)ou CCCryptorStatus status = CCCrypt(kCCDecrypt, self.algorithm, options, key, keyLength, iv, dataIn, dataInLength, buf, outputLength, &bytesWritten); if (status) { - [self.logger error:[NSString stringWithFormat:@"ARTCrypto error decrypting. Status is %d", status]]; + [self.logger error:@"ARTCrypto error decrypting. Status is %d", status]; free(buf); return [ARTStatus state:ARTStateError]; } diff --git a/ably-ios/ARTHttp.m b/ably-ios/ARTHttp.m index 8c520f7aa..b91469b47 100644 --- a/ably-ios/ARTHttp.m +++ b/ably-ios/ARTHttp.m @@ -171,7 +171,7 @@ - (instancetype)initWithBaseUrl:(NSURL *)baseUrl { NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:artRequest.url]; request.HTTPMethod = artRequest.method; - [self.logger verbose:[NSString stringWithFormat:@"ARTHttp request URL is %@", artRequest.url]]; + [self.logger verbose:@"ARTHttp request URL is %@", artRequest.url]; for (NSString *headerName in artRequest.headers) { NSString *headerValue = [artRequest.headers objectForKey:headerName]; @@ -179,26 +179,24 @@ - (instancetype)initWithBaseUrl:(NSURL *)baseUrl { } request.HTTPBody = artRequest.body; - [self.logger debug:[NSString stringWithFormat:@"ARTHttp: makeRequest %@", [request allHTTPHeaderFields]]]; + [self.logger debug:@"ARTHttp: makeRequest %@", [request allHTTPHeaderFields]]; CFRunLoopRef rl = CFRunLoopGetCurrent(); CFRetain(rl); NSURLSessionDataTask *task = [self.urlSession dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; - [self.logger verbose: - [NSString stringWithFormat:@"ARTHttp: Got response %@, err %@", - response,error]]; + [self.logger verbose:@"ARTHttp: Got response %@, err %@", response, error]; if(error) { - [self.logger error:[NSString stringWithFormat:@"ARTHttp receieved error: %@", error]]; + [self.logger error:@"ARTHttp receieved error: %@", error]; cb([ARTHttpResponse responseWithStatus:500 headers:nil body:nil]); } else { if (httpResponse) { int status = (int)httpResponse.statusCode; - [self.logger debug: - [NSString stringWithFormat:@"ARTHttp response status is %d", status]]; - [self.logger verbose:[NSString stringWithFormat:@"ARTHttp received response %@",[NSJSONSerialization JSONObjectWithData:data options:0 error:nil]]]; + [self.logger debug:@"ARTHttp response status is %d", status]; + [self.logger verbose:@"ARTHttp received response %@",[NSJSONSerialization JSONObjectWithData:data options:0 error:nil]]; + CFRunLoopPerformBlock(rl, kCFRunLoopDefaultMode, ^{ cb([ARTHttpResponse responseWithStatus:status headers:httpResponse.allHeaderFields body:data]); }); diff --git a/ably-ios/ARTJsonEncoder.m b/ably-ios/ARTJsonEncoder.m index 29fbdd720..614a755e8 100644 --- a/ably-ios/ARTJsonEncoder.m +++ b/ably-ios/ARTJsonEncoder.m @@ -107,7 +107,7 @@ - (ARTTokenDetails *) decodeAccessToken:(NSData *) data { - (NSDate *)decodeTime:(NSData *)data { NSArray *resp = [self decodeArray:data]; - [self.logger verbose:[NSString stringWithFormat:@"ARTJsonEncoder: decodeTime %@", resp]]; + [self.logger verbose:@"ARTJsonEncoder: decodeTime %@", resp]; if (resp && resp.count == 1) { NSNumber *num = resp[0]; if ([num isKindOfClass:[NSNumber class]]) { @@ -123,7 +123,7 @@ - (NSArray *)decodeStats:(NSData *)data { } - (ARTMessage *)messageFromDictionary:(NSDictionary *)input { - [self.logger verbose:[NSString stringWithFormat:@"ARTJsonEncoder: messageFromDictionary %@", input]]; + [self.logger verbose:@"ARTJsonEncoder: messageFromDictionary %@", input]; if (![input isKindOfClass:[NSDictionary class]]) { return nil; } @@ -169,7 +169,7 @@ -(ARTPresenceMessageAction) presenceMessageActionFromInt:(int) action case 4: return ARTPresenceMessageUpdate; } - [self.logger error:[NSString stringWithFormat:@"ARTJsonEncoder invalid ARTPresenceMessage action %d", action]]; + [self.logger error:@"ARTJsonEncoder invalid ARTPresenceMessage action %d", action]; return ArtPresenceMessageAbsent; } @@ -193,7 +193,7 @@ -(int) intFromPresenceMessageAction:(ARTPresenceMessageAction) action } - (ARTPresenceMessage *)presenceMessageFromDictionary:(NSDictionary *)input { - [self.logger verbose:[NSString stringWithFormat:@"ARTJsonEncoder: presenceMessageFromDictionary %@", input]]; + [self.logger verbose:@"ARTJsonEncoder: presenceMessageFromDictionary %@", input]; if (![input isKindOfClass:[NSDictionary class]]) { return nil; } @@ -247,7 +247,7 @@ - (NSDictionary *)messageToDictionary:(ARTMessage *)message { if (message.name) { [output setObject:message.name forKey:@"name"]; } - [self.logger verbose:[NSString stringWithFormat:@"ARTJsonEncoder: messageToDictionary %@", output]]; + [self.logger verbose:@"ARTJsonEncoder: messageToDictionary %@", output]; return output; } @@ -286,7 +286,7 @@ - (NSDictionary *)presenceMessageToDictionary:(ARTPresenceMessage *)message { int action = [self intFromPresenceMessageAction:message.action]; [output setObject:[NSNumber numberWithInt:action] forKey:@"action"]; - [self.logger verbose:[NSString stringWithFormat:@"ARTJsonEncoder: presenceMessageToDictionary %@", output]]; + [self.logger verbose:@"ARTJsonEncoder: presenceMessageToDictionary %@", output]; return output; } @@ -318,12 +318,12 @@ - (NSDictionary *)protocolMessageToDictionary:(ARTProtocolMessage *)message { if (message.presence) { output[@"presence"] = [self presenceMessagesToArray:message.presence]; } - [self.logger verbose:[NSString stringWithFormat:@"ARTJsonEncoder: protocolMessageToDictionary %@", output]]; + [self.logger verbose:@"ARTJsonEncoder: protocolMessageToDictionary %@", output]; return output; } -(ARTTokenDetails *) tokenFromDictionary:(NSDictionary *) input { - [self.logger verbose:[NSString stringWithFormat:@"ARTJsonEncoder: tokenFromDictionary %@", input]]; + [self.logger verbose:@"ARTJsonEncoder: tokenFromDictionary %@", input]; if (![input isKindOfClass:[NSDictionary class]]) { return nil; } @@ -341,7 +341,7 @@ -(ARTTokenDetails *) tokenFromDictionary:(NSDictionary *) input { } - (ARTProtocolMessage *)protocolMessageFromDictionary:(NSDictionary *)input { - [self.logger verbose:[NSString stringWithFormat:@"ARTJsonEncoder: protocolMessageFromDictionary %@", input]]; + [self.logger verbose:@"ARTJsonEncoder: protocolMessageFromDictionary %@", input]; if (![input isKindOfClass:[NSDictionary class]]) { return nil; } @@ -403,7 +403,7 @@ - (NSDate *)intervalFromString:(NSString *)string { } - (ARTStats *)statsFromDictionary:(NSDictionary *)input { - [self.logger verbose:[NSString stringWithFormat:@"ARTJsonEncoder: statsFromDictionary %@", input]]; + [self.logger verbose:@"ARTJsonEncoder: statsFromDictionary %@", input]; if (![input isKindOfClass:[NSDictionary class]]) { return nil; } @@ -517,7 +517,7 @@ - (ARTErrorInfo *) decodeError:(NSData *) error { } - (ARTStatsRequestCount *)statsRequestCountFromDictionary:(NSDictionary *)input { - [self.logger verbose:[NSString stringWithFormat:@"ARTJsonEncoder: statsRequestCountFromDictionary %@", input]]; + [self.logger verbose:@"ARTJsonEncoder: statsRequestCountFromDictionary %@", input]; if (![input isKindOfClass:[NSDictionary class]]) { return nil; } @@ -544,7 +544,7 @@ - (ARTPayload *)payloadFromDictionary:(NSDictionary *)input { ARTPayload *decoded = nil; ARTStatus *status = [[ARTBase64PayloadEncoder instance] decode:payload output:&decoded]; if (status.state != ARTStateOk) { - [self.logger error:[NSString stringWithFormat:@"ARTJsonEncoder failed to decode payload %@", payload]]; + [self.logger error:@"ARTJsonEncoder failed to decode payload %@", payload]; } return decoded; } @@ -585,7 +585,7 @@ - (NSArray *)decodeArray:(NSData *)data { } - (NSData *)encode:(id)obj { - [self.logger verbose:[NSString stringWithFormat:@"ARTJsonEncoder encoding '%@'", obj]]; + [self.logger verbose:@"ARTJsonEncoder encoding '%@'", obj]; return [NSJSONSerialization dataWithJSONObject:obj options:0 error:nil]; } diff --git a/ably-ios/ARTLog.h b/ably-ios/ARTLog.h index 5cb85783e..0c4cb3ad6 100644 --- a/ably-ios/ARTLog.h +++ b/ably-ios/ARTLog.h @@ -30,11 +30,11 @@ typedef NS_ENUM(NSUInteger, ARTLogLevel) { @interface ARTLog (Shorthand) -- (void)verbose:(NSString *)message; -- (void)debug:(NSString *)message; -- (void)info:(NSString *)message; -- (void)warn:(NSString *)message; -- (void)error:(NSString *)message; +- (void)verbose:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2); +- (void)debug:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2); +- (void)info:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2); +- (void)warn:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2); +- (void)error:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2); @end diff --git a/ably-ios/ARTLog.m b/ably-ios/ARTLog.m index 5fefea868..b1397a1d3 100644 --- a/ably-ios/ARTLog.m +++ b/ably-ios/ARTLog.m @@ -37,24 +37,45 @@ - (instancetype)init { return self; } -- (void)verbose:(NSString *)message { - [self log:message withLevel:ARTLogLevelVerbose]; +- (void)log:(NSString *)message level:(ARTLogLevel)level { + if (level >= self.logLevel) { + NSLog(@"%s: %@", logLevelName(level), message); + } +} + +- (void)verbose:(NSString *)format, ... { + va_list args; + va_start(args, format); + [self log:[[NSString alloc] initWithFormat:format arguments:args] level:ARTLogLevelVerbose]; + va_end(args); } -- (void)debug:(NSString *)message { - [self log:message withLevel:ARTLogLevelDebug]; +- (void)debug:(NSString *)format, ... { + va_list args; + va_start(args, format); + [self log:[[NSString alloc] initWithFormat:format arguments:args] level:ARTLogLevelDebug]; + va_end(args); } -- (void)info:(NSString *)message { - [self log:message withLevel:ARTLogLevelInfo]; +- (void)info:(NSString *)format, ... { + va_list args; + va_start(args, format); + [self log:[[NSString alloc] initWithFormat:format arguments:args] level:ARTLogLevelInfo]; + va_end(args); } -- (void)warn:(NSString *)message { - [self log:message withLevel:ARTLogLevelWarn]; +- (void)warn:(NSString *)format, ... { + va_list args; + va_start(args, format); + [self log:[[NSString alloc] initWithFormat:format arguments:args] level:ARTLogLevelWarn]; + va_end(args); } -- (void)error:(NSString *)message { - [self log:message withLevel:ARTLogLevelError]; +- (void)error:(NSString *)format, ... { + va_list args; + va_start(args, format); + [self log:[[NSString alloc] initWithFormat:format arguments:args] level:ARTLogLevelError]; + va_end(args); } - (void)log:(NSString *)message withLevel:(ARTLogLevel)level { diff --git a/ably-ios/ARTPayload.m b/ably-ios/ARTPayload.m index 9eb6017db..23e2609f3 100644 --- a/ably-ios/ARTPayload.m +++ b/ably-ios/ARTPayload.m @@ -424,7 +424,7 @@ - (ARTStatus *)decode:(ARTPayload *)payload output:(ARTPayload *__autoreleasing status = [enc decode:*output output:output]; if (status.state != ARTStateOk) { ARTPayload * p = *output; - [self.logger error:[NSString stringWithFormat:@"ARTPayload: error in ARTPayloadEncoderChain decoding with encoder %d. Remaining decoding jobs are %@", count, p.encoding]]; + [self.logger error:@"ARTPayload: error in ARTPayloadEncoderChain decoding with encoder %d. Remaining decoding jobs are %@", count, p.encoding]; break; } count++; diff --git a/ably-ios/ARTRealtime.m b/ably-ios/ARTRealtime.m index 6e70349cf..a93319ff1 100644 --- a/ably-ios/ARTRealtime.m +++ b/ably-ios/ARTRealtime.m @@ -409,7 +409,7 @@ - (void)publishMessages:(NSArray *)messages cb:(ARTStatusCallback)cb { ARTPayload *encodedPayload = nil; ARTStatus * status = [self.payloadEncoder encode:message.payload output:&encodedPayload]; if (status.state != ARTStateOk) { - [self.logger error:[NSString stringWithFormat:@"ARTRealtime: error decoding payload, status: %tu", status]]; + [self.logger error:@"ARTRealtime: error decoding payload, status: %tu", status]; } return [message messageWithPayload:encodedPayload]; }]; @@ -446,7 +446,7 @@ - (void)publishPresence:(ARTPresenceMessage *)msg cb:(ARTStatusCallback)cb { ARTPayload *encodedPayload = nil; ARTStatus * status = [self.payloadEncoder encode:msg.payload output:&encodedPayload]; if (status.state != ARTStateOk) { - [self.logger warn:[NSString stringWithFormat:@"bad status encoding presence message %d",(int) status]]; + [self.logger warn:@"bad status encoding presence message %d",(int) status]; } msg.payload = encodedPayload; } @@ -623,7 +623,7 @@ - (void)onChannelMessage:(ARTProtocolMessage *)message { case ARTProtocolMessageSync: break; default: - [self.logger warn:[NSString stringWithFormat:@"ARTRealtime, unknown ARTProtocolMessage action: %tu", message.action]]; + [self.logger warn:@"ARTRealtime, unknown ARTProtocolMessage action: %tu", message.action]; break; } @@ -967,7 +967,7 @@ - (BOOL)isFromResume { - (void)transition:(ARTRealtimeConnectionState)state { - [self.logger verbose:[NSString stringWithFormat:@"Transition to %@ requested", [ARTRealtime ARTRealtimeStateToStr:state]]]; + [self.logger verbose:@"Transition to %@ requested", [ARTRealtime ARTRealtimeStateToStr:state]]; // On exit logic switch (self.state) { @@ -1011,7 +1011,7 @@ - (void)transition:(ARTRealtimeConnectionState)state { case ARTRealtimeConnected: if([self isFromResume]) { if(![self.options.resumeKey isEqualToString:self.connectionKey] || self.options.connectionSerial != self.connectionSerial) { - [self.logger warn:[NSString stringWithFormat:@"ARTRealtime connection has reconnected, but resume failed. Detaching all channels"]]; + [self.logger warn:@"ARTRealtime connection has reconnected, but resume failed. Detaching all channels"]; for (NSString *channelName in self.allChannels) { ARTRealtimeChannel *channel = [self.allChannels objectForKey:channelName]; ARTErrorInfo * info = [[ARTErrorInfo alloc] init]; @@ -1165,7 +1165,7 @@ - (void)onHeartbeat:(ARTProtocolMessage *)message { if(self.pingCb) { [self cancelPingTimer]; if(self.state != ARTRealtimeConnected) { - [self.logger warn:[NSString stringWithFormat:@"ARTRealtime received a ping when in state %@", [ARTRealtime ARTRealtimeStateToStr:self.state]]]; + [self.logger warn:@"ARTRealtime received a ping when in state %@", [ARTRealtime ARTRealtimeStateToStr:self.state]]; self.pingCb([ARTStatus state:ARTStateError]); } else { @@ -1380,7 +1380,7 @@ - (void)failQueuedMessages:(ARTStatus *)error { } - (void)ack:(int64_t)serial count:(int64_t)count { - [self.logger verbose:[NSString stringWithFormat:@"ARTRealtime ack: %lld , count %lld", serial, count]]; + [self.logger verbose:@"ARTRealtime ack: %lld , count %lld", serial, count]; NSArray *nackMessages = nil; NSArray *ackMessages = nil; @@ -1420,7 +1420,7 @@ - (void)ack:(int64_t)serial count:(int64_t)count { } - (void)nack:(int64_t)serial count:(int64_t)count { - [self.logger verbose:[NSString stringWithFormat:@"ARTRealtime Nack: %lld , count %lld", serial, count]]; + [self.logger verbose:@"ARTRealtime Nack: %lld , count %lld", serial, count]; if (serial != self.pendingMessageStartSerial) { // This is an error condition and it shouldn't happen but // we can handle it gracefully by only processing the @@ -1465,7 +1465,7 @@ - (void)cancelTimer:(CFRunLoopTimerRef)timer { - (void)realtimeTransport:(id)transport didReceiveMessage:(ARTProtocolMessage *)message { // TODO add in protocolListener - [self.logger verbose:[NSString stringWithFormat:@"ARTRealtime didReceive Protocol Message %@", [ARTRealtime protocolStr:message.action]]]; + [self.logger verbose:@"ARTRealtime didReceive Protocol Message %@", [ARTRealtime protocolStr:message.action]]; if(message.error) { self.errorReason = message.error; } diff --git a/ably-ios/ARTRest.m b/ably-ios/ARTRest.m index bddb3b725..d8bc0baa5 100644 --- a/ably-ios/ARTRest.m +++ b/ably-ios/ARTRest.m @@ -72,7 +72,7 @@ - (instancetype)initWithRest:(ARTRest *)rest name:(NSString *)name cipherParams: if (self) { self.logger = rest.logger; _presence = [[ARTRestPresence alloc] initWithChannel:self]; - [self.logger debug:[NSString stringWithFormat:@"ARTRestChannel: instantiating under %@", name]]; + [self.logger debug:@"ARTRestChannel: instantiating under %@", name]; _rest = rest; _name = name; _basePath = [NSString stringWithFormat:@"/channels/%@", [name stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLPathAllowedCharacterSet]]]; @@ -96,7 +96,7 @@ + (instancetype)channelWithRest:(ARTRest *)rest name:(NSString *)name cipherPara ARTPayload * p = [ARTPayload payloadWithPayload:[messages objectAtIndex:i] encoding:self.rest.defaultEncoding]; ARTStatus * status = [self.payloadEncoder encode:p output:&encodedPayload]; if (status.state != ARTStateOk) { - [self.logger warn:[NSString stringWithFormat:@"ARTRest publishMessages could not encode message %d", i]]; + [self.logger warn:@"ARTRest publishMessages could not encode message %d", i]; } [encodedMessages addObject:encodedPayload.payload]; } @@ -118,7 +118,7 @@ + (instancetype)channelWithRest:(ARTRest *)rest name:(NSString *)name cipherPara } - (id)publish:(id)payload withName:(NSString *)name cb:(ARTStatusCallback)cb { - [self.logger debug:[NSString stringWithFormat:@"ARTRestChannel: publishing '%@' to channel with name '%@'", payload, name]]; + [self.logger debug:@"ARTRestChannel: publishing '%@' to channel with name '%@'", payload, name]; ARTMessage *message = [ARTMessage messageWithPayload:payload name:name];//[[ARTMessage alloc] init]; message = [message encode:self.payloadEncoder]; return [self publishMessage:message cb:cb]; @@ -212,7 +212,7 @@ - (void)setup { NSString * keyPath = [NSString stringWithFormat:@"/keys/%@/requestToken",params.keyName]; if([self.auth getAuthOptions].authUrl) { keyPath = [[self.auth getAuthOptions].authUrl absoluteString]; - [self.logger info:[NSString stringWithFormat:@"ARTRest is bypassing the default token request URL for this authURL:%@",keyPath]]; + [self.logger info:@"ARTRest is bypassing the default token request URL for this authURL:%@",keyPath]; } NSDictionary * paramsDict = [params asDictionary]; @@ -225,7 +225,7 @@ - (void)setup { return; } NSString * str = [[NSString alloc] initWithData:response.body encoding:NSUTF8StringEncoding]; - [self.logger verbose:[NSString stringWithFormat:@"ARTRest token is %@", str]]; + [self.logger verbose:@"ARTRest token is %@", str]; if(response.status == 201) { ARTTokenDetails * token =[self.defaultEncoder decodeAccessToken:response.body]; cb(ARTStateOk, token); @@ -233,7 +233,7 @@ - (void)setup { else { ARTErrorInfo * e = [self.defaultEncoder decodeError:response.body]; - [self.logger error:[NSString stringWithFormat:@"ARTRest: requestToken Error code: %d, Status %d, Message %@", e.code, e.statusCode, e.message]]; + [self.logger error:@"ARTRest: requestToken Error code: %d, Status %d, Message %@", e.code, e.statusCode, e.message]; cb([ARTStatus state:ARTStateError info:e], nil); } }]; @@ -321,12 +321,12 @@ -(bool) isAnErrorStatus:(int) status { __weak ARTRest * weakSelf = self; ARTHttpCb errorCheckingCb = ^(ARTHttpResponse * response) { ARTRest * s = weakSelf; - [self.logger verbose:[NSString stringWithFormat:@"ARTRest Http response is %d", response.status]]; + [self.logger verbose:@"ARTRest Http response is %d", response.status]; if([s isAnErrorStatus:response.status]) { if(response.body) { ARTErrorInfo * error = [s.defaultEncoder decodeError:response.body]; response.error = error; - [self.logger info:[NSString stringWithFormat:@"ARTRest received an error: \n status %d \n code %d \n message: %@", error.statusCode, error.code, error.message]]; + [self.logger info:@"ARTRest received an error: \n status %d \n code %d \n message: %@", error.statusCode, error.code, error.message]; } } if([ARTFallback shouldTryFallback:response options:self.options]) { diff --git a/ably-ios/ARTWebSocketTransport.m b/ably-ios/ARTWebSocketTransport.m index f0abeafd1..d7bbd4cac 100644 --- a/ably-ios/ARTWebSocketTransport.m +++ b/ably-ios/ARTWebSocketTransport.m @@ -88,12 +88,12 @@ - (instancetype)initWithRest:(ARTRest *)rest options:(ARTClientOptions *)options if([parts count] == 2) { NSString * conId = [parts objectAtIndex:0]; NSString * key = [parts objectAtIndex:1]; - [wRest.logger info:[NSString stringWithFormat:@"attempting recovery of connection %@", conId]]; + [wRest.logger info:@"attempting recovery of connection %@", conId]; queryParams[@"recover"] = conId; queryParams[@"connection_serial"] = key; } else { - [wRest.logger error:[NSString stringWithFormat:@"recovery string is malformed, ignoring: '%@'", options.recover]]; + [wRest.logger error:@"recovery string is malformed, ignoring: '%@'", options.recover]; } } else if(options.resumeKey != nil) { @@ -107,7 +107,7 @@ - (instancetype)initWithRest:(ARTRest *)rest options:(ARTClientOptions *)options NSString *queryString = [sRest formatQueryParams:queryParams]; NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"wss://%@:%d/?%@", realtimeHost, realtimePort, queryString]]; - [wRest.logger debug:[NSString stringWithFormat:@"ARTWebSocketTransport: url: %@", url]]; + [wRest.logger debug:@"ARTWebSocketTransport: url: %@", url]; sSelf.websocket = [[SRWebSocket alloc] initWithURL:url]; sSelf.websocket.delegate = sSelf; [sSelf.websocket setDelegateDispatchQueue:sSelf.q]; @@ -128,7 +128,7 @@ - (void)send:(ARTProtocolMessage *)msg { } - (void)connect { - [self.logger debug:[NSString stringWithFormat:@"ARTWebSocketTransport: websocket connect"]]; + [self.logger debug:@"ARTWebSocketTransport: websocket connect"]; [self.websocket open]; } @@ -160,7 +160,7 @@ - (void)abort:(ARTStatus *)reason { - (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message { ARTWebSocketTransport * __weak weakSelf = self; - [self.logger debug:[NSString stringWithFormat:@"ARTWebSocketTransport: websocket did receive message %@", message]]; + [self.logger debug:@"ARTWebSocketTransport: websocket did receive message %@", message]; CFRunLoopPerformBlock(self.rl, kCFRunLoopDefaultMode, ^{ NSData *data = nil; @@ -186,7 +186,7 @@ - (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message { - (void)webSocketDidOpen:(SRWebSocket *)webSocket { ARTWebSocketTransport * __weak weakSelf = self; - [self.logger debug:[NSString stringWithFormat:@"ARTWebSocketTransport: websocket did open"]]; + [self.logger debug:@"ARTWebSocketTransport: websocket did open"]; CFRunLoopPerformBlock(self.rl, kCFRunLoopDefaultMode, ^{ ARTWebSocketTransport *s = weakSelf; @@ -199,7 +199,7 @@ - (void)webSocketDidOpen:(SRWebSocket *)webSocket { - (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error { ARTWebSocketTransport * __weak weakSelf = self; - [self.logger error:[NSString stringWithFormat:@"ARTWebSocketTransport: websocket did fail with error %@", error]]; + [self.logger error:@"ARTWebSocketTransport: websocket did fail with error %@", error]; CFRunLoopPerformBlock(self.rl, kCFRunLoopDefaultMode, ^{ ARTWebSocketTransport *s = weakSelf; @@ -212,7 +212,7 @@ - (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error { - (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean { ARTWebSocketTransport * __weak weakSelf = self; - [self.logger debug:[NSString stringWithFormat:@"ARTWebSocketTransport: websocket did close with reason %@", reason]]; + [self.logger debug:@"ARTWebSocketTransport: websocket did close with reason %@", reason]; CFRunLoopPerformBlock(self.rl, kCFRunLoopDefaultMode, ^{ ARTWebSocketTransport *s = weakSelf; diff --git a/ablySpec/RestClient.swift b/ablySpec/RestClient.swift index e8004be92..05e08f48b 100644 --- a/ablySpec/RestClient.swift +++ b/ablySpec/RestClient.swift @@ -116,7 +116,7 @@ class RestClient: QuickSpec { let logTime = NSDate() let client = ARTRest(options: AblyTests.commonAppSetup()) - client.logger.warn("This is a warning") + client.logger.log("This is a warning", withLevel: .Warn) let logs = querySyslog(forLogsAfter: logTime) expect(client.logger.logLevel).to(equal(ARTLogLevel.Warn)) @@ -129,7 +129,7 @@ class RestClient: QuickSpec { let client = ARTRest(options: AblyTests.commonAppSetup()) client.logger.logLevel = .Error - client.logger.warn("This is a warning") + client.logger.log("This is a warning", withLevel: .Warn) let logs = querySyslog(forLogsAfter: logTime) expect(logs).toNot(contain("WARN: This is a warning")) @@ -151,7 +151,7 @@ class RestClient: QuickSpec { customLogger.logLevel = .Verbose let client = ARTRest(logger: customLogger, andOptions: options) - client.logger.warn("This is a warning") + client.logger.log("This is a warning", withLevel: .Warn) expect(Log.interceptedLog.0).to(equal("This is a warning")) expect(Log.interceptedLog.1).to(equal(ARTLogLevel.Warn)) From 37b8b4f01b398657ee69af5aed83cf067ce7d072 Mon Sep 17 00:00:00 2001 From: Ricardo Pereira Date: Wed, 30 Sep 2015 01:07:16 +0100 Subject: [PATCH 08/22] Queri-fy methods (Yavor Georgiev) Make Channel#history, Presence#history and Presence#get follow the query pattern same as RestClient#stats --- ably-ios/ARTDataQuery+Private.h | 19 ++++++++++ ably-ios/ARTDataQuery.h | 29 +++++++++++++++ ably-ios/ARTDataQuery.m | 49 +++++++++++++++++++++++++ ably-ios/ARTRealtime.h | 16 ++++----- ably-ios/ARTRealtime.m | 34 +++++------------- ably-ios/ARTRest+Private.h | 3 -- ably-ios/ARTRest.h | 11 +++--- ably-ios/ARTRest.m | 64 +++++++++++---------------------- ably-ios/ARTStats.h | 17 ++------- ably-ios/ARTStats.m | 30 +++------------- ably-ios/ably.h | 1 + ably-ios/ably.modulemap | 1 + ably.xcodeproj/project.pbxproj | 12 +++++++ 13 files changed, 156 insertions(+), 130 deletions(-) create mode 100755 ably-ios/ARTDataQuery+Private.h create mode 100755 ably-ios/ARTDataQuery.h create mode 100755 ably-ios/ARTDataQuery.m diff --git a/ably-ios/ARTDataQuery+Private.h b/ably-ios/ARTDataQuery+Private.h new file mode 100755 index 000000000..1c9243443 --- /dev/null +++ b/ably-ios/ARTDataQuery+Private.h @@ -0,0 +1,19 @@ +// +// ARTDataQuery+Private.h +// ably +// +// Created by Yavor Georgiev on 20.08.15. +// Copyright (c) 2015 г. Ably. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface ARTDataQuery(Private) + +- (NSMutableArray /* */ *)asQueryItems; + +@end + +NS_ASSUME_NONNULL_END \ No newline at end of file diff --git a/ably-ios/ARTDataQuery.h b/ably-ios/ARTDataQuery.h new file mode 100755 index 000000000..e0e42bcbf --- /dev/null +++ b/ably-ios/ARTDataQuery.h @@ -0,0 +1,29 @@ +// +// ARTDataQuery.h +// ably +// +// Created by Yavor Georgiev on 20.08.15. +// Copyright (c) 2015 г. Ably. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSUInteger, ARTQueryDirection) { + ARTQueryDirectionForwards, + ARTQueryDirectionBackwards +}; + +@interface ARTDataQuery : NSObject + +@property (nonatomic, strong, nullable) NSDate *start; +@property (nonatomic, strong, nullable) NSDate *end; + +@property (nonatomic, assign) uint16_t limit; + +@property (nonatomic, assign) ARTQueryDirection direction; + +@end + +NS_ASSUME_NONNULL_END \ No newline at end of file diff --git a/ably-ios/ARTDataQuery.m b/ably-ios/ARTDataQuery.m new file mode 100755 index 000000000..56de2c678 --- /dev/null +++ b/ably-ios/ARTDataQuery.m @@ -0,0 +1,49 @@ +// +// ARTDataQuery.m +// ably +// +// Created by Yavor Georgiev on 20.08.15. +// Copyright (c) 2015 г. Ably. All rights reserved. +// + +#import "ARTDataQuery.h" +#import "ARTDataQuery+Private.h" + +@implementation ARTDataQuery + +- (instancetype)init { + if (self = [super init]) { + _limit = 100; + _direction = ARTQueryDirectionBackwards; + } + + return self; +} + +static NSString *queryDirectionToString(ARTQueryDirection direction) { + switch (direction) { + case ARTQueryDirectionForwards: + return @"forwards"; + case ARTQueryDirectionBackwards: + default: + return @"backwards"; + } +} + +- (NSMutableArray *)asQueryItems { + NSMutableArray *items = [NSMutableArray array]; + + if (self.start) { + [items addObject:[NSURLQueryItem queryItemWithName:@"start" value:[NSString stringWithFormat:@"%llu", (uint64_t)(self.start.timeIntervalSince1970 * 1000)]]]; + } + if (self.end) { + [items addObject:[NSURLQueryItem queryItemWithName:@"end" value:[NSString stringWithFormat:@"%llu", (uint64_t)(self.end.timeIntervalSince1970 * 1000)]]]; + } + + [items addObject:[NSURLQueryItem queryItemWithName:@"limit" value:[NSString stringWithFormat:@"%hu", self.limit]]]; + [items addObject:[NSURLQueryItem queryItemWithName:@"direction" value:queryDirectionToString(self.direction)]]; + + return items; +} + +@end \ No newline at end of file diff --git a/ably-ios/ARTRealtime.h b/ably-ios/ARTRealtime.h index 2a6a80bca..14b754907 100644 --- a/ably-ios/ARTRealtime.h +++ b/ably-ios/ARTRealtime.h @@ -64,8 +64,7 @@ typedef NS_ENUM(NSUInteger, ARTRealtimeConnectionState) { - (void)publish:(id)payload withName:(NSString *)name cb:(ARTStatusCallback)cb; - (void)publish:(id)payload cb:(ARTStatusCallback)cb; -- (id)history:(ARTPaginatedResultCallback)callback; -- (id)historyWithParams:(NSDictionary *)queryParams cb:(ARTPaginatedResultCallback)callback; +- (void)history:(ARTDataQuery *)query callback:(void (^)(ARTStatus *status, ARTPaginatedResult /* */ *result))callback; typedef void (^ARTRealtimeChannelMessageCb)(ARTMessage *); - (id)subscribe:(ARTRealtimeChannelMessageCb)cb; @@ -90,17 +89,14 @@ typedef void (^ARTRealtimeChannelStateCb)(ARTRealtimeChannelState, ARTStatus *); @interface ARTPresence : NSObject -- (instancetype) initWithChannel:(ARTRealtimeChannel *) channel; -- (id)get:(ARTPaginatedResultCallback)callback; -- (id)getWithParams:(NSDictionary *) queryParams cb:(ARTPaginatedResultCallback)callback; -- (id)history:(ARTPaginatedResultCallback)callback; -- (id)historyWithParams:(NSDictionary *)queryParams cb:(ARTPaginatedResultCallback)callback; +- (instancetype)initWithChannel:(ARTRealtimeChannel *)channel; +- (void)get:(ARTDataQuery *)query callback:(void (^)(ARTStatus *status, ARTPaginatedResult /* */ *result))callback; +- (void)history:(ARTDataQuery *)query callback:(void (^)(ARTStatus *status, ARTPaginatedResult /* */ *result))callback; - (void)enter:(id)data cb:(ARTStatusCallback)cb; - (void)update:(id)data cb:(ARTStatusCallback)cb; - (void)leave:(id) data cb:(ARTStatusCallback)cb; - - (void)enterClient:(NSString *) clientId data:(id) data cb:(ARTStatusCallback) cb; - (void)updateClient:(NSString *) clientId data:(id) data cb:(ARTStatusCallback) cb; - (void)leaveClient:(NSString *) clientId data:(id) data cb:(ARTStatusCallback) cb; @@ -108,7 +104,7 @@ typedef void (^ARTRealtimeChannelStateCb)(ARTRealtimeChannelState, ARTStatus *); typedef void (^ARTRealtimeChannelPresenceCb)(ARTPresenceMessage *); - (id)subscribe:(ARTRealtimeChannelPresenceCb)cb; -- (id)subscribe:(ARTPresenceMessageAction) action cb:(ARTRealtimeChannelPresenceCb)cb; +- (id)subscribe:(ARTPresenceMessageAction)action cb:(ARTRealtimeChannelPresenceCb)cb; - (void)unsubscribe:(id)subscription; - (void)unsubscribe:(id)subscription action:(ARTPresenceMessageAction) action; @@ -150,7 +146,7 @@ Instance the Ably library with the given options. typedef void (^ARTRealtimePingCb)(ARTStatus *); - (void)ping:(ARTRealtimePingCb) cb; -- (id)stats:(ARTStatsQuery *)query callback:(ARTPaginatedResultCallback)callback; +- (void)stats:(ARTStatsQuery *)query callback:(void (^)(ARTStatus *status, ARTPaginatedResult /* */ *result))callback; - (ARTRealtimeChannel *)channel:(NSString *)channelName; - (ARTRealtimeChannel *)channel:(NSString *)channelName cipherParams:(ARTCipherParams *)cipherParams; diff --git a/ably-ios/ARTRealtime.m b/ably-ios/ARTRealtime.m index a93319ff1..3629f5f4a 100644 --- a/ably-ios/ARTRealtime.m +++ b/ably-ios/ARTRealtime.m @@ -501,17 +501,8 @@ -(void) throwOnDisconnectedOrFailed { } } -- (id)history:(ARTPaginatedResultCallback)callback { - [self throwOnDisconnectedOrFailed]; - return [self.restChannel history:callback]; -} - -- (id)historyWithParams:(NSDictionary *)queryParams cb:(ARTPaginatedResultCallback)callback { - [self throwOnDisconnectedOrFailed]; - if([queryParams objectForKey:@"until_attach"] != nil && self.state != ARTRealtimeChannelAttached) { - [NSException raise:@"Cannot ask for history with param untilAttach when not attached" format:@""]; - } - return [self.restChannel historyWithParams:queryParams cb:callback]; +- (void)history:(ARTDataQuery *)query callback:(void (^)(ARTStatus *, ARTPaginatedResult *))callback { + [self.restChannel history:query callback:callback]; } - (id)subscribe:(ARTRealtimeChannelMessageCb)cb { @@ -937,8 +928,8 @@ - (void)ping:(ARTRealtimePingCb) cb { [self.transport sendPing]; } -- (id)stats:(ARTStatsQuery *)query callback:(ARTPaginatedResultCallback)callback { - return [self.rest stats:query callback:callback]; +- (void)stats:(ARTStatsQuery *)query callback:(void (^)(ARTStatus *status, ARTPaginatedResult *result))callback { + [self.rest stats:query callback:callback]; } - (ARTRealtimeChannel *)channel:(NSString *)channelName { @@ -1618,26 +1609,17 @@ -(instancetype) initWithChannel:(ARTRealtimeChannel *) channel { } return self; } --(id) getWithParams:(NSDictionary *) queryParams cb:(ARTPaginatedResultCallback)callback { - [self.channel throwOnDisconnectedOrFailed]; - return [self.channel.restChannel.presence getWithParams:queryParams cb:callback]; -} --(id) get:(ARTPaginatedResultCallback)callback { - [self.channel throwOnDisconnectedOrFailed]; - return [self.channel.restChannel.presence get:callback]; -} -- (id)history:(ARTPaginatedResultCallback)callback { +- (void)get:(ARTDataQuery *)query callback:(void (^)(ARTStatus *, ARTPaginatedResult *))callback { [self.channel throwOnDisconnectedOrFailed]; - return [self.channel.restChannel.presence history:callback]; + [self.channel.restChannel.presence get:query callback:callback]; } -- (id) historyWithParams:(NSDictionary *)queryParams cb:(ARTPaginatedResultCallback)callback { +- (void)history:(ARTDataQuery *)query callback:(void (^)(ARTStatus *, ARTPaginatedResult *))callback { [self.channel throwOnDisconnectedOrFailed]; - return [self.channel.restChannel.presence historyWithParams:queryParams cb:callback]; + [self.channel.restChannel.presence history:query callback:callback]; } - - (void)enter:(id)data cb:(ARTStatusCallback)cb { [self enterClient:self.channel.clientId data:data cb:cb]; } diff --git a/ably-ios/ARTRest+Private.h b/ably-ios/ARTRest+Private.h index 83b21355e..5186c6a12 100644 --- a/ably-ios/ARTRest+Private.h +++ b/ably-ios/ARTRest+Private.h @@ -25,8 +25,6 @@ typedef NS_ENUM(NSUInteger, ARTAuthentication) { @property (readonly, strong, nonatomic) id defaultEncoder; @property (readonly, strong, nonatomic) ARTAuth *auth; - - - (NSURL *)getBaseURL; - (NSString *)formatQueryParams:(NSDictionary *)queryParams; @@ -43,5 +41,4 @@ typedef NS_ENUM(NSUInteger, ARTAuthentication) { -(id) postTestStats:(NSArray *) stats cb:(void(^)(ARTStatus * status)) cb; - @end diff --git a/ably-ios/ARTRest.h b/ably-ios/ARTRest.h index 355b7e31d..bbaebf6d3 100644 --- a/ably-ios/ARTRest.h +++ b/ably-ios/ARTRest.h @@ -27,8 +27,7 @@ - (id)publish:(id)payload withName:(NSString *)name cb:(ARTStatusCallback)cb; - (id)publish:(id)payload cb:(ARTStatusCallback)cb; -- (id)history:(ARTPaginatedResultCallback)callback; -- (id)historyWithParams:(NSDictionary *)queryParams cb:(ARTPaginatedResultCallback)callback; +- (void)history:(ARTDataQuery *)query callback:(void (^)(ARTStatus *status, ARTPaginatedResult /* */ *result))callback; @property (readonly, strong, nonatomic) ARTRestPresence *presence; @@ -40,10 +39,8 @@ @interface ARTRestPresence : NSObject - (instancetype) initWithChannel:(ARTRestChannel *) channel; -- (id)get:(ARTPaginatedResultCallback)callback; -- (id)getWithParams:(NSDictionary *)queryParams cb:(ARTPaginatedResultCallback)callback; -- (id)history:(ARTPaginatedResultCallback)callback; -- (id)historyWithParams:(NSDictionary *)queryParams cb:(ARTPaginatedResultCallback)callback; +- (void)get:(ARTDataQuery *)query callback:(void (^)(ARTStatus *status, ARTPaginatedResult /* */ *result))callback; +- (void)history:(ARTDataQuery *)query callback:(void (^)(ARTStatus *status, ARTPaginatedResult /* */ *result))callback; @end @@ -59,7 +56,7 @@ - (id)token:(ARTAuthTokenParams *)keyName tokenCb:(void (^)(ARTStatus * status, ARTTokenDetails *)) cb; - (id)time:(void(^)(ARTStatus *status, NSDate *time))cb; -- (id)stats:(ARTStatsQuery *)query callback:(ARTPaginatedResultCallback)callback; +- (void)stats:(ARTStatsQuery *)query callback:(void (^)(ARTStatus *status, ARTPaginatedResult /* */ *result))callback; - (id)internetIsUp:(void (^)(bool isUp)) cb; - (ARTRestChannel *)channel:(NSString *)channelName; - (ARTRestChannel *)channel:(NSString *)channelName cipherParams:(ARTCipherParams *)cipherParams; diff --git a/ably-ios/ARTRest.m b/ably-ios/ARTRest.m index d8bc0baa5..9aab63f54 100644 --- a/ably-ios/ARTRest.m +++ b/ably-ios/ARTRest.m @@ -8,7 +8,7 @@ #import "ARTRest.h" #import "ARTRest+Private.h" - +#import "ARTDataQuery+Private.h" #import "ARTAuth.h" #import "ARTHttp.h" #import "ARTEncoder.h" @@ -62,7 +62,7 @@ @interface ARTRest () - (id)makeRequestWithMethod:(NSString *)method relUrl:(NSString *)relUrl headers:(NSDictionary *)headers body:(NSData *)body authenticated:(ARTAuthentication)authenticated cb:(ARTHttpCb)cb; - (NSDictionary *)withAcceptHeader:(NSDictionary *)headers; -- (void)throwOnHighLimitCheck:(NSDictionary *) params; + @end @implementation ARTRestChannel @@ -133,17 +133,16 @@ + (instancetype)channelWithRest:(ARTRest *)rest name:(NSString *)name cipherPara } } -- (id)history:(ARTPaginatedResultCallback)callback { - return [self historyWithParams:nil cb:callback]; -} - -- (id)historyWithParams:(NSDictionary *)queryParams cb:(ARTPaginatedResultCallback)callback { - [self.rest throwOnHighLimitCheck:queryParams]; - return [self.rest withAuthHeaders:^(NSDictionary *authHeaders) { - NSString *relUrl = [NSString stringWithFormat:@"%@/messages", self.basePath]; - ARTHttpRequest *req = [[ARTHttpRequest alloc] initWithMethod:@"GET" url:[self.rest resolveUrl:relUrl queryParams:queryParams] headers:authHeaders body:nil]; - return [ARTHttpPaginatedResult makePaginatedRequest:self.rest.http request:req responseProcessor:^(ARTHttpResponse *response) { - id encoder = [self.rest.encoders objectForKey:response.contentType]; +- (void)history:(ARTDataQuery *)query callback:(void (^)(ARTStatus *status, ARTPaginatedResult *__nullable result))callback { + NSParameterAssert(query.limit < 1000); + NSParameterAssert([query.start compare:query.end] != NSOrderedDescending); + + [_rest withAuthHeaders:^(NSDictionary *authHeaders) { + NSURLComponents *requestUrl = [NSURLComponents componentsWithString:[self.basePath stringByAppendingPathComponent:@"messages"]]; + requestUrl.queryItems = [query asQueryItems]; + ARTHttpRequest *req = [[ARTHttpRequest alloc] initWithMethod:@"GET" url:[requestUrl URLRelativeToURL:_rest.baseUrl] headers:authHeaders body:nil]; + return [ARTHttpPaginatedResult makePaginatedRequest:_rest.http request:req responseProcessor:^(ARTHttpResponse *response) { + id encoder = [_rest.encoders objectForKey:response.contentType]; NSArray *messages = [encoder decodeMessages:response.body]; return [messages artMap:^id(ARTMessage *message) { return [message decode:self.payloadEncoder]; @@ -239,8 +238,6 @@ - (void)setup { }]; } - - -(ARTAuth *) auth { return _auth; } @@ -266,32 +263,16 @@ -(ARTAuth *) auth { cb(response.status == 200 && [str isEqualToString:@"yes\n"]); }]; return nil; - } --(void) throwOnHighLimitCheck:(NSDictionary *) params { - NSString * limit = [params valueForKey:@"limit"]; - if(!limit) { - return; - } - int value = [limit intValue]; - if(value > 1000) { - [NSException raise:@"cannot set a limit over 1000" format:@"%d", value]; - } -} - -- (id)stats:(ARTStatsQuery *)query callback:(ARTPaginatedResultCallback)callback { - if (query.limit > 1000) { - [NSException raise:@"AblyException" format:@"Cannot set a query limit over 1000"]; - } - if (query.start && query.end && [query.start compare:query.end] == NSOrderedDescending) { - [NSException raise:@"AblyException" format:@"The query start date cannot be more recent than the query end date"]; - } - - return [self withAuthHeaders:^(NSDictionary *authHeaders) { - NSURLComponents *requestUrl = [NSURLComponents componentsWithURL:[self resolveUrl:@"/stats"] resolvingAgainstBaseURL:YES]; +- (void)stats:(ARTStatsQuery *)query callback:(void (^)(ARTStatus *status, ARTPaginatedResult *result))callback { + NSParameterAssert(query.limit < 1000); + NSParameterAssert([query.start compare:query.end] != NSOrderedDescending); + + [self withAuthHeaders:^(NSDictionary *authHeaders) { + NSURLComponents *requestUrl = [NSURLComponents componentsWithString:@"/stats"]; requestUrl.queryItems = [query asQueryItems]; - ARTHttpRequest *req = [[ARTHttpRequest alloc] initWithMethod:@"GET" url:requestUrl.URL headers:authHeaders body:nil]; + ARTHttpRequest *req = [[ARTHttpRequest alloc] initWithMethod:@"GET" url:[requestUrl URLRelativeToURL:self.baseUrl] headers:authHeaders body:nil]; return [ARTHttpPaginatedResult makePaginatedRequest:self.http request:req responseProcessor:^(ARTHttpResponse *response) { id encoder = [self.encoders objectForKey:response.contentType]; return [encoder decodeStats:response.body]; @@ -316,7 +297,6 @@ -(bool) isAnErrorStatus:(int) status { return status >=400; } - - (id)makeRequestWithMethod:(NSString *)method relUrl:(NSString *)relUrl headers:(NSDictionary *)headers body:(NSData *)body authenticated:(ARTAuthentication)authenticated fb:(ARTFallback *) fb cb:(ARTHttpCb)cb { __weak ARTRest * weakSelf = self; ARTHttpCb errorCheckingCb = ^(ARTHttpResponse * response) { @@ -414,8 +394,6 @@ - (NSDictionary *)withAcceptHeader:(NSDictionary *)headers { return md; } - - @end @implementation ARTRest (Private) @@ -428,7 +406,7 @@ @implementation ARTRest (Private) }]; } -- (NSURL *) getBaseURL { +- (NSURL *)getBaseURL { return self.baseUrl; } @@ -495,11 +473,11 @@ - (NSURL *)resolveUrl:(NSString *)relUrl queryParams:(NSDictionary *)queryParams return [self.auth authParams:cb]; } - @end @implementation ARTRestPresence + -(instancetype) initWithChannel:(ARTRestChannel *)channel { self = [super init]; if(self) { diff --git a/ably-ios/ARTStats.h b/ably-ios/ARTStats.h index 9364d8d3e..03984362a 100644 --- a/ably-ios/ARTStats.h +++ b/ably-ios/ARTStats.h @@ -7,11 +7,7 @@ // #import - -typedef NS_ENUM(NSUInteger, ARTQueryDirection) { - ARTQueryDirectionForwards, - ARTQueryDirectionBackwards -}; +#import typedef NS_ENUM(NSUInteger, ARTStatsUnit) { ARTStatsUnitMinute, @@ -20,19 +16,10 @@ typedef NS_ENUM(NSUInteger, ARTStatsUnit) { ARTStatsUnitMonth }; -@interface ARTStatsQuery : NSObject - -@property (nonatomic, strong) NSDate *start; -@property (nonatomic, strong) NSDate *end; - -@property (nonatomic, assign) uint64_t limit; - -@property (nonatomic, assign) ARTQueryDirection direction; +@interface ARTStatsQuery : ARTDataQuery @property (nonatomic, assign) ARTStatsUnit unit; -- (NSArray *)asQueryItems; - @end @interface ARTStatsMessageCount : NSObject diff --git a/ably-ios/ARTStats.m b/ably-ios/ARTStats.m index d6b9f7703..4e195deaa 100644 --- a/ably-ios/ARTStats.m +++ b/ably-ios/ARTStats.m @@ -7,13 +7,12 @@ // #import "ARTStats.h" +#import "ARTDataQuery+Private.h" @implementation ARTStatsQuery - (instancetype)init { if (self = [super init]) { - _limit = 100; - _direction = ARTQueryDirectionBackwards; _unit = ARTStatsUnitMinute; } @@ -34,31 +33,10 @@ - (instancetype)init { } } -static NSString *queryDirectionToString(ARTQueryDirection direction) { - switch (direction) { - case ARTQueryDirectionForwards: - return @"forwards"; - case ARTQueryDirectionBackwards: - default: - return @"backwards"; - } -} - -- (NSArray *)asQueryItems { - NSMutableArray *items = [NSMutableArray array]; - - if (self.start) { - [items addObject:[NSURLQueryItem queryItemWithName:@"start" value:[NSString stringWithFormat:@"%llu", (uint64_t)(self.start.timeIntervalSince1970 * 1000)]]]; - } - if (self.end) { - [items addObject:[NSURLQueryItem queryItemWithName:@"end" value:[NSString stringWithFormat:@"%llu", (uint64_t)(self.end.timeIntervalSince1970 * 1000)]]]; - } - - [items addObject:[NSURLQueryItem queryItemWithName:@"limit" value:[NSString stringWithFormat:@"%llu", self.limit]]]; - [items addObject:[NSURLQueryItem queryItemWithName:@"direction" value:queryDirectionToString(self.direction)]]; +- (NSMutableArray *)asQueryItems { + NSMutableArray *items = [super asQueryItems]; [items addObject:[NSURLQueryItem queryItemWithName:@"unit" value:statsUnitToString(self.unit)]]; - - return [items copy]; + return items; } @end diff --git a/ably-ios/ably.h b/ably-ios/ably.h index f1f8e7226..fb4dc3d9e 100644 --- a/ably-ios/ably.h +++ b/ably-ios/ably.h @@ -17,6 +17,7 @@ FOUNDATION_EXPORT const unsigned char ablyVersionString[]; #import #import #import +#import #import #import #import diff --git a/ably-ios/ably.modulemap b/ably-ios/ably.modulemap index d330c23fb..8c34bc050 100644 --- a/ably-ios/ably.modulemap +++ b/ably-ios/ably.modulemap @@ -5,6 +5,7 @@ framework module ably { module * { export * } explicit module Private { + header "ARTDataQuery+Private.h" header "ARTPayload+Private.h" header "ARTTokenDetails+Private.h" header "ARTRest+Private.h" diff --git a/ably.xcodeproj/project.pbxproj b/ably.xcodeproj/project.pbxproj index fc5ed5b6a..65acae775 100644 --- a/ably.xcodeproj/project.pbxproj +++ b/ably.xcodeproj/project.pbxproj @@ -105,6 +105,9 @@ 96E408471A3895E800087F77 /* ARTWebSocketTransport.h in Headers */ = {isa = PBXBuildFile; fileRef = 96E408451A3895E800087F77 /* ARTWebSocketTransport.h */; }; 96E408481A3895E800087F77 /* ARTWebSocketTransport.m in Sources */ = {isa = PBXBuildFile; fileRef = 96E408461A3895E800087F77 /* ARTWebSocketTransport.m */; }; D72304701BB72CED00F1ABDA /* RealtimeClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = D723046F1BB72CED00F1ABDA /* RealtimeClient.swift */; }; + D746AE1D1BBB5207003ECEF8 /* ARTDataQuery.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE1A1BBB5207003ECEF8 /* ARTDataQuery.h */; }; + D746AE1E1BBB5207003ECEF8 /* ARTDataQuery+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE1B1BBB5207003ECEF8 /* ARTDataQuery+Private.h */; }; + D746AE1F1BBB5207003ECEF8 /* ARTDataQuery.m in Sources */ = {isa = PBXBuildFile; fileRef = D746AE1C1BBB5207003ECEF8 /* ARTDataQuery.m */; }; FC78549C1BCD5688539CCBE4 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FDA6E099E4BC04FC80F845C0 /* libPods.a */; }; /* End PBXBuildFile section */ @@ -257,6 +260,9 @@ 96E408461A3895E800087F77 /* ARTWebSocketTransport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTWebSocketTransport.m; sourceTree = ""; }; CDE13941D61BC4A0690896F6 /* Pods-ablySpec.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ablySpec.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ablySpec/Pods-ablySpec.debug.xcconfig"; sourceTree = ""; }; D723046F1BB72CED00F1ABDA /* RealtimeClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealtimeClient.swift; sourceTree = ""; }; + D746AE1A1BBB5207003ECEF8 /* ARTDataQuery.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTDataQuery.h; sourceTree = ""; }; + D746AE1B1BBB5207003ECEF8 /* ARTDataQuery+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ARTDataQuery+Private.h"; sourceTree = ""; }; + D746AE1C1BBB5207003ECEF8 /* ARTDataQuery.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTDataQuery.m; sourceTree = ""; }; DF8DF755839D59574B7B795F /* Pods-ablySpec.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ablySpec.release.xcconfig"; path = "Pods/Target Support Files/Pods-ablySpec/Pods-ablySpec.release.xcconfig"; sourceTree = ""; }; FDA6E099E4BC04FC80F845C0 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -380,6 +386,9 @@ 961343D71A42E0B7006DC822 /* ARTClientOptions.m */, 96BF61551A35B40E004CF2B3 /* ARTStatus.h */, 1C55427C1B148306003068DB /* ARTStatus.m */, + D746AE1A1BBB5207003ECEF8 /* ARTDataQuery.h */, + D746AE1B1BBB5207003ECEF8 /* ARTDataQuery+Private.h */, + D746AE1C1BBB5207003ECEF8 /* ARTDataQuery.m */, 96BF61561A35B52C004CF2B3 /* ARTHttp.h */, 96BF61571A35B52C004CF2B3 /* ARTHttp.m */, 96BF615C1A35C1C8004CF2B3 /* ARTTypes.h */, @@ -494,6 +503,7 @@ 96A507BD1A3791490077CDF8 /* ARTRealtime.h in Headers */, 961343D81A42E0B7006DC822 /* ARTClientOptions.h in Headers */, 96BF615E1A35C1C8004CF2B3 /* ARTTypes.h in Headers */, + D746AE1D1BBB5207003ECEF8 /* ARTDataQuery.h in Headers */, 1C1EC3FA1AE26A8B00AAADD7 /* ARTStatus.h in Headers */, 96E408431A38939E00087F77 /* ARTProtocolMessage.h in Headers */, 96BF61581A35B52C004CF2B3 /* ARTHttp.h in Headers */, @@ -518,6 +528,7 @@ 96E4083F1A3892C700087F77 /* ARTRealtimeTransport.h in Headers */, 96A507B51A37881C0077CDF8 /* ARTNSDate+ARTUtil.h in Headers */, 96A507A91A37806A0077CDF8 /* ARTEncoder.h in Headers */, + D746AE1E1BBB5207003ECEF8 /* ARTDataQuery+Private.h in Headers */, 1C6C18A31ADFDAB100AB79E4 /* ARTLog.h in Headers */, 1CA5E1C21AB72369006ADD70 /* ARTMsgPackEncoder.h in Headers */, 96A507A51A377DE90077CDF8 /* ARTNSDictionary+ARTDictionaryUtil.h in Headers */, @@ -765,6 +776,7 @@ 1C578E201B3435CA00EF46EC /* ARTFallback.m in Sources */, 96A507B61A37881C0077CDF8 /* ARTNSDate+ARTUtil.m in Sources */, 850BFB4D1B79323C009D0ADD /* ARTPaginatedResult.m in Sources */, + D746AE1F1BBB5207003ECEF8 /* ARTDataQuery.m in Sources */, 961343D91A42E0B7006DC822 /* ARTClientOptions.m in Sources */, 96BF615F1A35C1C8004CF2B3 /* ARTTypes.m in Sources */, 96A507AE1A3780F60077CDF8 /* ARTJsonEncoder.m in Sources */, From 882162658826da715a42e8a651c7bcab3db3dab3 Mon Sep 17 00:00:00 2001 From: Ricardo Pereira Date: Wed, 30 Sep 2015 02:13:02 +0100 Subject: [PATCH 09/22] Refactor Channels, Presence and Messages (Yavor Georgiev) There is a lot that can be shared between the Realtime and REST implementations of Channels, Presence and Messages, so this path mostly deals with getting things in place in anticipation of the sweet, sweet code reuse. In the mean time this adds the `channels` attribute on `ARTRest`, as well as tests for channel creation, publishing and presence. --- ably-ios/ARTChannels+Private.h | 36 ++++ ably-ios/ARTChannels.h | 63 +++++++ ably-ios/ARTChannels.m | 142 ++++++++++++++++ ably-ios/ARTJsonEncoder.m | 38 ++--- ably-ios/ARTMessage.h | 18 +- ably-ios/ARTMessage.m | 58 ++++--- ably-ios/ARTPayload.m | 8 +- ably-ios/ARTPresence.h | 39 +++++ ably-ios/ARTPresence.m | 32 ++++ ably-ios/ARTPresenceMap.m | 2 +- ably-ios/ARTPresenceMessage.h | 4 +- ably-ios/ARTPresenceMessage.m | 1 - ably-ios/ARTRealtime.m | 2 +- ably-ios/ARTRest.h | 46 +++--- ably-ios/ARTRest.m | 255 ++++++++++++----------------- ably-ios/ably.h | 10 +- ably-ios/ably.modulemap | 2 +- ably.xcodeproj/project.pbxproj | 30 +++- ablySpec/RestChannel.swift | 210 ++++++++++++++++++++++++ ablySpec/RestClient.channels.swift | 126 ++++++++++++++ ablySpec/RestClient.stats.swift | 8 +- ablySpec/RestClient.swift | 8 +- 22 files changed, 884 insertions(+), 254 deletions(-) create mode 100644 ably-ios/ARTChannels+Private.h create mode 100644 ably-ios/ARTChannels.h create mode 100644 ably-ios/ARTChannels.m create mode 100644 ably-ios/ARTPresence.h create mode 100644 ably-ios/ARTPresence.m create mode 100644 ablySpec/RestChannel.swift create mode 100644 ablySpec/RestClient.channels.swift diff --git a/ably-ios/ARTChannels+Private.h b/ably-ios/ARTChannels+Private.h new file mode 100644 index 000000000..c828bb83d --- /dev/null +++ b/ably-ios/ARTChannels+Private.h @@ -0,0 +1,36 @@ +// +// ARTChannels+Private.h +// ably +// +// Created by Yavor Georgiev on 20.08.15. +// Copyright (c) 2015 г. Ably. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface ARTChannel() { +@public + id _payloadEncoder; + __weak ARTLog *_logger; +} + +@property (nonatomic, strong, null_resettable) ARTChannelOptions *options; + +- (instancetype)initWithName:(NSString *)name presence:(ARTPresence *)presence options:(nullable ARTChannelOptions *)options; + +- (void)_postMessages:(id)payload callback:(nullable ARTStatusCallback)callback; + +@end + +@interface ARTChannelCollection() { +@protected + NSMutableDictionary /* */ *_channels; +} + +- (ARTChannel *)_createChannelWithName:(NSString *)name options:(nullable ARTChannelOptions *)options; + +@end + +NS_ASSUME_NONNULL_END \ No newline at end of file diff --git a/ably-ios/ARTChannels.h b/ably-ios/ARTChannels.h new file mode 100644 index 000000000..98243b8a4 --- /dev/null +++ b/ably-ios/ARTChannels.h @@ -0,0 +1,63 @@ +// +// ARTChannels.h +// ably +// +// Created by Yavor Georgiev on 20.08.15. +// Copyright (c) 2015 г. Ably. All rights reserved. +// + +#import +#import +#import +#import +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface ARTChannelOptions : NSObject + +@property (nonatomic, assign) BOOL isEncrypted; + +@property (nonatomic, strong, nullable) ARTCipherParams *cipherParams; + ++ (instancetype)unencrypted; + +- (instancetype)initEncrypted:(ARTCipherParams *)cipherParams; + +@end + +@class ARTPresence; + +@interface ARTChannel : NSObject + +@property (nonatomic, strong, readonly) NSString *name; + +@property (nonatomic, strong, readonly) ARTPresence *presence; + +- (void)publish:(nullable id)payload callback:(nullable ARTStatusCallback)callback; + +- (void)publish:(nullable id)payload name:(nullable NSString *)name callback:(nullable ARTStatusCallback)callback; + +- (void)publishMessage:(ARTMessage *)message callback:(nullable ARTStatusCallback)callback; + +- (void)publishMessages:(NSArray /* */ *)messages callback:(nullable ARTStatusCallback)callback; + +- (void)history:(nullable ARTDataQuery *)query callback:(void(^)(ARTStatus *status, ARTPaginatedResult /* */ *__nullable result))callback; + +@end + +@interface ARTChannelCollection : NSObject + +- (BOOL)exists:(NSString *)channelName; + +- (ARTChannel *)get:(NSString *)channelName; + +- (ARTChannel *)get:(NSString *)channelName options:(ARTChannelOptions *)options; + +- (void)releaseChannel:(ARTChannel *)channel; + +@end + +NS_ASSUME_NONNULL_END \ No newline at end of file diff --git a/ably-ios/ARTChannels.m b/ably-ios/ARTChannels.m new file mode 100644 index 000000000..d2195a9ec --- /dev/null +++ b/ably-ios/ARTChannels.m @@ -0,0 +1,142 @@ +// +// ARTChannels.m +// ably +// +// Created by Yavor Georgiev on 20.08.15. +// Copyright (c) 2015 г. Ably. All rights reserved. +// + +#import "ARTChannels.h" +#import "ARTChannels+Private.h" +#import "ARTNSArray+ARTFunctional.h" +#import "ARTEncoder.h" +#import "ARTLog.h" + +@implementation ARTChannelOptions + +- (instancetype)initEncrypted:(ARTCipherParams *)cipherParams { + if (self = [super init]) { + self->_isEncrypted = YES; + self->_cipherParams = cipherParams; + } + + return self; +} + ++ (instancetype)unencrypted { + static id unencrypted; + static dispatch_once_t once; + dispatch_once(&once, ^{ + unencrypted = [[ARTChannelOptions alloc] init]; + }); + + return unencrypted; +} + +@end + +@implementation ARTChannel + +- (instancetype)initWithName:(NSString *)name presence:(ARTPresence *)presence options:(nullable ARTChannelOptions *)options { + if (self = [super init]) { + _name = [name copy]; + _presence = presence; + self.options = options; + } + + return self; +} + +- (void)setOptions:(ARTChannelOptions *)options { + if (!options) { + _options = [ARTChannelOptions unencrypted]; + } else { + _options = options; + } + + _payloadEncoder = [ARTJsonPayloadEncoder instance]; +} + +- (void)publish:(nullable id)payload callback:(ARTStatusCallback)callback { + [self publish:payload name:nil callback:callback]; +} + +- (void)publish:(nullable id)payload name:(NSString *)name callback:(ARTStatusCallback)callback { + [self publishMessage:[[ARTMessage alloc] initWithData:payload name:name] callback:callback]; +} + +- (void)publishMessages:(NSArray *)messages callback:(ARTStatusCallback)callback { + messages = [messages artMap:^(ARTMessage *message) { + return [message encode:_payloadEncoder]; + }]; + + [self _postMessages:messages callback:callback]; +} + +- (void)publishMessage:(ARTMessage *)message callback:(ARTStatusCallback)callback { + [self _postMessages:[message encode:_payloadEncoder] callback:callback]; +} + +- (void)history:(ARTDataQuery *)query callback:(void (^)(ARTStatus *, ARTPaginatedResult * __nullable))callback { + NSAssert(false, @"-[%@ %@] should always be overriden.", self.class, NSStringFromSelector(_cmd)); +} + +- (void)_postMessages:(id)payload callback:(ARTStatusCallback)callback { + NSAssert(false, @"-[%@ %@] should always be overriden.", self.class, NSStringFromSelector(_cmd)); +} + +@end + +@implementation ARTChannelCollection + +- (instancetype)init { + if (self = [super init]) { + _channels = [[NSMutableDictionary alloc] init]; + } + + return self; +} + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(__unsafe_unretained id [])buffer count:(NSUInteger)len { + NSUInteger count = [self->_channels countByEnumeratingWithState:state objects:buffer count:len]; + for (NSUInteger i = 0; i < count; i++) { + buffer[i] = self->_channels[buffer[i]]; + } + + return count; +} + +- (BOOL)exists:(NSString *)channelName { + return self->_channels[channelName] != nil; +} + +- (ARTChannel *)get:(NSString *)channelName { + return [self _getChannel:channelName options:nil]; +} + +- (ARTChannel *)get:(NSString *)channelName options:(ARTChannelOptions *)options { + return [self _getChannel:channelName options:options]; +} + +- (void)releaseChannel:(ARTChannel *)channel { + [self->_channels removeObjectForKey:channel.name]; +} + +- (ARTChannel *)_getChannel:(NSString *)channelName options:(nullable ARTChannelOptions *)options { + ARTChannel *channel = self->_channels[channelName]; + if (!channel) { + channel = [self _createChannelWithName:channelName options:options]; + [self->_channels setObject:channel forKey:channelName]; + } else if (options) { + channel.options = options; + } + + return channel; +} + +- (ARTChannel *)_createChannelWithName:(NSString *)name options:(nullable ARTChannelOptions *)options { + NSAssert(false, @"-[%@ %@] should always be overriden.", self.class, NSStringFromSelector(_cmd)); + return 0; +} + +@end diff --git a/ably-ios/ARTJsonEncoder.m b/ably-ios/ARTJsonEncoder.m index 614a755e8..f0963919b 100644 --- a/ably-ios/ARTJsonEncoder.m +++ b/ably-ios/ARTJsonEncoder.m @@ -8,7 +8,7 @@ #import "ARTJsonEncoder.h" #import "ARTMessage.h" -#import "ARTPresenceMessage.h" +#import "ARTPresence.h" #import "ARTProtocolMessage.h" #import "ARTStats.h" #import "ARTNSDictionary+ARTDictionaryUtil.h" @@ -155,39 +155,39 @@ - (NSArray *)messagesFromArray:(NSArray *)input { return output; } --(ARTPresenceMessageAction) presenceMessageActionFromInt:(int) action +-(ARTPresenceAction) presenceActionFromInt:(int) action { switch (action) { case 0: - return ArtPresenceMessageAbsent; + return ARTPresenceAbsent; case 1: - return ArtPresenceMessagePresent; + return ARTPresencePresent; case 2: - return ARTPresenceMessageEnter; + return ARTPresenceEnter; case 3: - return ARTPresenceMessageLeave; + return ARTPresenceLeave; case 4: - return ARTPresenceMessageUpdate; + return ARTPresenceUpdate; } - [self.logger error:@"ARTJsonEncoder invalid ARTPresenceMessage action %d", action]; - return ArtPresenceMessageAbsent; + [self.logger error:@"ARTJsonEncoder invalid ARTPresenceAction %d", action]; + return ARTPresenceAbsent; } --(int) intFromPresenceMessageAction:(ARTPresenceMessageAction) action +-(int) intFromPresenceMessageAction:(ARTPresenceAction) action { switch (action) { - case ArtPresenceMessageAbsent: + case ARTPresenceAbsent: return 0; - case ArtPresenceMessagePresent: + case ARTPresencePresent: return 1; - case ARTPresenceMessageEnter: + case ARTPresenceEnter: return 2; - case ARTPresenceMessageLeave: + case ARTPresenceLeave: return 3; - case ARTPresenceMessageUpdate: + case ARTPresenceUpdate: return 4; - case ARTPresenceMessageLast: + case ARTPresenceLast: return 5; } } @@ -199,14 +199,14 @@ - (ARTPresenceMessage *)presenceMessageFromDictionary:(NSDictionary *)input { } ARTPresenceMessage *message = [[ARTPresenceMessage alloc] init]; message.id = [input artString:@"id"]; - message.encoding = [input artString:@"encoding"]; - message.clientId = [input artString:@"clientId"]; message.payload = [self payloadFromDictionary:input]; + message.payload.encoding = [input artString:@"encoding"]; + message.clientId = [input artString:@"clientId"]; message.timestamp = [input artDate:@"timestamp"]; int action = [[input artNumber:@"action"] intValue]; - message.action = [self presenceMessageActionFromInt:action]; + message.action = [self presenceActionFromInt:action]; message.connectionId = [input artString:@"connectionId"]; diff --git a/ably-ios/ARTMessage.h b/ably-ios/ARTMessage.h index 06c2b86a0..cb4b3ecbf 100644 --- a/ably-ios/ARTMessage.h +++ b/ably-ios/ARTMessage.h @@ -9,9 +9,9 @@ #import #import - @class ARTStatus; -@interface ARTMessage : NSObject + +@interface ARTMessage : NSObject @property (readwrite, strong, nonatomic) NSString *id; @property (readwrite, strong, nonatomic) NSString *name; @@ -22,12 +22,14 @@ @property (readwrite, strong, nonatomic) ARTStatus * status; - (instancetype)init; -- (ARTMessage *)messageWithPayload:(ARTPayload *)payload; +- (instancetype)initWithData:(id)data name:(NSString *)name; + +- (instancetype)decode:(id)encoder; +- (instancetype)encode:(id)encoder; -- (ARTMessage *)decode:(id)encoder; -- (ARTMessage *)encode:(id)encoder; - (id) content; -+ (ARTMessage *) messageWithPayload:(id) payload name:(NSString *) name; -+ (NSArray *) messagesWithPayloads:(NSArray *) payloads; -@end \ No newline at end of file ++ (instancetype)messageWithPayload:(id) payload name:(NSString *)name; ++ (NSArray *)messagesWithPayloads:(NSArray *)payloads; + +@end diff --git a/ably-ios/ARTMessage.m b/ably-ios/ARTMessage.m index 1f5322142..0a105e1a0 100644 --- a/ably-ios/ARTMessage.m +++ b/ably-ios/ARTMessage.m @@ -8,18 +8,21 @@ #import "ARTMessage.h" #import "ARTLog.h" + @implementation ARTMessage - (instancetype)init { - self = [super init]; - if (self) { - _id = nil; - _name = nil; - _clientId = nil; - _payload = nil; - _timestamp = nil; - _connectionId = nil; + return self = [self init]; +} + +- (instancetype)initWithData:(id)data name:(NSString *)name { + if (self = [self init]) { + _name = [name copy]; + if (data) { + _payload = [ARTPayload payloadWithPayload:data encoding:@""]; + } } + return self; } @@ -31,31 +34,38 @@ -(void) setClientId:(NSString *)clientId { else { _clientId = nil; } + +} +- (id)copyWithZone:(NSZone *)zone { + ARTMessage *message = [[self.class allocWithZone:zone] init]; + message->_id = self.id; + message->_name = self.name; + message->_clientId = self.clientId; + message->_timestamp = self.timestamp; + message->_payload = self.payload; + message->_connectionId = self.connectionId; + message->_status = self.status; + return message; } -- (ARTMessage *)messageWithPayload:(ARTPayload *)payload status:(ARTStatus * ) status { - ARTMessage *m = [[ARTMessage alloc] init]; - m.id = self.id; - m.name = self.name; - m.clientId = self.clientId; - m.timestamp = self.timestamp; - m.payload = payload; - m.connectionId = self.connectionId; - m.status = status; - return m; +- (instancetype)messageWithPayload:(ARTPayload *)payload status:(ARTStatus * ) status { + ARTMessage *message = [self copy]; + message.payload = payload; + message.status = status; + return message; } -- (ARTMessage *)messageWithPayload:(ARTPayload *)payload { - return [self messageWithPayload:payload status: nil]; +- (instancetype)messageWithPayload:(ARTPayload *)payload { + return [self messageWithPayload:payload status: nil]; } -- (ARTMessage *)decode:(id)encoder { +- (instancetype)decode:(id)encoder { ARTPayload *payload = self.payload; ARTStatus *status = [encoder decode:payload output:&payload]; return [self messageWithPayload:payload status: status]; } -- (ARTMessage *)encode:(id)encoder { +- (instancetype)encode:(id)encoder { ARTPayload *payload = self.payload; ARTStatus *status = [encoder encode:payload output:&payload]; return [self messageWithPayload:payload status:status]; @@ -84,8 +94,4 @@ + (NSArray *) messagesWithPayloads:(NSArray *) payloads { return messages; } - - - - @end diff --git a/ably-ios/ARTPayload.m b/ably-ios/ARTPayload.m index 23e2609f3..d8a6a85a0 100644 --- a/ably-ios/ARTPayload.m +++ b/ably-ios/ARTPayload.m @@ -285,7 +285,7 @@ - (ARTStatus *)encode:(ARTPayload *)payload output:(ARTPayload *__autoreleasing } } // otherwise do nothing besides confirm payload is nsdata or nsstring - else if(!([payload.payload isKindOfClass:[NSData class]] || [payload.payload isKindOfClass:[NSString class]])) { + else if(payload && !([payload.payload isKindOfClass:[NSData class]] || [payload.payload isKindOfClass:[NSString class]])) { [NSException raise:@"ARTPayload must be either NSDictionary, NSArray, NSData or NSString" format:@"%@", [payload.payload class]]; } return [ARTStatus state:ARTStateOk]; @@ -320,7 +320,7 @@ - (instancetype)initWithCipherParams:(ARTCipherParams *)cipherParams { if (self) { _cipher = [ARTCrypto cipherWithParams:cipherParams]; if (!_cipher) { - [self.logger error:[NSString stringWithFormat:@"ARTCipherPayloadEncoder failed to create cipher with name %@", cipherParams.algorithm]]; + [self.logger error:@"ARTCipherPayloadEncoder failed to create cipher with name %@", cipherParams.algorithm]; self = nil; return nil; } @@ -338,14 +338,14 @@ +(NSString *) getName256 { - (NSString *)name { size_t keyLen =[self.cipher keyLength]; - if(keyLen== 128) { + if (keyLen == 128) { return [ARTCipherPayloadEncoder getName128]; } else if(keyLen == 256) { return [ARTCipherPayloadEncoder getName256]; } else { - [self.logger error:[NSString stringWithFormat:@"ARTPayload: keyLength is invalid %zu", keyLen]]; + [self.logger error:@"ARTPayload: keyLength is invalid %zu", keyLen]; } return @""; } diff --git a/ably-ios/ARTPresence.h b/ably-ios/ARTPresence.h new file mode 100644 index 000000000..7a95450c4 --- /dev/null +++ b/ably-ios/ARTPresence.h @@ -0,0 +1,39 @@ +// +// ARTPresence.h +// ably +// +// Created by Yavor Georgiev on 26.08.15. +// Copyright (c) 2015 г. Ably. All rights reserved. +// + +#import +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSUInteger, ARTPresenceAction) { + ARTPresenceAbsent, + ARTPresencePresent, + ARTPresenceEnter, + ARTPresenceLeave, + ARTPresenceUpdate, + ARTPresenceLast +}; + +@interface ARTPresenceMessage : ARTMessage + +@property (nonatomic, assign) ARTPresenceAction action; + +@end + +@interface ARTPresence : NSObject + +- (void)get:(void (^)(ARTStatus *status, ARTPaginatedResult /* */ *__nullable result))callback; + +- (void)history:(nullable ARTDataQuery *)query callback:(void (^)(ARTStatus *status, ARTPaginatedResult /* */ *__nullable result))callback; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ably-ios/ARTPresence.m b/ably-ios/ARTPresence.m new file mode 100644 index 000000000..e1d910a1f --- /dev/null +++ b/ably-ios/ARTPresence.m @@ -0,0 +1,32 @@ +// +// ARTPresence.m +// ably +// +// Created by Yavor Georgiev on 26.08.15. +// Copyright (c) 2015 г. Ably. All rights reserved. +// + +#import "ARTDataQuery+Private.h" +#import "ARTPresence.h" + +@implementation ARTPresenceMessage + +- (id)copyWithZone:(NSZone *)zone { + ARTPresenceMessage *message = [super copyWithZone:zone]; + message->_action = self.action; + return message; +} + +@end + +@implementation ARTPresence + +- (void)get:(void (^)(ARTStatus *status, ARTPaginatedResult *__nullable result))callback { + NSAssert(false, @"-[%@ %@] should always be overriden.", self.class, NSStringFromSelector(_cmd)); +} + +- (void)history:(nullable ARTDataQuery *)query callback:(void (^)(ARTStatus *status, ARTPaginatedResult *__nullable result))callback { + NSAssert(false, @"-[%@ %@] should always be overriden.", self.class, NSStringFromSelector(_cmd)); +} + +@end diff --git a/ably-ios/ARTPresenceMap.m b/ably-ios/ARTPresenceMap.m index fbcd8133f..9c9af1c5d 100644 --- a/ably-ios/ARTPresenceMap.m +++ b/ably-ios/ARTPresenceMap.m @@ -63,7 +63,7 @@ - (void)endSync { NSArray * keys = [self.mostRecentMessageForMember allKeys]; for(NSString * key in keys) { ARTPresenceMessage * message = [self.mostRecentMessageForMember objectForKey:key]; - if(message.action == ArtPresenceMessageAbsent || message.action == ARTPresenceMessageLeave) { + if(message.action == ARTPresenceMessageAbsent || message.action == ARTPresenceMessageLeave) { [self.mostRecentMessageForMember removeObjectForKey:key]; } } diff --git a/ably-ios/ARTPresenceMessage.h b/ably-ios/ARTPresenceMessage.h index 7c443b9ec..f306b8511 100644 --- a/ably-ios/ARTPresenceMessage.h +++ b/ably-ios/ARTPresenceMessage.h @@ -11,8 +11,8 @@ @class ARTStatus; typedef NS_ENUM(NSUInteger, ARTPresenceMessageAction) { - ArtPresenceMessageAbsent, - ArtPresenceMessagePresent, + ARTPresenceMessageAbsent, + ARTPresenceMessagePresent, ARTPresenceMessageEnter, ARTPresenceMessageLeave, ARTPresenceMessageUpdate, diff --git a/ably-ios/ARTPresenceMessage.m b/ably-ios/ARTPresenceMessage.m index be86dd999..6ec56da87 100644 --- a/ably-ios/ARTPresenceMessage.m +++ b/ably-ios/ARTPresenceMessage.m @@ -42,7 +42,6 @@ - (ARTPresenceMessage *)messageWithPayload:(ARTPayload *)payload { - (ARTPresenceMessage *)decode:(id)encoder { ARTPayload *payload = self.payload; - ARTStatus *status = [encoder decode:payload output:&payload]; return [self messageWithPayload:payload]; } diff --git a/ably-ios/ARTRealtime.m b/ably-ios/ARTRealtime.m index 3629f5f4a..54c42ddd3 100644 --- a/ably-ios/ARTRealtime.m +++ b/ably-ios/ARTRealtime.m @@ -379,7 +379,7 @@ - (instancetype)initWithRealtime:(ARTRealtime *)realtime name:(NSString *)name c _clientId = realtime.clientId; _payloadEncoder = [ARTPayload defaultPayloadEncoder:cipherParams]; _presenceMap =[[ARTPresenceMap alloc] init]; - _lastPresenceAction = ArtPresenceMessageAbsent; + _lastPresenceAction = ARTPresenceMessageAbsent; } return self; } diff --git a/ably-ios/ARTRest.h b/ably-ios/ARTRest.h index bbaebf6d3..f63f92256 100644 --- a/ably-ios/ARTRest.h +++ b/ably-ios/ARTRest.h @@ -7,40 +7,32 @@ // #import +#import #import #import #import #import #import +#import @class ARTLog; -@class ARTCipherParams; -@class ARTTokenDetails; -@class ARTAuthTokenParams; -@class ARTRestPresence; +NS_ASSUME_NONNULL_BEGIN -# pragma mark - ARTRestChannel +@interface ARTRestPresence : ARTPresence -@interface ARTRestChannel : NSObject - -- (id)publish:(id)payload withName:(NSString *)name cb:(ARTStatusCallback)cb; -- (id)publish:(id)payload cb:(ARTStatusCallback)cb; +@end -- (void)history:(ARTDataQuery *)query callback:(void (^)(ARTStatus *status, ARTPaginatedResult /* */ *result))callback; +@interface ARTRestChannel : ARTChannel -@property (readonly, strong, nonatomic) ARTRestPresence *presence; +@property (nonatomic, strong, readonly) ARTRestPresence *presence; @end +@interface ARTRestChannelCollection : ARTChannelCollection -# pragma mark - ARTRestPresence - -@interface ARTRestPresence : NSObject - -- (instancetype) initWithChannel:(ARTRestChannel *) channel; -- (void)get:(ARTDataQuery *)query callback:(void (^)(ARTStatus *status, ARTPaginatedResult /* */ *result))callback; -- (void)history:(ARTDataQuery *)query callback:(void (^)(ARTStatus *status, ARTPaginatedResult /* */ *result))callback; +- (ARTRestChannel *)get:(NSString *)channelName; +- (ARTRestChannel *)get:(NSString *)channelName options:(ARTChannelOptions *)options; @end @@ -54,15 +46,19 @@ - (instancetype)initWithLogger:(ARTLog *)logger andOptions:(ARTClientOptions *)options; - (instancetype)initWithKey:(NSString *)key; -- (id)token:(ARTAuthTokenParams *)keyName tokenCb:(void (^)(ARTStatus * status, ARTTokenDetails *)) cb; -- (id)time:(void(^)(ARTStatus *status, NSDate *time))cb; -- (void)stats:(ARTStatsQuery *)query callback:(void (^)(ARTStatus *status, ARTPaginatedResult /* */ *result))callback; -- (id)internetIsUp:(void (^)(bool isUp)) cb; -- (ARTRestChannel *)channel:(NSString *)channelName; -- (ARTRestChannel *)channel:(NSString *)channelName cipherParams:(ARTCipherParams *)cipherParams; +- (id)token:(ARTAuthTokenParams *)keyName tokenCb:(void (^)(ARTStatus * status, ARTTokenDetails *))cb; -- (ARTAuth *)auth; +- (id)time:(void(^)(ARTStatus * status, NSDate *time))cb; + +- (void)stats:(nullable ARTStatsQuery *)query callback:(void (^)(ARTStatus *status, ARTPaginatedResult /* */ *__nullable result))callback; + +- (id)internetIsUp:(void (^)(bool isUp)) cb; @property (nonatomic, strong, readonly) ARTLog *logger; +@property (nonatomic, strong, readonly) ARTRestChannelCollection *channels; +@property (nonatomic, strong, readonly) ARTAuth *auth; +@property (nonatomic, strong, readonly) ARTClientOptions *options; @end + +NS_ASSUME_NONNULL_END diff --git a/ably-ios/ARTRest.m b/ably-ios/ARTRest.m index 9aab63f54..4546b6cc5 100644 --- a/ably-ios/ARTRest.m +++ b/ably-ios/ARTRest.m @@ -8,7 +8,9 @@ #import "ARTRest.h" #import "ARTRest+Private.h" +#import "ARTChannels+Private.h" #import "ARTDataQuery+Private.h" + #import "ARTAuth.h" #import "ARTHttp.h" #import "ARTEncoder.h" @@ -17,7 +19,6 @@ #import "ARTMessage.h" #import "ARTHttpPaginatedResult.h" #import "ARTStats.h" -#import "ARTPresenceMessage.h" #import "ARTNSDictionary+ARTDictionaryUtil.h" #import "ARTNSArray+ARTFunctional.h" @@ -27,125 +28,142 @@ #import "ARTDefault.h" #import "ARTFallback.h" -@interface ARTRestPresence () -@property (readonly, weak, nonatomic) ARTRestChannel *channel; -@end - - -// TODO base accept headers on encoders - -@interface ARTRestChannel () -@property (nonatomic, weak) ARTLog * logger; -@property (readonly, weak, nonatomic) ARTRest *rest; -@property (readonly, strong, nonatomic) NSString *name; -@property (readonly, strong, nonatomic) NSString *basePath; - -@property (readonly, strong, nonatomic) id payloadEncoder; - -- (instancetype)initWithRest:(ARTRest *)rest name:(NSString *)name cipherParams:(ARTCipherParams *)cipherParams; -+ (instancetype)channelWithRest:(ARTRest *)rest name:(NSString *)name cipherParams:(ARTCipherParams *)cipherParams; - -@end - @interface ARTRest () @property (readonly, strong, nonatomic) ARTHttp *http; -@property (readonly, weak, nonatomic) ARTClientOptions *options; -@property (readonly, strong, nonatomic) NSMutableDictionary *channels; -@property ( strong, nonatomic) ARTAuth *auth; +@property (strong, nonatomic) ARTAuth *auth; @property (readonly, strong, nonatomic) NSDictionary *encoders; @property (readonly, strong, nonatomic) NSString *defaultEncoding; @property (readwrite, assign, nonatomic) int fallbackCount; @property (readwrite, copy, nonatomic) NSURL *baseUrl; - - (id)makeRequestWithMethod:(NSString *)method relUrl:(NSString *)relUrl headers:(NSDictionary *)headers body:(NSData *)body authenticated:(ARTAuthentication)authenticated cb:(ARTHttpCb)cb; - (NSDictionary *)withAcceptHeader:(NSDictionary *)headers; @end -@implementation ARTRestChannel +@interface ARTRestPresence () + +- (instancetype)initWithChannel:(ARTRestChannel *)channel; + +@end + +@implementation ARTRestChannel { +@public + __weak ARTRest *_rest; + NSString *_basePath; +} -- (instancetype)initWithRest:(ARTRest *)rest name:(NSString *)name cipherParams:(ARTCipherParams *)cipherParams { - self = [super init]; - if (self) { - self.logger = rest.logger; - _presence = [[ARTRestPresence alloc] initWithChannel:self]; - [self.logger debug:@"ARTRestChannel: instantiating under %@", name]; +@dynamic presence; + +- (instancetype)initWithRest:(ARTRest *)rest name:(NSString *)name options:(ARTChannelOptions *)options { + if (self = [super initWithName:name presence:[[ARTRestPresence alloc] initWithChannel:self] options:options]) { + _logger = rest.logger; + [_logger debug:@"ARTRestChannel: instantiating under %@", name]; _rest = rest; - _name = name; _basePath = [NSString stringWithFormat:@"/channels/%@", [name stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLPathAllowedCharacterSet]]]; - _payloadEncoder = [ARTPayload defaultPayloadEncoder:cipherParams]; } + return self; } -+ (instancetype)channelWithRest:(ARTRest *)rest name:(NSString *)name cipherParams:(ARTCipherParams *)cipherParams { - return [[ARTRestChannel alloc] initWithRest:rest name:name cipherParams:cipherParams]; +- (void)history:(ARTDataQuery *)query callback:(void (^)(ARTStatus *status, ARTPaginatedResult *__nullable result))callback { + NSParameterAssert(query.limit < 1000); + NSParameterAssert([query.start compare:query.end] != NSOrderedDescending); + + [_rest withAuthHeaders:^(NSDictionary *authHeaders) { + NSURLComponents *requestUrl = [NSURLComponents componentsWithString:[_basePath stringByAppendingPathComponent:@"messages"]]; + requestUrl.queryItems = [query asQueryItems]; + ARTHttpRequest *req = [[ARTHttpRequest alloc] initWithMethod:@"GET" url:[requestUrl URLRelativeToURL:_rest.baseUrl] headers:authHeaders body:nil]; + return [ARTHttpPaginatedResult makePaginatedRequest:_rest.http request:req responseProcessor:^(ARTHttpResponse *response) { + id encoder = [_rest.encoders objectForKey:response.contentType]; + return [[encoder decodeMessages:response.body] artMap:^(ARTMessage *message) { + return [message decode:_payloadEncoder]; + }]; + } callback:callback]; + }]; } -- (id)publishMessages:(NSArray *)messages cb:(ARTStatusCallback)cb { - //not currently used. - [ARTMessage messagesWithPayloads:messages]; - - NSMutableArray * encodedMessages = [NSMutableArray array]; - for(int i=0; i < [messages count]; i++) { - ARTPayload *encodedPayload = nil; - - ARTPayload * p = [ARTPayload payloadWithPayload:[messages objectAtIndex:i] encoding:self.rest.defaultEncoding]; - ARTStatus * status = [self.payloadEncoder encode:p output:&encodedPayload]; - if (status.state != ARTStateOk) { - [self.logger warn:@"ARTRest publishMessages could not encode message %d", i]; - } - [encodedMessages addObject:encodedPayload.payload]; +- (void)_postMessages:(id)payload callback:(ARTStatusCallback)callback { + NSData *encodedMessage = nil; + if ([payload isKindOfClass:[ARTMessage class]]) { + encodedMessage = [_rest.defaultEncoder encodeMessage:payload]; + } else if ([payload isKindOfClass:[NSArray class]]) { + encodedMessage = [_rest.defaultEncoder encodeMessages:payload]; } - //ARTPayload * finalPayload = [ARTPayload payloadWithPayload:encodedMessages encoding:@""]; - ARTMessage * bigMessage = [ARTMessage messageWithPayload:encodedMessages name:nil]; - return [self publishMessage:bigMessage cb:cb]; -} - --(id) publishMessage:(ARTMessage *) message cb:(ARTStatusCallback) cb { - NSData *encodedMessage = [self.rest.defaultEncoder encodeMessage:message]; - NSString * defaultEncoding = self.rest.defaultEncoding ? self.rest.defaultEncoding :@""; - NSDictionary *headers = @{@"Content-Type":defaultEncoding}; - NSString *path = [NSString stringWithFormat:@"%@/messages", self.basePath]; - return [self.rest post:path headers:headers body:encodedMessage authenticated:ARTAuthenticationOn cb:^(ARTHttpResponse *response) { - ARTStatus *status = [ARTStatus state:(response.status >= 200 && response.status < 300 ? ARTStateOk : ARTStateError) info:response.error]; - cb(status); - }]; + [_rest post:[_basePath stringByAppendingPathComponent:@"messages"] + headers:@{ @"Content-Type": _rest.defaultEncoding ?: @"" } + body:encodedMessage + authenticated:ARTAuthenticationOn + cb:^(ARTHttpResponse *response) { + if (callback) { + ARTState state = response.status >= 200 && response.status < 300 ? ARTStateOk : ARTStateError; + callback([ARTStatus state:state info:response.error]); + } + }]; } -- (id)publish:(id)payload withName:(NSString *)name cb:(ARTStatusCallback)cb { - [self.logger debug:@"ARTRestChannel: publishing '%@' to channel with name '%@'", payload, name]; - ARTMessage *message = [ARTMessage messageWithPayload:payload name:name];//[[ARTMessage alloc] init]; - message = [message encode:self.payloadEncoder]; - return [self publishMessage:message cb:cb]; +@end + +@implementation ARTRestChannelCollection { + __weak ARTRest *_rest; } -- (id)publish:(id)payload cb:(ARTStatusCallback)cb { - if([payload isKindOfClass:[NSArray class]]) { - return [self publishMessages:payload cb:cb]; +- (instancetype)initWithRest:(ARTRest *)rest { + if (self = [super init]) { + _rest = rest; } - else { - return [self publish:payload withName:nil cb:cb]; + + return self; +} + +- (ARTChannel *)_createChannelWithName:(NSString *)name options:(ARTChannelOptions *)options { + return [[ARTRestChannel alloc] initWithRest:_rest name:name options:options]; +} + +@end + +@implementation ARTRestPresence { + ARTRestChannel *_channel; +} + +- (instancetype)initWithChannel:(ARTRestChannel *)channel { + if (self = [super init]) { + _channel = channel; } + + return self; } -- (void)history:(ARTDataQuery *)query callback:(void (^)(ARTStatus *status, ARTPaginatedResult *__nullable result))callback { +- (void)get:(void (^)(ARTStatus *status, ARTPaginatedResult *__nullable result))callback { + [_channel->_rest withAuthHeaders:^(NSDictionary *authHeaders) { + NSURLComponents *requestUrl = [NSURLComponents componentsWithString:[_channel->_basePath stringByAppendingPathComponent:@"presence"]]; + ARTHttpRequest *req = [[ARTHttpRequest alloc] initWithMethod:@"GET" url:[requestUrl URLRelativeToURL:_channel->_rest.baseUrl] headers:authHeaders body:nil]; + return [ARTHttpPaginatedResult makePaginatedRequest:_channel->_rest.http request:req responseProcessor:^(ARTHttpResponse *response) { + id encoder = [_channel->_rest.encoders objectForKey:response.contentType]; + NSArray *messages = [encoder decodePresenceMessages:response.body]; + return [messages artMap:^id(ARTPresenceMessage *pm) { + return [pm decode:_channel->_payloadEncoder]; + }]; + } callback:callback]; + }]; +} + +- (void)history:(nullable ARTDataQuery *)query callback:(void (^)(ARTStatus *status, ARTPaginatedResult *__nullable result))callback { NSParameterAssert(query.limit < 1000); NSParameterAssert([query.start compare:query.end] != NSOrderedDescending); - [_rest withAuthHeaders:^(NSDictionary *authHeaders) { - NSURLComponents *requestUrl = [NSURLComponents componentsWithString:[self.basePath stringByAppendingPathComponent:@"messages"]]; + [_channel->_rest withAuthHeaders:^(NSDictionary *authHeaders) { + NSURLComponents *requestUrl = [NSURLComponents componentsWithString:[_channel->_basePath stringByAppendingPathComponent:@"presence/history"]]; requestUrl.queryItems = [query asQueryItems]; - ARTHttpRequest *req = [[ARTHttpRequest alloc] initWithMethod:@"GET" url:[requestUrl URLRelativeToURL:_rest.baseUrl] headers:authHeaders body:nil]; - return [ARTHttpPaginatedResult makePaginatedRequest:_rest.http request:req responseProcessor:^(ARTHttpResponse *response) { - id encoder = [_rest.encoders objectForKey:response.contentType]; - NSArray *messages = [encoder decodeMessages:response.body]; - return [messages artMap:^id(ARTMessage *message) { - return [message decode:self.payloadEncoder]; + ARTHttpRequest *req = [[ARTHttpRequest alloc] initWithMethod:@"GET" url:[requestUrl URLRelativeToURL:_channel->_rest.baseUrl] headers:authHeaders body:nil]; + return [ARTHttpPaginatedResult makePaginatedRequest:_channel->_rest.http request:req responseProcessor:^(ARTHttpResponse *response) { + id encoder = [_channel->_rest.encoders objectForKey:response.contentType]; + NSArray *messages = [encoder decodePresenceMessages:response.body]; + return [messages artMap:^id(ARTPresenceMessage *pm) { + return [pm decode:_channel->_payloadEncoder]; }]; } callback:callback]; }]; @@ -189,11 +207,12 @@ - (instancetype)initWithKey:(NSString *) key { - (void)setup { _http = [[ARTHttp alloc] init]; - _channels = [NSMutableDictionary dictionary]; + _channels = [[ARTRestChannelCollection alloc] initWithRest:self]; + id defaultEncoder = [[ARTJsonEncoder alloc] init]; _encoders = @{ [defaultEncoder mimeType]: defaultEncoder, - }; + }; _defaultEncoding = [defaultEncoder mimeType]; _fallbackCount = 0; @@ -280,19 +299,6 @@ - (void)stats:(ARTStatsQuery *)query callback:(void (^)(ARTStatus *status, ARTPa }]; } -- (ARTRestChannel *)channel:(NSString *)channelName { - return [self channel:channelName cipherParams:nil]; -} - -- (ARTRestChannel *)channel:(NSString *)channelName cipherParams:(ARTCipherParams *)cipherParams { - ARTRestChannel *channel = [self.channels objectForKey:channelName]; - if (!channel) { - channel = [ARTRestChannel channelWithRest:self name:channelName cipherParams:cipherParams]; - [self.channels setObject:channel forKey:channelName]; - } - return channel; -} - -(bool) isAnErrorStatus:(int) status { return status >=400; } @@ -371,7 +377,6 @@ -(bool) isAnErrorStatus:(int) status { } }]; } - } - (id)makeRequestWithMethod:(NSString *)method relUrl:(NSString *)relUrl headers:(NSDictionary *)headers body:(NSData *)body authenticated:(ARTAuthentication)authenticated cb:(ARTHttpCb)cb { @@ -474,55 +479,3 @@ - (NSURL *)resolveUrl:(NSString *)relUrl queryParams:(NSDictionary *)queryParams } @end - - -@implementation ARTRestPresence - --(instancetype) initWithChannel:(ARTRestChannel *)channel { - self = [super init]; - if(self) { - _channel = channel; - } - return self; -} - -- (id)get:(ARTPaginatedResultCallback)callback { - return [self getWithParams:nil cb:callback]; -} - -- (id)getWithParams:(NSDictionary *)queryParams cb:(ARTPaginatedResultCallback)callback { - [self.channel.rest throwOnHighLimitCheck:queryParams]; - return [self.channel.rest withAuthHeaders:^(NSDictionary *authHeaders) { - NSString *relUrl = [NSString stringWithFormat:@"%@/presence", self.channel.basePath]; - ARTHttpRequest *req = [[ARTHttpRequest alloc] initWithMethod:@"GET" url:[self.channel.rest resolveUrl:relUrl queryParams:queryParams] headers:authHeaders body:nil]; - return [ARTHttpPaginatedResult makePaginatedRequest:self.channel.rest.http request:req responseProcessor:^id(ARTHttpResponse *response) { - id encoder = [self.channel.rest.encoders objectForKey:response.contentType]; - NSArray *messages = [encoder decodePresenceMessages:response.body]; - return [messages artMap:^id(ARTPresenceMessage *pm) { - return [pm decode:self.channel.payloadEncoder]; - }]; - } callback:callback]; - }]; -} - -- (id)history:(ARTPaginatedResultCallback)callback { - return [self historyWithParams:nil cb:callback]; -} - -- (id) historyWithParams:(NSDictionary *)queryParams cb:(ARTPaginatedResultCallback)callback { - [self.channel.rest throwOnHighLimitCheck:queryParams]; - return [self.channel.rest withAuthHeaders:^(NSDictionary *authHeaders) { - NSString *relUrl = [NSString stringWithFormat:@"%@/presence/history", self.channel.basePath]; - ARTHttpRequest *req = [[ARTHttpRequest alloc] initWithMethod:@"GET" url:[self.channel.rest resolveUrl:relUrl queryParams:queryParams] headers:authHeaders body:nil]; - return [ARTHttpPaginatedResult makePaginatedRequest:self.channel.rest.http request:req responseProcessor:^id(ARTHttpResponse *response) { - id encoder = [self.channel.rest.encoders objectForKey:response.contentType]; - NSArray *messages = [encoder decodePresenceMessages:response.body]; - return [messages artMap:^id(ARTPresenceMessage *pm) { - return [pm decode:self.channel.payloadEncoder]; - }]; - } callback:callback]; - }]; -} - - -@end diff --git a/ably-ios/ably.h b/ably-ios/ably.h index fb4dc3d9e..0ca2ab842 100644 --- a/ably-ios/ably.h +++ b/ably-ios/ably.h @@ -14,18 +14,16 @@ FOUNDATION_EXPORT double ablyVersionNumber; //! Project version string for ably-ios. FOUNDATION_EXPORT const unsigned char ablyVersionString[]; +#import #import -#import #import -#import #import #import #import -#import #import #import -#import -#import +#import #import #import -#import \ No newline at end of file +#import +#import \ No newline at end of file diff --git a/ably-ios/ably.modulemap b/ably-ios/ably.modulemap index 8c34bc050..f204c8676 100644 --- a/ably-ios/ably.modulemap +++ b/ably-ios/ably.modulemap @@ -5,11 +5,11 @@ framework module ably { module * { export * } explicit module Private { + header "ARTChannels+Private.h" header "ARTDataQuery+Private.h" header "ARTPayload+Private.h" header "ARTTokenDetails+Private.h" header "ARTRest+Private.h" - header "ARTRealtime+Private.h" header "ARTClientOptions+Private.h" header "ARTEncoder.h" header "ARTJsonEncoder.h" diff --git a/ably.xcodeproj/project.pbxproj b/ably.xcodeproj/project.pbxproj index 65acae775..a2aa2f2d3 100644 --- a/ably.xcodeproj/project.pbxproj +++ b/ably.xcodeproj/project.pbxproj @@ -61,7 +61,7 @@ 856AAC9B1B6E326E00B07119 /* ably-common in Resources */ = {isa = PBXBuildFile; fileRef = 856AAC9A1B6E326E00B07119 /* ably-common */; }; 85B2C2191B6FE8DE00EA5254 /* CompatibilityMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = 85B2C2181B6FE8DE00EA5254 /* CompatibilityMacros.h */; settings = {ATTRIBUTES = (Public, ); }; }; 85F0A60B1B6D039F00EFF45A /* ably.h in Headers */ = {isa = PBXBuildFile; fileRef = 85F0A60A1B6D039F00EFF45A /* ably.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 960D07931A45F1D800ED8C8C /* ARTCrypto.h in Headers */ = {isa = PBXBuildFile; fileRef = 960D07911A45F1D800ED8C8C /* ARTCrypto.h */; }; + 960D07931A45F1D800ED8C8C /* ARTCrypto.h in Headers */ = {isa = PBXBuildFile; fileRef = 960D07911A45F1D800ED8C8C /* ARTCrypto.h */; settings = {ATTRIBUTES = (Public, ); }; }; 960D07941A45F1D800ED8C8C /* ARTCrypto.m in Sources */ = {isa = PBXBuildFile; fileRef = 960D07921A45F1D800ED8C8C /* ARTCrypto.m */; }; 960D07971A46FFC300ED8C8C /* ARTRest+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 960D07951A46FFC300ED8C8C /* ARTRest+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; 961343D81A42E0B7006DC822 /* ARTClientOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 961343D61A42E0B7006DC822 /* ARTClientOptions.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -108,6 +108,13 @@ D746AE1D1BBB5207003ECEF8 /* ARTDataQuery.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE1A1BBB5207003ECEF8 /* ARTDataQuery.h */; }; D746AE1E1BBB5207003ECEF8 /* ARTDataQuery+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE1B1BBB5207003ECEF8 /* ARTDataQuery+Private.h */; }; D746AE1F1BBB5207003ECEF8 /* ARTDataQuery.m in Sources */ = {isa = PBXBuildFile; fileRef = D746AE1C1BBB5207003ECEF8 /* ARTDataQuery.m */; }; + D746AE221BBB60EE003ECEF8 /* ARTChannels.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE201BBB60EE003ECEF8 /* ARTChannels.h */; }; + D746AE231BBB60EE003ECEF8 /* ARTChannels.m in Sources */ = {isa = PBXBuildFile; fileRef = D746AE211BBB60EE003ECEF8 /* ARTChannels.m */; }; + D746AE251BBB611C003ECEF8 /* ARTChannels+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE241BBB611C003ECEF8 /* ARTChannels+Private.h */; }; + D746AE281BBB61C9003ECEF8 /* ARTPresence.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE261BBB61C9003ECEF8 /* ARTPresence.h */; }; + D746AE291BBB61C9003ECEF8 /* ARTPresence.m in Sources */ = {isa = PBXBuildFile; fileRef = D746AE271BBB61C9003ECEF8 /* ARTPresence.m */; }; + D746AE2C1BBB625E003ECEF8 /* RestChannel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D746AE2A1BBB625E003ECEF8 /* RestChannel.swift */; }; + D746AE2D1BBB625E003ECEF8 /* RestClient.channels.swift in Sources */ = {isa = PBXBuildFile; fileRef = D746AE2B1BBB625E003ECEF8 /* RestClient.channels.swift */; }; FC78549C1BCD5688539CCBE4 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FDA6E099E4BC04FC80F845C0 /* libPods.a */; }; /* End PBXBuildFile section */ @@ -263,6 +270,13 @@ D746AE1A1BBB5207003ECEF8 /* ARTDataQuery.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTDataQuery.h; sourceTree = ""; }; D746AE1B1BBB5207003ECEF8 /* ARTDataQuery+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ARTDataQuery+Private.h"; sourceTree = ""; }; D746AE1C1BBB5207003ECEF8 /* ARTDataQuery.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTDataQuery.m; sourceTree = ""; }; + D746AE201BBB60EE003ECEF8 /* ARTChannels.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTChannels.h; sourceTree = ""; }; + D746AE211BBB60EE003ECEF8 /* ARTChannels.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTChannels.m; sourceTree = ""; }; + D746AE241BBB611C003ECEF8 /* ARTChannels+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ARTChannels+Private.h"; sourceTree = ""; }; + D746AE261BBB61C9003ECEF8 /* ARTPresence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTPresence.h; sourceTree = ""; }; + D746AE271BBB61C9003ECEF8 /* ARTPresence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTPresence.m; sourceTree = ""; }; + D746AE2A1BBB625E003ECEF8 /* RestChannel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RestChannel.swift; sourceTree = ""; }; + D746AE2B1BBB625E003ECEF8 /* RestClient.channels.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RestClient.channels.swift; sourceTree = ""; }; DF8DF755839D59574B7B795F /* Pods-ablySpec.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ablySpec.release.xcconfig"; path = "Pods/Target Support Files/Pods-ablySpec/Pods-ablySpec.release.xcconfig"; sourceTree = ""; }; FDA6E099E4BC04FC80F845C0 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -341,6 +355,8 @@ 856AAC951B6E30C800B07119 /* ablySpec-Bridging-Header.h */, 856AAC981B6E312F00B07119 /* RestClient.swift */, 853ED7C31B7A1A3C006F1C6F /* RestClient.stats.swift */, + D746AE2A1BBB625E003ECEF8 /* RestChannel.swift */, + D746AE2B1BBB625E003ECEF8 /* RestClient.channels.swift */, D723046F1BB72CED00F1ABDA /* RealtimeClient.swift */, 851674EE1B7BA5CD00D35169 /* Stats.swift */, ); @@ -384,6 +400,11 @@ 961343D61A42E0B7006DC822 /* ARTClientOptions.h */, 1C70CB871B020D7F003D295A /* ARTClientOptions+Private.h */, 961343D71A42E0B7006DC822 /* ARTClientOptions.m */, + D746AE201BBB60EE003ECEF8 /* ARTChannels.h */, + D746AE241BBB611C003ECEF8 /* ARTChannels+Private.h */, + D746AE211BBB60EE003ECEF8 /* ARTChannels.m */, + D746AE261BBB61C9003ECEF8 /* ARTPresence.h */, + D746AE271BBB61C9003ECEF8 /* ARTPresence.m */, 96BF61551A35B40E004CF2B3 /* ARTStatus.h */, 1C55427C1B148306003068DB /* ARTStatus.m */, D746AE1A1BBB5207003ECEF8 /* ARTDataQuery.h */, @@ -500,9 +521,11 @@ buildActionMask = 2147483647; files = ( 96BF61531A35B39C004CF2B3 /* ARTRest.h in Headers */, + D746AE281BBB61C9003ECEF8 /* ARTPresence.h in Headers */, 96A507BD1A3791490077CDF8 /* ARTRealtime.h in Headers */, 961343D81A42E0B7006DC822 /* ARTClientOptions.h in Headers */, 96BF615E1A35C1C8004CF2B3 /* ARTTypes.h in Headers */, + D746AE221BBB60EE003ECEF8 /* ARTChannels.h in Headers */, D746AE1D1BBB5207003ECEF8 /* ARTDataQuery.h in Headers */, 1C1EC3FA1AE26A8B00AAADD7 /* ARTStatus.h in Headers */, 96E408431A38939E00087F77 /* ARTProtocolMessage.h in Headers */, @@ -532,6 +555,7 @@ 1C6C18A31ADFDAB100AB79E4 /* ARTLog.h in Headers */, 1CA5E1C21AB72369006ADD70 /* ARTMsgPackEncoder.h in Headers */, 96A507A51A377DE90077CDF8 /* ARTNSDictionary+ARTDictionaryUtil.h in Headers */, + D746AE251BBB611C003ECEF8 /* ARTChannels+Private.h in Headers */, 96A507AD1A3780F60077CDF8 /* ARTJsonEncoder.h in Headers */, 96A507951A370F860077CDF8 /* ARTStats.h in Headers */, 960D07931A45F1D800ED8C8C /* ARTCrypto.h in Headers */, @@ -755,10 +779,12 @@ buildActionMask = 2147483647; files = ( 856AAC991B6E312F00B07119 /* RestClient.swift in Sources */, + D746AE2C1BBB625E003ECEF8 /* RestChannel.swift in Sources */, 856AAC971B6E30C800B07119 /* TestUtilities.swift in Sources */, 853ED7C41B7A1A3C006F1C6F /* RestClient.stats.swift in Sources */, 851674EF1B7BA5CD00D35169 /* Stats.swift in Sources */, D72304701BB72CED00F1ABDA /* RealtimeClient.swift in Sources */, + D746AE2D1BBB625E003ECEF8 /* RestClient.channels.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -766,6 +792,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + D746AE231BBB60EE003ECEF8 /* ARTChannels.m in Sources */, 96A507A21A377AA50077CDF8 /* ARTPresenceMessage.m in Sources */, 96BF61541A35B39C004CF2B3 /* ARTRest.m in Sources */, 961343E91A432E7C006DC822 /* ARTPayload.m in Sources */, @@ -789,6 +816,7 @@ 1CD8DCA01B1C7315007EAF36 /* ARTDefault.m in Sources */, 96A5079E1A371F800077CDF8 /* ARTHttpPaginatedResult.m in Sources */, 1C6C18A41ADFDAB100AB79E4 /* ARTLog.m in Sources */, + D746AE291BBB61C9003ECEF8 /* ARTPresence.m in Sources */, 96A507BE1A3791490077CDF8 /* ARTRealtime.m in Sources */, 967A43221A39AEAF00E4CE23 /* ARTNSArray+ARTFunctional.m in Sources */, 96A507A61A377DE90077CDF8 /* ARTNSDictionary+ARTDictionaryUtil.m in Sources */, diff --git a/ablySpec/RestChannel.swift b/ablySpec/RestChannel.swift new file mode 100644 index 000000000..0de4c22af --- /dev/null +++ b/ablySpec/RestChannel.swift @@ -0,0 +1,210 @@ +// +// RestChannel.swift +// ably +// +// Created by Yavor Georgiev on 23.08.15. +// Copyright (c) 2015 г. Ably. All rights reserved. +// + +import Nimble +import Quick +import ably +import ably.Private +import Foundation +import SwiftyJSON + +extension ARTMessage { + public override func isEqual(object: AnyObject?) -> Bool { + if let other = object as? ARTMessage { + return self.name == other.name && self.payload == other.payload + } + + return super.isEqual(object) + } +} + +extension ARTPayload { + public override func isEqual(object: AnyObject?) -> Bool { + if let other = object as? ARTPayload { + if let selfPayload = self.payload as? NSObject { + if let otherPayload = other.payload as? NSObject { + return selfPayload == otherPayload && self.encoding == other.encoding + } + } + } + + return super.isEqual(object) + } +} + +class RestChannel: QuickSpec { + override func spec() { + var client: ARTRest! + var channel: ARTRestChannel! + + beforeEach { + client = ARTRest(options: AblyTests.setupOptions(AblyTests.jsonRestOptions)) + channel = client.channels.get(NSProcessInfo.processInfo().globallyUniqueString) + } + + // RSL1 + describe("publish") { + let name = "foo" + let data = "bar" + + // RSL1b + context("with name and data arguments") { + it("publishes the message and invokes callback with success") { + var publishStatus: ARTStatus? + var publishedMessage: ARTMessage? + + channel.publish(data, name: name) { status in + publishStatus = status + channel.history(nil) { _, result in + publishedMessage = result?.items.first as? ARTMessage + } + } + + expect(publishStatus?.state).toEventually(equal(ARTState.Ok), timeout: testTimeout) + expect(publishedMessage?.name).toEventually(equal(name), timeout: testTimeout) + expect(publishedMessage?.payload.payload as? String).toEventually(equal(data), timeout: testTimeout) + } + } + + // RSL1b, RSL1e + context("with name only") { + it("publishes the message and invokes callback with success") { + var publishStatus: ARTStatus? + var publishedMessage: ARTMessage? + + channel.publish(nil, name: name) { status in + publishStatus = status + channel.history(nil) { _, result in + publishedMessage = result?.items.first as? ARTMessage + } + } + + expect(publishStatus?.state).toEventually(equal(ARTState.Ok), timeout: testTimeout) + expect(publishedMessage?.name).toEventually(equal(name), timeout: testTimeout) + expect(publishedMessage?.payload.payload).toEventually(beNil(), timeout: testTimeout) + } + } + + // RSL1b, RSL1e + context("with data only") { + it("publishes the message and invokes callback with success") { + var publishStatus: ARTStatus? + var publishedMessage: ARTMessage? + + channel.publish(data) { status in + publishStatus = status + channel.history(nil) { _, result in + publishedMessage = result?.items.first as? ARTMessage + } + } + + expect(publishStatus?.state).toEventually(equal(ARTState.Ok), timeout: testTimeout) + expect(publishedMessage?.name).toEventually(beNil(), timeout: testTimeout) + expect(publishedMessage?.payload.payload as? String).toEventually(equal(data), timeout: testTimeout) + } + } + + // RSL1b, RSL1e + context("with neither name nor data") { + it("publishes the message and invokes callback with success") { + var publishStatus: ARTStatus? + var publishedMessage: ARTMessage? + + channel.publish(nil) { status in + publishStatus = status + channel.history(nil) { _, result in + publishedMessage = result?.items.first as? ARTMessage + } + } + + expect(publishStatus?.state).toEventually(equal(ARTState.Ok), timeout: testTimeout) + expect(publishedMessage?.name).toEventually(beNil(), timeout: testTimeout) + expect(publishedMessage?.payload.payload).toEventually(beNil(), timeout: testTimeout) + } + } + + context("with a Message object") { + it("publishes the message and invokes callback with success") { + var publishStatus: ARTStatus? + var publishedMessage: ARTMessage? + + channel.publishMessage(ARTMessage(data: data, name: name)) { status in + publishStatus = status + channel.history(nil) { _, result in + publishedMessage = result?.items.first as? ARTMessage + } + } + + expect(publishStatus?.state).toEventually(equal(ARTState.Ok), timeout: testTimeout) + expect(publishedMessage?.name).toEventually(beNil(), timeout: testTimeout) + expect(publishedMessage?.payload.payload).toEventually(beNil(), timeout: testTimeout) + } + } + + // RSL1c + context("with an array of Message objects") { + it("publishes the messages and invokes callback with success") { + var publishStatus: ARTStatus? + var publishedMessages: [ARTMessage] = [] + + let messages = [ + ARTMessage(data: "foo", name: "bar"), + ARTMessage(data: "baz", name: "bat") + ] + channel.publishMessages(messages) { status in + publishStatus = status + channel.history(nil) { _, result in + if let items = result?.items as? [ARTMessage] { + publishedMessages.extend(items) + } + } + } + + expect(publishStatus?.state).toEventually(equal(ARTState.Ok), timeout: testTimeout) + expect(publishedMessages.count).toEventually(equal(messages.count), timeout: testTimeout) + expect(publishedMessages).toEventually(contain(messages.first), timeout: testTimeout) + expect(publishedMessages).toEventually(contain(messages.last), timeout: testTimeout) + } + } + } + + // RSL3, RSP1 + describe("presence") { + let presenceFixtures = appSetupJson["post_apps"]["channels"][0]["presence"] + + // RSP3 + context("get") { + it("should return presence fixture data") { + let channel = client.channels.get("persisted:presence_fixtures") + var presenceMessages: [ARTPresenceMessage] = [] + + channel.presence.get() { status, result in + if let items = result?.items as? [ARTPresenceMessage] { + presenceMessages.extend(items) + } + } + + expect(presenceMessages.count).toEventually(equal(presenceFixtures.count), timeout: testTimeout) + for message in presenceMessages { + let fixtureMessage = filter(presenceFixtures) { (key, value) in + return message.clientId == value["clientId"].stringValue + }.first!.1 + + expect(message.content()).toNot(beNil()) + expect(message.action).to(equal(ARTPresenceAction.Present)) + + // skip the encrypted message for now + if message.payload?.encoding?.rangeOfString("cipher") == nil { + expect(message.content() as? NSObject).to(equal(fixtureMessage["data"].object as? NSObject)) + } + } + } + } + } + } +} diff --git a/ablySpec/RestClient.channels.swift b/ablySpec/RestClient.channels.swift new file mode 100644 index 000000000..4ab936b4f --- /dev/null +++ b/ablySpec/RestClient.channels.swift @@ -0,0 +1,126 @@ +// +// RestClient.channels.swift +// ably +// +// Created by Yavor Georgiev on 21.08.15. +// Copyright (c) 2015 г. Ably. All rights reserved. +// + +import Nimble +import Quick +import ably +import ably.Private +import Foundation + +// Swift isn't yet smart enough to do this automatically when bridging Objective-C APIs +extension ARTChannelCollection: SequenceType { + public func generate() -> NSFastGenerator { + return NSFastGenerator(self) + } +} + +private func beAChannel(named channelName: String) -> MatcherFunc { + return MatcherFunc { actualExpression, failureMessage in + let channel = actualExpression.evaluate() + failureMessage.expected = "expected \(channel)" + failureMessage.postfixMessage = "be a channel" + + return channel?.name == channelName + } +} + +class RestClientChannels: QuickSpec { + override func spec() { + var client: ARTRest! + var channelName: String! + + beforeEach { + client = ARTRest(key: "fake:key") + channelName = NSProcessInfo.processInfo().globallyUniqueString + } + + let cipherParams = ARTCipherParams(algorithm: nil, keySpec: nil, ivSpec: nil) + + describe("RestClient") { + // RSN1 + context("channels") { + // RSN3 + context("get") { + // RSN3a + it("should return a channel") { + let channel = client.channels.get(channelName) + expect(channel).to(beAChannel(named: channelName)) + } + + // RSN3b + it("should return a channel with the provided options") { + let options = ARTChannelOptions(encrypted: cipherParams) + let channel = client.channels.get(channelName, options: options) + + expect(channel).to(beAChannel(named: channelName)) + expect(channel.options).to(beIdenticalTo(options)) + } + + // RSN3b + it("should not replace the options on an existing channel when none are provided") { + let channel = client.channels.get(channelName) + let options = channel.options + + let newButSameChannel = client!.channels.get(channelName) + + expect(newButSameChannel).to(beIdenticalTo(channel)) + expect(newButSameChannel.options).to(beIdenticalTo(channel.options)) + } + + // RSN3c + it("should replace the options on an existing channel when new ones are provided") { + let channel = client.channels.get(channelName) + let oldOptions = channel.options + + let newOptions = ARTChannelOptions(encrypted: cipherParams) + let newButSameChannel = client!.channels.get(channelName, options: newOptions) + + expect(newButSameChannel).to(beIdenticalTo(channel)) + expect(newButSameChannel.options).to(beIdenticalTo(newOptions)) + expect(newButSameChannel.options).notTo(beIdenticalTo(oldOptions)) + } + } + + // RSN2 + context("channelExists") { + it("should check if a channel exists") { + expect(client.channels.exists(channelName)).to(beFalse()) + + let channel = client.channels.get(channelName) + + expect(client.channels.exists(channelName)).to(beTrue()) + } + } + + // RSN4 + context("releseChannel") { + it("should release a channel") { + weak var channel = client.channels.get(channelName) + + expect(channel).to(beAChannel(named: channelName)) + client.channels.releaseChannel(channel!) + + expect(channel).to(beNil()) + } + } + + // RSN2 + it("should be enumerable") { + let channels = [ + client.channels.get(channelName), + client.channels.get(String(reverse(channelName))) + ] + + for channel in client.channels { + expect(channels).to(contain(channel)) + } + } + } + } + } +} diff --git a/ablySpec/RestClient.stats.swift b/ablySpec/RestClient.stats.swift index 7993137cd..f1a21e8c3 100644 --- a/ablySpec/RestClient.stats.swift +++ b/ablySpec/RestClient.stats.swift @@ -300,10 +300,10 @@ class RestClientStats: QuickSpec { let client = ARTRest(key: "fake:key") let query = ARTStatsQuery() - query.start = NSDate.distantFuture() as! NSDate - query.end = NSDate.distantPast() as! NSDate + query.start = NSDate.distantFuture() as? NSDate + query.end = NSDate.distantPast() as? NSDate - expect{ client.stats(query, callback: nil) }.to(raiseException()) + expect{ client.stats(query, callback:{ status, result in }) }.to(raiseException()) } } @@ -330,7 +330,7 @@ class RestClientStats: QuickSpec { query.limit = 1001; - expect{ client.stats(query, callback: nil) }.to(raiseException()) + expect{ client.stats(query, callback: { status, result in }) }.to(raiseException()) } } diff --git a/ablySpec/RestClient.swift b/ablySpec/RestClient.swift index 05e08f48b..46d0f1d73 100644 --- a/ablySpec/RestClient.swift +++ b/ablySpec/RestClient.swift @@ -35,7 +35,7 @@ func getTestToken() -> String { let client = ARTRest(options: options) var token: String? - client.auth().requestToken() { tokenDetails in + client.auth.requestToken() { tokenDetails in token = tokenDetails.token return nil } @@ -92,7 +92,7 @@ class RestClient: QuickSpec { let publishTask = publishTestMessage(client) - expect(client.auth().getAuthMethod()).to(equal(ARTAuthMethod.Token)) + expect(client.auth.getAuthMethod()).to(equal(ARTAuthMethod.Token)) expect(publishTask.status?.state).toEventually(equal(ARTState.Ok), timeout: testTimeout) } @@ -104,7 +104,7 @@ class RestClient: QuickSpec { let publishTask = publishTestMessage(client, failOnError: false) - expect(client.auth().getAuthMethod()).to(equal(ARTAuthMethod.Token)) + expect(client.auth.getAuthMethod()).to(equal(ARTAuthMethod.Token)) expect(publishTask.status?.state).toEventually(equal(ARTState.Error), timeout: testTimeout) expect(publishTask.status?.errorInfo.code).toEventually(equal(40005)) } @@ -181,7 +181,7 @@ class RestClient: QuickSpec { let options = AblyTests.commonAppSetup() let client = ARTRest(options: options) - let authOptions = client.auth().getAuthOptions() + let authOptions = client.auth.getAuthOptions() expect(authOptions).to(beIdenticalTo(options.authOptions)) } From fcd95b3b3451af1456d44102073205954942cec0 Mon Sep 17 00:00:00 2001 From: Ricardo Pereira Date: Wed, 30 Sep 2015 12:27:43 +0100 Subject: [PATCH 10/22] Refactored HTTP and Errors (Yavor Georgiev) Move to a new (testable) composable HTTP pipeline Use NSError instead of ARTErrorInfo --- ably-ios/ARTChannels+Private.h | 4 +- ably-ios/ARTChannels.h | 14 +- ably-ios/ARTChannels.m | 12 +- ably-ios/ARTHttp.h | 11 +- ably-ios/ARTHttp.m | 11 ++ ably-ios/ARTHttpPaginatedResult.h | 32 ----- ably-ios/ARTHttpPaginatedResult.m | 94 ------------- ably-ios/ARTJsonEncoder.m | 1 + ably-ios/ARTPaginatedResult+Private.h | 20 +++ ably-ios/ARTPaginatedResult.h | 16 +-- ably-ios/ARTPaginatedResult.m | 109 +++++++++++++++- ably-ios/ARTPresence.h | 4 +- ably-ios/ARTPresenceMessage.h | 3 +- ably-ios/ARTRest.h | 7 +- ably-ios/ARTRest.m | 181 +++++++++++++++----------- ably-ios/ARTStatus.h | 8 +- ably-ios/ARTStatus.m | 13 +- ably-ios/ably.modulemap | 1 + ably.xcodeproj/project.pbxproj | 12 +- ablySpec/RestChannel.swift | 120 ++++++++--------- ablySpec/RestClient.stats.swift | 146 +++++++++++---------- ablySpec/RestClient.swift | 30 +++-- 22 files changed, 446 insertions(+), 403 deletions(-) delete mode 100644 ably-ios/ARTHttpPaginatedResult.h delete mode 100644 ably-ios/ARTHttpPaginatedResult.m create mode 100644 ably-ios/ARTPaginatedResult+Private.h diff --git a/ably-ios/ARTChannels+Private.h b/ably-ios/ARTChannels+Private.h index c828bb83d..90a3a99b7 100644 --- a/ably-ios/ARTChannels+Private.h +++ b/ably-ios/ARTChannels+Private.h @@ -20,7 +20,7 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithName:(NSString *)name presence:(ARTPresence *)presence options:(nullable ARTChannelOptions *)options; -- (void)_postMessages:(id)payload callback:(nullable ARTStatusCallback)callback; +- (void)_postMessages:(id)payload callback:(nullable ARTErrorCallback)callback; @end @@ -33,4 +33,4 @@ NS_ASSUME_NONNULL_BEGIN @end -NS_ASSUME_NONNULL_END \ No newline at end of file +NS_ASSUME_NONNULL_END diff --git a/ably-ios/ARTChannels.h b/ably-ios/ARTChannels.h index 98243b8a4..748a92de0 100644 --- a/ably-ios/ARTChannels.h +++ b/ably-ios/ARTChannels.h @@ -19,7 +19,6 @@ NS_ASSUME_NONNULL_BEGIN @interface ARTChannelOptions : NSObject @property (nonatomic, assign) BOOL isEncrypted; - @property (nonatomic, strong, nullable) ARTCipherParams *cipherParams; + (instancetype)unencrypted; @@ -33,18 +32,17 @@ NS_ASSUME_NONNULL_BEGIN @interface ARTChannel : NSObject @property (nonatomic, strong, readonly) NSString *name; - @property (nonatomic, strong, readonly) ARTPresence *presence; -- (void)publish:(nullable id)payload callback:(nullable ARTStatusCallback)callback; +- (void)publish:(nullable id)payload callback:(nullable ARTErrorCallback)callback; -- (void)publish:(nullable id)payload name:(nullable NSString *)name callback:(nullable ARTStatusCallback)callback; +- (void)publish:(nullable id)payload name:(nullable NSString *)name callback:(nullable ARTErrorCallback)callback; -- (void)publishMessage:(ARTMessage *)message callback:(nullable ARTStatusCallback)callback; +- (void)publishMessage:(ARTMessage *)message callback:(nullable ARTErrorCallback)callback; -- (void)publishMessages:(NSArray /* */ *)messages callback:(nullable ARTStatusCallback)callback; +- (void)publishMessages:(NSArray /* */ *)messages callback:(nullable ARTErrorCallback)callback; -- (void)history:(nullable ARTDataQuery *)query callback:(void(^)(ARTStatus *status, ARTPaginatedResult /* */ *__nullable result))callback; +- (void)history:(nullable ARTDataQuery *)query callback:(void(^)(ARTPaginatedResult /* */ *__nullable result, NSError *__nullable error))callback; @end @@ -60,4 +58,4 @@ NS_ASSUME_NONNULL_BEGIN @end -NS_ASSUME_NONNULL_END \ No newline at end of file +NS_ASSUME_NONNULL_END diff --git a/ably-ios/ARTChannels.m b/ably-ios/ARTChannels.m index d2195a9ec..bb5695e5d 100644 --- a/ably-ios/ARTChannels.m +++ b/ably-ios/ARTChannels.m @@ -57,15 +57,15 @@ - (void)setOptions:(ARTChannelOptions *)options { _payloadEncoder = [ARTJsonPayloadEncoder instance]; } -- (void)publish:(nullable id)payload callback:(ARTStatusCallback)callback { +- (void)publish:(nullable id)payload callback:(ARTErrorCallback)callback { [self publish:payload name:nil callback:callback]; } -- (void)publish:(nullable id)payload name:(NSString *)name callback:(ARTStatusCallback)callback { +- (void)publish:(nullable id)payload name:(NSString *)name callback:(ARTErrorCallback)callback { [self publishMessage:[[ARTMessage alloc] initWithData:payload name:name] callback:callback]; } -- (void)publishMessages:(NSArray *)messages callback:(ARTStatusCallback)callback { +- (void)publishMessages:(NSArray *)messages callback:(ARTErrorCallback)callback { messages = [messages artMap:^(ARTMessage *message) { return [message encode:_payloadEncoder]; }]; @@ -73,15 +73,15 @@ - (void)publishMessages:(NSArray *)messages callback:(ARTStatusCallback)callback [self _postMessages:messages callback:callback]; } -- (void)publishMessage:(ARTMessage *)message callback:(ARTStatusCallback)callback { +- (void)publishMessage:(ARTMessage *)message callback:(ARTErrorCallback)callback { [self _postMessages:[message encode:_payloadEncoder] callback:callback]; } -- (void)history:(ARTDataQuery *)query callback:(void (^)(ARTStatus *, ARTPaginatedResult * __nullable))callback { +- (void)history:(ARTDataQuery *)query callback:(void (^)(ARTPaginatedResult * __nullable, NSError *__nullable))callback { NSAssert(false, @"-[%@ %@] should always be overriden.", self.class, NSStringFromSelector(_cmd)); } -- (void)_postMessages:(id)payload callback:(ARTStatusCallback)callback { +- (void)_postMessages:(id)payload callback:(ARTErrorCallback)callback { NSAssert(false, @"-[%@ %@] should always be overriden.", self.class, NSStringFromSelector(_cmd)); } diff --git a/ably-ios/ARTHttp.h b/ably-ios/ARTHttp.h index 1d4034225..97eff1c03 100644 --- a/ably-ios/ARTHttp.h +++ b/ably-ios/ARTHttp.h @@ -9,8 +9,15 @@ #import #import #import +#import -@class ARTLog; +@protocol ARTHTTPExecutor + +@property (nonatomic, weak) ARTLog *logger; + +- (void)executeRequest:(NSMutableURLRequest *)request callback:(void (^)(NSHTTPURLResponse *response, NSData *data, NSError *error))callback; + +@end @interface ARTHttpRequest : NSObject @@ -43,7 +50,7 @@ @end -@interface ARTHttp : NSObject +@interface ARTHttp : NSObject { } diff --git a/ably-ios/ARTHttp.m b/ably-ios/ARTHttp.m index b91469b47..b6486a256 100644 --- a/ably-ios/ARTHttp.m +++ b/ably-ios/ARTHttp.m @@ -159,6 +159,17 @@ - (instancetype)initWithBaseUrl:(NSURL *)baseUrl { return self; } +- (void)executeRequest:(NSMutableURLRequest *)request callback:(void (^)(NSHTTPURLResponse *, NSData *, NSError *))callback { + CFRunLoopRef currentRunloop = CFRunLoopGetCurrent(); + NSURLSessionDataTask *task = [_urlSession dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { + CFRunLoopPerformBlock(currentRunloop, kCFRunLoopCommonModes, ^{ + callback((NSHTTPURLResponse *)response, data, error); + }); + CFRunLoopWakeUp(currentRunloop); + }]; + [task resume]; +} + - (id)makeRequestWithMethod:(NSString *)method url:(NSURL *)url headers:(NSDictionary *)headers body:(NSData *)body cb:(ARTHttpCb)cb { return [self makeRequest:[[ARTHttpRequest alloc] initWithMethod:method url:url headers:headers body:body] cb:cb]; } diff --git a/ably-ios/ARTHttpPaginatedResult.h b/ably-ios/ARTHttpPaginatedResult.h deleted file mode 100644 index b24e37791..000000000 --- a/ably-ios/ARTHttpPaginatedResult.h +++ /dev/null @@ -1,32 +0,0 @@ -// -// ARTHttpPaginatedResult.h -// ably-ios -// -// Created by Jason Choy on 09/12/2014. -// Copyright (c) 2014 Ably. All rights reserved. -// - -#import -#import -#import - -@interface ARTHttpPaginatedResult : ARTPaginatedResult - -typedef NSArray *(^ARTHttpResponseProcessor)(ARTHttpResponse *); - -- (instancetype)init UNAVAILABLE_ATTRIBUTE; - -- (instancetype)initWithHttp:(ARTHttp *)http - items:(NSArray *)items - contentType:(NSString *)contentType - relFirst:(ARTHttpRequest *)relFirst - relCurrent:(ARTHttpRequest *)relCurrent - relNext:(ARTHttpRequest *)relNext - responseProcessor:(ARTHttpResponseProcessor)responseProcessor; - -+ (id)makePaginatedRequest:(ARTHttp *)http - request:(ARTHttpRequest *)request - responseProcessor:(ARTHttpResponseProcessor)responseProcessor - callback:(ARTPaginatedResultCallback)callback; - -@end diff --git a/ably-ios/ARTHttpPaginatedResult.m b/ably-ios/ARTHttpPaginatedResult.m deleted file mode 100644 index 35bdac0bb..000000000 --- a/ably-ios/ARTHttpPaginatedResult.m +++ /dev/null @@ -1,94 +0,0 @@ -// -// ARTHttpPaginatedResult.m -// ably-ios -// -// Created by Jason Choy on 09/12/2014. -// Copyright (c) 2014 Ably. All rights reserved. -// - -#import "ARTHttpPaginatedResult.h" -#import "ARTLog.h" -#import "ARTStatus.h" - -@implementation ARTHttpPaginatedResult { - ARTHttp *_http; - NSString *_contentType; - ARTHttpRequest *_relFirst; - ARTHttpRequest *_relCurrent; - ARTHttpRequest *_relNext; - ARTHttpResponseProcessor _responseProcessor; -} - -- (instancetype)initWithHttp:(ARTHttp *)http items:(NSArray *)items contentType:(NSString *)contentType relFirst:(ARTHttpRequest *)relFirst relCurrent:(ARTHttpRequest *)relCurrent relNext:(ARTHttpRequest *)relNext responseProcessor:(ARTHttpResponseProcessor)responseProcessor { - self = [super init]; - if (self) { - _http = http; - - _items = [items copy]; - _contentType = [contentType copy]; - - _hasFirst = !!relFirst; - _relFirst = relFirst; - - _hasCurrent = !!relCurrent; - _relCurrent = relCurrent; - - _hasNext = !!relNext; - _relNext = relNext; - - _responseProcessor = responseProcessor; - } - return self; -} - -- (void)first:(ARTPaginatedResultCallback)callback { - [ARTHttpPaginatedResult makePaginatedRequest:_http request:_relFirst responseProcessor:_responseProcessor callback:callback]; -} - -- (void)current:(ARTPaginatedResultCallback)callback { - [ARTHttpPaginatedResult makePaginatedRequest:_http request:_relCurrent responseProcessor:_responseProcessor callback:callback]; -} - -- (void)next:(ARTPaginatedResultCallback)callback { - [ARTHttpPaginatedResult makePaginatedRequest:_http request:_relNext responseProcessor:_responseProcessor callback:callback]; -} - - -+ (id)makePaginatedRequest:(ARTHttp *)http request:(ARTHttpRequest *)request responseProcessor:(ARTHttpResponseProcessor)responseProcessor callback:(ARTPaginatedResultCallback)callback { - return [http makeRequest:request cb:^(ARTHttpResponse *response) { - if (!response) { - ARTErrorInfo * info = [[ARTErrorInfo alloc] init]; - [info setCode:40000 message:@"ARTHttpPaginatedResult got no response"]; - [ARTStatus state:ARTStateError info:info]; - callback([ARTStatus state:ARTStateError info:info], nil); - return; - } - - if (response.status < 200 || response.status >= 300) { - ARTErrorInfo * info = [[ARTErrorInfo alloc] init]; - [info setCode:40000 message:[NSString stringWithFormat:@"ARTHttpPaginatedResult response.status invalid: %d", response.status]]; - callback([ARTStatus state:ARTStateError info:info], nil); - - return; - } - - NSArray *items = responseProcessor(response); - - NSString *contentType = response.contentType; - NSDictionary *links = response.links; - - ARTHttpRequest *firstRelRequest = [request requestWithRelativeUrl:[links objectForKey:@"first"]]; - ARTHttpRequest *currentRelRequest = [request requestWithRelativeUrl:[links objectForKey:@"current"]]; - ARTHttpRequest *nextRelRequest = [request requestWithRelativeUrl:[links objectForKey:@"next"]]; - - ARTPaginatedResult *result = [[ARTHttpPaginatedResult alloc] initWithHttp:http - items:items - contentType:contentType - relFirst:firstRelRequest - relCurrent:currentRelRequest - relNext:nextRelRequest responseProcessor:responseProcessor]; - callback([ARTStatus state:ARTStateOk], result); - }]; -} - -@end diff --git a/ably-ios/ARTJsonEncoder.m b/ably-ios/ARTJsonEncoder.m index f0963919b..299dcb842 100644 --- a/ably-ios/ARTJsonEncoder.m +++ b/ably-ios/ARTJsonEncoder.m @@ -9,6 +9,7 @@ #import "ARTJsonEncoder.h" #import "ARTMessage.h" #import "ARTPresence.h" +#import "ARTPresenceMessage.h" #import "ARTProtocolMessage.h" #import "ARTStats.h" #import "ARTNSDictionary+ARTDictionaryUtil.h" diff --git a/ably-ios/ARTPaginatedResult+Private.h b/ably-ios/ARTPaginatedResult+Private.h new file mode 100644 index 000000000..70b6fe28f --- /dev/null +++ b/ably-ios/ARTPaginatedResult+Private.h @@ -0,0 +1,20 @@ +// +// ARTPaginatedResult+Private.h +// ably +// +// Created by Yavor Georgiev on 28.08.15. +// Copyright (c) 2015 г. Ably. All rights reserved. +// + +#import +#import + +@interface ARTPaginatedResult () + +typedef NSArray *(^ARTPaginatedResultResponseProcessor)(NSHTTPURLResponse *, NSData *); + ++ (void)executePaginatedRequest:(NSMutableURLRequest *)request executor:(id)executor + responseProcessor:(ARTPaginatedResultResponseProcessor)responseProcessor + callback:(ARTPaginatedResultCallback)callback; + +@end \ No newline at end of file diff --git a/ably-ios/ARTPaginatedResult.h b/ably-ios/ARTPaginatedResult.h index eec388b35..3809f61ac 100644 --- a/ably-ios/ARTPaginatedResult.h +++ b/ably-ios/ARTPaginatedResult.h @@ -9,13 +9,11 @@ #import #import -@interface ARTPaginatedResult : NSObject { - @protected - NSArray *_items; - BOOL _hasFirst; - BOOL _hasCurrent; - BOOL _hasNext; -} +NS_ASSUME_NONNULL_BEGIN + +@interface ARTPaginatedResult : NSObject + +typedef void(^ARTPaginatedResultCallback)(ARTPaginatedResult *__nullable result, NSError *__nullable error); @property (nonatomic, strong, readonly) NSArray *items; @@ -25,10 +23,12 @@ @property (nonatomic, readonly) BOOL isLast; -typedef void(^ARTPaginatedResultCallback)(ARTStatus *status, ARTPaginatedResult *result); +- (instancetype)init UNAVAILABLE_ATTRIBUTE; - (void)first:(ARTPaginatedResultCallback)callback; - (void)current:(ARTPaginatedResultCallback)callback; - (void)next:(ARTPaginatedResultCallback)callback; @end + +NS_ASSUME_NONNULL_END diff --git a/ably-ios/ARTPaginatedResult.m b/ably-ios/ARTPaginatedResult.m index ac0d7f0fc..617eb15b4 100644 --- a/ably-ios/ARTPaginatedResult.m +++ b/ably-ios/ARTPaginatedResult.m @@ -6,24 +6,119 @@ // Copyright (c) 2015 г. Ably. All rights reserved. // -#import "ARTPaginatedResult.h" +#import "ARTPaginatedResult+Private.h" -@implementation ARTPaginatedResult +@implementation ARTPaginatedResult { + id _executor; + NSMutableURLRequest *_relFirst; + NSMutableURLRequest *_relCurrent; + NSMutableURLRequest *_relNext; + ARTPaginatedResultResponseProcessor _responseProcessor; +} + +- (instancetype)initWithItems:(NSArray *)items + executor:(id)executor + relFirst:(NSMutableURLRequest *)relFirst + relCurrent:(NSMutableURLRequest *)relCurrent + relNext:(NSMutableURLRequest *)relNext + responseProcessor:(ARTPaginatedResultResponseProcessor)responseProcessor { + if (self = [super init]) { + _items = items; + + _relFirst = relFirst; + _hasFirst = !!relFirst; + + _relCurrent = relCurrent; + _hasCurrent = !!relCurrent; + + _relNext = relNext; + _hasNext = !!relNext; + _isLast = !_hasNext; + + _executor = executor; + _responseProcessor = responseProcessor; + } + + return self; +} - (void)first:(ARTPaginatedResultCallback)callback { - NSAssert(false, @"-[ARTPaginatedResult first] should always be overriden."); + [self.class executePaginatedRequest:_relFirst executor:_executor responseProcessor:_responseProcessor callback:callback]; } - (void)current:(ARTPaginatedResultCallback)callback { - NSAssert(false, @"-[ARTPaginatedResult current] should always be overriden."); + [self.class executePaginatedRequest:_relCurrent executor:_executor responseProcessor:_responseProcessor callback:callback]; } - (void)next:(ARTPaginatedResultCallback)callback { - NSAssert(false, @"-[ARTPaginatedResult next] should always be overriden."); + [self.class executePaginatedRequest:_relNext executor:_executor responseProcessor:_responseProcessor callback:callback]; +} + +static NSDictionary *extractLinks(NSHTTPURLResponse *response) { + NSString *linkHeader = response.allHeaderFields[@"Link"]; + if (!linkHeader) { + return nil; + } + + static NSRegularExpression *linkRegex; + static dispatch_once_t onceToken; + + dispatch_once(&onceToken, ^{ + linkRegex = [NSRegularExpression regularExpressionWithPattern:@"\\s*<([^>]*)>;\\s*rel=\"([^\"]*)\"" options:0 error:nil]; + }); + + NSMutableDictionary *links = [NSMutableDictionary dictionary]; + + NSArray *matches = [linkRegex matchesInString:linkHeader options:0 range:NSMakeRange(0, linkHeader.length)]; + for (NSTextCheckingResult *match in matches) { + NSRange linkUrlRange = [match rangeAtIndex:1]; + NSRange linkRelRange = [match rangeAtIndex:2]; + + NSString *linkUrl = [linkHeader substringWithRange:linkUrlRange]; + NSString *linkRels = [linkHeader substringWithRange:linkRelRange]; + + for (NSString *linkRel in [linkRels componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]) { + [links setObject:linkUrl forKey:linkRel]; + } + } + + return links; +} + +static NSMutableURLRequest *requestRelativeTo(NSMutableURLRequest *request, NSString *path) { + if (!path) { + return nil; + } + + NSURL *url = [NSURL URLWithString:path relativeToURL:request.URL]; + return [NSMutableURLRequest requestWithURL:url]; } -- (BOOL)isLast { - return !self.hasNext; ++ (void)executePaginatedRequest:(NSMutableURLRequest *)request executor:(id)executor + responseProcessor:(ARTPaginatedResultResponseProcessor)responseProcessor + callback:(ARTPaginatedResultCallback)callback { + [executor executeRequest:request callback:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { + if (error) { + callback(nil, error); + } else { + NSArray *items = responseProcessor(response, data); + + NSDictionary *links = extractLinks(response); + + NSMutableURLRequest *firstRel = requestRelativeTo(request, links[@"first"]); + NSMutableURLRequest *currentRel = requestRelativeTo(request, links[@"current"]);; + NSMutableURLRequest *nextRel = requestRelativeTo(request, links[@"next"]);; + + ARTPaginatedResult *result = [[ARTPaginatedResult alloc] initWithItems:items + executor:executor + relFirst:firstRel + relCurrent:currentRel + relNext:nextRel + responseProcessor:responseProcessor]; + + callback(result, nil); + } + }]; } @end diff --git a/ably-ios/ARTPresence.h b/ably-ios/ARTPresence.h index 7a95450c4..a7735088e 100644 --- a/ably-ios/ARTPresence.h +++ b/ably-ios/ARTPresence.h @@ -30,9 +30,9 @@ typedef NS_ENUM(NSUInteger, ARTPresenceAction) { @interface ARTPresence : NSObject -- (void)get:(void (^)(ARTStatus *status, ARTPaginatedResult /* */ *__nullable result))callback; +- (void)get:(void (^)(ARTPaginatedResult /* */ *__nullable result, NSError *__nullable error))callback; -- (void)history:(nullable ARTDataQuery *)query callback:(void (^)(ARTStatus *status, ARTPaginatedResult /* */ *__nullable result))callback; +- (void)history:(nullable ARTDataQuery *)query callback:(void (^)(ARTPaginatedResult /* */ *__nullable result, NSError *__nullable error))callback; @end diff --git a/ably-ios/ARTPresenceMessage.h b/ably-ios/ARTPresenceMessage.h index f306b8511..bf516dc47 100644 --- a/ably-ios/ARTPresenceMessage.h +++ b/ably-ios/ARTPresenceMessage.h @@ -10,6 +10,7 @@ #import @class ARTStatus; + typedef NS_ENUM(NSUInteger, ARTPresenceMessageAction) { ARTPresenceMessageAbsent, ARTPresenceMessagePresent, @@ -35,6 +36,6 @@ typedef NS_ENUM(NSUInteger, ARTPresenceMessageAction) { - (ARTPresenceMessage *)decode:(id)encoder; - (ARTPresenceMessage *)encode:(id)encoder; -- (id) content; +- (id)content; @end diff --git a/ably-ios/ARTRest.h b/ably-ios/ARTRest.h index f63f92256..d077b3496 100644 --- a/ably-ios/ARTRest.h +++ b/ably-ios/ARTRest.h @@ -31,8 +31,8 @@ NS_ASSUME_NONNULL_BEGIN @interface ARTRestChannelCollection : ARTChannelCollection -- (ARTRestChannel *)get:(NSString *)channelName; -- (ARTRestChannel *)get:(NSString *)channelName options:(ARTChannelOptions *)options; +//- (ARTRestChannel *)get:(NSString *)channelName; +//- (ARTRestChannel *)get:(NSString *)channelName options:(ARTChannelOptions *)options; @end @@ -50,11 +50,12 @@ NS_ASSUME_NONNULL_BEGIN - (id)time:(void(^)(ARTStatus * status, NSDate *time))cb; -- (void)stats:(nullable ARTStatsQuery *)query callback:(void (^)(ARTStatus *status, ARTPaginatedResult /* */ *__nullable result))callback; +- (void)stats:(nullable ARTStatsQuery *)query callback:(void (^)(ARTPaginatedResult /* */ *__nullable result, NSError *__nullable error))callback; - (id)internetIsUp:(void (^)(bool isUp)) cb; @property (nonatomic, strong, readonly) ARTLog *logger; +@property (nonatomic, strong, null_resettable) id httpExecutor; @property (nonatomic, strong, readonly) ARTRestChannelCollection *channels; @property (nonatomic, strong, readonly) ARTAuth *auth; @property (nonatomic, strong, readonly) ARTClientOptions *options; diff --git a/ably-ios/ARTRest.m b/ably-ios/ARTRest.m index 4546b6cc5..f0089e193 100644 --- a/ably-ios/ARTRest.m +++ b/ably-ios/ARTRest.m @@ -17,7 +17,8 @@ #import "ARTJsonEncoder.h" #import "ARTMsgPackEncoder.h" #import "ARTMessage.h" -#import "ARTHttpPaginatedResult.h" +#import "ARTPresenceMessage.h" +#import "ARTPaginatedResult+Private.h" #import "ARTStats.h" #import "ARTNSDictionary+ARTDictionaryUtil.h" @@ -28,7 +29,7 @@ #import "ARTDefault.h" #import "ARTFallback.h" -@interface ARTRest () +@interface ARTRest () @property (readonly, strong, nonatomic) ARTHttp *http; @property (strong, nonatomic) ARTAuth *auth; @@ -40,6 +41,7 @@ @interface ARTRest () - (id)makeRequestWithMethod:(NSString *)method relUrl:(NSString *)relUrl headers:(NSDictionary *)headers body:(NSData *)body authenticated:(ARTAuthentication)authenticated cb:(ARTHttpCb)cb; - (NSDictionary *)withAcceptHeader:(NSDictionary *)headers; +- (void)throwOnHighLimitCheck:(NSDictionary *)params; @end @@ -68,24 +70,25 @@ - (instancetype)initWithRest:(ARTRest *)rest name:(NSString *)name options:(ARTC return self; } -- (void)history:(ARTDataQuery *)query callback:(void (^)(ARTStatus *status, ARTPaginatedResult *__nullable result))callback { +- (void)history:(ARTDataQuery *)query callback:(void (^)(ARTPaginatedResult *__nullable, NSError *__nullable))callback { NSParameterAssert(query.limit < 1000); NSParameterAssert([query.start compare:query.end] != NSOrderedDescending); - [_rest withAuthHeaders:^(NSDictionary *authHeaders) { - NSURLComponents *requestUrl = [NSURLComponents componentsWithString:[_basePath stringByAppendingPathComponent:@"messages"]]; - requestUrl.queryItems = [query asQueryItems]; - ARTHttpRequest *req = [[ARTHttpRequest alloc] initWithMethod:@"GET" url:[requestUrl URLRelativeToURL:_rest.baseUrl] headers:authHeaders body:nil]; - return [ARTHttpPaginatedResult makePaginatedRequest:_rest.http request:req responseProcessor:^(ARTHttpResponse *response) { - id encoder = [_rest.encoders objectForKey:response.contentType]; - return [[encoder decodeMessages:response.body] artMap:^(ARTMessage *message) { - return [message decode:_payloadEncoder]; - }]; - } callback:callback]; - }]; + NSURLComponents *requestUrl = [NSURLComponents componentsWithString:[_basePath stringByAppendingPathComponent:@"messages"]]; + requestUrl.queryItems = [query asQueryItems]; + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:requestUrl.URL]; + + ARTPaginatedResultResponseProcessor responseProcessor = ^(NSHTTPURLResponse *response, NSData *data) { + id encoder = [_rest.encoders objectForKey:response.MIMEType]; + return [[encoder decodeMessages:data] artMap:^(ARTMessage *message) { + return [message decode:_payloadEncoder]; + }]; + }; + + [ARTPaginatedResult executePaginatedRequest:request executor:_rest responseProcessor:responseProcessor callback:callback]; } -- (void)_postMessages:(id)payload callback:(ARTStatusCallback)callback { +- (void)_postMessages:(id)payload callback:(ARTErrorCallback)callback { NSData *encodedMessage = nil; if ([payload isKindOfClass:[ARTMessage class]]) { encodedMessage = [_rest.defaultEncoder encodeMessage:payload]; @@ -93,16 +96,18 @@ - (void)_postMessages:(id)payload callback:(ARTStatusCallback)callback { encodedMessage = [_rest.defaultEncoder encodeMessages:payload]; } - [_rest post:[_basePath stringByAppendingPathComponent:@"messages"] - headers:@{ @"Content-Type": _rest.defaultEncoding ?: @"" } - body:encodedMessage - authenticated:ARTAuthenticationOn - cb:^(ARTHttpResponse *response) { - if (callback) { - ARTState state = response.status >= 200 && response.status < 300 ? ARTStateOk : ARTStateError; - callback([ARTStatus state:state info:response.error]); - } - }]; + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[_basePath stringByAppendingPathComponent:@"messages"]]]; + request.HTTPMethod = @"POST"; + request.HTTPBody = encodedMessage; + if (_rest.defaultEncoding) { + [request setValue:_rest.defaultEncoding forHTTPHeaderField:@"Content-Type"]; + } + + [_rest executeRequest:request callback:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { + if (callback) { + callback(error); + } + }]; } @end @@ -126,7 +131,7 @@ - (ARTChannel *)_createChannelWithName:(NSString *)name options:(ARTChannelOptio @end @implementation ARTRestPresence { - ARTRestChannel *_channel; + __weak ARTRestChannel *_channel; } - (instancetype)initWithChannel:(ARTRestChannel *)channel { @@ -137,36 +142,38 @@ - (instancetype)initWithChannel:(ARTRestChannel *)channel { return self; } -- (void)get:(void (^)(ARTStatus *status, ARTPaginatedResult *__nullable result))callback { - [_channel->_rest withAuthHeaders:^(NSDictionary *authHeaders) { - NSURLComponents *requestUrl = [NSURLComponents componentsWithString:[_channel->_basePath stringByAppendingPathComponent:@"presence"]]; - ARTHttpRequest *req = [[ARTHttpRequest alloc] initWithMethod:@"GET" url:[requestUrl URLRelativeToURL:_channel->_rest.baseUrl] headers:authHeaders body:nil]; - return [ARTHttpPaginatedResult makePaginatedRequest:_channel->_rest.http request:req responseProcessor:^(ARTHttpResponse *response) { - id encoder = [_channel->_rest.encoders objectForKey:response.contentType]; - NSArray *messages = [encoder decodePresenceMessages:response.body]; - return [messages artMap:^id(ARTPresenceMessage *pm) { - return [pm decode:_channel->_payloadEncoder]; - }]; - } callback:callback]; - }]; +- (void)get:(void (^)(ARTPaginatedResult *__nullable, NSError *__nullable))callback { + NSURL *requestUrl = [NSURL URLWithString:[_channel->_basePath stringByAppendingPathComponent:@"presence"]]; + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:requestUrl]; + + ARTPaginatedResultResponseProcessor responseProcessor = ^(NSHTTPURLResponse *response, NSData *data) { + id encoder = [_channel->_rest.encoders objectForKey:response.MIMEType]; + NSArray *messages = [encoder decodePresenceMessages:data]; + return [messages artMap:^id(ARTPresenceMessage *pm) { + return [pm decode:_channel->_payloadEncoder]; + }]; + }; + + [ARTPaginatedResult executePaginatedRequest:request executor:_channel->_rest responseProcessor:responseProcessor callback:callback]; } -- (void)history:(nullable ARTDataQuery *)query callback:(void (^)(ARTStatus *status, ARTPaginatedResult *__nullable result))callback { +- (void)history:(nullable ARTDataQuery *)query callback:(void (^)(ARTPaginatedResult *__nullable, NSError *__nullable))callback { NSParameterAssert(query.limit < 1000); NSParameterAssert([query.start compare:query.end] != NSOrderedDescending); - [_channel->_rest withAuthHeaders:^(NSDictionary *authHeaders) { - NSURLComponents *requestUrl = [NSURLComponents componentsWithString:[_channel->_basePath stringByAppendingPathComponent:@"presence/history"]]; - requestUrl.queryItems = [query asQueryItems]; - ARTHttpRequest *req = [[ARTHttpRequest alloc] initWithMethod:@"GET" url:[requestUrl URLRelativeToURL:_channel->_rest.baseUrl] headers:authHeaders body:nil]; - return [ARTHttpPaginatedResult makePaginatedRequest:_channel->_rest.http request:req responseProcessor:^(ARTHttpResponse *response) { - id encoder = [_channel->_rest.encoders objectForKey:response.contentType]; - NSArray *messages = [encoder decodePresenceMessages:response.body]; - return [messages artMap:^id(ARTPresenceMessage *pm) { - return [pm decode:_channel->_payloadEncoder]; - }]; - } callback:callback]; - }]; + NSURLComponents *requestUrl = [NSURLComponents componentsWithString:[_channel->_basePath stringByAppendingPathComponent:@"presence/history"]]; + requestUrl.queryItems = [query asQueryItems]; + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:requestUrl.URL]; + + ARTPaginatedResultResponseProcessor responseProcessor = ^(NSHTTPURLResponse *response, NSData *data) { + id encoder = [_channel->_rest.encoders objectForKey:response.MIMEType]; + NSArray *messages = [encoder decodePresenceMessages:data]; + return [messages artMap:^id(ARTPresenceMessage *pm) { + return [pm decode:_channel->_payloadEncoder]; + }]; + }; + + [ARTPaginatedResult executePaginatedRequest:request executor:_channel->_rest responseProcessor:responseProcessor callback:callback]; } @end @@ -206,6 +213,7 @@ - (instancetype)initWithKey:(NSString *) key { - (void)setup { _http = [[ARTHttp alloc] init]; + _httpExecutor = _http; _channels = [[ARTRestChannelCollection alloc] initWithRest:self]; @@ -218,7 +226,36 @@ - (void)setup { _fallbackCount = 0; } -- (id) token:(ARTAuthTokenParams *) params tokenCb:(void (^)(ARTStatus *status, ARTTokenDetails *)) cb { +- (void)executeRequest:(NSMutableURLRequest *)request callback:(void (^)(NSHTTPURLResponse *, NSData *, NSError *))callback { + request.URL = [NSURL URLWithString:request.URL.relativeString relativeToURL:self.baseUrl]; + + NSString *accept = [[_encoders.allValues valueForKeyPath:@"mimeType"] componentsJoinedByString:@","]; + [request setValue:accept forHTTPHeaderField:@"Accept"]; + + [self withAuthHeaders:^id(NSDictionary *authHeaders) { + for (NSString *header in authHeaders) { + [request setValue:authHeaders[header] forHTTPHeaderField:header]; + } + + [self.httpExecutor executeRequest:request callback:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { + if (response.statusCode >= 400) { + ARTErrorInfo *errorInfo = [self->_encoders[response.MIMEType] decodeError:data]; + if (errorInfo.code == 40140) { + // TODO: request token or error if no token information + } else { + NSDictionary *userInfo = @{ NSLocalizedFailureReasonErrorKey: errorInfo.message }; + NSError *error = [NSError errorWithDomain:@"ARTAblyErrorDomain" code:errorInfo.code userInfo:userInfo]; + callback(nil, nil, error); + } + } else { + callback(response, data, error); + } + }]; + return nil; + }]; +} + +- (id)token:(ARTAuthTokenParams *)params tokenCb:(void (^)(ARTStatus *status, ARTTokenDetails *)) cb { [self.logger debug:@"ARTRest is requesting a fresh token"]; if(![self.auth canRequestToken]) { @@ -284,23 +321,24 @@ -(ARTAuth *) auth { return nil; } -- (void)stats:(ARTStatsQuery *)query callback:(void (^)(ARTStatus *status, ARTPaginatedResult *result))callback { +- (void)stats:(ARTStatsQuery *)query callback:(void (^)(ARTPaginatedResult *__nullable, NSError *__nullable))callback { NSParameterAssert(query.limit < 1000); NSParameterAssert([query.start compare:query.end] != NSOrderedDescending); - [self withAuthHeaders:^(NSDictionary *authHeaders) { - NSURLComponents *requestUrl = [NSURLComponents componentsWithString:@"/stats"]; - requestUrl.queryItems = [query asQueryItems]; - ARTHttpRequest *req = [[ARTHttpRequest alloc] initWithMethod:@"GET" url:[requestUrl URLRelativeToURL:self.baseUrl] headers:authHeaders body:nil]; - return [ARTHttpPaginatedResult makePaginatedRequest:self.http request:req responseProcessor:^(ARTHttpResponse *response) { - id encoder = [self.encoders objectForKey:response.contentType]; - return [encoder decodeStats:response.body]; - } callback:callback]; - }]; + NSURLComponents *requestUrl = [NSURLComponents componentsWithString:@"/stats"]; + requestUrl.queryItems = [query asQueryItems]; + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[requestUrl URLRelativeToURL:self.baseUrl]]; + + ARTPaginatedResultResponseProcessor responseProcessor = ^(NSHTTPURLResponse *response, NSData *data) { + id encoder = [self.encoders objectForKey:response.MIMEType]; + return [encoder decodeStats:data]; + }; + + [ARTPaginatedResult executePaginatedRequest:request executor:self responseProcessor:responseProcessor callback:callback]; } --(bool) isAnErrorStatus:(int) status { - return status >=400; +- (bool)isAnErrorStatus:(int)status { + return status >= 400; } - (id)makeRequestWithMethod:(NSString *)method relUrl:(NSString *)relUrl headers:(NSDictionary *)headers body:(NSData *)body authenticated:(ARTAuthentication)authenticated fb:(ARTFallback *) fb cb:(ARTHttpCb)cb { @@ -403,18 +441,6 @@ - (NSDictionary *)withAcceptHeader:(NSDictionary *)headers { @implementation ARTRest (Private) -- (id) postTestStats:(NSArray *) stats cb:(void(^)(ARTStatus * status)) cb { - NSDictionary *headers = @{@"Content-Type":self.defaultEncoding}; - NSData * statsData = [NSJSONSerialization dataWithJSONObject:stats options:0 error:nil]; - return [self post:@"/stats" headers:headers body:statsData authenticated:ARTAuthenticationOn cb:^(ARTHttpResponse *response) { - cb([ARTStatus state:ARTStateOk info:response.error]); - }]; -} - -- (NSURL *)getBaseURL { - return self.baseUrl; -} - - (id)defaultEncoder { return self.encoders[self.defaultEncoding]; } @@ -465,8 +491,7 @@ - (NSURL *)resolveUrl:(NSString *)relUrl queryParams:(NSDictionary *)queryParams return [self makeRequestWithMethod:@"POST" relUrl:relUrl headers:headers body:body authenticated:authenticated cb:cb]; } -- (id)withAuthHeaders:(id(^) - (NSDictionary *))cb { +- (id)withAuthHeaders:(id(^)(NSDictionary *))cb { return [self withAuthHeadersUseBasic:false cb:cb]; } diff --git a/ably-ios/ARTStatus.h b/ably-ios/ARTStatus.h index 1089e0e4d..36be856ee 100644 --- a/ably-ios/ARTStatus.h +++ b/ably-ios/ARTStatus.h @@ -24,12 +24,19 @@ typedef NS_ENUM(NSUInteger, ARTState) { ARTStateError = 99999 }; +FOUNDATION_EXPORT NSString *const ARTAblyErrorDomain; + +typedef void(^ARTErrorCallback)(NSError *error); + @interface ARTErrorInfo : NSObject + @property (readonly, copy, nonatomic) NSString *message; @property (readonly, assign, nonatomic) int statusCode; @property (readonly, assign, nonatomic) int code; + -(void) setCode:(int) code message:(NSString *) message; -(void) setCode:(int) code status:(int) status message:(NSString *) message; + @end @@ -42,5 +49,4 @@ typedef NS_ENUM(NSUInteger, ARTState) { +(ARTStatus *) state:(ARTState) state; +(ARTStatus *) state:(ARTState) state info:(ARTErrorInfo *) info; - @end diff --git a/ably-ios/ARTStatus.m b/ably-ios/ARTStatus.m index 383e14e4a..a67fc5acb 100644 --- a/ably-ios/ARTStatus.m +++ b/ably-ios/ARTStatus.m @@ -9,15 +9,17 @@ #import #import "ARTStatus.h" +NSString *const ARTAblyErrorDomain = @"ARTAblyErrorDomain"; @implementation ARTErrorInfo --(void) setCode:(int) code message:(NSString *) message { + +- (void)setCode:(int)code message:(NSString *)message { _code = code; _statusCode = code / 100; _message = message; } --(void) setCode:(int) code status:(int) status message:(NSString *) message { +- (void)setCode:(int)code status:(int)status message:(NSString *)message { _code = code; _statusCode = status; _message =message; @@ -27,7 +29,7 @@ -(void) setCode:(int) code status:(int) status message:(NSString *) message { @implementation ARTStatus --(instancetype) init { +- (instancetype)init { self = [super init]; if(self) { _state = ARTStateOk; @@ -36,18 +38,19 @@ -(instancetype) init { return self; } -+(ARTStatus *) state:(ARTState) state { ++ (ARTStatus *)state:(ARTState)state { ARTStatus *s = [[ARTStatus alloc] init]; s.state = state; return s; } -+(ARTStatus *) state:(ARTState) state info:(ARTErrorInfo *) info { ++ (ARTStatus *)state:(ARTState)state info:(ARTErrorInfo *)info { ARTStatus * s = [ARTStatus state:state]; s.errorInfo = info; return s; } + #pragma mark private -(void) setErrorInfo:(ARTErrorInfo *)errorInfo { diff --git a/ably-ios/ably.modulemap b/ably-ios/ably.modulemap index f204c8676..c87ea4f27 100644 --- a/ably-ios/ably.modulemap +++ b/ably-ios/ably.modulemap @@ -13,5 +13,6 @@ framework module ably { header "ARTClientOptions+Private.h" header "ARTEncoder.h" header "ARTJsonEncoder.h" + header "ARTPaginatedResult+Private.h" } } diff --git a/ably.xcodeproj/project.pbxproj b/ably.xcodeproj/project.pbxproj index a2aa2f2d3..aef176908 100644 --- a/ably.xcodeproj/project.pbxproj +++ b/ably.xcodeproj/project.pbxproj @@ -72,8 +72,6 @@ 967A43221A39AEAF00E4CE23 /* ARTNSArray+ARTFunctional.m in Sources */ = {isa = PBXBuildFile; fileRef = 967A43201A39AEAF00E4CE23 /* ARTNSArray+ARTFunctional.m */; }; 96A507951A370F860077CDF8 /* ARTStats.h in Headers */ = {isa = PBXBuildFile; fileRef = 96A507931A370F860077CDF8 /* ARTStats.h */; settings = {ATTRIBUTES = (Public, ); }; }; 96A507961A370F860077CDF8 /* ARTStats.m in Sources */ = {isa = PBXBuildFile; fileRef = 96A507941A370F860077CDF8 /* ARTStats.m */; }; - 96A5079D1A371F800077CDF8 /* ARTHttpPaginatedResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 96A5079B1A371F800077CDF8 /* ARTHttpPaginatedResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 96A5079E1A371F800077CDF8 /* ARTHttpPaginatedResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 96A5079C1A371F800077CDF8 /* ARTHttpPaginatedResult.m */; }; 96A507A11A377AA50077CDF8 /* ARTPresenceMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = 96A5079F1A377AA50077CDF8 /* ARTPresenceMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; 96A507A21A377AA50077CDF8 /* ARTPresenceMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 96A507A01A377AA50077CDF8 /* ARTPresenceMessage.m */; }; 96A507A51A377DE90077CDF8 /* ARTNSDictionary+ARTDictionaryUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 96A507A31A377DE90077CDF8 /* ARTNSDictionary+ARTDictionaryUtil.h */; }; @@ -115,6 +113,7 @@ D746AE291BBB61C9003ECEF8 /* ARTPresence.m in Sources */ = {isa = PBXBuildFile; fileRef = D746AE271BBB61C9003ECEF8 /* ARTPresence.m */; }; D746AE2C1BBB625E003ECEF8 /* RestChannel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D746AE2A1BBB625E003ECEF8 /* RestChannel.swift */; }; D746AE2D1BBB625E003ECEF8 /* RestClient.channels.swift in Sources */ = {isa = PBXBuildFile; fileRef = D746AE2B1BBB625E003ECEF8 /* RestClient.channels.swift */; }; + D746AE2F1BBBE7D7003ECEF8 /* ARTPaginatedResult+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE2E1BBBE7D7003ECEF8 /* ARTPaginatedResult+Private.h */; }; FC78549C1BCD5688539CCBE4 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FDA6E099E4BC04FC80F845C0 /* libPods.a */; }; /* End PBXBuildFile section */ @@ -229,8 +228,6 @@ 967A43201A39AEAF00E4CE23 /* ARTNSArray+ARTFunctional.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "ARTNSArray+ARTFunctional.m"; sourceTree = ""; }; 96A507931A370F860077CDF8 /* ARTStats.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTStats.h; sourceTree = ""; }; 96A507941A370F860077CDF8 /* ARTStats.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTStats.m; sourceTree = ""; }; - 96A5079B1A371F800077CDF8 /* ARTHttpPaginatedResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTHttpPaginatedResult.h; sourceTree = ""; }; - 96A5079C1A371F800077CDF8 /* ARTHttpPaginatedResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTHttpPaginatedResult.m; sourceTree = ""; }; 96A5079F1A377AA50077CDF8 /* ARTPresenceMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTPresenceMessage.h; sourceTree = ""; }; 96A507A01A377AA50077CDF8 /* ARTPresenceMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTPresenceMessage.m; sourceTree = ""; }; 96A507A31A377DE90077CDF8 /* ARTNSDictionary+ARTDictionaryUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ARTNSDictionary+ARTDictionaryUtil.h"; sourceTree = ""; }; @@ -277,6 +274,7 @@ D746AE271BBB61C9003ECEF8 /* ARTPresence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTPresence.m; sourceTree = ""; }; D746AE2A1BBB625E003ECEF8 /* RestChannel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RestChannel.swift; sourceTree = ""; }; D746AE2B1BBB625E003ECEF8 /* RestClient.channels.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RestClient.channels.swift; sourceTree = ""; }; + D746AE2E1BBBE7D7003ECEF8 /* ARTPaginatedResult+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ARTPaginatedResult+Private.h"; sourceTree = ""; }; DF8DF755839D59574B7B795F /* Pods-ablySpec.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ablySpec.release.xcconfig"; path = "Pods/Target Support Files/Pods-ablySpec/Pods-ablySpec.release.xcconfig"; sourceTree = ""; }; FDA6E099E4BC04FC80F845C0 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -421,9 +419,8 @@ 96A507931A370F860077CDF8 /* ARTStats.h */, 96A507941A370F860077CDF8 /* ARTStats.m */, 850BFB4A1B79323C009D0ADD /* ARTPaginatedResult.h */, + D746AE2E1BBBE7D7003ECEF8 /* ARTPaginatedResult+Private.h */, 850BFB4B1B79323C009D0ADD /* ARTPaginatedResult.m */, - 96A5079B1A371F800077CDF8 /* ARTHttpPaginatedResult.h */, - 96A5079C1A371F800077CDF8 /* ARTHttpPaginatedResult.m */, 96A5079F1A377AA50077CDF8 /* ARTPresenceMessage.h */, 96A507A01A377AA50077CDF8 /* ARTPresenceMessage.m */, 96A507A31A377DE90077CDF8 /* ARTNSDictionary+ARTDictionaryUtil.h */, @@ -525,6 +522,7 @@ 96A507BD1A3791490077CDF8 /* ARTRealtime.h in Headers */, 961343D81A42E0B7006DC822 /* ARTClientOptions.h in Headers */, 96BF615E1A35C1C8004CF2B3 /* ARTTypes.h in Headers */, + D746AE2F1BBBE7D7003ECEF8 /* ARTPaginatedResult+Private.h in Headers */, D746AE221BBB60EE003ECEF8 /* ARTChannels.h in Headers */, D746AE1D1BBB5207003ECEF8 /* ARTDataQuery.h in Headers */, 1C1EC3FA1AE26A8B00AAADD7 /* ARTStatus.h in Headers */, @@ -536,7 +534,6 @@ 1C2B0FFD1B136A6D00E3633C /* ARTPresenceMap.h in Headers */, 850BFB4C1B79323C009D0ADD /* ARTPaginatedResult.h in Headers */, 1CD8DC9F1B1C7315007EAF36 /* ARTDefault.h in Headers */, - 96A5079D1A371F800077CDF8 /* ARTHttpPaginatedResult.h in Headers */, 1C8065051AE7C8FA00D49357 /* ARTPayload+Private.h in Headers */, 1CEFC60E1B0F9EF500D84463 /* ARTTokenDetails+Private.h in Headers */, 1C70CB881B020D7F003D295A /* ARTClientOptions+Private.h in Headers */, @@ -814,7 +811,6 @@ 96BF61651A35CDE1004CF2B3 /* ARTMessage.m in Sources */, 1CA5E1C31AB72369006ADD70 /* ARTMsgPackEncoder.m in Sources */, 1CD8DCA01B1C7315007EAF36 /* ARTDefault.m in Sources */, - 96A5079E1A371F800077CDF8 /* ARTHttpPaginatedResult.m in Sources */, 1C6C18A41ADFDAB100AB79E4 /* ARTLog.m in Sources */, D746AE291BBB61C9003ECEF8 /* ARTPresence.m in Sources */, 96A507BE1A3791490077CDF8 /* ARTRealtime.m in Sources */, diff --git a/ablySpec/RestChannel.swift b/ablySpec/RestChannel.swift index 0de4c22af..d28fca0f3 100644 --- a/ablySpec/RestChannel.swift +++ b/ablySpec/RestChannel.swift @@ -18,7 +18,7 @@ extension ARTMessage { if let other = object as? ARTMessage { return self.name == other.name && self.payload == other.payload } - + return super.isEqual(object) } } @@ -32,7 +32,7 @@ extension ARTPayload { } } } - + return super.isEqual(object) } } @@ -41,163 +41,163 @@ class RestChannel: QuickSpec { override func spec() { var client: ARTRest! var channel: ARTRestChannel! - + beforeEach { client = ARTRest(options: AblyTests.setupOptions(AblyTests.jsonRestOptions)) channel = client.channels.get(NSProcessInfo.processInfo().globallyUniqueString) } - + // RSL1 describe("publish") { let name = "foo" let data = "bar" - + // RSL1b context("with name and data arguments") { it("publishes the message and invokes callback with success") { - var publishStatus: ARTStatus? + var publishError: NSError? = NSError(domain: "", code: -1, userInfo: nil) var publishedMessage: ARTMessage? - - channel.publish(data, name: name) { status in - publishStatus = status - channel.history(nil) { _, result in + + channel.publish(data, name: name) { error in + publishError = error + channel.history(nil) { result, _ in publishedMessage = result?.items.first as? ARTMessage } } - - expect(publishStatus?.state).toEventually(equal(ARTState.Ok), timeout: testTimeout) + + expect(publishError).toEventually(beNil(), timeout: testTimeout) expect(publishedMessage?.name).toEventually(equal(name), timeout: testTimeout) expect(publishedMessage?.payload.payload as? String).toEventually(equal(data), timeout: testTimeout) } } - + // RSL1b, RSL1e context("with name only") { it("publishes the message and invokes callback with success") { - var publishStatus: ARTStatus? + var publishError: NSError? = NSError(domain: "", code: -1, userInfo: nil) var publishedMessage: ARTMessage? - - channel.publish(nil, name: name) { status in - publishStatus = status - channel.history(nil) { _, result in + + channel.publish(nil, name: name) { error in + publishError = error + channel.history(nil) { result, _ in publishedMessage = result?.items.first as? ARTMessage } } - - expect(publishStatus?.state).toEventually(equal(ARTState.Ok), timeout: testTimeout) + + expect(publishError).toEventually(beNil(), timeout: testTimeout) expect(publishedMessage?.name).toEventually(equal(name), timeout: testTimeout) expect(publishedMessage?.payload.payload).toEventually(beNil(), timeout: testTimeout) } } - + // RSL1b, RSL1e context("with data only") { it("publishes the message and invokes callback with success") { - var publishStatus: ARTStatus? + var publishError: NSError? = NSError(domain: "", code: -1, userInfo: nil) var publishedMessage: ARTMessage? - - channel.publish(data) { status in - publishStatus = status - channel.history(nil) { _, result in + + channel.publish(data) { error in + publishError = error + channel.history(nil) { result, _ in publishedMessage = result?.items.first as? ARTMessage } } - - expect(publishStatus?.state).toEventually(equal(ARTState.Ok), timeout: testTimeout) + + expect(publishError).toEventually(beNil(), timeout: testTimeout) expect(publishedMessage?.name).toEventually(beNil(), timeout: testTimeout) expect(publishedMessage?.payload.payload as? String).toEventually(equal(data), timeout: testTimeout) } } - + // RSL1b, RSL1e context("with neither name nor data") { it("publishes the message and invokes callback with success") { - var publishStatus: ARTStatus? + var publishError: NSError? = NSError(domain: "", code: -1, userInfo: nil) var publishedMessage: ARTMessage? - - channel.publish(nil) { status in - publishStatus = status - channel.history(nil) { _, result in + + channel.publish(nil) { error in + publishError = error + channel.history(nil) { result, _ in publishedMessage = result?.items.first as? ARTMessage } } - - expect(publishStatus?.state).toEventually(equal(ARTState.Ok), timeout: testTimeout) + + expect(publishError).toEventually(beNil(), timeout: testTimeout) expect(publishedMessage?.name).toEventually(beNil(), timeout: testTimeout) expect(publishedMessage?.payload.payload).toEventually(beNil(), timeout: testTimeout) } } - + context("with a Message object") { it("publishes the message and invokes callback with success") { - var publishStatus: ARTStatus? + var publishError: NSError? = NSError(domain: "", code: -1, userInfo: nil) var publishedMessage: ARTMessage? - - channel.publishMessage(ARTMessage(data: data, name: name)) { status in - publishStatus = status - channel.history(nil) { _, result in + + channel.publishMessage(ARTMessage(data:data, name: name)) { error in + publishError = error + channel.history(nil) { result, _ in publishedMessage = result?.items.first as? ARTMessage } } - - expect(publishStatus?.state).toEventually(equal(ARTState.Ok), timeout: testTimeout) + + expect(publishError).toEventually(beNil(), timeout: testTimeout) expect(publishedMessage?.name).toEventually(beNil(), timeout: testTimeout) expect(publishedMessage?.payload.payload).toEventually(beNil(), timeout: testTimeout) } } - + // RSL1c context("with an array of Message objects") { it("publishes the messages and invokes callback with success") { - var publishStatus: ARTStatus? + var publishError: NSError? = NSError(domain: "", code: -1, userInfo: nil) var publishedMessages: [ARTMessage] = [] - + let messages = [ ARTMessage(data: "foo", name: "bar"), ARTMessage(data: "baz", name: "bat") ] - channel.publishMessages(messages) { status in - publishStatus = status - channel.history(nil) { _, result in + channel.publishMessages(messages) { error in + publishError = error + channel.history(nil) { result, _ in if let items = result?.items as? [ARTMessage] { publishedMessages.extend(items) } } } - - expect(publishStatus?.state).toEventually(equal(ARTState.Ok), timeout: testTimeout) + + expect(publishError).toEventually(beNil(), timeout: testTimeout) expect(publishedMessages.count).toEventually(equal(messages.count), timeout: testTimeout) expect(publishedMessages).toEventually(contain(messages.first), timeout: testTimeout) expect(publishedMessages).toEventually(contain(messages.last), timeout: testTimeout) } } } - + // RSL3, RSP1 describe("presence") { let presenceFixtures = appSetupJson["post_apps"]["channels"][0]["presence"] - + // RSP3 context("get") { it("should return presence fixture data") { let channel = client.channels.get("persisted:presence_fixtures") var presenceMessages: [ARTPresenceMessage] = [] - - channel.presence.get() { status, result in + + channel.presence.get() { result, _ in if let items = result?.items as? [ARTPresenceMessage] { presenceMessages.extend(items) } } - + expect(presenceMessages.count).toEventually(equal(presenceFixtures.count), timeout: testTimeout) for message in presenceMessages { let fixtureMessage = filter(presenceFixtures) { (key, value) in return message.clientId == value["clientId"].stringValue - }.first!.1 - + }.first!.1 + expect(message.content()).toNot(beNil()) expect(message.action).to(equal(ARTPresenceAction.Present)) - + // skip the encrypted message for now if message.payload?.encoding?.rangeOfString("cipher") == nil { expect(message.content() as? NSObject).to(equal(fixtureMessage["data"].object as? NSObject)) diff --git a/ablySpec/RestClient.stats.swift b/ablySpec/RestClient.stats.swift index f1a21e8c3..d7c136d12 100644 --- a/ablySpec/RestClient.stats.swift +++ b/ablySpec/RestClient.stats.swift @@ -13,32 +13,32 @@ import SwiftyJSON import Foundation private func postTestStats(stats: JSON) -> ARTClientOptions { - let options = AblyTests.commonAppSetup() + let options = AblyTests.setupOptions(AblyTests.jsonRestOptions); let key = ("\(options.authOptions.keyName):\(options.authOptions.keySecret)" as NSString) .dataUsingEncoding(NSUTF8StringEncoding)! .base64EncodedStringWithOptions(NSDataBase64EncodingOptions(0)) - - let request = NSMutableURLRequest(URL: NSURL(string: "https://\(options.restHost)/stats")!) + + let request = NSMutableURLRequest(URL: NSURL(string: "https://\(restHost)/stats")!) request.HTTPMethod = "POST" request.HTTPBody = stats.rawData() request.setValue("application/json", forHTTPHeaderField: "Content-Type") request.setValue("Basic \(key)", forHTTPHeaderField: "Authorization") - + var responseError: NSError? var httpResponse: NSHTTPURLResponse? var requestCompleted = false - + NSURLSession.sharedSession() .dataTaskWithRequest(request) { _, response, error in responseError = error httpResponse = response as? NSHTTPURLResponse requestCompleted = true }.resume() - + while !requestCompleted { CFRunLoopRunInMode(kCFRunLoopDefaultMode, CFTimeInterval(0.1), Boolean(0)) } - + if let error = responseError { XCTFail(error.localizedDescription) } else if let response = httpResponse { @@ -46,45 +46,47 @@ private func postTestStats(stats: JSON) -> ARTClientOptions { XCTFail("Posting stats fixtures failed") } } - + return options } private func queryStats(client: ARTRest, query: ARTStatsQuery) -> ARTPaginatedResult { var stats: ARTPaginatedResult? - var status: ARTStatus? - client.stats(query, callback: { (statsStatus, result) in + let dummyError = NSError(domain: "", code: -1, userInfo: nil); + var error: NSError? = dummyError + client.stats(query, callback: { result, err in stats = result - status = statsStatus + error = err }) - - while status == nil { + + while error === dummyError { CFRunLoopRunInMode(kCFRunLoopDefaultMode, CFTimeInterval(0.1), Boolean(0)) } - - if status!.state != .Ok { - XCTFail(status!.errorInfo.message) + + if let error = error { + XCTFail(error.localizedDescription) } - + return stats! } -private func getPage(paginator: (ARTPaginatedResultCallback!) -> Void) -> ARTPaginatedResult { +private func getPage(paginator: (ARTPaginatedResultCallback) -> Void) -> ARTPaginatedResult { var newResult: ARTPaginatedResult? - var status: ARTStatus? - paginator({ (paginatorStatus, paginatorResult) in + let dummyError = NSError(domain: "", code: -1, userInfo: nil); + var error: NSError? = dummyError + paginator({ paginatorResult, err in newResult = paginatorResult - status = paginatorStatus + error = err }) - - while status == nil { + + while error === dummyError { CFRunLoopRunInMode(kCFRunLoopDefaultMode, CFTimeInterval(0.1), Boolean(0)) } - - if status!.state != .Ok { - XCTFail(status!.errorInfo.message) + + if let error = error { + XCTFail(error.localizedDescription) } - + return newResult! } @@ -106,7 +108,7 @@ class RestClientStats: QuickSpec { let dateFormatter = NSDateFormatter() dateFormatter.timeZone = NSTimeZone(name: "UTC") dateFormatter.dateFormat = "YYYY-MM-dd:HH:mm" - + let statsFixtures: JSON = [ [ "intervalId": dateFormatter.stringFromDate(date), // 20XX-02-03:16:03 @@ -129,169 +131,169 @@ class RestClientStats: QuickSpec { "tokenRequests": [ "succeeded": 60, "failed": 20 ] ] ] - + var statsOptions = ARTClientOptions() beforeEach { statsOptions = postTestStats(statsFixtures) } - + it("should match minute-level inbound and outbound fixture data (forwards)") { let client = ARTRest(options: statsOptions) let query = ARTStatsQuery() query.start = date query.direction = .Forwards - + let result = queryStats(client, query) expect(result.items.count).to(equal(3)) - + let totalInbound = (result.items as! [ARTStats]).reduce(0, combine: { $0 + $1.inbound.all.messages.count }) expect(totalInbound).to(equal(50 + 60 + 70)) - + let totalOutbound = (result.items as! [ARTStats]).reduce(0, combine: { $0 + $1.outbound.all.messages.count }) expect(totalOutbound).to(equal(20 + 10 + 40)) } - + it("should match hour-level inbound and outbound fixture data (forwards)") { let client = ARTRest(options: statsOptions) let query = ARTStatsQuery() query.start = date query.direction = .Forwards query.unit = .Hour - + let result = queryStats(client, query) let totalInbound = (result.items as! [ARTStats]).reduce(0, combine: { $0 + $1.inbound.all.messages.count }) let totalOutbound = (result.items as! [ARTStats]).reduce(0, combine: { $0 + $1.outbound.all.messages.count }) - + expect(result.items.count).to(equal(1)) expect(totalInbound).to(equal(50 + 60 + 70)) expect(totalOutbound).to(equal(20 + 10 + 40)) } - + it("should match day-level inbound and outbound fixture data (forwards)") { let client = ARTRest(options: statsOptions) let query = ARTStatsQuery() query.end = calendar.dateByAddingUnit(.CalendarUnitDay, value: 1, toDate: date, options: NSCalendarOptions(0)) query.direction = .Forwards query.unit = .Month - + let result = queryStats(client, query) let totalInbound = (result.items as! [ARTStats]).reduce(0, combine: { $0 + $1.inbound.all.messages.count }) let totalOutbound = (result.items as! [ARTStats]).reduce(0, combine: { $0 + $1.outbound.all.messages.count }) - + expect(result.items.count).to(equal(1)) expect(totalInbound).to(equal(50 + 60 + 70)) expect(totalOutbound).to(equal(20 + 10 + 40)) } - + it("should match month-level inbound and outbound fixture data (forwards)") { let client = ARTRest(options: statsOptions) let query = ARTStatsQuery() query.end = calendar.dateByAddingUnit(.CalendarUnitMonth, value: 1, toDate: date, options: NSCalendarOptions(0)) query.direction = .Forwards query.unit = .Month - + let result = queryStats(client, query) let totalInbound = (result.items as! [ARTStats]).reduce(0, combine: { $0 + $1.inbound.all.messages.count }) let totalOutbound = (result.items as! [ARTStats]).reduce(0, combine: { $0 + $1.outbound.all.messages.count }) - + expect(result.items.count).to(equal(1)) expect(totalInbound).to(equal(50 + 60 + 70)) expect(totalOutbound).to(equal(20 + 10 + 40)) } - + it("should contain only one item when limit is 1 (backwards") { let client = ARTRest(options: statsOptions) let query = ARTStatsQuery() query.end = date.dateByAddingTimeInterval(60) // 20XX-02-03:16:04 query.limit = 1 - + let result = queryStats(client, query) let totalInbound = (result.items as! [ARTStats]).reduce(0, combine: { $0 + $1.inbound.all.messages.count }) let totalOutbound = (result.items as! [ARTStats]).reduce(0, combine: { $0 + $1.outbound.all.messages.count }) - + expect(result.items.count).to(equal(1)) expect(totalInbound).to(equal(60)) expect(totalOutbound).to(equal(10)) } - + it("should contain only one item when limit is 1 (forwards") { let client = ARTRest(options: statsOptions) let query = ARTStatsQuery() query.end = date.dateByAddingTimeInterval(60) // 20XX-02-03:16:04 query.limit = 1 query.direction = .Forwards - + let result = queryStats(client, query) let totalInbound = (result.items as! [ARTStats]).reduce(0, combine: { $0 + $1.inbound.all.messages.count }) let totalOutbound = (result.items as! [ARTStats]).reduce(0, combine: { $0 + $1.outbound.all.messages.count }) - + expect(result.items.count).to(equal(1)) expect(totalInbound).to(equal(50)) expect(totalOutbound).to(equal(20)) } - + it("should be paginated according to the limit (backwards") { let client = ARTRest(options: statsOptions) let query = ARTStatsQuery() query.end = date.dateByAddingTimeInterval(120) // 20XX-02-03:16:05 query.limit = 1 - - + + let firstPage = queryStats(client, query) expect(firstPage.items.count).to(equal(1)) expect((firstPage.items as! [ARTStats])[0].inbound.all.messages.data).to(equal(7000)) expect(firstPage.hasNext).to(beTrue()) expect(firstPage.isLast).to(beFalse()) - + let secondPage = getPage(firstPage.next) expect(secondPage.items.count).to(equal(1)) expect((secondPage.items as! [ARTStats])[0].inbound.all.messages.data).to(equal(6000)) expect(secondPage.hasNext).to(beTrue()) expect(secondPage.isLast).to(beFalse()) - + let thirdPage = getPage(secondPage.next) expect(thirdPage.items.count).to(equal(1)) expect((thirdPage.items as! [ARTStats])[0].inbound.all.messages.data).to(equal(5000)) expect(thirdPage.hasFirst).to(beTrue()) expect(thirdPage.isLast).to(beTrue()) - + let firstPageAgain = getPage(thirdPage.first) expect(firstPageAgain.items.count).to(equal(1)) expect((firstPageAgain.items as! [ARTStats])[0].inbound.all.messages.data).to(equal(7000)) } - + it("should be paginated according to the limit (fowards)") { let client = ARTRest(options: statsOptions) let query = ARTStatsQuery() query.end = date.dateByAddingTimeInterval(120) // 20XX-02-03:16:05 query.limit = 1 query.direction = .Forwards - - + + let firstPage = queryStats(client, query) expect(firstPage.items.count).to(equal(1)) expect((firstPage.items as! [ARTStats])[0].inbound.all.messages.data).to(equal(5000)) expect(firstPage.hasNext).to(beTrue()) expect(firstPage.isLast).to(beFalse()) - + let secondPage = getPage(firstPage.next) expect(secondPage.items.count).to(equal(1)) expect((secondPage.items as! [ARTStats])[0].inbound.all.messages.data).to(equal(6000)) expect(secondPage.hasNext).to(beTrue()) expect(secondPage.isLast).to(beFalse()) - + let thirdPage = getPage(secondPage.next) expect(thirdPage.items.count).to(equal(1)) expect((thirdPage.items as! [ARTStats])[0].inbound.all.messages.data).to(equal(7000)) expect(thirdPage.hasFirst).to(beTrue()) expect(thirdPage.isLast).to(beTrue()) - + let firstPageAgain = getPage(thirdPage.first) expect(firstPageAgain.items.count).to(equal(1)) expect((firstPageAgain.items as! [ARTStats])[0].inbound.all.messages.data).to(equal(5000)) } } - + // RSC6b context("query") { // RSC6b1 @@ -299,46 +301,46 @@ class RestClientStats: QuickSpec { it("should throw when later than end") { let client = ARTRest(key: "fake:key") let query = ARTStatsQuery() - + query.start = NSDate.distantFuture() as? NSDate query.end = NSDate.distantPast() as? NSDate - + expect{ client.stats(query, callback:{ status, result in }) }.to(raiseException()) } } - + // RSC6b2 context("direction") { it("should be backwards by default") { let query = ARTStatsQuery() - + expect(query.direction).to(equal(ARTQueryDirection.Backwards)); } } - + // RSC6b3 context("limit") { it("should have a default value of 100") { let query = ARTStatsQuery() - + expect(query.limit).to(equal(100)); } - + it("should throw when greater than 1000") { let client = ARTRest(key: "fake:key") let query = ARTStatsQuery() - + query.limit = 1001; - + expect{ client.stats(query, callback: { status, result in }) }.to(raiseException()) } } - + // RSC6b4 context("unit") { it("should default to minute") { let query = ARTStatsQuery() - + expect(query.unit).to(equal(ARTStatsUnit.Minute)) } } diff --git a/ablySpec/RestClient.swift b/ablySpec/RestClient.swift index 46d0f1d73..3ab1159ce 100644 --- a/ablySpec/RestClient.swift +++ b/ablySpec/RestClient.swift @@ -12,13 +12,15 @@ import ably import ably.Private class PublishTestMessage { - var status: ARTStatus? - + var error: NSError? + init(client: ARTRest, failOnError: Bool) { - client.channel("test").publish("message") { status in - self.status = status - if failOnError && status.state != .Ok { - XCTFail("Got status \(status.state.rawValue) with error '\(status.errorInfo?.message)'") + self.error = NSError(domain: "", code: -1, userInfo: nil) + + client.channels.get("test").publish("message") { error in + self.error = error + if failOnError && error != nil { + XCTFail("Got error '\(error!.localizedDescription)'") } } } @@ -58,7 +60,7 @@ class RestClient: QuickSpec { let publishTask = publishTestMessage(client) - expect(publishTask.status?.state).toEventually(equal(ARTState.Ok), timeout: testTimeout) + expect(publishTask.error).toEventually(beNil(), timeout: testTimeout) } it("should throw when provided an invalid key") { @@ -71,8 +73,8 @@ class RestClient: QuickSpec { let publishTask = publishTestMessage(client, failOnError: false) - expect(publishTask.status?.state).toEventually(equal(ARTState.Error), timeout: testTimeout) - expect(publishTask.status?.errorInfo.code).toEventually(equal(40005)) + expect(publishTask.error?.domain).toEventually(equal(ARTAblyErrorDomain), timeout: testTimeout) + expect(publishTask.error?.code).toEventually(equal(40005)) } it("should accept an options object") { @@ -81,7 +83,7 @@ class RestClient: QuickSpec { let publishTask = publishTestMessage(client) - expect(publishTask.status?.state).toEventually(equal(ARTState.Ok), timeout: testTimeout) + expect(publishTask.error).toEventually(beNil(), timeout: testTimeout) } it("should accept an options object with token authentication") { @@ -93,7 +95,7 @@ class RestClient: QuickSpec { let publishTask = publishTestMessage(client) expect(client.auth.getAuthMethod()).to(equal(ARTAuthMethod.Token)) - expect(publishTask.status?.state).toEventually(equal(ARTState.Ok), timeout: testTimeout) + expect(publishTask.error).toEventually(beNil(), timeout: testTimeout) } it("should result in error status when provided a bad token") { @@ -105,8 +107,8 @@ class RestClient: QuickSpec { let publishTask = publishTestMessage(client, failOnError: false) expect(client.auth.getAuthMethod()).to(equal(ARTAuthMethod.Token)) - expect(publishTask.status?.state).toEventually(equal(ARTState.Error), timeout: testTimeout) - expect(publishTask.status?.errorInfo.code).toEventually(equal(40005)) + expect(publishTask.error?.domain).toEventually(equal(ARTAblyErrorDomain), timeout: testTimeout) + expect(publishTask.error?.code).toEventually(equal(40005), timeout: testTimeout) } } @@ -172,7 +174,7 @@ class RestClient: QuickSpec { let publishTask = publishTestMessage(client) - expect(publishTask.status?.state).toEventually(equal(ARTState.Ok), timeout: testTimeout) + expect(publishTask.error).toEventually(beNil(), timeout: testTimeout) } } From 0f7eb26ff45675b927b03c14010bff0c433f8eec Mon Sep 17 00:00:00 2001 From: Ricardo Pereira Date: Wed, 30 Sep 2015 13:54:43 +0100 Subject: [PATCH 11/22] Initial endpoint tests now that http mocking is here (Yavor Georgiev) --- ably-ios/ARTClientOptions.h | 5 ++- ably-ios/ARTClientOptions.m | 12 +++++- ably-ios/ARTEncoder.h | 4 +- ably-ios/ARTHttp.h | 2 +- ably-ios/ARTHttp.m | 14 ++++++- ably-ios/ARTJsonEncoder.m | 15 +++---- ably-ios/ARTRest+Private.h | 6 ++- ably-ios/ARTRest.h | 5 +-- ably-ios/ARTRest.m | 38 +++++++++--------- ably.xcodeproj/project.pbxproj | 8 ++-- ablySpec/RestClient.swift | 71 +++++++++++++++++++++++++--------- ablySpec/TestUtilities.swift | 20 ++++++++-- 12 files changed, 134 insertions(+), 66 deletions(-) diff --git a/ably-ios/ARTClientOptions.h b/ably-ios/ARTClientOptions.h index fe01bd4de..28881124a 100644 --- a/ably-ios/ARTClientOptions.h +++ b/ably-ios/ARTClientOptions.h @@ -18,9 +18,10 @@ @property (readonly, getter=getRestHost) NSString *restHost; @property (readonly, getter=getRealtimeHost) NSString *realtimeHost; -@property (readwrite, assign, nonatomic) int restPort; -@property (readwrite, assign, nonatomic) int realtimePort; +@property (nonatomic, assign, nonatomic) int restPort; +@property (nonatomic, assign, nonatomic) int realtimePort; @property (readwrite, strong, nonatomic) NSString *environment; +@property (nonatomic, assign) BOOL tls; @property (readwrite, assign, nonatomic) BOOL queueMessages; @property (readwrite, assign, nonatomic) BOOL echoMessages; diff --git a/ably-ios/ARTClientOptions.m b/ably-ios/ARTClientOptions.m index 2d6d53447..e8941e0e4 100644 --- a/ably-ios/ARTClientOptions.m +++ b/ably-ios/ARTClientOptions.m @@ -63,6 +63,7 @@ - (instancetype)initDefaults { _autoConnect = true; _resumeKey = nil; _environment = nil; + _tls = YES; return self; } @@ -75,8 +76,15 @@ + (instancetype)optionsWithKey:(NSString *)key { } + (NSURL*)restUrl:(NSString *)host port:(int)port { - NSString *urlStr = [NSString stringWithFormat:@"https://%@:%d", host, port]; - return [NSURL URLWithString:urlStr]; + NSURLComponents *components = [[NSURLComponents alloc] init]; + components.scheme = self.tls ? @"https" : @"http"; + components.host = host; + components.port = port; + return components.URL; +} + +- (NSURL *)restUrl { + return [ARTClientOptions restUrl:self.restHost port:self.restPort]; } - (bool)isFallbackPermitted { diff --git a/ably-ios/ARTEncoder.h b/ably-ios/ARTEncoder.h index 4f0804a29..177122187 100644 --- a/ably-ios/ARTEncoder.h +++ b/ably-ios/ARTEncoder.h @@ -18,8 +18,6 @@ - (NSString *)mimeType; - - - (ARTTokenDetails *) decodeAccessToken:(NSData *) data; - (ARTMessage *)decodeMessage:(NSData *)data; - (NSArray *)decodeMessages:(NSData *)data; @@ -35,7 +33,7 @@ - (ARTProtocolMessage *)decodeProtocolMessage:(NSData *)data; - (NSDate *)decodeTime:(NSData *)data; -- (ARTErrorInfo *) decodeError:(NSData *) error; +- (NSError *)decodeError:(NSData *)error; - (NSArray *)decodeStats:(NSData *)data; @end diff --git a/ably-ios/ARTHttp.h b/ably-ios/ARTHttp.h index 97eff1c03..629604a87 100644 --- a/ably-ios/ARTHttp.h +++ b/ably-ios/ARTHttp.h @@ -11,7 +11,7 @@ #import #import -@protocol ARTHTTPExecutor +@protocol ARTHTTPExecutor @property (nonatomic, weak) ARTLog *logger; diff --git a/ably-ios/ARTHttp.m b/ably-ios/ARTHttp.m index b6486a256..060d11f00 100644 --- a/ably-ios/ARTHttp.m +++ b/ably-ios/ARTHttp.m @@ -160,12 +160,24 @@ - (instancetype)initWithBaseUrl:(NSURL *)baseUrl { } - (void)executeRequest:(NSMutableURLRequest *)request callback:(void (^)(NSHTTPURLResponse *, NSData *, NSError *))callback { + [self.logger debug:@"%@ %@", request.HTTPMethod, request.URL.absoluteString]; + [self.logger verbose:@"Headers %@", request.allHTTPHeaderFields]; + CFRunLoopRef currentRunloop = CFRunLoopGetCurrent(); NSURLSessionDataTask *task = [_urlSession dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; + CFRunLoopPerformBlock(currentRunloop, kCFRunLoopCommonModes, ^{ - callback((NSHTTPURLResponse *)response, data, error); + if (error) { + [self.logger error:@"%@ %@: error %@", request.HTTPMethod, request.URL.absoluteString, error]; + } else { + [self.logger debug:@"%@ %@: statusCode %ld", request.HTTPMethod, request.URL.absoluteString, httpResponse.statusCode]; + [self.logger verbose:@"Headers %@", httpResponse.allHeaderFields]; + } + callback(httpResponse, data, error); }); CFRunLoopWakeUp(currentRunloop); + }]; [task resume]; } diff --git a/ably-ios/ARTJsonEncoder.m b/ably-ios/ARTJsonEncoder.m index 299dcb842..246c22fed 100644 --- a/ably-ios/ARTJsonEncoder.m +++ b/ably-ios/ARTJsonEncoder.m @@ -508,13 +508,14 @@ - (ARTStatsResourceCount *)statsResourceCountFromDictionary:(NSDictionary *)inpu refused:refused.doubleValue]; } -- (ARTErrorInfo *) decodeError:(NSData *) error { - ARTErrorInfo * e = [[ARTErrorInfo alloc] init]; - NSDictionary * d = [[self decodeDictionary:error] valueForKey:@"error"]; - [e setCode:[[d artNumber:@"code"] intValue] - status:[[d artNumber:@"statusCode"] intValue] - message:[d artString:@"message"]]; - return e; +- (NSError *)decodeError:(NSData *)error { + NSDictionary *decodedError = [[self decodeDictionary:error] valueForKey:@"error"]; + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: @"", + NSLocalizedFailureReasonErrorKey: decodedError[@"message"], + @"ARTErrorStatusCode": decodedError[@"statusCode"] + }; + return [NSError errorWithDomain:ARTAblyErrorDomain code:[decodedError[@"code"] intValue] userInfo:userInfo]; } - (ARTStatsRequestCount *)statsRequestCountFromDictionary:(NSDictionary *)input { diff --git a/ably-ios/ARTRest+Private.h b/ably-ios/ARTRest+Private.h index 5186c6a12..06a8529de 100644 --- a/ably-ios/ARTRest+Private.h +++ b/ably-ios/ARTRest+Private.h @@ -7,6 +7,7 @@ // #import +#import @protocol ARTEncoder; @@ -20,10 +21,11 @@ typedef NS_ENUM(NSUInteger, ARTAuthentication) { ARTAuthenticationUseBasic }; -@interface ARTRest (Private) +@interface ARTRest () @property (readonly, strong, nonatomic) id defaultEncoder; -@property (readonly, strong, nonatomic) ARTAuth *auth; +@property (nonatomic, strong) id httpExecutor; +@property (nonatomic, strong) NSURL *baseUrl; - (NSURL *)getBaseURL; - (NSString *)formatQueryParams:(NSDictionary *)queryParams; diff --git a/ably-ios/ARTRest.h b/ably-ios/ARTRest.h index d077b3496..0fdb89ea3 100644 --- a/ably-ios/ARTRest.h +++ b/ably-ios/ARTRest.h @@ -31,8 +31,8 @@ NS_ASSUME_NONNULL_BEGIN @interface ARTRestChannelCollection : ARTChannelCollection -//- (ARTRestChannel *)get:(NSString *)channelName; -//- (ARTRestChannel *)get:(NSString *)channelName options:(ARTChannelOptions *)options; +- (ARTRestChannel *)get:(NSString *)channelName; +- (ARTRestChannel *)get:(NSString *)channelName options:(ARTChannelOptions *)options; @end @@ -55,7 +55,6 @@ NS_ASSUME_NONNULL_BEGIN - (id)internetIsUp:(void (^)(bool isUp)) cb; @property (nonatomic, strong, readonly) ARTLog *logger; -@property (nonatomic, strong, null_resettable) id httpExecutor; @property (nonatomic, strong, readonly) ARTRestChannelCollection *channels; @property (nonatomic, strong, readonly) ARTAuth *auth; @property (nonatomic, strong, readonly) ARTClientOptions *options; diff --git a/ably-ios/ARTRest.m b/ably-ios/ARTRest.m index f0089e193..ef1953371 100644 --- a/ably-ios/ARTRest.m +++ b/ably-ios/ARTRest.m @@ -17,7 +17,6 @@ #import "ARTJsonEncoder.h" #import "ARTMsgPackEncoder.h" #import "ARTMessage.h" -#import "ARTPresenceMessage.h" #import "ARTPaginatedResult+Private.h" #import "ARTStats.h" @@ -36,7 +35,6 @@ @interface ARTRest () @property (readonly, strong, nonatomic) NSDictionary *encoders; @property (readonly, strong, nonatomic) NSString *defaultEncoding; @property (readwrite, assign, nonatomic) int fallbackCount; -@property (readwrite, copy, nonatomic) NSURL *baseUrl; - (id)makeRequestWithMethod:(NSString *)method relUrl:(NSString *)relUrl headers:(NSDictionary *)headers body:(NSData *)body authenticated:(ARTAuthentication)authenticated cb:(ARTHttpCb)cb; @@ -214,13 +212,14 @@ - (instancetype)initWithKey:(NSString *) key { - (void)setup { _http = [[ARTHttp alloc] init]; _httpExecutor = _http; + _httpExecutor.logger = _logger; _channels = [[ARTRestChannelCollection alloc] initWithRest:self]; id defaultEncoder = [[ARTJsonEncoder alloc] init]; _encoders = @{ [defaultEncoder mimeType]: defaultEncoder, - }; + }; _defaultEncoding = [defaultEncoder mimeType]; _fallbackCount = 0; @@ -239,12 +238,10 @@ - (void)executeRequest:(NSMutableURLRequest *)request callback:(void (^)(NSHTTPU [self.httpExecutor executeRequest:request callback:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { if (response.statusCode >= 400) { - ARTErrorInfo *errorInfo = [self->_encoders[response.MIMEType] decodeError:data]; - if (errorInfo.code == 40140) { + NSError *error = [self->_encoders[response.MIMEType] decodeError:data]; + if (error.code == 40140) { // TODO: request token or error if no token information } else { - NSDictionary *userInfo = @{ NSLocalizedFailureReasonErrorKey: errorInfo.message }; - NSError *error = [NSError errorWithDomain:@"ARTAblyErrorDomain" code:errorInfo.code userInfo:userInfo]; callback(nil, nil, error); } } else { @@ -256,14 +253,14 @@ - (void)executeRequest:(NSMutableURLRequest *)request callback:(void (^)(NSHTTPU } - (id)token:(ARTAuthTokenParams *)params tokenCb:(void (^)(ARTStatus *status, ARTTokenDetails *)) cb { - + [self.logger debug:@"ARTRest is requesting a fresh token"]; if(![self.auth canRequestToken]) { cb([ARTStatus state:ARTStateError], nil); id c = nil; return c; } - + NSString * keyPath = [NSString stringWithFormat:@"/keys/%@/requestToken",params.keyName]; if([self.auth getAuthOptions].authUrl) { keyPath = [[self.auth getAuthOptions].authUrl absoluteString]; @@ -286,7 +283,7 @@ - (void)executeRequest:(NSMutableURLRequest *)request callback:(void (^)(NSHTTPU cb(ARTStateOk, token); } else { - + ARTErrorInfo * e = [self.defaultEncoder decodeError:response.body]; [self.logger error:@"ARTRest: requestToken Error code: %d, Status %d, Message %@", e.code, e.statusCode, e.message]; cb([ARTStatus state:ARTStateError info:e], nil); @@ -367,7 +364,7 @@ - (bool)isAnErrorStatus:(int)status { } else { [self.logger warn:@"ARTRest has no more fallback hosts to attempt. Giving up."]; - self.baseUrl = [ARTClientOptions restUrl:self.options.restHost port:self.options.restPort]; + self.baseUrl = [self.options restUrl]; cb(response); return; } @@ -424,16 +421,16 @@ - (bool)isAnErrorStatus:(int)status { - (NSDictionary *)withAcceptHeader:(NSDictionary *)headers { NSMutableDictionary *md = [NSMutableDictionary dictionaryWithDictionary:headers]; - + NSMutableArray *mimeTypes = [NSMutableArray arrayWithObject:self.defaultEncoding]; - + for (NSString *mimeType in self.encoders) { if (![mimeType isEqualToString:self.defaultEncoding]) { [mimeTypes addObject:mimeType]; } } md[@"Accept"] = [mimeTypes componentsJoinedByString:@","]; - + return md; } @@ -447,13 +444,13 @@ @implementation ARTRest (Private) - (NSString *)formatQueryParams:(NSDictionary *)queryParams { NSMutableArray *encodedParams = [NSMutableArray array]; - + for (NSString *queryParamName in queryParams) { NSString *queryParamValue = [queryParams objectForKey:queryParamName]; NSString *encoded = [NSString stringWithFormat:@"%@=%@", [queryParamName stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]], [queryParamValue stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]]; [encodedParams addObject:encoded]; } - + return [encodedParams componentsJoinedByString:@"&"]; } @@ -466,16 +463,16 @@ - (NSURL *)resolveUrl:(NSString *)relUrl { } [self.logger verbose:@"ARTRest is treating the relative url as the base"]; return [NSURL URLWithString:relUrl]; - + } - (NSURL *)resolveUrl:(NSString *)relUrl queryParams:(NSDictionary *)queryParams { NSString *queryString = [self formatQueryParams:queryParams]; - + if (queryString.length) { relUrl = [NSString stringWithFormat:@"%@?%@", relUrl, queryString]; } - + return [self resolveUrl:relUrl]; } @@ -491,7 +488,8 @@ - (NSURL *)resolveUrl:(NSString *)relUrl queryParams:(NSDictionary *)queryParams return [self makeRequestWithMethod:@"POST" relUrl:relUrl headers:headers body:body authenticated:authenticated cb:cb]; } -- (id)withAuthHeaders:(id(^)(NSDictionary *))cb { +- (id)withAuthHeaders:(id(^) + (NSDictionary *))cb { return [self withAuthHeadersUseBasic:false cb:cb]; } diff --git a/ably.xcodeproj/project.pbxproj b/ably.xcodeproj/project.pbxproj index aef176908..9b690f4b8 100644 --- a/ably.xcodeproj/project.pbxproj +++ b/ably.xcodeproj/project.pbxproj @@ -401,8 +401,6 @@ D746AE201BBB60EE003ECEF8 /* ARTChannels.h */, D746AE241BBB611C003ECEF8 /* ARTChannels+Private.h */, D746AE211BBB60EE003ECEF8 /* ARTChannels.m */, - D746AE261BBB61C9003ECEF8 /* ARTPresence.h */, - D746AE271BBB61C9003ECEF8 /* ARTPresence.m */, 96BF61551A35B40E004CF2B3 /* ARTStatus.h */, 1C55427C1B148306003068DB /* ARTStatus.m */, D746AE1A1BBB5207003ECEF8 /* ARTDataQuery.h */, @@ -421,8 +419,12 @@ 850BFB4A1B79323C009D0ADD /* ARTPaginatedResult.h */, D746AE2E1BBBE7D7003ECEF8 /* ARTPaginatedResult+Private.h */, 850BFB4B1B79323C009D0ADD /* ARTPaginatedResult.m */, + D746AE261BBB61C9003ECEF8 /* ARTPresence.h */, + D746AE271BBB61C9003ECEF8 /* ARTPresence.m */, 96A5079F1A377AA50077CDF8 /* ARTPresenceMessage.h */, 96A507A01A377AA50077CDF8 /* ARTPresenceMessage.m */, + 1C2B0FFB1B136A6D00E3633C /* ARTPresenceMap.h */, + 1C2B0FFC1B136A6D00E3633C /* ARTPresenceMap.m */, 96A507A31A377DE90077CDF8 /* ARTNSDictionary+ARTDictionaryUtil.h */, 96A507A41A377DE90077CDF8 /* ARTNSDictionary+ARTDictionaryUtil.m */, 96A507A71A37806A0077CDF8 /* ARTEncoder.h */, @@ -447,8 +449,6 @@ 1C6C18A21ADFDAB100AB79E4 /* ARTLog.m */, 1C8065041AE7C8FA00D49357 /* ARTPayload+Private.h */, 1CEFC60D1B0F9EF500D84463 /* ARTTokenDetails+Private.h */, - 1C2B0FFB1B136A6D00E3633C /* ARTPresenceMap.h */, - 1C2B0FFC1B136A6D00E3633C /* ARTPresenceMap.m */, 1CD8DC9D1B1C7315007EAF36 /* ARTDefault.h */, 1CD8DC9E1B1C7315007EAF36 /* ARTDefault.m */, 1C578E1D1B3435CA00EF46EC /* ARTFallback.h */, diff --git a/ablySpec/RestClient.swift b/ablySpec/RestClient.swift index 3ab1159ce..2f03e1ee5 100644 --- a/ablySpec/RestClient.swift +++ b/ablySpec/RestClient.swift @@ -20,7 +20,7 @@ class PublishTestMessage { client.channels.get("test").publish("message") { error in self.error = error if failOnError && error != nil { - XCTFail("Got error '\(error!.localizedDescription)'") + XCTFail("Got error '\(error!)'") } } } @@ -57,6 +57,7 @@ class RestClient: QuickSpec { it("should accept an API key") { let options = AblyTests.commonAppSetup() let client = ARTRest(key: "\(options.authOptions.keyName):\(options.authOptions.keySecret)") + client.baseUrl = options.restUrl let publishTask = publishTestMessage(client) @@ -88,7 +89,6 @@ class RestClient: QuickSpec { it("should accept an options object with token authentication") { let options = AblyTests.commonAppSetup() - options.authOptions.useTokenAuth = true options.authOptions.token = getTestToken() let client = ARTRest(options: options) @@ -164,41 +164,76 @@ class RestClient: QuickSpec { // RSC11 context("endpoint") { + var mockExecutor: MockHTTPExecutor! + beforeEach { + mockExecutor = MockHTTPExecutor() + } + + it("should accept an options object with a host set") { + let options = ARTClientOptions(key: "fake:key") + options.restHost = "fake.ably.host" + let client = ARTRest(options: options) + client.httpExecutor = mockExecutor + + publishTestMessage(client, failOnError: false) + + expect(mockExecutor.requests.first?.URL?.host).toEventually(equal("fake.ably.host"), timeout: testTimeout) + } + it("should accept an options object with an environment set") { - let options = AblyTests.commonAppSetup() - let newOptions = ARTClientOptions() - newOptions.authOptions.keyName = options.authOptions.keyName - newOptions.authOptions.keySecret = options.authOptions.keySecret - newOptions.environment = "sandbox" - let client = ARTRest(options: newOptions) - - let publishTask = publishTestMessage(client) - - expect(publishTask.error).toEventually(beNil(), timeout: testTimeout) + let options = ARTClientOptions(key: "fake:key") + options.environment = "myEnvironment" + let client = ARTRest(options: options) + client.httpExecutor = mockExecutor + + publishTestMessage(client, failOnError: false) + + expect(mockExecutor.requests.first?.URL?.host).toEventually(equal("myEnvironment-rest.ably.io"), timeout: testTimeout) + } + + it("should default to https://rest.ably.io") { + let options = ARTClientOptions(key: "fake:key") + let client = ARTRest(options: options) + client.httpExecutor = mockExecutor + + publishTestMessage(client, failOnError: false) + + expect(mockExecutor.requests.first?.URL?.absoluteString).toEventually(beginWith("https://rest.ably.io"), timeout: testTimeout) + } + + it("should connect over plain http:// when tls is off") { + let options = ARTClientOptions(key: "fake:key") + options.tls = false; + let client = ARTRest(options: options) + client.httpExecutor = mockExecutor + + publishTestMessage(client, failOnError: false) + + expect(mockExecutor.requests.first?.URL?.scheme).toEventually(equal("http"), timeout: testTimeout) } } // RSC5 it("should provide access to the AuthOptions object passed in ClientOptions") { - let options = AblyTests.commonAppSetup() + let options = AblyTests.setupOptions(AblyTests.jsonRestOptions) let client = ARTRest(options: options) let authOptions = client.auth.getAuthOptions() - + expect(authOptions).to(beIdenticalTo(options.authOptions)) } - + // RSC16 context("time") { it("should return server time") { - let options = AblyTests.commonAppSetup() + let options = AblyTests.setupOptions(AblyTests.jsonRestOptions) let client = ARTRest(options: options) - + var time: NSDate? client.time({ (status, date) in time = date }) - + expect(time?.timeIntervalSince1970).toEventually(beCloseTo(NSDate().timeIntervalSince1970, within: 60), timeout: testTimeout) } } diff --git a/ablySpec/TestUtilities.swift b/ablySpec/TestUtilities.swift index 24da62727..cdb9885ec 100644 --- a/ablySpec/TestUtilities.swift +++ b/ablySpec/TestUtilities.swift @@ -9,15 +9,15 @@ import Foundation import XCTest +import Quick import SwiftyJSON import ably -import Quick +import ably.Private class Configuration : QuickConfiguration { override class func configure(configuration: Quick.Configuration!) { configuration.beforeEach { - //ARTClientOptions.getDefaultRestHost("sandbox-rest.ably.io", modify: true) - //ARTClientOptions.getDefaultRealtimeHost("sandbox-realtime.ably.io", modify: true) + } } } @@ -110,3 +110,17 @@ func querySyslog(forLogsAfter startingTime: NSDate? = nil) -> GeneratorOf Void)!) { + self.requests.append(request) + self._executor.executeRequest(request, callback: callback) + } +} From dcc76f2f59493888f315f3f6ae9b4c0664b8a9cd Mon Sep 17 00:00:00 2001 From: Ricardo Pereira Date: Thu, 1 Oct 2015 16:31:11 +0100 Subject: [PATCH 12/22] Merged last commit from Yavor Georgiev. - Back to Xcode 6 version - No warnings --- ably-ios/ARTAuth.h | 114 +-- ably-ios/ARTAuth.m | 667 +++++-------- ably-ios/ARTBaseMessage.h | 39 + ably-ios/ARTBaseMessage.m | 71 ++ ...hannels+Private.h => ARTChannel+Private.h} | 15 +- ably-ios/{ARTChannels.h => ARTChannel.h} | 40 +- ably-ios/ARTChannel.m | 67 ++ ably-ios/ARTChannelCollection+Private.h | 27 + ably-ios/ARTChannelCollection.h | 21 + ably-ios/ARTChannelCollection.m | 65 ++ ably-ios/ARTChannelOptions.h | 26 + ably-ios/ARTChannelOptions.m | 34 + ably-ios/ARTChannels.m | 142 --- ably-ios/ARTClientOptions+Private.h | 13 - ably-ios/ARTClientOptions.h | 6 +- ably-ios/ARTClientOptions.m | 37 +- ably-ios/ARTCrypto.h | 1 + ably-ios/ARTCrypto.m | 2 - ably-ios/ARTDataQuery+Private.h | 2 +- ably-ios/ARTDataQuery.m | 2 +- ably-ios/ARTDefault.h | 4 +- ably-ios/ARTEncoder.h | 10 +- ably-ios/ARTEventEmitter.h | 21 + ably-ios/ARTEventEmitter.m | 37 + ably-ios/ARTFallback.h | 2 +- ably-ios/ARTFallback.m | 15 +- ably-ios/ARTHttp.h | 10 +- ably-ios/ARTHttp.m | 3 - ably-ios/ARTJsonEncoder.h | 8 +- ably-ios/ARTJsonEncoder.m | 129 +-- ably-ios/ARTMessage.h | 28 +- ably-ios/ARTMessage.m | 79 +- ably-ios/ARTMsgPackEncoder.h | 15 - ably-ios/ARTMsgPackEncoder.m | 275 ------ ably-ios/ARTPaginatedResult.m | 1 + ably-ios/ARTPayload+Private.h | 10 +- ably-ios/ARTPayload.h | 8 +- ably-ios/ARTPayload.m | 29 +- ably-ios/ARTPresence.h | 42 +- ably-ios/ARTPresence.m | 136 ++- ably-ios/ARTPresenceMap.h | 5 +- ably-ios/ARTPresenceMap.m | 7 +- ably-ios/ARTPresenceMessage.h | 41 +- ably-ios/ARTPresenceMessage.m | 44 +- ably-ios/ARTProtocolMessage.h | 10 +- ably-ios/ARTProtocolMessage.m | 6 +- ably-ios/ARTQueuedMessage.h | 24 + ably-ios/ARTQueuedMessage.m | 45 + ably-ios/ARTRealtime+Private.h | 10 +- ably-ios/ARTRealtime.h | 106 +- ably-ios/ARTRealtime.m | 908 +----------------- ably-ios/ARTRealtimeChannel+Private.h | 18 + ably-ios/ARTRealtimeChannel.h | 92 ++ ably-ios/ARTRealtimeChannel.m | 491 ++++++++++ ably-ios/ARTRealtimeChannelSubscription.h | 65 ++ ably-ios/ARTRealtimeChannelSubscription.m | 112 +++ ably-ios/ARTRealtimeTransport.h | 5 +- ably-ios/ARTRest+Private.h | 13 +- ably-ios/ARTRest.h | 45 +- ably-ios/ARTRest.m | 455 ++------- ably-ios/ARTStatus.h | 5 +- ably-ios/ARTStatus.m | 1 + ably-ios/ARTSubscription.h | 18 + ably-ios/ARTTokenDetails+Private.h | 22 - ably-ios/ARTTypes.h | 42 +- ably-ios/ARTWebSocketTransport.m | 16 +- ably-ios/ably.h | 17 +- ably-ios/ably.modulemap | 5 +- ably.xcodeproj/project.pbxproj | 278 ++++-- ablySpec/Auth.swift | 92 ++ 70 files changed, 2367 insertions(+), 2884 deletions(-) create mode 100644 ably-ios/ARTBaseMessage.h create mode 100644 ably-ios/ARTBaseMessage.m rename ably-ios/{ARTChannels+Private.h => ARTChannel+Private.h} (66%) rename ably-ios/{ARTChannels.h => ARTChannel.h} (56%) create mode 100644 ably-ios/ARTChannel.m create mode 100644 ably-ios/ARTChannelCollection+Private.h create mode 100644 ably-ios/ARTChannelCollection.h create mode 100644 ably-ios/ARTChannelCollection.m create mode 100644 ably-ios/ARTChannelOptions.h create mode 100644 ably-ios/ARTChannelOptions.m delete mode 100644 ably-ios/ARTChannels.m delete mode 100644 ably-ios/ARTClientOptions+Private.h create mode 100644 ably-ios/ARTEventEmitter.h create mode 100644 ably-ios/ARTEventEmitter.m delete mode 100644 ably-ios/ARTMsgPackEncoder.h delete mode 100644 ably-ios/ARTMsgPackEncoder.m create mode 100644 ably-ios/ARTQueuedMessage.h create mode 100644 ably-ios/ARTQueuedMessage.m create mode 100644 ably-ios/ARTRealtimeChannel+Private.h create mode 100644 ably-ios/ARTRealtimeChannel.h create mode 100644 ably-ios/ARTRealtimeChannel.m create mode 100644 ably-ios/ARTRealtimeChannelSubscription.h create mode 100644 ably-ios/ARTRealtimeChannelSubscription.m create mode 100644 ably-ios/ARTSubscription.h delete mode 100644 ably-ios/ARTTokenDetails+Private.h create mode 100644 ablySpec/Auth.swift diff --git a/ably-ios/ARTAuth.h b/ably-ios/ARTAuth.h index f2285a1dd..55135aaa0 100644 --- a/ably-ios/ARTAuth.h +++ b/ably-ios/ARTAuth.h @@ -13,97 +13,99 @@ @class ARTRest; @class ARTLog; +NS_ASSUME_NONNULL_BEGIN -#pragma mark - ARTTokenDetails +@interface ARTAuthTokenDetails : NSObject -@interface ARTTokenDetails : NSObject - -@property (readonly, strong, nonatomic) NSString *token; -@property (readonly, assign, nonatomic) int64_t expires; -@property (readonly, assign, nonatomic) int64_t issued; -@property (readonly, strong, nonatomic) NSString *capability; -@property (readonly, strong, nonatomic) NSString *clientId; +@property (nonatomic, readonly, copy) NSString *token; +@property (nonatomic, readonly, strong, nullable) NSDate *expires; +@property (nonatomic, readonly, strong, nullable) NSDate *issued; +@property (nonatomic, readonly, copy, nullable) NSString *capability; +@property (nonatomic, readonly, copy, nullable) NSString *clientId; - (instancetype)init UNAVAILABLE_ATTRIBUTE; -- (instancetype)initWithId:(NSString *)id expires:(int64_t)expires issued:(int64_t)issued capability:(NSString *)capability clientId:(NSString *)clientId; +- (instancetype)initWithToken:(NSString *)token; + +- (instancetype)initWithToken:(NSString *)token expires:(NSDate *)expires issued:(NSDate *)issued capability:(NSString *)capability clientId:(NSString *)clientId; @end +@interface ARTAuthTokenParams : NSObject -#pragma mark - ARTAuthTokenParams +@property (nonatomic, assign) NSTimeInterval ttl; +@property (nonatomic, copy) NSString *capability; +@property (nonatomic, copy) NSString *clientId; +@property (nonatomic, strong, null_resettable) NSDate *timestamp; -@interface ARTAuthTokenParams : NSObject +- (instancetype)init; -@property (readonly, strong, nonatomic) NSString *keyName; -@property (readonly, assign, nonatomic) int64_t ttl; -@property (readonly, strong, nonatomic) NSString *capability; -@property (readonly, strong, nonatomic) NSString *clientId; -@property (readonly, assign, nonatomic) int64_t timestamp; -@property (readonly, strong, nonatomic) NSString *nonce; -@property (readonly, strong, nonatomic) NSString *mac; +@end + +@interface ARTAuthTokenRequest : ARTAuthTokenParams + +@property (nonatomic, readonly, copy) NSString *keyName; +@property (nonatomic, readonly, copy) NSString *nonce; +@property (nonatomic, readonly, copy) NSString *mac; - (instancetype)init UNAVAILABLE_ATTRIBUTE; -- (instancetype)initWithId:(NSString *)id ttl:(int64_t)ttl capability:(NSString *)capability clientId:(NSString *)clientId timestamp:(int64_t)timestamp nonce:(NSString *)nonce mac:(NSString *)mac; +- (instancetype)initWithTokenParams:(ARTAuthTokenParams *)tokenParams keyName:(NSString *)keyName nonce:(NSString *)nonce mac:(NSString *)mac; + +@end --(NSDictionary *) asDictionary; +@interface ARTAuthTokenParams(SignedRequest) + +- (ARTAuthTokenRequest *)sign:(NSString *)key; @end -typedef id(^ARTAuthCb)(void(^continuation)(ARTStatus *,ARTTokenDetails *)); -typedef id(^ARTSignedTokenRequestCb)(ARTAuthTokenParams *, void(^continuation)(ARTAuthTokenParams *)); +typedef void (^ARTAuthCallback)(ARTAuthTokenParams *tokenParams, void(^callback)(ARTAuthTokenRequest *__nullable tokenRequest, NSError *__nullable error)); + typedef NS_ENUM(NSUInteger, ARTAuthMethod) { ARTAuthMethodBasic, ARTAuthMethodToken }; +@interface ARTAuthOptions : NSObject -#pragma mark - ARTAuthOptions +@property (nonatomic, copy, nullable) NSString *key; -@interface ARTAuthOptions : NSObject +@property (nonatomic, copy, nullable) NSString *token; +@property (nonatomic, strong, nullable) ARTAuthTokenDetails *tokenDetails; +@property (nonatomic, assign) BOOL useTokenAuth; -@property (readwrite, strong, nonatomic) ARTAuthCb authCallback; -@property (readwrite, strong, nonatomic) ARTSignedTokenRequestCb signedTokenRequestCallback; -@property (readwrite, strong, nonatomic) ARTAuthTokenParams *tokenParams; -@property (readwrite, strong, nonatomic) NSURL *authUrl; -@property (readwrite, strong, nonatomic) NSString *keyName; -@property (readwrite, strong, nonatomic) NSString *keySecret; -@property (readwrite, strong, nonatomic) NSString *token; -@property (readwrite, strong, nonatomic) NSString *capability; -@property (readwrite, strong, nonatomic) NSString *nonce; -@property (readwrite, assign, nonatomic) int64_t ttl; -@property (readwrite, strong, nonatomic) NSDictionary *authHeaders; -@property (readwrite, strong, nonatomic) NSString *clientId; -@property (readwrite, assign, nonatomic) BOOL queryTime; -@property (readwrite, assign, nonatomic) BOOL useTokenAuth; -@property (readwrite, assign, nonatomic) ARTTokenDetails * tokenDetails; +@property (nonatomic, copy, nullable) ARTAuthCallback authCallback; -- (instancetype)init; -- (instancetype)initWithKey:(NSString *)key; +@property (nonatomic, strong, nullable) NSURL *authUrl; +@property (nonatomic, copy, null_resettable) NSString *authMethod; +@property (nonatomic, copy, nullable) NSDictionary *authHeaders; //X7: NSDictionary *authHeaders; +@property (nonatomic, copy, nullable) NSArray *authParams; //X7: NSArray *authParams; -+ (instancetype)options; -+ (instancetype)optionsWithKey:(NSString *)key; +@property (nonatomic, assign, nonatomic) BOOL queryTime; -- (instancetype)clone; +- (instancetype)initWithKey:(NSString *)key; @end +@interface ARTAuth : NSObject -#pragma mark - ARTAuth +@property (nonatomic, weak) ARTLog *logger; +@property (nonatomic, readonly, strong) ARTAuthOptions *options; +@property (nonatomic, readonly, assign) ARTAuthMethod authMethod; +@property (nonatomic, readonly, strong) ARTAuthTokenDetails *currentToken; -@interface ARTAuth : NSObject +- (instancetype)initWithRest:(ARTRest *)rest options:(ARTAuthOptions *)options; -- (instancetype)initWithRest:(ARTRest *) rest options:(ARTAuthOptions *) options; -- (ARTAuthOptions *)getAuthOptions; -- (ARTAuthMethod)getAuthMethod; +- (void)requestToken:(nullable ARTAuthTokenParams *)tokenParams options:(nullable ARTAuthOptions *)options + callback:(void (^)(ARTAuthTokenDetails *__nullable tokenDetails, NSError *__nullable error))callback; -- (ARTAuthTokenParams *) getTokenParams; -- (id)authHeadersUseBasic:(BOOL)useBasic cb:(id(^)(NSDictionary *))cb; -- (id)authParams:(id(^)(NSDictionary *))cb; -- (id)requestToken:(id(^)(ARTTokenDetails *))cb; -- (id)authTokenForceReauth:(BOOL)force cb:(id(^)(ARTTokenDetails *))cb; -- (void)attemptTokenFetch:(void (^)()) cb; -- (bool)canRequestToken; +- (void)authorise:(nullable ARTAuthTokenParams *)tokenParams options:(nullable ARTAuthOptions *)options force:(BOOL)force + callback:(void (^)(ARTAuthTokenDetails *__nullable tokenDetails, NSError *__nullable error))callback; + +- (void)createTokenRequest:(nullable ARTAuthTokenParams *)tokenParams options:(nullable ARTAuthOptions *)options + callback:(void (^)(ARTAuthTokenRequest *__nullable tokenRequest, NSError *__nullable error))callback; @end + +NS_ASSUME_NONNULL_END diff --git a/ably-ios/ARTAuth.m b/ably-ios/ARTAuth.m index aa9123899..0bcf5ab6a 100644 --- a/ably-ios/ARTAuth.m +++ b/ably-ios/ARTAuth.m @@ -12,539 +12,308 @@ #include #import "ARTRest.h" -#import "ARTPayload.h" +#import "ARTRest+Private.h" +#import "ARTEncoder.h" #import "ARTLog.h" -#import "ARTTokenDetails+Private.h" - - -@interface ARTAuthTokenCancellable : NSObject - -@property (readwrite, assign, nonatomic) BOOL isCancelled; -@property (readwrite, strong, nonatomic) id(^cb)(ARTTokenDetails*token); -@property (readwrite, strong, nonatomic) id cancellable; - -- (instancetype)init UNAVAILABLE_ATTRIBUTE; -- (instancetype)initWithCb:(id(^)(ARTTokenDetails*token))cb; - -- (void)onAuthToken:(ARTTokenDetails *)token; - -@end - -@interface ARTAuth () - -@property (nonatomic, weak) ARTLog * logger; -@property (readonly, weak, nonatomic) ARTRest *rest; -@property (readwrite, strong, nonatomic) ARTTokenDetails *token; -@property (assign, nonatomic) ARTAuthMethod authMethod; -@property (readonly, strong, nonatomic) NSString *basicCredentials; -@property (readonly, strong, nonatomic) NSString *keyName; -@property (readonly, strong, nonatomic) NSString *keySecret; -@property (readonly, strong, nonatomic) ARTAuthCb authTokenCb; -@property (readwrite, strong, nonatomic) NSMutableArray *tokenCbs; -@property (readwrite, strong, nonatomic) id tokenRequest; -@property (readwrite, strong, nonatomic) ARTAuthOptions *options; - -+ (ARTSignedTokenRequestCb)defaultSignedTokenRequestCallback:(ARTAuthOptions *)authOptions forRest:(ARTRest *)rest withLogger:(ARTLog *)logger; -+ (NSString *)random; -+ (NSArray *)checkValidKey:(NSString *) key; +#import "ARTPayload.h" -@end +//X7: NSArray +static NSArray *decomposeKey(NSString *key) { + return [key componentsSeparatedByString:@":"]; +} -@implementation ARTTokenDetails +@implementation ARTAuthTokenDetails -- (instancetype)initWithId:(NSString *)token expires:(int64_t)expires issued:(int64_t)issued capability:(NSString *)capability clientId:(NSString *)clientId { - self = [super init]; - if (self) { - - _token = token; +- (instancetype)initWithToken:(NSString *)token expires:(NSDate *)expires issued:(NSDate *)issued capability:(NSString *)capability clientId:(NSString *)clientId { + if (self = [super init]) { + _token = [token copy]; _expires = expires; _issued = issued; - _capability = capability; - _clientId = clientId; + _capability = [capability copy]; + _clientId = [clientId copy]; } + return self; } -@end - -@implementation ARTTokenDetails (Private) - --(void) setExpiresTime:(int64_t)time { - _expires = time; -} - -@end - -@implementation ARTAuthTokenParams - -- (instancetype)initWithId:(NSString *)keyName ttl:(int64_t)ttl capability:(NSString *)capability clientId:(NSString *)clientId timestamp:(int64_t)timestamp nonce:(NSString *)nonce mac:(NSString *)mac { - self = [super init]; - if (self) { - _keyName = keyName; - _ttl = ttl; - _capability = capability; - _clientId = clientId; - _timestamp = timestamp; - _nonce = nonce; - _mac = mac; - +- (instancetype)initWithToken:(NSString *)token { + if (self = [super init]) { + _token = [token copy]; } + return self; } --(NSDictionary *) asDictionary { - NSMutableDictionary *reqObj = [NSMutableDictionary dictionary]; - reqObj[@"keyName"] = self.keyName ? self.keyName : @""; - reqObj[@"capability"] = self.capability ? self.capability : @""; - reqObj[@"ttl"] = [NSNumber numberWithLongLong:self.ttl]; - reqObj[@"clientId"] = self.clientId ? self.clientId : @""; - reqObj[@"nonce"] = self.nonce ? self.nonce : @""; - reqObj[@"timestamp"] = [NSNumber numberWithLongLong:self.timestamp]; - reqObj[@"mac"] = self.mac ? self.mac : @""; - return reqObj; -} - @end -@implementation ARTAuthOptions +@implementation ARTAuthTokenParams - (instancetype)init { - self = [super init]; - if (self) { - _authCallback = nil; - _authUrl = nil; - _keyName = nil; - _keySecret = nil; - _token = nil; - _authHeaders = nil; - _clientId = nil; - _capability = nil; - _useTokenAuth = false; - _queryTime = true; - _tokenDetails = nil; - _nonce =nil; - _ttl = 3600000; - + if (self = [super init]) { + _ttl = 60 * 60; + _timestamp = [NSDate date]; + _capability = @"{ \"*\": [ \"*\" ] }"; // allow all } + return self; } - --(void) setTokenParams:(ARTAuthTokenParams *)tokenParams { - _tokenParams =tokenParams; - self.keyName = tokenParams.keyName; -} - - -- (instancetype)initWithKey:(NSString *)key { - self = [self init]; - if (self) { - NSArray * keyBits =[ARTAuth checkValidKey:key]; - _keyName = keyBits[0]; - _keySecret = keyBits[1]; +- (void)setTimestamp:(NSDate *)timestamp { + if (timestamp == nil) { + timestamp = [NSDate date]; } - return self; + + _timestamp = timestamp; } - -+ (instancetype)options { - return [[ARTAuthOptions alloc] init]; +static NSString *generateNonce() { + // Generate two random numbers up to 8 digits long and concatenate them to produce a 16 digit random number + NSUInteger r1 = arc4random_uniform(100000000); + NSUInteger r2 = arc4random_uniform(100000000); + return [NSString stringWithFormat:@"%08lu%08lu", (long)r1, (long)r2]; } -+ (instancetype)optionsWithKey:(NSString *)key { - return [[ARTAuthOptions alloc] initWithKey:key]; +static NSString *hmacForDataAndKey(NSData *data, NSData *key) { + const void *cKey = [key bytes]; + const void *cData = [data bytes]; + size_t keyLen = [key length]; + size_t dataLen = [data length]; + + unsigned char hmac[CC_SHA256_DIGEST_LENGTH]; + + CCHmac(kCCHmacAlgSHA256, cKey, keyLen, cData, dataLen, hmac); + NSData *mac = [[NSData alloc] initWithBytes:hmac length:sizeof(hmac)]; + NSString *str = [ARTBase64PayloadEncoder toBase64:mac]; + return str; } -- (instancetype)clone { - ARTAuthOptions *clone = [[ARTAuthOptions alloc] init]; - clone.authCallback = self.authCallback; - clone.signedTokenRequestCallback = self.signedTokenRequestCallback; - clone.authUrl = self.authUrl; - clone.keyName = self.keyName; - clone.keySecret = self.keySecret; - clone.token = self.token; - clone.capability = self.capability; - clone.nonce = self.nonce; - clone.ttl = self.ttl; - clone.authHeaders = self.authHeaders; - clone.clientId = self.clientId; - clone.queryTime = self.queryTime; - clone.useTokenAuth =self.useTokenAuth; - clone.tokenDetails = self.tokenDetails; - return clone; +- (ARTAuthTokenRequest *)sign:(NSString *)key { + //X7: NSArray + NSArray *keyComponents = decomposeKey(key); + NSString *keyName = keyComponents[0]; + NSString *keySecret = keyComponents[1]; + NSString *nonce = generateNonce(); + + NSString *signText = [NSString stringWithFormat:@"%@\n%lld\n%@\n%@\n%lld\n%@\n", keyName, (int64_t)(self.ttl * 1000), self.capability, self.clientId, (int64_t)(self.timestamp.timeIntervalSince1970 * 1000), nonce]; + NSString *mac = hmacForDataAndKey([signText dataUsingEncoding:NSUTF8StringEncoding], [keySecret dataUsingEncoding:NSUTF8StringEncoding]); + + return [[ARTAuthTokenRequest alloc] initWithTokenParams:self keyName:keyName nonce:nonce mac:mac]; } @end -@implementation ARTAuthOptions (Private) +@implementation ARTAuthTokenRequest --(void) setKeySecretTo:(NSString *)keySecret { - _keySecret = keySecret; -} +@dynamic timestamp; -@end - -@implementation ARTAuthTokenCancellable - -- (instancetype)initWithCb:(id(^)(ARTTokenDetails *token))cb { - self = [super init]; - if (self) { - _cb = cb; +- (instancetype)initWithTokenParams:(ARTAuthTokenParams *)tokenParams keyName:(NSString *)keyName nonce:(NSString *)nonce mac:(NSString *)mac { + if (self = [super init]) { + self.ttl = tokenParams.ttl; + self.capability = tokenParams.capability; + self.clientId = tokenParams.clientId; + self.timestamp = tokenParams.timestamp; + _keyName = [keyName copy]; + _nonce = [nonce copy]; + _mac = [mac copy]; } + return self; } -- (void)onAuthToken:(ARTTokenDetails *)token { - self.cancellable = self.cb(token); -} - -- (void)cancel { - self.isCancelled = YES; - self.cb = nil; - [self.cancellable cancel]; - self.cancellable = nil; +- (NSDictionary *)asDictionary { + return nil; } @end +@implementation ARTAuthOptions -@implementation ARTAuth (Private) - --(ARTAuthCb) getTheAuthCb { - return self.authTokenCb; -} - -@end -@implementation ARTAuth - -- (instancetype)initWithRest:(ARTRest *) rest options:(ARTAuthOptions *) options { - self = [super init]; - if(self) { - _rest = rest; - _token = nil; - _basicCredentials = nil; - _authMethod = ARTAuthMethodBasic; - _tokenCbs = nil; - _tokenRequest = nil; - _options = options; - self.logger = rest.logger; - - if (options.keyName != nil) { - [self.logger debug:@"ARTAuth: setting up auth method Basic"]; - _basicCredentials = [NSString stringWithFormat:@"Basic %@", - [[[NSString stringWithFormat:@"%@:%@", options.keyName, options.keySecret] dataUsingEncoding:NSUTF8StringEncoding] base64EncodedStringWithOptions:0]]; - [ARTAuth checkValidKey:[NSString stringWithFormat:@"%@:%@", options.keyName, options.keySecret]]; - _keyName = options.keyName; - _keySecret = options.keySecret; - } - else if(options.token == nil && options.tokenParams == nil) { - [NSException raise:@"Either a token, token param, or a keyName and secret are required to connect to Ably" format:nil]; +- (instancetype)initWithKey:(NSString *)key { + self = [self init]; + if (self) { + if (decomposeKey(key).count != 2) { + [NSException raise:@"Invalid key" format:@"%@ should be of the form :", key]; } - - [self prepConnection]; + _key = [key copy]; } return self; } -+ (NSArray *)checkValidKey:(NSString *) key { - NSArray *keyBits = [key componentsSeparatedByString:@":"]; - if(keyBits.count !=2) { - [NSException raise:@"Invalid key" format:@"%@ should be of the form :", key]; - } - return keyBits; +- (id)copyWithZone:(NSZone *)zone { + ARTAuthOptions *options = [[ARTAuthOptions allocWithZone:zone] init]; + options.key = self.key; + options.token = self.token; + options.useTokenAuth = self.useTokenAuth; + options.authCallback = self.authCallback; + options.authUrl = self.authUrl; + options.authMethod = self.authMethod; + options.authHeaders = self.authHeaders; + options.authParams = self.authParams; + options.queryTime = self.queryTime; + + return options; } --(bool) canRequestToken { - if(self.options.keyName && self.options.keySecret) { - [self.logger verbose:@"ARTAuth can request token via key"]; - return true; - } - if(self.options.authCallback) { - [self.logger verbose:@"ARTAuth can request token via authCb"]; - return true; - } - if(self.options.authUrl) { - [self.logger verbose:@"ARTAuth can request token via authURL"]; - return true; - } - if(self.options.tokenParams) { - [self.logger verbose:@"ARTAuth can request token via tokenParams"]; - return true; - } - [self.logger verbose:@"ARTAuth cannot request token"]; - return false; +- (NSString *)token { + return self.tokenDetails.token; } - -- (ARTAuthTokenParams *) getTokenParams { - //TODO what if tokenParams is nil - - - return self.options.tokenParams ? self.options.tokenParams: [[ ARTAuthTokenParams alloc] initWithId:self.options.keyName - ttl:self.options.ttl - capability:self.options.capability - clientId:self.options.clientId - timestamp:0 - nonce:self.options.nonce - mac:nil]; +- (void)setToken:(NSString *)token { + self.tokenDetails = [[ARTAuthTokenDetails alloc] initWithToken:token]; } --(void) prepConnection { - if(![self shouldUseTokenAuth:self.options]) { - return; +- (void)setAuthMethod:(NSString *)authMethod { + if (authMethod == nil) { + authMethod = @"GET"; } - _authMethod = ARTAuthMethodToken; - [self.logger debug:@"ARTAuth: setting up auth method Token"]; - _tokenCbs = [NSMutableArray array]; - - if (self.options.token) { - [self.logger debug:@"ARTAuth:using provided authToken %@", self.options.token]; - - _token = [[ARTTokenDetails alloc] initWithId:self.options.token - expires:self.options.tokenDetails.expires - issued:self.options.tokenDetails.issued - capability:self.options.capability - clientId:self.options.clientId]; - } - if (self.options.authCallback) { - [self.logger debug:@"ARTAuth: using provided authCallback"]; - _authTokenCb = self.options.authCallback; - return; - } - - [self.logger debug:@"ARTAuth: signed token request."]; - ARTSignedTokenRequestCb strCb = (self.options.signedTokenRequestCallback ? self.options.signedTokenRequestCallback : [ARTAuth defaultSignedTokenRequestCallback:self.options forRest:self.rest withLogger:self.logger]); - - __weak ARTAuth * weakSelf = self; - _authTokenCb = ^(void(^authCb)(ARTStatus *,ARTTokenDetails *)) { - ARTIndirectCancellable *ic = [[ARTIndirectCancellable alloc] init]; - ARTAuth * s = weakSelf; - ARTAuthTokenParams * params = nil; - if(s) { - params =[s getTokenParams]; - s.options.tokenParams = params; - } - id c = strCb(params,^( ARTAuthTokenParams *params) { - [weakSelf.logger debug:@"ARTAuth tokenRequest strCb got %@", [params asDictionary]]; - ARTAuth * s = weakSelf; - if(s) { - [s.rest token:params tokenCb:^(ARTStatus * status, ARTTokenDetails * tokenDetails) { - ARTAuth * s = weakSelf; - if(s) { - //TOOD set one of these and delete the others. - s.token = tokenDetails; - s.options.token = tokenDetails.token; - s.options.tokenDetails = tokenDetails; - } - else { - [weakSelf.logger error:@"ARTAuth became nil during token request. Can't assign token"]; - } - authCb(status, tokenDetails); - }]; - } - else { - [weakSelf.logger error:@"ARTAuth has no ARTRest to use to request a token"]; - } - }); - ic.cancellable = c; - return ic; - }; -} - --(void) attemptTokenFetch:(void (^)()) cb { - self.token = nil; - - if(self.authTokenCb) { - self.authTokenCb(^(ARTStatus * status, ARTTokenDetails * details){ - cb(); - }); - } - else { - cb(); - } + _authMethod = [authMethod copy]; } --(bool) shouldUseTokenAuth:(ARTAuthOptions *) options { - return options.useTokenAuth || - options.clientId || - options.token || - options.authUrl || - options.authCallback || - options.tokenParams; -} - - --(ARTAuthOptions *) getAuthOptions { - return self.options; -} - -- (ARTAuthMethod) getAuthMethod { - return self.authMethod; -} +@end -- (id)authHeadersUseBasic:(BOOL)useBasic cb:(id(^)(NSDictionary *))cb { - if(useBasic || self.authMethod == ARTAuthMethodBasic) { - [self.logger verbose:@"using auth basic"]; - return cb(@{@"Authorization": self.basicCredentials}); - } - else if(self.authMethod == ARTAuthMethodToken) { - [self.logger verbose:@"using auth token"]; - return [self requestToken:^(ARTTokenDetails *token) { - [self.logger verbose:@"retrieved token via request token"]; - return cb(@{@"Authorization": [NSString stringWithFormat:@"Bearer %@", token.token]}); - }]; - } - else { - NSAssert(NO, @"Invalid auth method"); - return nil; - } +@implementation ARTAuth { + __weak ARTRest *_rest; } -- (id)authParams:(id(^)(NSDictionary *))cb { - switch (self.authMethod) { - case ARTAuthMethodBasic: - return cb(@{@"key_id":self.keyName, @"key_value":self.keySecret}); - case ARTAuthMethodToken: - return [self requestToken:^(ARTTokenDetails *token) { - return cb(@{@"access_token:": token.token}); - }]; - default: - NSAssert(NO, @"Invalid auth method"); - return nil; +- (instancetype)initWithRest:(ARTRest *)rest options:(ARTAuthOptions *)options { + if (self = [super init]) { + _rest = rest; + _currentToken = options.tokenDetails; + _options = options; + _logger = rest.logger; + + if (options.key != nil && !options.useTokenAuth) { + [self.logger debug:@"ARTAuth: setting up auth method Basic"]; + _authMethod = ARTAuthMethodBasic; + } else if ([self shouldUseTokenAuth]) { + [self.logger debug:@"ARTAuth: setting up auth method Token"]; + _authMethod = ARTAuthMethodToken; + } else { + [NSException raise:@"ARTAuthException" format:@"Could not setup authentication method with given options."]; + } } + + return self; } -- (id)requestToken:(id(^)(ARTTokenDetails *))cb { - return [self authTokenForceReauth:NO cb:cb]; +- (BOOL)shouldUseTokenAuth { + return NO; } -- (id)authTokenForceReauth:(BOOL)force cb:(id(^)(ARTTokenDetails *))cb { - [self.logger verbose:@"ARTAuth authTokenForceReauth"]; - if (self.token) { - if (0 == self.token.expires || self.token.expires > [[NSDate date] timeIntervalSince1970] * 1000) { - if (!force) { - [self.logger verbose:@"ARTAuth has a valid token to use"]; - return cb(self.token); - } - else { - [self.logger debug:@"ARTAuth forcing new token request"]; - } +- (void)requestToken:(ARTAuthTokenParams *)tokenParams options:(ARTAuthOptions *)options + callback:(void (^)(ARTAuthTokenDetails *, NSError *))callback { + ARTAuthOptions *mergedOptions = options; + + if (mergedOptions.authUrl) { + NSURLComponents *urlComponents = [NSURLComponents componentsWithURL:mergedOptions.authUrl resolvingAgainstBaseURL:YES]; + if (mergedOptions.authParams) { + urlComponents.queryItems = [[NSArray arrayWithArray:urlComponents.queryItems] arrayByAddingObjectsFromArray:mergedOptions.authParams]; } - else { - [self.logger debug:@"ARTAuth token expired %f milliseconds ago. Expiry: %lld", ([[NSDate date] timeIntervalSince1970]*1000)- self.token.expires, self.token.expires]; + + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:urlComponents.URL]; + request.HTTPMethod = mergedOptions.authMethod; + for (NSString *key in mergedOptions.authHeaders) { + [request setValue:mergedOptions.authHeaders[key] forHTTPHeaderField:key]; } - self.token = nil; - } - else { - [self.logger debug:@"ARTAuth has no token. Requesting one now"]; - } - - ARTAuthTokenCancellable *c = [[ARTAuthTokenCancellable alloc] initWithCb:cb]; - [self.tokenCbs addObject:c]; - if (!self.tokenRequest) { - self.tokenRequest = self.authTokenCb(^(ARTStatus * status, ARTTokenDetails *token) { - if(status.state != ARTStateOk) { - [self.logger error:@"ARTAuth: error fetching token"]; - cb(nil); - return; + [request setValue:@"application/json" forHTTPHeaderField:@"Accept"]; + + [_rest.httpExecutor executeRequest:request callback:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { + if (error) { + callback(nil, error); + } else { + // check if response is TokenRequest or TokenDetails and act accordingly } - self.tokenRequest = nil; - NSMutableArray *cbs = self.tokenCbs; - self.tokenCbs = [NSMutableArray array]; - for (ARTAuthTokenCancellable *c in cbs) { - [c onAuthToken:token]; + }]; + } else { + ARTAuthCallback tokenRequestFactory = mergedOptions.authCallback ?: ^(ARTAuthTokenParams *tokenParams, void(^callback)(ARTAuthTokenRequest *tokenRequest, NSError *error)) { + [self createTokenRequest:tokenParams options:mergedOptions callback:callback]; + }; + + tokenRequestFactory(tokenParams, ^(ARTAuthTokenRequest *tokenRequest, NSError *error) { + if (error) { + callback(nil, error); + } else { + [self requestToken:tokenRequest callback:callback]; } }); } - return c; } -+ (NSString *)random { - // Generate two random numbers up to 8 digits long and concatenate them to produce a 16 digit random number - NSUInteger r1 = arc4random_uniform(100000000); - NSUInteger r2 = arc4random_uniform(100000000); - return [NSString stringWithFormat:@"%08lu%08lu", (long)r1, (long)r2]; -} +- (void)requestToken:(ARTAuthTokenRequest *)tokenRequest callback:(void (^)(ARTAuthTokenDetails *, NSError *))callback { + NSURL *requestUrl = [NSURL URLWithString:[NSString stringWithFormat:@"/keys/%@/requestToken", tokenRequest.keyName] + relativeToURL:_rest.baseUrl]; + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:requestUrl]; + request.HTTPMethod = @"POST"; + + id defaultEncoder = _rest.defaultEncoder; -+ (ARTSignedTokenRequestCb)defaultSignedTokenRequestCallback:(ARTAuthOptions *)authOptions forRest:(ARTRest *)rest withLogger:(ARTLog *)logger { - __weak ARTRest *weakRest = rest; - [logger verbose:@"ARTAUTH creating signed token request callback"]; + request.HTTPBody = [defaultEncoder encodeTokenRequest:tokenRequest]; + [request setValue:[defaultEncoder mimeType] forHTTPHeaderField:@"Accept"]; + [request setValue:[defaultEncoder mimeType] forHTTPHeaderField:@"Content-Type"]; - return ^id(ARTAuthTokenParams *params, void(^cb)(ARTAuthTokenParams *)) { - [logger verbose:@"ARTAUTH signed token request callback called"]; - - NSString *keySecret = authOptions.keySecret; - BOOL queryTime = authOptions.queryTime; - - if (authOptions.keyName != nil && params.keyName && ![params.keyName isEqualToString:authOptions.keyName]) { - [NSException raise:@"ARTAuthParams keyName is not equal to ARTAuthOptions keyName" format:@"'%@' != '%@'", params.keyName, authOptions.keyName]; + [_rest.httpExecutor executeRequest:request callback:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { + if (error) { + callback(nil, error); + } else { + callback([defaultEncoder decodeAccessToken:data], nil); } + }]; +} - int64_t ttl = params.ttl ? params.ttl : 3600000; - NSString * ttlText = [NSString stringWithFormat:@"%lld", ttl]; - NSString * keyName = params.keyName; - NSString * nonce = params.nonce ? params.nonce : [ARTAuth random]; - NSString * capability = params.capability ? params.capability : @""; - NSString * clientId = params.clientId ? params.clientId : @""; - void (^timeCb)(void(^)(int64_t)) = nil; - if (!params.timestamp) { - if (queryTime) { - [logger debug:@"ARTAuth: query time is being used"]; - timeCb = ^(void(^cb)(int64_t)) { - ARTRest *strongRest = weakRest; - if (strongRest) { - [strongRest time:^(ARTStatus * status, NSDate *time) { - if (status.state == ARTStateOk) { - cb((int64_t)([time timeIntervalSince1970] *1000.0)); - } else { - cb(0); - } - }]; - } else { - cb(0); - } - }; +- (void)authorise:(ARTAuthTokenParams *)tokenParams options:(ARTAuthOptions *)options force:(BOOL)force + callback:(void (^)(ARTAuthTokenDetails *, NSError *))callback { + if (!force && self.currentToken && [self.currentToken.expires timeIntervalSinceNow] > 0) { + [self.logger verbose:@"ARTAuth authorise not forced and current token is not expired yet, reuse current token."]; + callback(self.currentToken, nil); + } else { + [self.logger verbose:@"ARTAuth authorise requesting new token."]; + [self requestToken:tokenParams options:options callback:^(ARTAuthTokenDetails *tokenDetails, NSError *error) { + if (error) { + callback(nil, error); } else { - timeCb = ^(void(^cb)(int64_t)) { - [logger debug:@"ARTAuth: client time is being used"]; - cb((int64_t)([[NSDate date] timeIntervalSince1970] *1000.0 )); - }; + _currentToken = tokenDetails; + _authMethod = ARTAuthMethodToken; + callback(tokenDetails, nil); } - } else { - timeCb = ^(void(^cb)(int64_t) ) { - cb(params.timestamp); - }; - } + }]; + } +} - ARTIndirectCancellable *ic = [[ARTIndirectCancellable alloc] init]; - timeCb(^(int64_t timestamp) { - if ([ic isCancelled]) { - return; +- (void)createTokenRequest:(ARTAuthTokenParams *)tokenParams options:(ARTAuthOptions *)options callback:(void (^)(ARTAuthTokenRequest *, NSError *))callback { + ARTAuthOptions *mergedOptions = options; + if (mergedOptions.queryTime) { + ARTAuthTokenParams *newParams = [[ARTAuthTokenParams alloc] init]; + newParams.ttl = tokenParams.ttl; + newParams.capability = tokenParams.capability; + newParams.clientId = tokenParams.clientId; + [_rest time:^(NSDate *time, NSError *error) { + if (error) { + callback(nil, error); + } else { + newParams.timestamp = time; + callback([newParams sign:mergedOptions.key], nil); } - - NSString *signText = [NSString stringWithFormat:@"%@\n%@\n%@\n%@\n%lld\n%@\n", keyName, ttlText, capability, clientId, timestamp, nonce]; - NSString * mac =params.mac ? params.mac : [ARTAuth hmacForData:[signText dataUsingEncoding:NSUTF8StringEncoding] key:[keySecret dataUsingEncoding:NSUTF8StringEncoding]]; - - ARTAuthTokenParams * p = [[ARTAuthTokenParams alloc] initWithId:keyName ttl:ttl capability:capability clientId:clientId timestamp:timestamp nonce:nonce mac:mac]; - cb(p); - }); - return ic; - }; + }]; + } else { + callback([tokenParams sign:mergedOptions.key], nil); + } } -+ (NSString *)hmacForData:(NSData *)data key:(NSData *)key { - const void *cKey = [key bytes]; - const void *cData = [data bytes]; - size_t keyLen = [key length]; - size_t dataLen = [data length]; - - unsigned char hmac[CC_SHA256_DIGEST_LENGTH]; - - CCHmac(kCCHmacAlgSHA256, cKey, keyLen, cData, dataLen, hmac); - NSData *mac = [[NSData alloc] initWithBytes:hmac length:sizeof(hmac)]; - NSString * str = [ARTBase64PayloadEncoder toBase64:mac]; - return str; +- (BOOL)canRequestToken { + if (self.options.authCallback) { + [self.logger verbose:@"ARTAuth can request token via authCb"]; + return YES; + } else if (self.options.authUrl) { + [self.logger verbose:@"ARTAuth can request token via authURL"]; + return YES; + } else if (self.options.key) { + [self.logger verbose:@"ARTAuth can request token via key"]; + return YES; + } else { + [self.logger error:@"ARTAuth cannot request token"]; + return NO; + } } -@end \ No newline at end of file +@end diff --git a/ably-ios/ARTBaseMessage.h b/ably-ios/ARTBaseMessage.h new file mode 100644 index 000000000..21254db36 --- /dev/null +++ b/ably-ios/ARTBaseMessage.h @@ -0,0 +1,39 @@ +// +// ARTBaseMessage.h +// ably-ios +// +// Created by Jason Choy on 08/12/2014. +// Copyright (c) 2014 Ably. All rights reserved. +// + +#import +#import + +@interface ARTBaseMessage : NSObject + +/// A unique id for this message +@property (strong, nonatomic) NSString *id; + +/// The timestamp for this message +@property (strong, nonatomic) NSDate *timestamp; + +/// The id of the publisher of this message +@property (strong, nonatomic) NSString *clientId; + +/// The connection id of the publisher of this message +@property (strong, nonatomic) NSString *connectionId; + +/// Any transformation applied to the data for this message +@property (strong, nonatomic) NSString *encoding; + +/// The message payload. +@property (strong, nonatomic) ARTPayload *payload; + +- (instancetype)decode:(id)encoder; +- (instancetype)encode:(id)encoder; + +- (id)content; + ++ (NSArray *)messagesWithPayloads:(NSArray *)payloads; + +@end diff --git a/ably-ios/ARTBaseMessage.m b/ably-ios/ARTBaseMessage.m new file mode 100644 index 000000000..f1dffd94c --- /dev/null +++ b/ably-ios/ARTBaseMessage.m @@ -0,0 +1,71 @@ +// +// ARTBaseMessage.m +// ably-ios +// +// Created by Jason Choy on 08/12/2014. +// Copyright (c) 2014 Ably. All rights reserved. +// + +#import "ARTBaseMessage.h" +#import "ARTLog.h" + +@implementation ARTBaseMessage + +- (void)setClientId:(NSString *)clientId { + if(clientId) { + const char* c = [clientId UTF8String]; + _clientId = [NSString stringWithUTF8String:c]; + } + else { + _clientId = nil; + } +} + +- (id)copyWithZone:(NSZone *)zone { + ARTBaseMessage *message = [[self.class allocWithZone:zone] init]; + message->_id = self.id; + message->_clientId = self.clientId; + message->_timestamp = self.timestamp; + message->_payload = self.payload; + message->_connectionId = self.connectionId; + message->_encoding = self.encoding; + return message; +} + +- (instancetype)messageWithPayload:(ARTPayload *)payload { + ARTBaseMessage *message = [self copy]; + message.payload = payload; + return message; +} + +- (instancetype)decode:(id)encoder { + ARTPayload *payload = self.payload; + // FIXME: + [encoder decode:payload output:&payload]; + return [self messageWithPayload:payload]; +} + +- (instancetype)encode:(id)encoder { + ARTPayload *payload = self.payload; + // FIXME: + [encoder encode:payload output:&payload]; + return [self messageWithPayload:payload]; +} + +- (id)content { + return self.payload.payload; +} + ++ (NSArray *)messagesWithPayloads:(NSArray *)payloads { + if([payloads count] > [ARTPayload payloadArraySizeLimit]) { + [NSException raise:@"Too many items in payload array" format:@"%lu > %lu", (unsigned long)[payloads count], [ARTPayload payloadArraySizeLimit]]; + } + NSMutableArray * messages =[[NSMutableArray alloc] init]; + for(int i=0; i < [payloads count]; i++) { + // FIXME + //[messages addObject:[ARTMessage messageWithPayload:[payloads objectAtIndex:i] name:nil]]; + } + return messages; +} + +@end diff --git a/ably-ios/ARTChannels+Private.h b/ably-ios/ARTChannel+Private.h similarity index 66% rename from ably-ios/ARTChannels+Private.h rename to ably-ios/ARTChannel+Private.h index 90a3a99b7..de66a3df5 100644 --- a/ably-ios/ARTChannels+Private.h +++ b/ably-ios/ARTChannel+Private.h @@ -1,12 +1,14 @@ // -// ARTChannels+Private.h +// ARTChannel+Private.h // ably // // Created by Yavor Georgiev on 20.08.15. // Copyright (c) 2015 г. Ably. All rights reserved. // -#import +#import + +@protocol ARTPayloadEncoder; NS_ASSUME_NONNULL_BEGIN @@ -24,13 +26,4 @@ NS_ASSUME_NONNULL_BEGIN @end -@interface ARTChannelCollection() { -@protected - NSMutableDictionary /* */ *_channels; -} - -- (ARTChannel *)_createChannelWithName:(NSString *)name options:(nullable ARTChannelOptions *)options; - -@end - NS_ASSUME_NONNULL_END diff --git a/ably-ios/ARTChannels.h b/ably-ios/ARTChannel.h similarity index 56% rename from ably-ios/ARTChannels.h rename to ably-ios/ARTChannel.h index 748a92de0..eec05117d 100644 --- a/ably-ios/ARTChannels.h +++ b/ably-ios/ARTChannel.h @@ -1,5 +1,5 @@ // -// ARTChannels.h +// ARTChannel.h // ably // // Created by Yavor Georgiev on 20.08.15. @@ -7,27 +7,15 @@ // #import -#import -#import -#import -#import -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface ARTChannelOptions : NSObject - -@property (nonatomic, assign) BOOL isEncrypted; -@property (nonatomic, strong, nullable) ARTCipherParams *cipherParams; - -+ (instancetype)unencrypted; - -- (instancetype)initEncrypted:(ARTCipherParams *)cipherParams; - -@end +#import "ably.h" +@class ARTChannelOptions; @class ARTPresence; +@class ARTMessage; +@class ARTPaginatedResult; +@class ARTDataQuery; + +NS_ASSUME_NONNULL_BEGIN @interface ARTChannel : NSObject @@ -46,16 +34,4 @@ NS_ASSUME_NONNULL_BEGIN @end -@interface ARTChannelCollection : NSObject - -- (BOOL)exists:(NSString *)channelName; - -- (ARTChannel *)get:(NSString *)channelName; - -- (ARTChannel *)get:(NSString *)channelName options:(ARTChannelOptions *)options; - -- (void)releaseChannel:(ARTChannel *)channel; - -@end - NS_ASSUME_NONNULL_END diff --git a/ably-ios/ARTChannel.m b/ably-ios/ARTChannel.m new file mode 100644 index 000000000..ff8ae6b8a --- /dev/null +++ b/ably-ios/ARTChannel.m @@ -0,0 +1,67 @@ +// +// ARTChannel.m +// ably +// +// Created by Yavor Georgiev on 20.08.15. +// Copyright (c) 2015 г. Ably. All rights reserved. +// + +#import "ARTChannel.h" +#import "ARTChannel+Private.h" + +#import "ARTNSArray+ARTFunctional.h" +#import "ARTPayload.h" +#import "ARTMessage.h" +#import "ARTChannelOptions.h" + +@implementation ARTChannel + +- (instancetype)initWithName:(NSString *)name presence:(ARTPresence *)presence options:(nullable ARTChannelOptions *)options { + if (self = [super init]) { + _name = [name copy]; + _presence = presence; + self.options = options; + } + + return self; +} + +- (void)setOptions:(ARTChannelOptions *)options { + if (!options) { + _options = [ARTChannelOptions unencrypted]; + } else { + _options = options; + } + + _payloadEncoder = [ARTJsonPayloadEncoder instance]; +} + +- (void)publish:(nullable id)payload callback:(ARTErrorCallback)callback { + [self publish:payload name:nil callback:callback]; +} + +- (void)publish:(nullable id)payload name:(NSString *)name callback:(ARTErrorCallback)callback { + [self publishMessage:[[ARTMessage alloc] initWithData:payload name:name] callback:callback]; +} + +- (void)publishMessages:(NSArray *)messages callback:(ARTErrorCallback)callback { + messages = [messages artMap:^(ARTMessage *message) { + return [message encode:_payloadEncoder]; + }]; + + [self _postMessages:messages callback:callback]; +} + +- (void)publishMessage:(ARTMessage *)message callback:(ARTErrorCallback)callback { + [self _postMessages:[message encode:_payloadEncoder] callback:callback]; +} + +- (void)history:(ARTDataQuery *)query callback:(void (^)(ARTPaginatedResult * __nullable, NSError *__nullable))callback { + NSAssert(false, @"-[%@ %@] should always be overriden.", self.class, NSStringFromSelector(_cmd)); +} + +- (void)_postMessages:(id)payload callback:(ARTErrorCallback)callback { + NSAssert(false, @"-[%@ %@] should always be overriden.", self.class, NSStringFromSelector(_cmd)); +} + +@end diff --git a/ably-ios/ARTChannelCollection+Private.h b/ably-ios/ARTChannelCollection+Private.h new file mode 100644 index 000000000..e6ab77d86 --- /dev/null +++ b/ably-ios/ARTChannelCollection+Private.h @@ -0,0 +1,27 @@ +// +// ARTChannelCollection+Private.h +// ably +// +// Created by Ricardo Pereira on 01/10/2015. +// Copyright (c) 2015 Ably. All rights reserved. +// + +#import + +@class ARTChannel; +@class ARTChannelOptions; + +NS_ASSUME_NONNULL_BEGIN + +@interface ARTChannelCollection() { +@protected + NSMutableDictionary /* */ *_channels; +} + +@property (nonatomic, readonly) NSMutableDictionary *channels; + +- (ARTChannel *)_createChannelWithName:(NSString *)name options:(nullable ARTChannelOptions *)options; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ably-ios/ARTChannelCollection.h b/ably-ios/ARTChannelCollection.h new file mode 100644 index 000000000..ff5dda92a --- /dev/null +++ b/ably-ios/ARTChannelCollection.h @@ -0,0 +1,21 @@ +// +// ARTChannelCollection.h +// ably +// +// Created by Ricardo Pereira on 01/10/2015. +// Copyright (c) 2015 Ably. All rights reserved. +// + +#import + +@class ARTChannel; +@class ARTChannelOptions; + +@interface ARTChannelCollection : NSObject + +- (BOOL)exists:(NSString *)channelName; +- (ARTChannel *)get:(NSString *)channelName; +- (ARTChannel *)get:(NSString *)channelName options:(ARTChannelOptions *)options; +- (void)releaseChannel:(ARTChannel *)channel; + +@end diff --git a/ably-ios/ARTChannelCollection.m b/ably-ios/ARTChannelCollection.m new file mode 100644 index 000000000..fc2eacd01 --- /dev/null +++ b/ably-ios/ARTChannelCollection.m @@ -0,0 +1,65 @@ +// +// ARTChannelCollection.m +// ably +// +// Created by Ricardo Pereira on 01/10/2015. +// Copyright (c) 2015 Ably. All rights reserved. +// + +#import "ARTChannelCollection.h" +#import "ARTChannelCollection+Private.h" + +#import "ARTChannel.h" +#import "ARTChannel+Private.h" +#import "ARTChannelOptions.h" + +@implementation ARTChannelCollection + +- (instancetype)init { + if (self = [super init]) { + _channels = [[NSMutableDictionary alloc] init]; + } + return self; +} + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(__unsafe_unretained id [])buffer count:(NSUInteger)len { + NSUInteger count = [self->_channels countByEnumeratingWithState:state objects:buffer count:len]; + for (NSUInteger i = 0; i < count; i++) { + buffer[i] = self->_channels[buffer[i]]; + } + return count; +} + +- (BOOL)exists:(NSString *)channelName { + return self->_channels[channelName] != nil; +} + +- (ARTChannel *)get:(NSString *)channelName { + return [self _getChannel:channelName options:nil]; +} + +- (ARTChannel *)get:(NSString *)channelName options:(ARTChannelOptions *)options { + return [self _getChannel:channelName options:options]; +} + +- (void)releaseChannel:(ARTChannel *)channel { + [self->_channels removeObjectForKey:channel.name]; +} + +- (ARTChannel *)_getChannel:(NSString *)channelName options:(nullable ARTChannelOptions *)options { + ARTChannel *channel = self->_channels[channelName]; + if (!channel) { + channel = [self _createChannelWithName:channelName options:options]; + [self->_channels setObject:channel forKey:channelName]; + } else if (options) { + channel.options = options; + } + return channel; +} + +- (ARTChannel *)_createChannelWithName:(NSString *)name options:(nullable ARTChannelOptions *)options { + NSAssert(false, @"-[%@ %@] should always be overriden.", self.class, NSStringFromSelector(_cmd)); + return 0; +} + +@end diff --git a/ably-ios/ARTChannelOptions.h b/ably-ios/ARTChannelOptions.h new file mode 100644 index 000000000..28aed5e3c --- /dev/null +++ b/ably-ios/ARTChannelOptions.h @@ -0,0 +1,26 @@ +// +// ARTChannelOptions.h +// ably +// +// Created by Ricardo Pereira on 01/10/2015. +// Copyright (c) 2015 Ably. All rights reserved. +// + +#import + +@class ARTCipherParams; + +NS_ASSUME_NONNULL_BEGIN + +@interface ARTChannelOptions : NSObject + +@property (nonatomic, assign) BOOL isEncrypted; +@property (nonatomic, strong, nullable) ARTCipherParams *cipherParams; + ++ (instancetype)unencrypted; + +- (instancetype)initEncrypted:(ARTCipherParams *)cipherParams; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ably-ios/ARTChannelOptions.m b/ably-ios/ARTChannelOptions.m new file mode 100644 index 000000000..233451dd0 --- /dev/null +++ b/ably-ios/ARTChannelOptions.m @@ -0,0 +1,34 @@ +// +// ARTChannelOptions.m +// ably +// +// Created by Ricardo Pereira on 01/10/2015. +// Copyright (c) 2015 Ably. All rights reserved. +// + +#import "ARTChannelOptions.h" + +#import "ARTEncoder.h" + +@implementation ARTChannelOptions + +- (instancetype)initEncrypted:(ARTCipherParams *)cipherParams { + if (self = [super init]) { + self->_isEncrypted = YES; + self->_cipherParams = cipherParams; + } + + return self; +} + ++ (instancetype)unencrypted { + static id unencrypted; + static dispatch_once_t once; + dispatch_once(&once, ^{ + unencrypted = [[ARTChannelOptions alloc] init]; + }); + + return unencrypted; +} + +@end diff --git a/ably-ios/ARTChannels.m b/ably-ios/ARTChannels.m deleted file mode 100644 index bb5695e5d..000000000 --- a/ably-ios/ARTChannels.m +++ /dev/null @@ -1,142 +0,0 @@ -// -// ARTChannels.m -// ably -// -// Created by Yavor Georgiev on 20.08.15. -// Copyright (c) 2015 г. Ably. All rights reserved. -// - -#import "ARTChannels.h" -#import "ARTChannels+Private.h" -#import "ARTNSArray+ARTFunctional.h" -#import "ARTEncoder.h" -#import "ARTLog.h" - -@implementation ARTChannelOptions - -- (instancetype)initEncrypted:(ARTCipherParams *)cipherParams { - if (self = [super init]) { - self->_isEncrypted = YES; - self->_cipherParams = cipherParams; - } - - return self; -} - -+ (instancetype)unencrypted { - static id unencrypted; - static dispatch_once_t once; - dispatch_once(&once, ^{ - unencrypted = [[ARTChannelOptions alloc] init]; - }); - - return unencrypted; -} - -@end - -@implementation ARTChannel - -- (instancetype)initWithName:(NSString *)name presence:(ARTPresence *)presence options:(nullable ARTChannelOptions *)options { - if (self = [super init]) { - _name = [name copy]; - _presence = presence; - self.options = options; - } - - return self; -} - -- (void)setOptions:(ARTChannelOptions *)options { - if (!options) { - _options = [ARTChannelOptions unencrypted]; - } else { - _options = options; - } - - _payloadEncoder = [ARTJsonPayloadEncoder instance]; -} - -- (void)publish:(nullable id)payload callback:(ARTErrorCallback)callback { - [self publish:payload name:nil callback:callback]; -} - -- (void)publish:(nullable id)payload name:(NSString *)name callback:(ARTErrorCallback)callback { - [self publishMessage:[[ARTMessage alloc] initWithData:payload name:name] callback:callback]; -} - -- (void)publishMessages:(NSArray *)messages callback:(ARTErrorCallback)callback { - messages = [messages artMap:^(ARTMessage *message) { - return [message encode:_payloadEncoder]; - }]; - - [self _postMessages:messages callback:callback]; -} - -- (void)publishMessage:(ARTMessage *)message callback:(ARTErrorCallback)callback { - [self _postMessages:[message encode:_payloadEncoder] callback:callback]; -} - -- (void)history:(ARTDataQuery *)query callback:(void (^)(ARTPaginatedResult * __nullable, NSError *__nullable))callback { - NSAssert(false, @"-[%@ %@] should always be overriden.", self.class, NSStringFromSelector(_cmd)); -} - -- (void)_postMessages:(id)payload callback:(ARTErrorCallback)callback { - NSAssert(false, @"-[%@ %@] should always be overriden.", self.class, NSStringFromSelector(_cmd)); -} - -@end - -@implementation ARTChannelCollection - -- (instancetype)init { - if (self = [super init]) { - _channels = [[NSMutableDictionary alloc] init]; - } - - return self; -} - -- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(__unsafe_unretained id [])buffer count:(NSUInteger)len { - NSUInteger count = [self->_channels countByEnumeratingWithState:state objects:buffer count:len]; - for (NSUInteger i = 0; i < count; i++) { - buffer[i] = self->_channels[buffer[i]]; - } - - return count; -} - -- (BOOL)exists:(NSString *)channelName { - return self->_channels[channelName] != nil; -} - -- (ARTChannel *)get:(NSString *)channelName { - return [self _getChannel:channelName options:nil]; -} - -- (ARTChannel *)get:(NSString *)channelName options:(ARTChannelOptions *)options { - return [self _getChannel:channelName options:options]; -} - -- (void)releaseChannel:(ARTChannel *)channel { - [self->_channels removeObjectForKey:channel.name]; -} - -- (ARTChannel *)_getChannel:(NSString *)channelName options:(nullable ARTChannelOptions *)options { - ARTChannel *channel = self->_channels[channelName]; - if (!channel) { - channel = [self _createChannelWithName:channelName options:options]; - [self->_channels setObject:channel forKey:channelName]; - } else if (options) { - channel.options = options; - } - - return channel; -} - -- (ARTChannel *)_createChannelWithName:(NSString *)name options:(nullable ARTChannelOptions *)options { - NSAssert(false, @"-[%@ %@] should always be overriden.", self.class, NSStringFromSelector(_cmd)); - return 0; -} - -@end diff --git a/ably-ios/ARTClientOptions+Private.h b/ably-ios/ARTClientOptions+Private.h deleted file mode 100644 index 31da3bffe..000000000 --- a/ably-ios/ARTClientOptions+Private.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// ARTClientOptions+Private.h -// ably -// -// Created by vic on 12/05/2015. -// Copyright (c) 2015 Ably. All rights reserved. -// - -@interface ARTClientOptions (Private) { - -} - -@end \ No newline at end of file diff --git a/ably-ios/ARTClientOptions.h b/ably-ios/ARTClientOptions.h index 28881124a..df0462688 100644 --- a/ably-ios/ARTClientOptions.h +++ b/ably-ios/ARTClientOptions.h @@ -10,7 +10,7 @@ #import "ARTAuth.h" -@interface ARTClientOptions : NSObject +@interface ARTClientOptions : NSObject @property (readwrite, strong, nonatomic) ARTAuthOptions *authOptions; @property (readwrite, strong, nonatomic) NSString *clientId; @@ -36,8 +36,6 @@ - (bool)isFallbackPermitted; -+ (instancetype)options; -+ (instancetype)optionsWithKey:(NSString *)key; -+ (NSURL*)restUrl:(NSString *)host port:(int)port; ++ (NSURL*)restUrl:(NSString *)host port:(int)port tls:(BOOL)tls; @end diff --git a/ably-ios/ARTClientOptions.m b/ably-ios/ARTClientOptions.m index e8941e0e4..c6abef3b0 100644 --- a/ably-ios/ARTClientOptions.m +++ b/ably-ios/ARTClientOptions.m @@ -7,7 +7,6 @@ // #import "ARTClientOptions.h" -#import "ARTClientOptions+Private.h" #import "ARTDefault.h" @interface ARTClientOptions () @@ -21,7 +20,7 @@ @implementation ARTClientOptions - (instancetype)init { self = [super init]; if (self) { - _authOptions = [ARTAuthOptions options]; + _authOptions = [[ARTAuthOptions alloc] init]; if (!_authOptions) { self = nil; } @@ -33,7 +32,7 @@ - (instancetype)init { - (instancetype)initWithKey:(NSString *)key { self = [super init]; if (self) { - _authOptions = [ARTAuthOptions optionsWithKey:key]; + _authOptions = [[ARTAuthOptions alloc] initWithKey:key]; if (!_authOptions) { self = nil; @@ -75,16 +74,16 @@ + (instancetype)optionsWithKey:(NSString *)key { return [[ARTClientOptions alloc] initWithKey:key]; } -+ (NSURL*)restUrl:(NSString *)host port:(int)port { ++ (NSURL*)restUrl:(NSString *)host port:(int)port tls:(BOOL)tls { NSURLComponents *components = [[NSURLComponents alloc] init]; - components.scheme = self.tls ? @"https" : @"http"; + components.scheme = tls ? @"https" : @"http"; components.host = host; - components.port = port; + components.port = [NSNumber numberWithInt:port]; return components.URL; } - (NSURL *)restUrl { - return [ARTClientOptions restUrl:self.restHost port:self.restPort]; + return [ARTClientOptions restUrl:self.restHost port:self.restPort tls:self.tls]; } - (bool)isFallbackPermitted { @@ -92,4 +91,28 @@ - (bool)isFallbackPermitted { return [self.restHost isEqualToString:[ARTDefault restHost]]; } +- (id)copyWithZone:(NSZone *)zone { + ARTClientOptions *options = [[ARTClientOptions allocWithZone:zone] init]; + + options.authOptions = [self.authOptions copy]; + if (!options.authOptions) { + return nil; + } + + options.clientId = self.clientId; + options.restPort = self.restPort; + options.realtimePort = self.realtimePort; + options.queueMessages = self.queueMessages; + options.echoMessages = self.echoMessages; + options.recover = self.recover; + options.binary = self.binary; + options.autoConnect = self.autoConnect; + options.connectionSerial = self.connectionSerial; + options.resumeKey = self.resumeKey; + options.environment = self.environment; + options.tls = self.tls; + + return options; +} + @end diff --git a/ably-ios/ARTCrypto.h b/ably-ios/ARTCrypto.h index d0b895133..0c9d81ae2 100644 --- a/ably-ios/ARTCrypto.h +++ b/ably-ios/ARTCrypto.h @@ -7,6 +7,7 @@ // #import +#import "ably.h" #import "ARTStatus.h" diff --git a/ably-ios/ARTCrypto.m b/ably-ios/ARTCrypto.m index 1b1c9db6e..b316ec604 100644 --- a/ably-ios/ARTCrypto.m +++ b/ably-ios/ARTCrypto.m @@ -10,8 +10,6 @@ #import -#import "ARTLog.h" - @interface ARTCipherParams () - (BOOL)ccAlgorithm:(CCAlgorithm *)algorithm; diff --git a/ably-ios/ARTDataQuery+Private.h b/ably-ios/ARTDataQuery+Private.h index 1c9243443..041636390 100755 --- a/ably-ios/ARTDataQuery+Private.h +++ b/ably-ios/ARTDataQuery+Private.h @@ -16,4 +16,4 @@ NS_ASSUME_NONNULL_BEGIN @end -NS_ASSUME_NONNULL_END \ No newline at end of file +NS_ASSUME_NONNULL_END diff --git a/ably-ios/ARTDataQuery.m b/ably-ios/ARTDataQuery.m index 56de2c678..010aaf608 100755 --- a/ably-ios/ARTDataQuery.m +++ b/ably-ios/ARTDataQuery.m @@ -46,4 +46,4 @@ - (NSMutableArray *)asQueryItems { return items; } -@end \ No newline at end of file +@end diff --git a/ably-ios/ARTDefault.h b/ably-ios/ARTDefault.h index 8d1f2e269..27c1365ac 100644 --- a/ably-ios/ARTDefault.h +++ b/ably-ios/ARTDefault.h @@ -8,9 +8,7 @@ #import -@interface ARTDefault : NSObject { - -} +@interface ARTDefault : NSObject + (NSArray*)fallbackHosts; + (NSString*)restHost; diff --git a/ably-ios/ARTEncoder.h b/ably-ios/ARTEncoder.h index 177122187..86999d061 100644 --- a/ably-ios/ARTEncoder.h +++ b/ably-ios/ARTEncoder.h @@ -11,14 +11,16 @@ @class ARTMessage; @class ARTPresenceMessage; @class ARTProtocolMessage; -@class ARTTokenDetails; -@class ARTHttpError; -@class ARTErrorInfo; +@class ARTAuthTokenDetails; +@class ARTAuthTokenRequest; + @protocol ARTEncoder - (NSString *)mimeType; -- (ARTTokenDetails *) decodeAccessToken:(NSData *) data; +- (NSData *)encodeTokenRequest:(ARTAuthTokenRequest *)request; + +- (ARTAuthTokenDetails *)decodeAccessToken:(NSData *)data; - (ARTMessage *)decodeMessage:(NSData *)data; - (NSArray *)decodeMessages:(NSData *)data; - (NSData *)encodeMessage:(ARTMessage *)message; diff --git a/ably-ios/ARTEventEmitter.h b/ably-ios/ARTEventEmitter.h new file mode 100644 index 000000000..9f4c2e7ef --- /dev/null +++ b/ably-ios/ARTEventEmitter.h @@ -0,0 +1,21 @@ +// +// ARTEventEmitter.h +// ably +// +// Created by Ricardo Pereira on 30/09/2015. +// Copyright (c) 2015 Ably. All rights reserved. +// + +#import +#import "ably.h" + +@protocol ARTSubscription; + +@class ARTRealtime; + +@interface ARTEventEmitter : NSObject + +- (instancetype)initWithRealtime:(ARTRealtime *)realtime; +- (id)on:(ARTRealtimeConnectionStateCb)cb; + +@end diff --git a/ably-ios/ARTEventEmitter.m b/ably-ios/ARTEventEmitter.m new file mode 100644 index 000000000..1c7a70af6 --- /dev/null +++ b/ably-ios/ARTEventEmitter.m @@ -0,0 +1,37 @@ +// +// ARTEventEmitter.m +// ably +// +// Created by Ricardo Pereira on 30/09/2015. +// Copyright (c) 2015 Ably. All rights reserved. +// + +#import "ARTEventEmitter.h" + +#import "ARTRealtimeChannelSubscription.h" + +@interface ARTEventEmitter () + +@property (readonly, weak, nonatomic) ARTRealtime * realtime; + +@end + +@implementation ARTEventEmitter + +- (instancetype)initWithRealtime:(ARTRealtime *)realtime { + self = [super init]; + if(self) { + _realtime = realtime; + } + return self; +} + +- (id)on:(ARTRealtimeConnectionStateCb)cb { + ARTRealtimeConnectionStateSubscription *subscription = [[ARTRealtimeConnectionStateSubscription alloc] initWithRealtime:self.realtime cb:cb]; + // FIXME: + //[self.realtime.stateSubscriptions addObject:subscription]; + //cb(self.realtime.state); + return subscription; +} + +@end diff --git a/ably-ios/ARTFallback.h b/ably-ios/ARTFallback.h index 145ec80e4..03b5f7989 100644 --- a/ably-ios/ARTFallback.h +++ b/ably-ios/ARTFallback.h @@ -8,9 +8,9 @@ #import - @class ARTHttpResponse; @class ARTClientOptions; + @interface ARTFallback : NSObject { diff --git a/ably-ios/ARTFallback.m b/ably-ios/ARTFallback.m index c1447dcad..b5427ae81 100644 --- a/ably-ios/ARTFallback.m +++ b/ably-ios/ARTFallback.m @@ -7,18 +7,21 @@ // #import "ARTFallback.h" + +#import "ARTDefault.h" +#import "ARTStatus.h" #import "ARTHttp.h" #import "ARTClientOptions.h" -#import "ARTDefault.h" @interface ARTFallback () @property (readwrite, strong, nonatomic) NSMutableArray * hosts; + @end -@implementation ARTFallback +@implementation ARTFallback --(id) init { +- (id)init { self = [super init]; if(self) { self.hosts = [NSMutableArray array]; @@ -33,16 +36,16 @@ -(id) init { return self; } --(NSString *) popFallbackHost { +- (NSString *)popFallbackHost { if([self.hosts count] ==0) { return nil; } - NSString * host= [self.hosts lastObject]; + NSString *host= [self.hosts lastObject]; [self.hosts removeLastObject]; return host; } -+(bool) shouldTryFallback:(ARTHttpResponse *) response options:(ARTClientOptions *) options { ++ (bool)shouldTryFallback:(ARTHttpResponse *) response options:(ARTClientOptions *) options { if(![options isFallbackPermitted]) { return false; } diff --git a/ably-ios/ARTHttp.h b/ably-ios/ARTHttp.h index 629604a87..5977158f6 100644 --- a/ably-ios/ARTHttp.h +++ b/ably-ios/ARTHttp.h @@ -7,9 +7,9 @@ // #import -#import -#import -#import +#import "ably.h" + +@class ARTErrorInfo; @protocol ARTHTTPExecutor @@ -55,10 +55,10 @@ } -@property (nonatomic, weak) ARTLog * logger; +@property (nonatomic, weak) ARTLog *logger; + - (instancetype)init; -typedef void (^ARTHttpCb)(ARTHttpResponse *response); - (id)makeRequestWithMethod:(NSString *)method url:(NSURL *)url headers:(NSDictionary *)headers body:(NSData *)body cb:(ARTHttpCb)cb; - (id)makeRequest:(ARTHttpRequest *)req cb:(ARTHttpCb)cb; diff --git a/ably-ios/ARTHttp.m b/ably-ios/ARTHttp.m index 060d11f00..36884dfc0 100644 --- a/ably-ios/ARTHttp.m +++ b/ably-ios/ARTHttp.m @@ -8,9 +8,6 @@ #import "ARTHttp.h" -#import "ARTLog.h" - - @interface ARTHttp () @property (readonly, copy, nonatomic) NSURL *baseUrl; diff --git a/ably-ios/ARTJsonEncoder.h b/ably-ios/ARTJsonEncoder.h index f2c9a3cef..a4e7438c1 100644 --- a/ably-ios/ARTJsonEncoder.h +++ b/ably-ios/ARTJsonEncoder.h @@ -11,9 +11,9 @@ #import "ARTEncoder.h" @class ARTLog; + @interface ARTJsonEncoder : NSObject -{ - -} -@property (nonatomic, weak) ARTLog * logger; + +@property (nonatomic, weak) ARTLog *logger; + @end diff --git a/ably-ios/ARTJsonEncoder.m b/ably-ios/ARTJsonEncoder.m index 246c22fed..bb9c09586 100644 --- a/ably-ios/ARTJsonEncoder.m +++ b/ably-ios/ARTJsonEncoder.m @@ -7,6 +7,7 @@ // #import "ARTJsonEncoder.h" + #import "ARTMessage.h" #import "ARTPresence.h" #import "ARTPresenceMessage.h" @@ -36,6 +37,8 @@ - (NSArray *)presenceMessagesToArray:(NSArray *)messages; - (NSDictionary *)protocolMessageToDictionary:(ARTProtocolMessage *)message; - (ARTProtocolMessage *)protocolMessageFromDictionary:(NSDictionary *)input; +- (NSDictionary *)tokenRequestToDictionary:(ARTAuthTokenRequest *)tokenRequest; + - (NSArray *)statsFromArray:(NSArray *)input; - (ARTStats *)statsFromDictionary:(NSDictionary *)input; - (ARTStatsMessageTypes *)statsMessageTypesFromDictionary:(NSDictionary *)input; @@ -102,10 +105,14 @@ - (ARTProtocolMessage *)decodeProtocolMessage:(NSData *)data { return [self protocolMessageFromDictionary:[self decodeDictionary:data]]; } -- (ARTTokenDetails *) decodeAccessToken:(NSData *) data { +- (ARTAuthTokenDetails *)decodeAccessToken:(NSData *) data { return [self tokenFromDictionary:[self decodeDictionary:data]]; } +- (NSData *)encodeTokenRequest:(ARTAuthTokenRequest *)request { + return [self encode:[self tokenRequestToDictionary:request]]; +} + - (NSDate *)decodeTime:(NSData *)data { NSArray *resp = [self decodeArray:data]; [self.logger verbose:@"ARTJsonEncoder: decodeTime %@", resp]; @@ -128,7 +135,7 @@ - (ARTMessage *)messageFromDictionary:(NSDictionary *)input { if (![input isKindOfClass:[NSDictionary class]]) { return nil; } - + ARTMessage *message = [[ARTMessage alloc] init]; message.id = [input artString:@"id"]; message.name = [input artString:@"name"]; @@ -136,7 +143,7 @@ - (ARTMessage *)messageFromDictionary:(NSDictionary *)input { message.payload = [self payloadFromDictionary:input]; message.timestamp = [input artDate:@"timestamp"]; message.connectionId = [input artString:@"connectionId"]; - + return message; } @@ -144,7 +151,7 @@ - (NSArray *)messagesFromArray:(NSArray *)input { if (![input isKindOfClass:[NSArray class]]) { return nil; } - + NSMutableArray *output = [NSMutableArray array]; for (NSDictionary *item in input) { ARTMessage *message = [self messageFromDictionary:item]; @@ -204,13 +211,13 @@ - (ARTPresenceMessage *)presenceMessageFromDictionary:(NSDictionary *)input { message.payload.encoding = [input artString:@"encoding"]; message.clientId = [input artString:@"clientId"]; message.timestamp = [input artDate:@"timestamp"]; - + int action = [[input artNumber:@"action"] intValue]; - + message.action = [self presenceActionFromInt:action]; - + message.connectionId = [input artString:@"connectionId"]; - + return message; } @@ -218,7 +225,7 @@ - (NSArray *)presenceMessagesFromArray:(NSArray *)input { if (![input isKindOfClass:[NSArray class]]) { return nil; } - + NSMutableArray *output = [NSMutableArray array]; for (NSDictionary *item in input) { ARTPresenceMessage *message = [self presenceMessageFromDictionary:item]; @@ -232,19 +239,19 @@ - (NSArray *)presenceMessagesFromArray:(NSArray *)input { - (NSDictionary *)messageToDictionary:(ARTMessage *)message { NSMutableDictionary *output = [NSMutableDictionary dictionary]; - + if (message.timestamp) { [output setObject:[message.timestamp artToNumberMs] forKey:@"timestamp"]; } - + if (message.clientId) { [output setObject:message.clientId forKey:@"clientId"]; } - + if (message.payload) { [self writePayload:message.payload toDictionary:output]; } - + if (message.name) { [output setObject:message.name forKey:@"name"]; } @@ -254,7 +261,7 @@ - (NSDictionary *)messageToDictionary:(ARTMessage *)message { - (NSArray *)messagesToArray:(NSArray *)messages { NSMutableArray *output = [NSMutableArray array]; - + for (ARTMessage *message in messages) { NSDictionary *item = [self messageToDictionary:message]; if (!(item)) { @@ -262,30 +269,30 @@ - (NSArray *)messagesToArray:(NSArray *)messages { } [output addObject:item]; } - + return output; } - (NSDictionary *)presenceMessageToDictionary:(ARTPresenceMessage *)message { NSMutableDictionary *output = [NSMutableDictionary dictionary]; - + if (message.timestamp) { [output setObject:[message.timestamp artToNumberMs] forKey:@"timestamp"]; } - + if (message.clientId) { [output setObject:message.clientId forKey:@"clientId"]; } - + if (message.payload) { [self writePayload:message.payload toDictionary:output]; } if(message.connectionId) { [output setObject:message.connectionId forKey:@"connectionId"]; } - + int action = [self intFromPresenceMessageAction:message.action]; - + [output setObject:[NSNumber numberWithInt:action] forKey:@"action"]; [self.logger verbose:@"ARTJsonEncoder: presenceMessageToDictionary %@", output]; return output; @@ -293,7 +300,7 @@ - (NSDictionary *)presenceMessageToDictionary:(ARTPresenceMessage *)message { - (NSArray *)presenceMessagesToArray:(NSArray *)messages { NSMutableArray *output = [NSMutableArray array]; - + for (ARTPresenceMessage *message in messages) { NSDictionary *item = [self presenceMessageToDictionary:message]; if (!(item)) { @@ -311,11 +318,11 @@ - (NSDictionary *)protocolMessageToDictionary:(ARTProtocolMessage *)message { output[@"channel"] = message.channel; } output[@"msgSerial"] = [NSNumber numberWithLongLong:message.msgSerial]; - + if (message.messages) { output[@"messages"] = [self messagesToArray:message.messages]; } - + if (message.presence) { output[@"presence"] = [self presenceMessagesToArray:message.presence]; } @@ -323,22 +330,32 @@ - (NSDictionary *)protocolMessageToDictionary:(ARTProtocolMessage *)message { return output; } --(ARTTokenDetails *) tokenFromDictionary:(NSDictionary *) input { +- (ARTAuthTokenDetails *)tokenFromDictionary:(NSDictionary *)input { [self.logger verbose:@"ARTJsonEncoder: tokenFromDictionary %@", input]; if (![input isKindOfClass:[NSDictionary class]]) { return nil; } - NSData * tokenData = [[input artString:@"token"] dataUsingEncoding:NSUTF8StringEncoding]; - NSString * token =[ARTBase64PayloadEncoder toBase64:tokenData]; - ARTTokenDetails * tok = [[ARTTokenDetails alloc] - initWithId: token - expires: [[input artNumber:@"expires"] longLongValue] - issued: [[input artNumber:@"issued"] longLongValue] - capability: [input artString:@"capability"] - clientId: [input artString:@"clientId"]]; - return tok; + NSData *tokenData = [[input artString:@"token"] dataUsingEncoding:NSUTF8StringEncoding]; + NSString *token = [ARTBase64PayloadEncoder toBase64:tokenData]; + NSNumber *expiresTimeInterval = [input artNumber:@"expires"]; + NSDate *expires = expiresTimeInterval ? [NSDate dateWithTimeIntervalSince1970:expiresTimeInterval.longLongValue / 1000] : nil; + NSNumber *issuedInterval = [input artNumber:@"issued"]; + NSDate *issued = issuedInterval ? [NSDate dateWithTimeIntervalSince1970:issuedInterval.longLongValue / 1000] : nil; + return [[ARTAuthTokenDetails alloc] initWithToken:token + expires:expires + issued:issued + capability:[input artString:@"capability"] + clientId:[input artString:@"clientId"]]; + +} + +- (NSDictionary *)tokenRequestToDictionary:(ARTAuthTokenRequest *)tokenRequest { + [self.logger verbose:@"ARTJsonEncoder: tokenRequestToDictionary %@", tokenRequest]; + return @{ + + }; } - (ARTProtocolMessage *)protocolMessageFromDictionary:(NSDictionary *)input { @@ -346,7 +363,7 @@ - (ARTProtocolMessage *)protocolMessageFromDictionary:(NSDictionary *)input { if (![input isKindOfClass:[NSDictionary class]]) { return nil; } - + ARTProtocolMessage *message = [[ARTProtocolMessage alloc] init]; message.action = (ARTProtocolMessageAction)[[input artNumber:@"action"] intValue]; message.count = [[input artNumber:@"count"] intValue]; @@ -369,15 +386,15 @@ - (ARTProtocolMessage *)protocolMessageFromDictionary:(NSDictionary *)input { [message.error setCode:[[error artNumber:@"code"] intValue] status:[[error artNumber:@"statusCode"] intValue] message:[error artString:@"message"]]; } return message; - } +} - (NSArray *)statsFromArray:(NSArray *)input { if (![input isKindOfClass:[NSArray class]]) { return nil; } - + NSMutableArray *output = [NSMutableArray array]; - + for (NSDictionary *item in input) { if (![item isKindOfClass:[NSDictionary class]]) { return nil; @@ -388,7 +405,7 @@ - (NSArray *)statsFromArray:(NSArray *)input { } [output addObject:statsItem]; } - + return output; } @@ -399,7 +416,7 @@ - (NSDate *)intervalFromString:(NSString *)string { formatter.dateFormat = @"yyyy-MM-dd:HH:mm"; formatter.timeZone = [NSTimeZone timeZoneWithName:@"UTC"]; } - + return [formatter dateFromString:string]; } @@ -417,7 +434,7 @@ - (ARTStats *)statsFromDictionary:(NSDictionary *)input { ARTStatsRequestCount *apiRequests = [self statsRequestCountFromDictionary:[input objectForKey:@"apiRequests"]]; ARTStatsRequestCount *tokenRequests = [self statsRequestCountFromDictionary:[input objectForKey:@"tokenRequests"]]; NSDate *interval = [self intervalFromString:input[@"intervalId"]]; - + return [[ARTStats alloc] initWithAll:all inbound:inbound outbound:outbound @@ -433,15 +450,15 @@ - (ARTStatsMessageTypes *)statsMessageTypesFromDictionary:(NSDictionary *)input if (![input isKindOfClass:[NSDictionary class]]) { return nil; } - + ARTStatsMessageCount *all = [self statsMessageCountFromDictionary:[input objectForKey:@"all"]]; ARTStatsMessageCount *messages = [self statsMessageCountFromDictionary:[input objectForKey:@"messages"]]; ARTStatsMessageCount *presence = [self statsMessageCountFromDictionary:[input objectForKey:@"presence"]]; - + if (all || messages || presence) { return [[ARTStatsMessageTypes alloc] initWithAll:all messages:messages presence:presence]; } - + return nil; } @@ -449,10 +466,10 @@ - (ARTStatsMessageCount *)statsMessageCountFromDictionary:(NSDictionary *)input if (![input isKindOfClass:[NSDictionary class]]) { return nil; } - + NSNumber *count = [input artTyped:[NSNumber class] key:@"count"]; NSNumber *data = [input artTyped:[NSNumber class] key:@"data"]; - + return [[ARTStatsMessageCount alloc] initWithCount:count.doubleValue data:data.doubleValue]; } @@ -460,17 +477,17 @@ - (ARTStatsMessageTraffic *)statsMessageTrafficFromDictionary:(NSDictionary *)in if (![input isKindOfClass:[NSDictionary class]]) { return nil; } - + ARTStatsMessageTypes *all = [self statsMessageTypesFromDictionary:[input objectForKey:@"all"]]; ARTStatsMessageTypes *realtime = [self statsMessageTypesFromDictionary:[input objectForKey:@"realtime"]]; ARTStatsMessageTypes *rest = [self statsMessageTypesFromDictionary:[input objectForKey:@"rest"]]; ARTStatsMessageTypes *push = [self statsMessageTypesFromDictionary:[input objectForKey:@"push"]]; ARTStatsMessageTypes *httpStream = [self statsMessageTypesFromDictionary:[input objectForKey:@"httpStream"]]; - + if (all || realtime || rest || push || httpStream) { return [[ARTStatsMessageTraffic alloc] initWithAll:all realtime:realtime rest:rest push:push httpStream:httpStream]; } - + return nil; } @@ -478,15 +495,15 @@ - (ARTStatsConnectionTypes *)statsConnectionTypesFromDictionary:(NSDictionary *) if (![input isKindOfClass:[NSDictionary class]]) { return nil; } - + ARTStatsResourceCount *all = [self statsResourceCountFromDictionary:[input objectForKey:@"all"]]; ARTStatsResourceCount *plain = [self statsResourceCountFromDictionary:[input objectForKey:@"plain"]]; ARTStatsResourceCount *tls = [self statsResourceCountFromDictionary:[input objectForKey:@"tls"]]; - + if (all || plain || tls) { return [[ARTStatsConnectionTypes alloc] initWithAll:all plain:plain tls:tls]; } - + return nil; } @@ -494,13 +511,13 @@ - (ARTStatsResourceCount *)statsResourceCountFromDictionary:(NSDictionary *)inpu if (![input isKindOfClass:[NSDictionary class]]) { return nil; } - + NSNumber *opened = [input artTyped:[NSNumber class] key:@"opened"]; NSNumber *peak = [input artTyped:[NSNumber class] key:@"peak"]; NSNumber *mean = [input artTyped:[NSNumber class] key:@"mean"]; NSNumber *min = [input artTyped:[NSNumber class] key:@"min"]; NSNumber *refused = [input artTyped:[NSNumber class] key:@"refused"]; - + return [[ARTStatsResourceCount alloc] initWithOpened:opened.doubleValue peak:peak.doubleValue mean:mean.doubleValue @@ -523,11 +540,11 @@ - (ARTStatsRequestCount *)statsRequestCountFromDictionary:(NSDictionary *)input if (![input isKindOfClass:[NSDictionary class]]) { return nil; } - + NSNumber *succeeded = [input artTyped:[NSNumber class] key:@"succeeded"]; NSNumber *failed = [input artTyped:[NSNumber class] key:@"failed"]; NSNumber *refused = [input artTyped:[NSNumber class] key:@"refused"]; - + return [[ARTStatsRequestCount alloc] initWithSucceeded:succeeded.doubleValue failed:failed.doubleValue refused:refused.doubleValue]; @@ -559,7 +576,7 @@ - (void)writePayload:(ARTPayload *)payload toDictionary:(NSMutableDictionary *)o } NSAssert(status.state == ARTStateOk, @"Error encoding payload"); NSAssert([payload.payload isKindOfClass:[NSString class]], @"Only string payloads are accepted"); - + if (encoded.encoding.length) { output[@"encoding"] = encoded.encoding; } diff --git a/ably-ios/ARTMessage.h b/ably-ios/ARTMessage.h index cb4b3ecbf..ba7288342 100644 --- a/ably-ios/ARTMessage.h +++ b/ably-ios/ARTMessage.h @@ -1,35 +1,19 @@ // // ARTMessage.h -// ably-ios +// ably // -// Created by Jason Choy on 08/12/2014. -// Copyright (c) 2014 Ably. All rights reserved. +// Created by Ricardo Pereira on 30/09/2015. +// Copyright (c) 2015 Ably. All rights reserved. // #import -#import +#import "ARTBaseMessage.h" -@class ARTStatus; +@interface ARTMessage : ARTBaseMessage -@interface ARTMessage : NSObject - -@property (readwrite, strong, nonatomic) NSString *id; +/// The event name, if available @property (readwrite, strong, nonatomic) NSString *name; -@property (readwrite, strong, nonatomic) NSString *clientId; -@property (readwrite, strong, nonatomic) NSString *connectionId; -@property (readwrite, strong, nonatomic) ARTPayload *payload; -@property (readwrite, strong, nonatomic) NSDate *timestamp; -@property (readwrite, strong, nonatomic) ARTStatus * status; -- (instancetype)init; - (instancetype)initWithData:(id)data name:(NSString *)name; -- (instancetype)decode:(id)encoder; -- (instancetype)encode:(id)encoder; - -- (id) content; - -+ (instancetype)messageWithPayload:(id) payload name:(NSString *)name; -+ (NSArray *)messagesWithPayloads:(NSArray *)payloads; - @end diff --git a/ably-ios/ARTMessage.m b/ably-ios/ARTMessage.m index 0a105e1a0..a21ebda46 100644 --- a/ably-ios/ARTMessage.m +++ b/ably-ios/ARTMessage.m @@ -1,97 +1,30 @@ // // ARTMessage.m -// ably-ios +// ably // -// Created by Jason Choy on 08/12/2014. -// Copyright (c) 2014 Ably. All rights reserved. +// Created by Ricardo Pereira on 30/09/2015. +// Copyright (c) 2015 Ably. All rights reserved. // #import "ARTMessage.h" -#import "ARTLog.h" @implementation ARTMessage -- (instancetype)init { - return self = [self init]; -} - - (instancetype)initWithData:(id)data name:(NSString *)name { if (self = [self init]) { _name = [name copy]; if (data) { - _payload = [ARTPayload payloadWithPayload:data encoding:@""]; + // FIXME: + //_payload = nil; //[ARTPayload payloadWithPayload:data encoding:@""]; } } - return self; } --(void) setClientId:(NSString *)clientId { - if(clientId) { - const char* c = [clientId UTF8String]; - _clientId = [NSString stringWithUTF8String:c]; - } - else { - _clientId = nil; - } - -} - - (id)copyWithZone:(NSZone *)zone { - ARTMessage *message = [[self.class allocWithZone:zone] init]; - message->_id = self.id; + ARTMessage *message = [super init]; message->_name = self.name; - message->_clientId = self.clientId; - message->_timestamp = self.timestamp; - message->_payload = self.payload; - message->_connectionId = self.connectionId; - message->_status = self.status; return message; } -- (instancetype)messageWithPayload:(ARTPayload *)payload status:(ARTStatus * ) status { - ARTMessage *message = [self copy]; - message.payload = payload; - message.status = status; - return message; -} -- (instancetype)messageWithPayload:(ARTPayload *)payload { - return [self messageWithPayload:payload status: nil]; -} - -- (instancetype)decode:(id)encoder { - ARTPayload *payload = self.payload; - ARTStatus *status = [encoder decode:payload output:&payload]; - return [self messageWithPayload:payload status: status]; -} - -- (instancetype)encode:(id)encoder { - ARTPayload *payload = self.payload; - ARTStatus *status = [encoder encode:payload output:&payload]; - return [self messageWithPayload:payload status:status]; -} - -- (id) content { - return self.payload.payload; -} - -+ (ARTMessage *) messageWithPayload:(id) payload name:(NSString *) name { - ARTMessage *message = [[ARTMessage alloc] init]; - message.name = name; - message.payload = [ARTPayload payloadWithPayload:payload encoding:@""]; - return message; -} - -+ (NSArray *) messagesWithPayloads:(NSArray *) payloads { - - if([payloads count] > [ARTPayload payloadArraySizeLimit]) { - [NSException raise:@"Too many items in payload array" format:@"%lu > %lu", (unsigned long)[payloads count], [ARTPayload payloadArraySizeLimit]]; - } - NSMutableArray * messages =[[NSMutableArray alloc] init]; - for(int i=0; i < [payloads count]; i++) { - [messages addObject:[ARTMessage messageWithPayload:[payloads objectAtIndex:i] name:nil]]; - } - return messages; -} - @end diff --git a/ably-ios/ARTMsgPackEncoder.h b/ably-ios/ARTMsgPackEncoder.h deleted file mode 100644 index 1877e6095..000000000 --- a/ably-ios/ARTMsgPackEncoder.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// ARTMsgPackEncoder.h -// ably-ios -// -// Created by vic on 16/03/2015. -// Copyright (c) 2015 Ably. All rights reserved. -// - -#import - - -#import "ARTEncoder.h" -@interface ARTMsgPackEncoder : NSObject - -@end diff --git a/ably-ios/ARTMsgPackEncoder.m b/ably-ios/ARTMsgPackEncoder.m deleted file mode 100644 index c621e202f..000000000 --- a/ably-ios/ARTMsgPackEncoder.m +++ /dev/null @@ -1,275 +0,0 @@ -// -// ARTJsonEncoder.m -// ably-ios -// -// Created by Jason Choy on 09/12/2014. -// Copyright (c) 2014 Ably. All rights reserved. -// - -#import "ARTMsgPackEncoder.h" - -#import "ARTMessage.h" -#import "ARTPresenceMessage.h" -#import "ARTProtocolMessage.h" -#import "ARTStats.h" -#import "ARTNSDictionary+ARTDictionaryUtil.h" -#import "ARTNSDate+ARTUtil.h" - -/* -#import -#import -#import -#import -*/ - - -@interface ARTMsgPackEncoder () - -- (ARTMessage *)messageFromDictionary:(NSDictionary *)input; -- (NSArray *)messagesFromArray:(NSArray *)input; - -- (ARTPresenceMessage *)presenceMessageFromDictionary:(NSDictionary *)input; -- (NSArray *)presenceMessagesFromArray:(NSArray *)input; - -- (NSDictionary *)messageToDictionary:(ARTMessage *)message; -- (NSArray *)messagesToArray:(NSArray *)messages; - -- (NSDictionary *)presenceMessageToDictionary:(ARTPresenceMessage *)message; -- (NSArray *)presenceMessagesToArray:(NSArray *)messages; - -- (NSDictionary *)protocolMessageToDictionary:(ARTProtocolMessage *)message; -- (ARTProtocolMessage *)protocolMessageFromDictionary:(NSDictionary *)input; - -- (NSArray *)statsFromArray:(NSArray *)input; -- (ARTStats *)statsFromDictionary:(NSDictionary *)input; -- (ARTStatsMessageTypes *)statsMessageTypesFromDictionary:(NSDictionary *)input; -- (ARTStatsMessageCount *)statsMessageCountFromDictionary:(NSDictionary *)input; -- (ARTStatsMessageTraffic *)statsMessageTrafficFromDictionary:(NSDictionary *)input; -- (ARTStatsConnectionTypes *)statsConnectionTypesFromDictionary:(NSDictionary *)input; -- (ARTStatsResourceCount *)statsResourceCountFromDictionary:(NSDictionary *)input; -- (ARTStatsRequestCount *)statsRequestCountFromDictionary:(NSDictionary *)input; - -- (ARTPayload *)payloadFromDictionary:(NSDictionary *)input; -- (void)writePayload:(ARTPayload *)payload toDictionary:(NSMutableDictionary *)output; - -- (id)decode:(NSData *)data; -- (NSDictionary *)decodeDictionary:(NSData *)data; -- (NSArray *)decodeArray:(NSData *)data; - -- (NSData *)encode:(id)obj; - -@end - -@implementation ARTMsgPackEncoder - - - --(id) decodeError:(NSData *) data { - return nil; -} - - - -- (NSData *)encode:(id)obj -{ - return nil; -} -- (NSString *)mimeType { - return @"application/msgpack"; -} - - --(ARTTokenDetails *) decodeAccessToken:(NSData *)data { - return nil; -} -- (ARTMessage *)decodeMessage:(NSData *)data { - return [self messageFromDictionary:[self decodeDictionary:data]]; -} - -- (NSArray *)decodeMessages:(NSData *)data { - return [self messagesFromArray:[self decodeArray:data]]; -} - -- (NSData *)encodeMessage:(ARTMessage *)message { - return [self encode:[self messageToDictionary:message]]; -} - -- (NSData *)encodeMessages:(NSArray *)messages { - return [self encode:[self messagesToArray:messages]]; -} - -- (ARTPresenceMessage *)decodePresenceMessage:(NSData *)data { - return [self presenceMessageFromDictionary:[self decodeDictionary:data]]; -} - -- (NSArray *)decodePresenceMessages:(NSData *)data { - return [self presenceMessagesFromArray:[self decodeArray:data]]; -} - -- (NSData *)encodePresenceMessage:(ARTPresenceMessage *)message { - return [self encode:[self presenceMessageToDictionary:message]]; -} - -- (NSData *)encodePresenceMessages:(NSArray *)messages { - return [self encode:[self presenceMessagesToArray:messages]]; -} - -- (NSData *)encodeProtocolMessage:(ARTProtocolMessage *)message { - return [self encode:[self protocolMessageToDictionary:message]]; -} - -- (ARTProtocolMessage *)decodeProtocolMessage:(NSData *)data { - return [self protocolMessageFromDictionary:[self decodeDictionary:data]]; -} - -- (NSDate *)decodeTime:(NSData *)data { - //TODO implement - return nil; -} - -- (NSArray *)decodeStats:(NSData *)data { - return [self statsFromArray:[self decodeArray:data]]; -} - -- (ARTMessage *)messageFromDictionary:(NSDictionary *)input { - //TODO implement - return nil; -} - -- (NSArray *)messagesFromArray:(NSArray *)input { - //TODO implement - return nil; -} - - - -- (ARTPresenceMessage *)presenceMessageFromDictionary:(NSDictionary *)input { - //TODO implement - return nil; -} - - -//TODO dup with json? -- (NSArray *)presenceMessagesFromArray:(NSArray *)input { - //TODO implement - return nil; -} - -- (NSDictionary *)messageToDictionary:(ARTMessage *)message { - //TODO implement - return nil; -} - -- (NSArray *)messagesToArray:(NSArray *)messages { - //TODO implement - return nil; -} - -- (NSDictionary *)presenceMessageToDictionary:(ARTPresenceMessage *)message { - //TODO implement - return nil; -} - -- (NSArray *)presenceMessagesToArray:(NSArray *)messages { - //TODO implement - return nil; -} - -- (NSDictionary *)protocolMessageToDictionary:(ARTProtocolMessage *)message { - //TODO implement - return nil; -} - -- (ARTProtocolMessage *)protocolMessageFromDictionary:(NSDictionary *)input { - //TODO implement - return nil; -} - -- (NSArray *)statsFromArray:(NSArray *)input { - //TODO implement - return nil; -} - -- (ARTStats *)statsFromDictionary:(NSDictionary *)input { - //TODO implement - return nil; -} - -- (ARTStatsMessageTypes *)statsMessageTypesFromDictionary:(NSDictionary *)input { - //TODO implement - return nil; -} - -- (ARTStatsMessageCount *)statsMessageCountFromDictionary:(NSDictionary *)input { - //TODO implement - return nil; -} - -- (ARTStatsMessageTraffic *)statsMessageTrafficFromDictionary:(NSDictionary *)input { - //TODO implement - return nil; -} - -- (ARTStatsConnectionTypes *)statsConnectionTypesFromDictionary:(NSDictionary *)input { - //TODO implement - return nil; -} - -- (ARTStatsResourceCount *)statsResourceCountFromDictionary:(NSDictionary *)input { - //TODO implement - return nil; -} - -- (ARTStatsRequestCount *)statsRequestCountFromDictionary:(NSDictionary *)input { - //TODO implement - return nil; -} - -- (ARTPayload *)payloadFromDictionary:(NSDictionary *)input { - //TODO implement - return nil; -} - -- (void)writePayload:(ARTPayload *)payload toDictionary:(NSMutableDictionary *) input -{ - //TODO implement -} - -- (id)decode:(NSData *)data { - // id obj = [data messagePackParse]; - return nil; -} - -- (NSDictionary *)decodeDictionary:(NSData *)data { - id obj = [self decode:data]; - if (![obj isKindOfClass:[NSDictionary class]]) { - return nil; - } - return obj; -} - -- (NSArray *)decodeArray:(NSData *)data { - id obj = [self decode:data]; - if (![obj isKindOfClass:[NSArray class]]) { - return nil; - } - return obj; -} - - -//TODO write thie up. -//- (NSData *)encode:(id)obj { -// return [NSJSONSerialization dataWithJSONObject:obj options:0 error:nil]; -//} - --(NSData *) encodeDict:(NSDictionary *) obj -{ - return nil;//[obj messagePack]; -} --(NSData *) encodeArr:(NSArray *) obj -{ - return nil;// [obj messagePack]; -} - - -@end diff --git a/ably-ios/ARTPaginatedResult.m b/ably-ios/ARTPaginatedResult.m index 617eb15b4..4c807c6a1 100644 --- a/ably-ios/ARTPaginatedResult.m +++ b/ably-ios/ARTPaginatedResult.m @@ -97,6 +97,7 @@ - (void)next:(ARTPaginatedResultCallback)callback { + (void)executePaginatedRequest:(NSMutableURLRequest *)request executor:(id)executor responseProcessor:(ARTPaginatedResultResponseProcessor)responseProcessor callback:(ARTPaginatedResultCallback)callback { + [executor executeRequest:request callback:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { if (error) { callback(nil, error); diff --git a/ably-ios/ARTPayload+Private.h b/ably-ios/ARTPayload+Private.h index 61b919a3a..20557999e 100644 --- a/ably-ios/ARTPayload+Private.h +++ b/ably-ios/ARTPayload+Private.h @@ -9,11 +9,9 @@ #import @interface ARTPayload (Private) -{ - -} -+(NSArray *) parseEncodingChain:(NSString *) encodingChain key:(NSData *) key iv:(NSData *) iv; -+(id) createEncoder:(NSString *) name key:(NSData *) key iv:(NSData *) iv; -+(size_t) getPayloadArraySizeLimit:(size_t) newLimit modify:(bool) modify; ++ (NSArray *)parseEncodingChain:(NSString *) encodingChain key:(NSData *)key iv:(NSData *)iv; ++ (id)createEncoder:(NSString *) name key:(NSData *)key iv:(NSData *)iv; ++ (size_t)getPayloadArraySizeLimit:(size_t)newLimit modify:(bool)modify; + @end diff --git a/ably-ios/ARTPayload.h b/ably-ios/ARTPayload.h index a7ef54fe1..7daffe2af 100644 --- a/ably-ios/ARTPayload.h +++ b/ably-ios/ARTPayload.h @@ -9,11 +9,11 @@ #import #import -@class ARTCipherParams; @protocol ARTPayloadEncoder; -@interface ARTPayload : NSObject +@class ARTCipherParams; +@interface ARTPayload : NSObject @property (readwrite, strong, nonatomic) id payload; @property (readwrite, strong, nonatomic) NSString *encoding; @@ -42,6 +42,7 @@ - (ARTStatus *)encode:(ARTPayload *)payload output:(ARTPayload *__autoreleasing *)output; - (ARTStatus *)decode:(ARTPayload *)payload output:(ARTPayload *__autoreleasing *)output; - (NSString *)name; + @end @interface ARTBase64PayloadEncoder : NSObject @@ -49,6 +50,7 @@ + (instancetype)instance; +(NSString *) toBase64:(NSData *) input; +(NSString *) fromBase64:(NSString *) base64; + @end @interface ARTUtf8PayloadEncoder : NSObject @@ -75,4 +77,4 @@ - (instancetype)init; - (instancetype)initWithEncoders:(NSArray *)encoders; -@end \ No newline at end of file +@end diff --git a/ably-ios/ARTPayload.m b/ably-ios/ARTPayload.m index d8a6a85a0..32639c98e 100644 --- a/ably-ios/ARTPayload.m +++ b/ably-ios/ARTPayload.m @@ -8,10 +8,10 @@ #import "ARTPayload.h" #import "ARTPayload+Private.h" + #import "ARTCrypto.h" #import "ARTLog.h" - @interface ARTBase64PayloadEncoder () + (BOOL)canEncode:(ARTPayload *)payload; @@ -27,6 +27,7 @@ @interface ARTCipherPayloadEncoder () @end @interface ARTPayloadEncoderChain () + @property (nonatomic, weak) ARTLog * logger; @property (readonly, nonatomic, strong) NSArray *encoders; @@ -66,7 +67,7 @@ + (instancetype)payloadWithPayload:(id)payload encoding:(NSString *)encoding { [[ARTCipherPayloadEncoder alloc] initWithCipherParams:cipherParams]]]; } -+(id) createEncoder:(NSString *) name key:(NSData *) keySpec iv:(NSData *) iv { ++ (id)createEncoder:(NSString *) name key:(NSData *) keySpec iv:(NSData *) iv { if([name isEqualToString:@"json"]) { return [ARTJsonPayloadEncoder instance]; } @@ -85,7 +86,7 @@ + (instancetype)payloadWithPayload:(id)payload encoding:(NSString *)encoding { return nil; } -+(NSArray *) parseEncodingChain:(NSString *) encodingChain key:(NSData *) key iv:(NSData *) iv { ++ (NSArray *)parseEncodingChain:(NSString *) encodingChain key:(NSData *) key iv:(NSData *) iv { NSArray * strArray = [encodingChain componentsSeparatedByString:@"/"]; NSMutableArray * encoders= [[NSMutableArray alloc] init]; size_t l = [strArray count]; @@ -103,7 +104,7 @@ + (size_t) payloadArraySizeLimit { return [ARTPayload getPayloadArraySizeLimit:0 modify:false]; } -+(size_t) getPayloadArraySizeLimit:(size_t) newLimit modify:(bool) modify { ++ (size_t)getPayloadArraySizeLimit:(size_t) newLimit modify:(bool) modify { static size_t limit = SIZE_T_MAX; if(modify) { limit = newLimit; @@ -140,7 +141,7 @@ + (instancetype)instance { return instance; } -+(NSString *) toBase64:(NSData *) input { ++ (NSString *)toBase64:(NSData *) input { ARTPayload * p = [[ARTPayload alloc] initWithPayload:input encoding:@"base64"]; ARTPayload * output = nil; ARTBase64PayloadEncoder * e = [ARTBase64PayloadEncoder instance]; @@ -148,7 +149,7 @@ +(NSString *) toBase64:(NSData *) input { return output.payload; } -+(NSString *) fromBase64:(NSString *) base64 { ++ (NSString *)fromBase64:(NSString *) base64 { ARTPayload * p = [[ARTPayload alloc] initWithPayload:base64 encoding:@"base64"]; ARTPayload * output = nil; ARTBase64PayloadEncoder * e = [ARTBase64PayloadEncoder instance]; @@ -156,7 +157,7 @@ +(NSString *) fromBase64:(NSString *) base64 { return [[NSString alloc] initWithData:output.payload encoding:NSUTF8StringEncoding]; } -+(NSString *) getName { ++ (NSString *)getName { return @"base64"; } - (NSString *)name { @@ -215,12 +216,14 @@ + (instancetype)instance { return instance; } -+(NSString *) getName { ++ (NSString *)getName { return @"utf-8"; } + - (NSString *)name { return [ARTUtf8PayloadEncoder getName]; } + - (ARTStatus *)decode:(ARTPayload *)payload output:(ARTPayload *__autoreleasing *)output { *output = payload; if ([[payload.encoding artLastEncoding] isEqualToString:[ARTUtf8PayloadEncoder getName]]) { @@ -262,7 +265,7 @@ + (instancetype)instance { return instance; } -+ (NSString *) getName { ++ (NSString *)getName { return @"json"; } - (NSString *)name { @@ -289,8 +292,6 @@ - (ARTStatus *)encode:(ARTPayload *)payload output:(ARTPayload *__autoreleasing [NSException raise:@"ARTPayload must be either NSDictionary, NSArray, NSData or NSString" format:@"%@", [payload.payload class]]; } return [ARTStatus state:ARTStateOk]; - - } - (ARTStatus *)decode:(ARTPayload *)payload output:(ARTPayload *__autoreleasing *)output { @@ -328,11 +329,11 @@ - (instancetype)initWithCipherParams:(ARTCipherParams *)cipherParams { return self; } -+(NSString *) getName128 { ++ (NSString *)getName128 { return @"cipher+aes-128-cbc"; } -+(NSString *) getName256 { ++ (NSString *)getName256 { return @"cipher+aes-256-cbc"; } @@ -397,7 +398,7 @@ - (instancetype)initWithEncoders:(NSArray *)encoders { return self; } --(NSString *) name { +- (NSString *)name { return @"chain"; //not used. } diff --git a/ably-ios/ARTPresence.h b/ably-ios/ARTPresence.h index a7735088e..a762795bf 100644 --- a/ably-ios/ARTPresence.h +++ b/ably-ios/ARTPresence.h @@ -7,33 +7,43 @@ // #import -#import -#import -#import +#import "ably.h" -NS_ASSUME_NONNULL_BEGIN - -typedef NS_ENUM(NSUInteger, ARTPresenceAction) { - ARTPresenceAbsent, - ARTPresencePresent, - ARTPresenceEnter, - ARTPresenceLeave, - ARTPresenceUpdate, - ARTPresenceLast -}; +#import "ARTPresenceMessage.h" -@interface ARTPresenceMessage : ARTMessage +@protocol ARTSubscription; -@property (nonatomic, assign) ARTPresenceAction action; +@class ARTPaginatedResult; +@class ARTDataQuery; +@class ARTRealtimeChannel; -@end +NS_ASSUME_NONNULL_BEGIN +/// Provides access to presence operations and state for the associated Channel @interface ARTPresence : NSObject +- (instancetype)initWithChannel:(ARTRealtimeChannel *)channel; + +/// Get the presence state for one channel - (void)get:(void (^)(ARTPaginatedResult /* */ *__nullable result, NSError *__nullable error))callback; +/// Obtain recent presence history for one channel - (void)history:(nullable ARTDataQuery *)query callback:(void (^)(ARTPaginatedResult /* */ *__nullable result, NSError *__nullable error))callback; +- (void)enter:(id)data cb:(ARTStatusCallback)cb; +- (void)update:(id)data cb:(ARTStatusCallback)cb; +- (void)leave:(id) data cb:(ARTStatusCallback)cb; + +- (void)enterClient:(NSString *) clientId data:(id) data cb:(ARTStatusCallback) cb; +- (void)updateClient:(NSString *) clientId data:(id) data cb:(ARTStatusCallback) cb; +- (void)leaveClient:(NSString *) clientId data:(id) data cb:(ARTStatusCallback) cb; +- (BOOL)isSyncComplete; + +- (id)subscribe:(ARTRealtimeChannelPresenceCb)cb; +- (id)subscribe:(ARTPresenceAction)action cb:(ARTRealtimeChannelPresenceCb)cb; +- (void)unsubscribe:(id)subscription; +- (void)unsubscribe:(id)subscription action:(ARTPresenceAction)action; + @end NS_ASSUME_NONNULL_END diff --git a/ably-ios/ARTPresence.m b/ably-ios/ARTPresence.m index e1d910a1f..83f33b7bd 100644 --- a/ably-ios/ARTPresence.m +++ b/ably-ios/ARTPresence.m @@ -6,27 +6,141 @@ // Copyright (c) 2015 г. Ably. All rights reserved. // -#import "ARTDataQuery+Private.h" #import "ARTPresence.h" -@implementation ARTPresenceMessage +#import "ARTRealtime.h" +#import "ARTRealtimeChannel.h" +#import "ARTPresenceMap.h" +#import "ARTStatus.h" +#import "ARTRealtimeChannelSubscription.h" -- (id)copyWithZone:(NSZone *)zone { - ARTPresenceMessage *message = [super copyWithZone:zone]; - message->_action = self.action; - return message; -} +#import "ARTDataQuery+Private.h" + +@interface ARTPresence () + +@property (nonatomic, weak) ARTLog *logger; +@property (readonly, weak, nonatomic) ARTRealtimeChannel *channel; @end @implementation ARTPresence -- (void)get:(void (^)(ARTStatus *status, ARTPaginatedResult *__nullable result))callback { - NSAssert(false, @"-[%@ %@] should always be overriden.", self.class, NSStringFromSelector(_cmd)); +-(instancetype) initWithChannel:(ARTRealtimeChannel *) channel { + self = [super init]; + if(self) { + _channel = channel; + self.logger = channel.logger; + } + return self; +} + +- (void)get:(void (^)(ARTPaginatedResult /* */ *__nullable result, NSError *__nullable error))callback { + // FIXME: + //[self.channel throwOnDisconnectedOrFailed]; + //[self.channel.restChannel.presence get:query callback:callback]; +} + +- (void)history:(nullable ARTDataQuery *)query callback:(void (^)(ARTPaginatedResult /* */ *__nullable result, NSError *__nullable error))callback { + // FIXME: + //[self.channel throwOnDisconnectedOrFailed]; + //[self.channel.restChannel.presence history:query callback:callback]; +} + +- (void)enter:(id)data cb:(ARTStatusCallback)cb { + [self enterClient:self.channel.clientId data:data cb:cb]; +} + +- (void) enterClient:(NSString *) clientId data:(id) data cb:(ARTStatusCallback) cb { + if(!clientId) { + [NSException raise:@"Cannot publish presence without a clientId" format:@""]; + } + ARTPresenceMessage *msg = [[ARTPresenceMessage alloc] init]; + msg.action = ARTPresenceEnter; + msg.clientId = clientId; + if(data) { + msg.payload = [ARTPayload payloadWithPayload:data encoding:@""]; + } + + msg.connectionId = self.channel.realtime.connectionId; + [self.channel publishPresence:msg cb:cb]; + +} + +- (void)update:(id)data cb:(ARTStatusCallback)cb { + [self updateClient:self.channel.clientId data:data cb:cb]; +} + +- (void)updateClient:(NSString *) clientId data:(id) data cb:(ARTStatusCallback) cb { + ARTPresenceMessage *msg = [[ARTPresenceMessage alloc] init]; + msg.action = ARTPresenceUpdate; + msg.clientId = clientId; + if(!msg.clientId) { + cb([ARTStatus state:ARTStateNoClientId]); + return; + } + if(data) { + msg.payload = [ARTPayload payloadWithPayload:data encoding:@""]; + } + msg.connectionId = self.channel.realtime.connectionId; + + [self.channel publishPresence:msg cb:cb]; + +} + +- (void)leave:(id) data cb:(ARTStatusCallback)cb { + [self leaveClient:self.channel.clientId data:data cb:cb]; +} + +- (void) leaveClient:(NSString *) clientId data:(id) data cb:(ARTStatusCallback) cb { + + if([clientId isEqualToString:self.channel.clientId]) { + if(self.channel.lastPresenceAction != ARTPresenceEnter && self.channel.lastPresenceAction != ARTPresenceUpdate) { + [NSException raise:@"Cannot leave a channel before you've entered it" format:@""]; + } + } + ARTPresenceMessage *msg = [[ARTPresenceMessage alloc] init]; + msg.action = ARTPresenceLeave; + + if(data) { + msg.payload= [ARTPayload payloadWithPayload:data encoding:@""]; + } + msg.clientId = clientId; + msg.connectionId = self.channel.realtime.connectionId; + if(!msg.clientId) { + cb([ARTStatus state:ARTStateNoClientId]); + return; + } + [self.channel publishPresence:msg cb:cb]; + +} + +- (BOOL)isSyncComplete { + return [self.channel.presenceMap isSyncComplete]; +} + +- (id)subscribe:(ARTRealtimeChannelPresenceCb)cb { + ARTRealtimeChannelPresenceSubscription *subscription = [[ARTRealtimeChannelPresenceSubscription alloc] initWithChannel:self.channel cb:cb]; + [self.channel.presenceSubscriptions addObject:subscription]; + [self.channel attach]; + return subscription; +} + +- (id)subscribe:(ARTPresenceAction) action cb:(ARTRealtimeChannelPresenceCb)cb { + ARTRealtimeChannelPresenceSubscription *subscription = (ARTRealtimeChannelPresenceSubscription *) [self subscribe:cb]; + // FIXME: + //[subscription excludeAllActionsExcept:action]; + return subscription; +} + +- (void)unsubscribe:(id)subscription action:(ARTPresenceAction) action { + // FIXME: + //ARTRealtimeChannelPresenceSubscription * s = (ARTRealtimeChannelPresenceSubscription *) subscription; + //[s excludeAction:action]; } -- (void)history:(nullable ARTDataQuery *)query callback:(void (^)(ARTStatus *status, ARTPaginatedResult *__nullable result))callback { - NSAssert(false, @"-[%@ %@] should always be overriden.", self.class, NSStringFromSelector(_cmd)); +- (void)unsubscribe:(ARTRealtimeChannelPresenceSubscription *)subscription { + ARTRealtimeChannelPresenceSubscription *s = (ARTRealtimeChannelPresenceSubscription *) subscription; + [self.channel.presenceSubscriptions removeObject:s]; } @end diff --git a/ably-ios/ARTPresenceMap.h b/ably-ios/ARTPresenceMap.h index 90550aecc..6743c6c63 100644 --- a/ably-ios/ARTPresenceMap.h +++ b/ably-ios/ARTPresenceMap.h @@ -8,13 +8,15 @@ #import - @class ARTPresenceMessage; + +/// Used to maintain a list of members present on a channel @interface ARTPresenceMap : NSObject { } @property (readwrite, nonatomic, assign) int64_t syncSerial; + - (ARTPresenceMessage *)getClient:(NSString *) clientId; - (void)put:(ARTPresenceMessage *) message; @@ -29,4 +31,5 @@ typedef void(^VoidCb)(); - (void) syncMessageProcessed; - (void)onSync:(VoidCb) cb; + @end diff --git a/ably-ios/ARTPresenceMap.m b/ably-ios/ARTPresenceMap.m index 9c9af1c5d..40ae65026 100644 --- a/ably-ios/ARTPresenceMap.m +++ b/ably-ios/ARTPresenceMap.m @@ -10,10 +10,12 @@ #import "ARTPresenceMessage.h" @interface ARTPresenceMap () -@property (readwrite, strong, atomic) NSMutableDictionary * mostRecentMessageForMember; // message + +@property (readwrite, strong, atomic) NSMutableDictionary *mostRecentMessageForMember; // message @property (readonly, nonatomic, assign) bool syncStarted; @property (readonly, nonatomic, assign) bool syncComplete; @property (nonatomic, copy) VoidCb cb; + @end @implementation ARTPresenceMap @@ -59,11 +61,12 @@ - (void)startSync { - (NSDictionary *) members { return self.mostRecentMessageForMember; } + - (void)endSync { NSArray * keys = [self.mostRecentMessageForMember allKeys]; for(NSString * key in keys) { ARTPresenceMessage * message = [self.mostRecentMessageForMember objectForKey:key]; - if(message.action == ARTPresenceMessageAbsent || message.action == ARTPresenceMessageLeave) { + if(message.action == ARTPresenceAbsent || message.action == ARTPresenceLeave) { [self.mostRecentMessageForMember removeObjectForKey:key]; } } diff --git a/ably-ios/ARTPresenceMessage.h b/ably-ios/ARTPresenceMessage.h index bf516dc47..086b45f5a 100644 --- a/ably-ios/ARTPresenceMessage.h +++ b/ably-ios/ARTPresenceMessage.h @@ -6,36 +6,21 @@ // Copyright (c) 2014 Ably. All rights reserved. // -#import -#import - -@class ARTStatus; - -typedef NS_ENUM(NSUInteger, ARTPresenceMessageAction) { - ARTPresenceMessageAbsent, - ARTPresenceMessagePresent, - ARTPresenceMessageEnter, - ARTPresenceMessageLeave, - ARTPresenceMessageUpdate, - ARTPresenceMessageLast +#import "ARTBaseMessage.h" + +/// Presence action type +typedef NS_ENUM(NSUInteger, ARTPresenceAction) { + ARTPresenceAbsent, + ARTPresencePresent, + ARTPresenceEnter, + ARTPresenceLeave, + ARTPresenceUpdate, + ARTPresenceLast }; -@interface ARTPresenceMessage : NSObject - -@property (readwrite, strong, nonatomic) NSString *id; -@property (readwrite, strong, nonatomic) NSString *clientId; -@property (readwrite, strong, nonatomic) NSString *encoding; -@property (readwrite, strong, nonatomic) ARTStatus * status; -@property (readwrite, strong, nonatomic) ARTPayload *payload; -@property (readwrite, strong, nonatomic) NSDate *timestamp; - -@property (readwrite, assign, nonatomic) ARTPresenceMessageAction action; -@property (readwrite, strong, nonatomic) NSString *connectionId; - -- (ARTPresenceMessage *)messageWithPayload:(ARTPayload *)payload; +/// List of members present on a channel +@interface ARTPresenceMessage : ARTBaseMessage -- (ARTPresenceMessage *)decode:(id)encoder; -- (ARTPresenceMessage *)encode:(id)encoder; -- (id)content; +@property (readwrite, assign, nonatomic) ARTPresenceAction action; @end diff --git a/ably-ios/ARTPresenceMessage.m b/ably-ios/ARTPresenceMessage.m index 6ec56da87..152b8dedd 100644 --- a/ably-ios/ARTPresenceMessage.m +++ b/ably-ios/ARTPresenceMessage.m @@ -7,52 +7,22 @@ // #import "ARTPresenceMessage.h" -#import "ARTStatus.h" + @implementation ARTPresenceMessage - (instancetype)init { self = [super init]; if (self) { - _id = nil; - _clientId = nil; - _payload = nil; - _timestamp = nil; - _action = ARTPresenceMessageEnter; - _connectionId = nil; - _encoding = nil; + // Default + _action = ARTPresenceEnter; } return self; } -- (ARTPresenceMessage *)messageWithPayload:(ARTPayload *)payload status:(ARTStatus *) status{ - ARTPresenceMessage *m = [[ARTPresenceMessage alloc] init]; - m.status = status; - m.id = self.id; - m.clientId = self.clientId; - m.payload = payload; - m.timestamp = self.timestamp; - m.action = self.action; - m.connectionId = self.connectionId; - m.encoding = self.encoding; - return m; -} -- (ARTPresenceMessage *)messageWithPayload:(ARTPayload *)payload { - return [self messageWithPayload:payload status:nil]; -} - -- (ARTPresenceMessage *)decode:(id)encoder { - ARTPayload *payload = self.payload; - return [self messageWithPayload:payload]; -} - -- (ARTPresenceMessage *)encode:(id)encoder { - ARTPayload *payload = self.payload; - ARTStatus *status = [encoder encode:payload output:&payload]; - return [self messageWithPayload:payload status:status]; -} - -- (id) content { - return self.payload.payload; +- (id)copyWithZone:(NSZone *)zone { + ARTPresenceMessage *message = [super init]; + message->_action = self.action; + return message; } @end diff --git a/ably-ios/ARTProtocolMessage.h b/ably-ios/ARTProtocolMessage.h index 27c7d8e4f..bdf6b77cc 100644 --- a/ably-ios/ARTProtocolMessage.h +++ b/ably-ios/ARTProtocolMessage.h @@ -8,13 +8,9 @@ #import -#import "ARTStatus.h" - - +@class ARTErrorInfo; typedef NS_ENUM(NSUInteger, ARTProtocolMessageAction) { - - ARTProtocolMessageHeartbeat = 0, ARTProtocolMessageAck = 1, ARTProtocolMessageNack = 2, @@ -38,7 +34,7 @@ typedef NS_ENUM(NSUInteger, ARTProtocolMessageAction) { @property (readwrite, assign, nonatomic) ARTProtocolMessageAction action; @property (readwrite, assign, nonatomic) int count; -@property (readwrite, strong, nonatomic) ARTErrorInfo * error; +@property (readwrite, strong, nonatomic) ARTErrorInfo *error; @property (readwrite, strong, nonatomic) NSString *id; @property (readwrite, strong, nonatomic) NSString *channel; @property (readwrite, strong, nonatomic) NSString *channelSerial; @@ -53,7 +49,7 @@ typedef NS_ENUM(NSUInteger, ARTProtocolMessageAction) { @property (readonly, assign, nonatomic) BOOL ackRequired; @property (readwrite, assign, nonatomic) int64_t flags; --(BOOL) isSyncEnabled; +- (BOOL)isSyncEnabled; - (BOOL)mergeFrom:(ARTProtocolMessage *)msg; diff --git a/ably-ios/ARTProtocolMessage.m b/ably-ios/ARTProtocolMessage.m index b735a82a0..87eb65c63 100644 --- a/ably-ios/ARTProtocolMessage.m +++ b/ably-ios/ARTProtocolMessage.m @@ -8,6 +8,7 @@ #import "ARTProtocolMessage.h" #import "ARTStatus.h" + @implementation ARTProtocolMessage - (id)init { @@ -48,7 +49,7 @@ - (BOOL)mergeFrom:(ARTProtocolMessage *)other { } } -- (void) setConnectionSerial:(int64_t)connectionSerial { +- (void)setConnectionSerial:(int64_t)connectionSerial { _connectionSerial =connectionSerial; _hasConnectionSerial = true; } @@ -56,7 +57,8 @@ - (void) setConnectionSerial:(int64_t)connectionSerial { - (BOOL)ackRequired { return self.action == ARTProtocolMessageMessage || self.action == ARTProtocolMessagePresence || self.action == ARTProtocolMessageDetach; } --(BOOL) isSyncEnabled { + +- (BOOL)isSyncEnabled { return self.flags & 0x1; } diff --git a/ably-ios/ARTQueuedMessage.h b/ably-ios/ARTQueuedMessage.h new file mode 100644 index 000000000..3494f9e4d --- /dev/null +++ b/ably-ios/ARTQueuedMessage.h @@ -0,0 +1,24 @@ +// +// ARTQueuedMessage.h +// ably +// +// Created by Ricardo Pereira on 01/10/2015. +// Copyright (c) 2015 Ably. All rights reserved. +// + +#import +#import "ably.h" + +@class ARTProtocolMessage; + +@interface ARTQueuedMessage : NSObject + +@property (readonly, strong, nonatomic) ARTProtocolMessage *msg; +@property (readonly, strong, nonatomic) NSMutableArray *cbs; + +- (instancetype)initWithProtocolMessage:(ARTProtocolMessage *)msg cb:(ARTStatusCallback)cb; +- (BOOL)mergeFrom:(ARTProtocolMessage *)msg cb:(ARTStatusCallback)cb; + +- (ARTStatusCallback)cb; + +@end diff --git a/ably-ios/ARTQueuedMessage.m b/ably-ios/ARTQueuedMessage.m new file mode 100644 index 000000000..f5435208a --- /dev/null +++ b/ably-ios/ARTQueuedMessage.m @@ -0,0 +1,45 @@ +// +// ARTQueuedMessage.m +// ably +// +// Created by Ricardo Pereira on 01/10/2015. +// Copyright (c) 2015 Ably. All rights reserved. +// + +#import "ARTQueuedMessage.h" + +#import "ARTProtocolMessage.h" + +@implementation ARTQueuedMessage + +- (instancetype)initWithProtocolMessage:(ARTProtocolMessage *)msg cb:(ARTStatusCallback)cb { + self = [super init]; + if (self) { + _msg = msg; + _cbs = [NSMutableArray array]; + if (cb) { + [_cbs addObject:cb]; + } + } + return self; +} + +- (BOOL)mergeFrom:(ARTProtocolMessage *)msg cb:(ARTStatusCallback)cb { + if ([self.msg mergeFrom:msg]) { + if (cb) { + [self.cbs addObject:cb]; + } + return YES; + } + return NO; +} + +- (ARTStatusCallback)cb { + return ^(ARTStatus * status) { + for (ARTStatusCallback cb in self.cbs) { + cb(status); + } + }; +} + +@end diff --git a/ably-ios/ARTRealtime+Private.h b/ably-ios/ARTRealtime+Private.h index 15d12ff38..78169281f 100644 --- a/ably-ios/ARTRealtime+Private.h +++ b/ably-ios/ARTRealtime+Private.h @@ -9,12 +9,10 @@ #import #import "ARTRealtime.h" -@class ARTProtocolMessage; +@class ARTProtocolMessage; -/** - ARTRealtime private methods that are used for whitebox testing. - */ +/// ARTRealtime private methods that are used for whitebox testing. @interface ARTRealtime (Private) // Transport Events @@ -28,8 +26,6 @@ - (int64_t) connectionSerial; - (void)onSuspended; -@end -@interface ARTRealtimeChannel (Private) --(void) setFailed:(ARTStatus *) error; @end + diff --git a/ably-ios/ARTRealtime.h b/ably-ios/ARTRealtime.h index 14b754907..5fe2882d1 100644 --- a/ably-ios/ARTRealtime.h +++ b/ably-ios/ARTRealtime.h @@ -7,14 +7,14 @@ // #import +#import "ably.h" + #import -#import +#import #import #import #import #import -#import - #define ART_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) @@ -22,93 +22,7 @@ @class ARTPresenceMap; @class ARTRealtimeChannelPresenceSubscription; @class ARTEventEmitter; - - -#pragma mark - Enumerations - -typedef NS_ENUM(NSUInteger, ARTRealtimeChannelState) { - ARTRealtimeChannelInitialised, - ARTRealtimeChannelAttaching, - ARTRealtimeChannelAttached, - ARTRealtimeChannelDetaching, - ARTRealtimeChannelDetached, - ARTRealtimeChannelClosed, - ARTRealtimeChannelFailed -}; - -typedef NS_ENUM(NSUInteger, ARTRealtimeConnectionState) { - ARTRealtimeInitialized, - ARTRealtimeConnecting, - ARTRealtimeConnected, - ARTRealtimeDisconnected, - ARTRealtimeSuspended, - ARTRealtimeClosing, - ARTRealtimeClosed, - ARTRealtimeFailed -}; - - -#pragma mark - Protocols - -@protocol ARTSubscription - -- (void)unsubscribe; - -@end - - -#pragma mark - ARTRealtimeChannel - -@interface ARTRealtimeChannel : NSObject - -- (void)publish:(id)payload withName:(NSString *)name cb:(ARTStatusCallback)cb; -- (void)publish:(id)payload cb:(ARTStatusCallback)cb; - -- (void)history:(ARTDataQuery *)query callback:(void (^)(ARTStatus *status, ARTPaginatedResult /* */ *result))callback; - -typedef void (^ARTRealtimeChannelMessageCb)(ARTMessage *); -- (id)subscribe:(ARTRealtimeChannelMessageCb)cb; -- (id)subscribeToName:(NSString *)name cb:(ARTRealtimeChannelMessageCb)cb; -- (id)subscribeToNames:(NSArray *)names cb:(ARTRealtimeChannelMessageCb)cb; - -typedef void (^ARTRealtimeChannelStateCb)(ARTRealtimeChannelState, ARTStatus *); -- (id)subscribeToStateChanges:(ARTRealtimeChannelStateCb)cb; - -- (BOOL)attach; -- (BOOL)detach; -- (void)releaseChannel; //ARC forbids implementation of release -- (ARTRealtimeChannelState)state; -- (ARTPresenceMap *) presenceMap; - -@property (readonly, strong, nonatomic) ARTPresence *presence; - -@end - - -#pragma mark - ARTPresence - -@interface ARTPresence : NSObject - -- (instancetype)initWithChannel:(ARTRealtimeChannel *)channel; -- (void)get:(ARTDataQuery *)query callback:(void (^)(ARTStatus *status, ARTPaginatedResult /* */ *result))callback; -- (void)history:(ARTDataQuery *)query callback:(void (^)(ARTStatus *status, ARTPaginatedResult /* */ *result))callback; - -- (void)enter:(id)data cb:(ARTStatusCallback)cb; -- (void)update:(id)data cb:(ARTStatusCallback)cb; -- (void)leave:(id) data cb:(ARTStatusCallback)cb; - -- (void)enterClient:(NSString *) clientId data:(id) data cb:(ARTStatusCallback) cb; -- (void)updateClient:(NSString *) clientId data:(id) data cb:(ARTStatusCallback) cb; -- (void)leaveClient:(NSString *) clientId data:(id) data cb:(ARTStatusCallback) cb; -- (BOOL)isSyncComplete; - -typedef void (^ARTRealtimeChannelPresenceCb)(ARTPresenceMessage *); -- (id)subscribe:(ARTRealtimeChannelPresenceCb)cb; -- (id)subscribe:(ARTPresenceMessageAction)action cb:(ARTRealtimeChannelPresenceCb)cb; -- (void)unsubscribe:(id)subscription; -- (void)unsubscribe:(id)subscription action:(ARTPresenceMessageAction) action; - -@end +@class ARTRealtimeChannel; #pragma mark - ARTRealtime @@ -155,15 +69,3 @@ typedef void (^ARTRealtimePingCb)(ARTStatus *); @property (readonly, getter=getLogger) ARTLog *logger; @end - - -#pragma mark - ARTEventEmitter - -@interface ARTEventEmitter : NSObject - --(instancetype) initWithRealtime:(ARTRealtime *) realtime; - -typedef void (^ARTRealtimeConnectionStateCb)(ARTRealtimeConnectionState); -- (id)on:(ARTRealtimeConnectionStateCb)cb; - -@end diff --git a/ably-ios/ARTRealtime.m b/ably-ios/ARTRealtime.m index 54c42ddd3..936cbbfa0 100644 --- a/ably-ios/ARTRealtime.m +++ b/ably-ios/ARTRealtime.m @@ -8,144 +8,22 @@ // #import "ARTRealtime.h" +#import "ARTRealtime+Private.h" + #import "ARTRealtimeTransport.h" +#import "ARTRealtimeChannel.h" +#import "ARTStatus.h" +#import "ARTDefault.h" #import "ARTRest.h" #import "ARTMessage.h" #import "ARTPresenceMessage.h" #import "ARTWebSocketTransport.h" #import "ARTNSArray+ARTFunctional.h" -#import "ARTRealtime+Private.h" -#import "ARTLog.h" #import "ARTPresenceMap.h" -#import "ARTStatus.h" -#import "ARTDefault.h" - -@interface ARTQueuedMessage : NSObject - -@property (readonly, strong, nonatomic) ARTProtocolMessage *msg; -@property (readonly, strong, nonatomic) NSMutableArray *cbs; - -- (instancetype)initWithProtocolMessage:(ARTProtocolMessage *)msg cb:(ARTStatusCallback)cb; -- (BOOL)mergeFrom:(ARTProtocolMessage *)msg cb:(ARTStatusCallback)cb; - -- (ARTStatusCallback)cb; - -@end - - -@interface ARTRealtimeChannelSubscription : NSObject - -@property (readonly, weak, nonatomic) ARTRealtimeChannel *channel; -@property (readonly, strong, nonatomic) ARTRealtimeChannelMessageCb cb; - -- (instancetype)initWithChannel:(ARTRealtimeChannel *)channel cb:(ARTRealtimeChannelMessageCb)cb; - -- (void)unsubscribe; - -@end - - -@interface ARTRealtimeChannelPresenceSubscription : NSObject - -@property (readonly, strong, nonatomic) NSMutableSet * excludedActions; -@property (readonly, assign, nonatomic) ARTPresenceMessageAction action; -@property (readonly, weak, nonatomic) ARTRealtimeChannel *channel; -@property (readonly, strong, nonatomic) ARTRealtimeChannelPresenceCb cb; - -- (instancetype)initWithChannel:(ARTRealtimeChannel *)channel cb:(ARTRealtimeChannelPresenceCb)cb; - -- (void)unsubscribe; - -@end - - -@interface ARTRealtimeChannelStateSubscription : NSObject - -@property (readonly, weak, nonatomic) ARTRealtimeChannel *channel; -@property (readonly, strong, nonatomic) ARTRealtimeChannelStateCb cb; - -- (instancetype)initWithChannel:(ARTRealtimeChannel *)channel cb:(ARTRealtimeChannelStateCb)cb; - -- (void)unsubscribe; - -@end - - -@interface ARTRealtimeConnectionStateSubscription : NSObject - -@property (readonly, weak, nonatomic) ARTRealtime *realtime; -@property (readonly, strong, nonatomic) ARTRealtimeConnectionStateCb cb; - -- (instancetype)initWithRealtime:(ARTRealtime *)realtime cb:(ARTRealtimeConnectionStateCb)cb; - -- (void)unsubscribe; - -@end - - -@interface ARTPresence () - -@property (nonatomic, weak) ARTLog * logger; -@property (readonly, weak, nonatomic) ARTRealtimeChannel *channel; - -@end - -@interface ARTEventEmitter () - -@property (readonly, weak, nonatomic) ARTRealtime * realtime; - -@end - - -#pragma mark - ARTRealtimeChannel interface - -@interface ARTRealtimeChannel () - -@property (nonatomic, weak) ARTLog * logger; -@property (readonly, strong, nonatomic) ARTRealtime *realtime; -@property (readonly, strong, nonatomic) NSString *name; -@property (readonly, strong, nonatomic) ARTRestChannel *restChannel; -@property (readwrite, assign, nonatomic)ARTRealtimeChannelState state; -@property (readwrite, strong, nonatomic) NSMutableArray *queuedMessages; -@property (readwrite, strong, nonatomic) NSString *attachSerial; -@property (readonly, strong, nonatomic) NSMutableDictionary *subscriptions; -@property (readonly, strong, nonatomic) NSMutableArray *presenceSubscriptions; -@property (readonly, strong, nonatomic) NSMutableDictionary *presenceDict; -@property (readonly, strong, nonatomic) NSString *clientId; -@property (readonly, strong, nonatomic) NSMutableArray *stateSubscriptions; -@property (readonly, strong, nonatomic) id payloadEncoder; -@property (readwrite, strong, nonatomic) ARTPresenceMap * presenceMap; -@property (readwrite, assign, nonatomic) ARTPresenceMessageAction lastPresenceAction; - -- (instancetype)initWithRealtime:(ARTRealtime *)realtime name:(NSString *)name cipherParams:(ARTCipherParams *)cipherParams; -+ (instancetype)channelWithRealtime:(ARTRealtime *)realtime name:(NSString *)name cipherParams:(ARTCipherParams *)cipherParams; - -- (void)transition:(ARTRealtimeChannelState)state status:(ARTStatus *)status; - -- (void)onChannelMessage:(ARTProtocolMessage *)message; -- (void)publishMessages:(NSArray *)messages cb:(ARTStatusCallback)cb; -- (void)publishPresence:(ARTPresenceMessage *)pm cb:(ARTStatusCallback)cb; -- (void)publishProtocolMessage:(ARTProtocolMessage *)pm cb:(ARTStatusCallback)cb; - -- (void)setAttached:(ARTProtocolMessage *)message; -- (void)setDetached:(ARTProtocolMessage *)message; -- (void)onMessage:(ARTProtocolMessage *)message; -- (void)onPresence:(ARTProtocolMessage *)message; -- (void)onError:(ARTProtocolMessage *)error; -- (void)setSuspended:(ARTStatus *)error; - -- (void)sendQueuedMessages; -- (void)failQueuedMessages:(ARTStatus *)status; - -- (void)unsubscribe:(ARTRealtimeChannelSubscription *)subscription; -- (void)unsubscribeState:(ARTRealtimeChannelStateSubscription *)subscription; - -- (void)broadcastPresence:(ARTPresenceMessage *)pm; - -@end - - -#pragma mark - ARTRealtime interface +#import "ARTProtocolMessage.h" +#import "ARTRealtimeChannelSubscription.h" +#import "ARTEventEmitter.h" +#import "ARTQueuedMessage.h" @interface ARTRealtime () @@ -175,7 +53,6 @@ @interface ARTRealtime () @property (readonly, weak, nonatomic) ARTClientOptions *options; @property (readwrite, strong, nonatomic) ARTErrorInfo *errorReason; - - (void)transition:(ARTRealtimeConnectionState)state; - (BOOL)connect; @@ -214,7 +91,7 @@ - (void)failQueuedMessages:(ARTStatus *)error; - (void)ack:(int64_t)serial count:(int64_t)count; - (void)nack:(int64_t)serial count:(int64_t)count; -// util +// Util - (id)createTransport; - (CFRunLoopTimerRef)startTimer:(void(^)())onTimeout interval:(NSTimeInterval)interval; - (void)cancelTimer:(CFRunLoopTimerRef)timer; @@ -224,596 +101,7 @@ - (void)unsubscribeState:(ARTRealtimeConnectionStateSubscription *)subscription; @end -#pragma mark - ARTQueuedMessage - -@implementation ARTQueuedMessage - -- (instancetype)initWithProtocolMessage:(ARTProtocolMessage *)msg cb:(ARTStatusCallback)cb { - self = [super init]; - if (self) { - _msg = msg; - _cbs = [NSMutableArray array]; - if (cb) { - [_cbs addObject:cb]; - } - } - return self; -} - -- (BOOL)mergeFrom:(ARTProtocolMessage *)msg cb:(ARTStatusCallback)cb { - if ([self.msg mergeFrom:msg]) { - if (cb) { - [self.cbs addObject:cb]; - } - return YES; - } - return NO; -} - -- (ARTStatusCallback)cb { - return ^(ARTStatus * status) { - for (ARTStatusCallback cb in self.cbs) { - cb(status); - } - }; -} - -@end - - -#pragma mark - ARTRealtimeChannelSubscription - -@implementation ARTRealtimeChannelSubscription - -- (instancetype)initWithChannel:(ARTRealtimeChannel *)channel cb:(ARTRealtimeChannelMessageCb)cb { - self = [super init]; - if (self) { - _channel = channel; - _cb = cb; - } - return self; -} - -- (void)unsubscribe { - [self.channel unsubscribe:self]; -} - -@end - - -#pragma mark - ARTRealtimeChannelPresenceSubscription - -@implementation ARTRealtimeChannelPresenceSubscription - -- (instancetype)initWithChannel:(ARTRealtimeChannel *)channel cb:(ARTRealtimeChannelPresenceCb)cb { - self = [super init]; - if (self) { - _channel = channel; - _cb = cb; - _action = ARTPresenceMessageLast; - _excludedActions = [NSMutableSet set]; - } - return self; -} - -- (void)excludeAction:(ARTPresenceMessageAction) action { - [_excludedActions addObject:[NSNumber numberWithInt:(int) action]]; -} -- (void)excludeAllActionsExcept:(ARTPresenceMessageAction) action { - for(int i=0; i<(int) ARTPresenceMessageLast; i++) { - if(i != (int) action) { - [_excludedActions addObject:[NSNumber numberWithInt:(int) i]]; - } - } -} - -- (void)includeAction:(ARTPresenceMessageAction) action { - [_excludedActions removeObject:[NSNumber numberWithInt:(int) action]]; -} - -- (void)unsubscribe { - [self.channel.presence unsubscribe:self]; -} - -@end - - -#pragma mark - ARTRealtimeChannelStateSubscription - -@implementation ARTRealtimeChannelStateSubscription - -- (instancetype)initWithChannel:(ARTRealtimeChannel *)channel cb:(ARTRealtimeChannelStateCb)cb { - self = [super init]; - if (self) { - _channel = channel; - _cb = cb; - } - return self; -} - -- (void)unsubscribe { - [self.channel unsubscribeState:self]; -} - -@end - - -#pragma mark - ARTRealtimeConnectionStateSubscription - -@implementation ARTRealtimeConnectionStateSubscription - -- (instancetype)initWithRealtime:(ARTRealtime *)realtime cb:(ARTRealtimeConnectionStateCb)cb { - self = [super init]; - if (self) { - _realtime = realtime; - _cb = cb; - } - return self; -} - -- (void)unsubscribe { - [self.realtime unsubscribeState:self]; -} - -@end - - -#pragma mark - ARTRealtimeChannel - -@implementation ARTRealtimeChannel - -- (instancetype)initWithRealtime:(ARTRealtime *)realtime name:(NSString *)name cipherParams:(ARTCipherParams *)cipherParams { - self = [super init]; - if (self) { - self.logger = realtime.logger; - _presence = [[ARTPresence alloc] initWithChannel:self]; - _realtime = realtime; - _name = name; - _restChannel = [realtime.rest channel:name]; - _state = ARTRealtimeChannelInitialised; - _queuedMessages = [NSMutableArray array]; - _attachSerial = nil; - _subscriptions = [NSMutableDictionary dictionary]; - _presenceSubscriptions = [NSMutableArray array]; - _stateSubscriptions = [NSMutableArray array]; - _clientId = realtime.clientId; - _payloadEncoder = [ARTPayload defaultPayloadEncoder:cipherParams]; - _presenceMap =[[ARTPresenceMap alloc] init]; - _lastPresenceAction = ARTPresenceMessageAbsent; - } - return self; -} - -+ (instancetype)channelWithRealtime:(ARTRealtime *)realtime name:(NSString *)name cipherParams:(ARTCipherParams *)cipherParams { - return [[ARTRealtimeChannel alloc] initWithRealtime:realtime name:name cipherParams:cipherParams]; -} - -- (void)publish:(id)payload cb:(ARTStatusCallback)cb { - if([payload isKindOfClass:[NSArray class]]) { - NSArray * messages = [ARTMessage messagesWithPayloads:(NSArray *) payload]; - [self publishMessages:messages cb:cb]; - } - else { - [self publish:payload withName:nil cb:cb]; - } -} - -- (void)publish:(id)payload withName:(NSString *)name cb:(ARTStatusCallback)cb { - NSArray *messages = [NSArray arrayWithObject:[ARTMessage messageWithPayload:payload name:name]]; - [self publishMessages:messages cb:cb]; -} - -- (void)publishMessages:(NSArray *)messages cb:(ARTStatusCallback)cb { - if (self.payloadEncoder) { - messages = [messages artMap:^id(ARTMessage *message) { - ARTPayload *encodedPayload = nil; - ARTStatus * status = [self.payloadEncoder encode:message.payload output:&encodedPayload]; - if (status.state != ARTStateOk) { - [self.logger error:@"ARTRealtime: error decoding payload, status: %tu", status]; - } - return [message messageWithPayload:encodedPayload]; - }]; - } - ARTProtocolMessage *msg = [[ARTProtocolMessage alloc] init]; - msg.action = ARTProtocolMessageMessage; - msg.channel = self.name; - msg.messages = messages; - [self publishProtocolMessage:msg cb:cb]; -} - -- (void) requestContinueSync { - [self.logger info:@"ARTRealtime requesting to continue sync operation after reconnect"]; - - ARTProtocolMessage * msg = [[ARTProtocolMessage alloc] init]; - msg.action = ARTProtocolMessageSync; - msg.msgSerial = self.presenceMap.syncSerial; - msg.channel = self.name; - - [self.realtime send:msg cb:^(ARTStatus *status) {}]; -} - -- (void)publishPresence:(ARTPresenceMessage *)msg cb:(ARTStatusCallback)cb { - if (!msg.clientId) { - msg.clientId = self.clientId; - } - if(!msg.clientId) { - cb([ARTStatus state:ARTStateNoClientId]); - return; - } - _lastPresenceAction = msg.action; - - if (msg.payload && self.payloadEncoder) { - ARTPayload *encodedPayload = nil; - ARTStatus * status = [self.payloadEncoder encode:msg.payload output:&encodedPayload]; - if (status.state != ARTStateOk) { - [self.logger warn:@"bad status encoding presence message %d",(int) status]; - } - msg.payload = encodedPayload; - } - - ARTProtocolMessage *pm = [[ARTProtocolMessage alloc] init]; - pm.action = ARTProtocolMessagePresence; - pm.channel = self.name; - pm.presence = @[msg]; - - [self publishProtocolMessage:pm cb:cb]; -} - -- (void)publishProtocolMessage:(ARTProtocolMessage *)pm cb:(ARTStatusCallback)cb { - switch (self.state) { - case ARTRealtimeChannelInitialised: - [self attach]; - // intentional fall-through - case ARTRealtimeChannelAttaching: - { - ARTQueuedMessage *qm = [[ARTQueuedMessage alloc] initWithProtocolMessage:pm cb:cb]; - [self.queuedMessages addObject:qm]; - break; - } - case ARTRealtimeChannelDetaching: - case ARTRealtimeChannelDetached: - case ARTRealtimeChannelFailed: - { - if (cb) { - ARTStatus *status = [ARTStatus state:ARTStateError]; - [status.errorInfo setCode:90001 message:@"invalid channel state"]; - cb(status); - } - break; - } - case ARTRealtimeChannelAttached: - { - [self.realtime send:pm cb:cb]; - break; - } - default: - NSAssert(NO, @"Invalid State"); - } -} - -- (ARTPresenceMap *) presenceMap { - return _presenceMap; -} - --(void) throwOnDisconnectedOrFailed { - if(self.realtime.state == ARTRealtimeFailed || self.realtime.state == ARTRealtimeDisconnected) { - [NSException raise:@"realtime cannot perform action in disconnected or failed state" format:@"state: %d", (int)self.realtime.state]; - } -} - -- (void)history:(ARTDataQuery *)query callback:(void (^)(ARTStatus *, ARTPaginatedResult *))callback { - [self.restChannel history:query callback:callback]; -} - -- (id)subscribe:(ARTRealtimeChannelMessageCb)cb { - // Empty string used for blanket subscriptions - return [self subscribeToName:@"" cb:cb]; -} - -- (id)subscribeToName:(NSString *)name cb:(ARTRealtimeChannelMessageCb)cb { - return [self subscribeToNames:@[name] cb:cb]; -} - -- (id)subscribeToNames:(NSArray *)names cb:(ARTRealtimeChannelMessageCb)cb { - NSSet *nameSet = [NSSet setWithArray:names]; - - ARTRealtimeChannelSubscription *subscription = [[ARTRealtimeChannelSubscription alloc] initWithChannel:self cb:cb]; - - for (NSString *name in nameSet) { - NSMutableArray *subscriptions = [self.subscriptions objectForKey:name]; - if (!subscriptions) { - subscriptions = [NSMutableArray array]; - [self.subscriptions setValue:subscriptions forKey:name]; - } - - [subscriptions addObject:subscription]; - } - - // Trigger attach - [self attach]; - - return subscription; -} - -- (void)unsubscribe:(ARTRealtimeChannelSubscription *)subscription { - NSMutableArray *toRemove = [NSMutableArray array]; - for (NSString *name in self.subscriptions) { - NSMutableArray *subscriptions = [self.subscriptions objectForKey:name]; - [subscriptions removeObject:subscription]; - if (subscriptions.count == 0) { - [toRemove addObject:name]; - } - } - - [self.subscriptions removeObjectsForKeys:toRemove]; -} - -- (id)subscribeToStateChanges:(ARTRealtimeChannelStateCb)cb { - ARTRealtimeChannelStateSubscription *subscription = [[ARTRealtimeChannelStateSubscription alloc] initWithChannel:self cb:cb]; - [self.stateSubscriptions addObject:subscription]; - return subscription; -} - -- (void)unsubscribeState:(ARTRealtimeChannelStateSubscription *)subscription { - [self.stateSubscriptions removeObject:subscription]; -} - -- (void)transition:(ARTRealtimeChannelState)state status:(ARTStatus *)status { - self.state = state; - - for (ARTRealtimeChannelStateSubscription *subscription in self.stateSubscriptions) { - subscription.cb(state, status); - } -} - -/** - Checks that a channelSerial is the final serial in a sequence of sync messages, - by checking that there is nothing after the colon - */ --(bool) isLastChannelSerial:(NSString *) channelSerial { - NSArray * a = [channelSerial componentsSeparatedByString:@":"]; - if([a count] >1 && ![[a objectAtIndex:1] isEqualToString:@""] ) { - return false; - } - return true; -} - -- (void)onChannelMessage:(ARTProtocolMessage *)message { - - if(message.action ==ARTProtocolMessageAttached && [message isSyncEnabled]) { - [self.presenceMap startSync]; - } - else if(message.action == ARTProtocolMessageSync || message.action == ARTProtocolMessagePresence) { - [self.logger info:@"ARTRealtime sync message received"]; - self.presenceMap.syncSerial = message.connectionSerial; - for(int i=0; i< [message.presence count]; i++) { - [self.presenceMap put:[message.presence objectAtIndex:i]]; - } - NSString * channelSerial = message.channelSerial; - if([self isLastChannelSerial:channelSerial]) { - [self.presenceMap endSync]; - } - } - - switch (message.action) { - case ARTProtocolMessageAttached: - [self setAttached:message]; - break; - case ARTProtocolMessageDetached: - [self setDetached:message]; - break; - case ARTProtocolMessageMessage: - [self onMessage:message]; - break; - case ARTProtocolMessagePresence: - [self onPresence:message]; - break; - case ARTProtocolMessageError: - [self onError:message]; - break; - case ARTProtocolMessageSync: - break; - default: - [self.logger warn:@"ARTRealtime, unknown ARTProtocolMessage action: %tu", message.action]; - break; - } - - if(message.action == ARTProtocolMessageSync) { - [self.presenceMap syncMessageProcessed]; - } -} - -- (ARTRealtimeChannelState)state { - return _state; -} - -- (void)setAttached:(ARTProtocolMessage *)message { - self.attachSerial = message.channelSerial; - [self sendQueuedMessages]; - - for (ARTPresenceMessage *pm in message.presence) { - [self.presenceDict setObject:pm forKey:pm.clientId]; - } - [self transition:ARTRealtimeChannelAttached status:ARTStateOk]; -} - -- (void)setDetached:(ARTProtocolMessage *)message { - self.attachSerial = nil; - - ARTStatus *reason = [ARTStatus state:ARTStateNotAttached info:message.error]; - [self detachChannel:reason]; -} - -- (void)releaseChannel { - [self detachChannel:ARTStateOk]; - [self.realtime.allChannels removeObjectForKey:self.name]; -} - -- (void) detachChannel:(ARTStatus *) error { - [self failQueuedMessages:error]; - [self transition:ARTRealtimeChannelDetached status:error]; -} - --(void) setFailed:(ARTStatus *) error { - [self failQueuedMessages:error]; - [self transition:ARTRealtimeChannelFailed status:error]; -} - --(void) setClosed:(ARTStatus *) error { - [self failQueuedMessages:error]; - [self transition:ARTRealtimeChannelClosed status:error]; - -} - -- (void)setSuspended:(ARTStatus *)error { - [self failQueuedMessages:error]; - [self transition:ARTRealtimeChannelDetached status:error]; -} - -- (void)onMessage:(ARTProtocolMessage *)message { - NSArray *blanketSubscriptions = [self.subscriptions objectForKey:@""]; - - int i = 0; - id payloadEncoder = self.payloadEncoder; - for (ARTMessage *m in message.messages) { - ARTMessage *msg = m; - if (payloadEncoder) { - msg = [msg decode:payloadEncoder]; - } - - if (!msg.timestamp) { - msg.timestamp = message.timestamp; - } - if (!msg.id) { - msg.id = [NSString stringWithFormat:@"%@:%d", message.id, i]; - } - - // Notify subscribers that are interested in everything - for (ARTRealtimeChannelSubscription *subscription in blanketSubscriptions) { - subscription.cb(msg); - } - - if (msg.name && msg.name.length) { - // Notify subscribers that are interested in this message - NSArray *nameSubscriptions = [self.subscriptions objectForKey:msg.name]; - for (ARTRealtimeChannelSubscription *subscription in nameSubscriptions) { - subscription.cb(msg); - } - } - - ++i; - } -} - -- (void)onPresence:(ARTProtocolMessage *)message { - int i = 0; - id payloadEncoder = self.payloadEncoder; - for (ARTPresenceMessage *p in message.presence) { - ARTPresenceMessage *pm = p; - if (payloadEncoder) { - pm = [pm decode:payloadEncoder]; - } - - if (!pm.timestamp) { - pm.timestamp = message.timestamp; - } - - if (!pm.id) { - pm.id = [NSString stringWithFormat:@"%@:%d", message.id, i]; - } - - [self.presenceDict setObject:pm forKey:pm.clientId]; - [self broadcastPresence:pm]; - - ++i; - } -} - -- (void)broadcastPresence:(ARTPresenceMessage *)pm { - for (ARTRealtimeChannelPresenceSubscription *subscription in self.presenceSubscriptions) { - if(![[subscription excludedActions] containsObject:[NSNumber numberWithInt:(int) pm.action]]) { - subscription.cb(pm); - } - } -} - -- (void)onError:(ARTProtocolMessage *)msg { - [self failQueuedMessages:[ARTStatus state:ARTStateError info: msg.error]]; - [self transition:ARTRealtimeChannelFailed status:[ARTStatus state:ARTStateError info: msg.error]]; -} - - -- (BOOL)attach { - switch (self.state) { - case ARTRealtimeChannelAttaching: - case ARTRealtimeChannelAttached: - [self.realtime.errorReason setCode:90000 message:@"Already attached"]; - return false; - default: - break; - } - - if (![self.realtime isActive]) { - [self.realtime.errorReason setCode:90000 message:@"Can't attach when not in an active state"]; - return false; - } - - ARTProtocolMessage *attachMessage = [[ARTProtocolMessage alloc] init]; - attachMessage.action = ARTProtocolMessageAttach; - attachMessage.channel = self.name; - - // TODO should queueEvents be forced? - [self.realtime send:attachMessage cb:nil]; - - [self transition:ARTRealtimeChannelAttaching status:ARTStateOk]; - return true; -} - -- (BOOL)detach { - switch (self.state) { - case ARTRealtimeChannelInitialised: - case ARTRealtimeChannelDetaching: - case ARTRealtimeChannelDetached: - [self.realtime.errorReason setCode:90000 message:@"Can't detach when not attahed"]; - return false; - default: - break; - } - - if (![self.realtime isActive]) { - [self.realtime.errorReason setCode:90000 message:@"Can't detach when not in an active state"]; - return false; - } - - ARTProtocolMessage *detachMessage = [[ARTProtocolMessage alloc] init]; - detachMessage.action = ARTProtocolMessageDetach; - detachMessage.channel = self.name; - - [self.realtime send:detachMessage cb:nil]; - [self transition:ARTRealtimeChannelDetaching status:ARTStateOk]; - return true; -} - -- (void)sendQueuedMessages { - NSArray *qms = self.queuedMessages; - self.queuedMessages = [NSMutableArray array]; - for (ARTQueuedMessage *qm in qms) { - [self.realtime send:qm.msg cb:qm.cb]; - } -} - -- (void)failQueuedMessages:(ARTStatus *)status { - NSArray *qms = self.queuedMessages; - self.queuedMessages = [NSMutableArray array]; - for (ARTQueuedMessage *qm in qms) { - qm.cb(status); - } -} - -@end - - -#pragma mark - ARTRealtime +#pragma mark - ARTRealtime implementation @implementation ARTRealtime @@ -822,7 +110,7 @@ - (instancetype)initWithOptions:(ARTClientOptions *)options { } - (instancetype)initWithKey:(NSString *)key { - return [self initWithOptions:[ARTClientOptions optionsWithKey:key]]; + return [self initWithOptions:[[ARTClientOptions alloc] initWithKey:key]]; } - (instancetype)initWithLogger:(ARTLog *)logger andOptions:(ARTClientOptions *)options { @@ -916,7 +204,8 @@ - (void)close { } - (id)time:(void(^)(ARTStatus * status, NSDate *time))cb { - return [self.rest time:cb]; + // FIXME: + return nil; //[self.rest time:cb]; } - (void)ping:(ARTRealtimePingCb) cb { @@ -929,7 +218,8 @@ - (void)ping:(ARTRealtimePingCb) cb { } - (void)stats:(ARTStatsQuery *)query callback:(void (^)(ARTStatus *status, ARTPaginatedResult *result))callback { - [self.rest stats:query callback:callback]; + // FIXME: + //[self.rest stats:query callback:callback]; } - (ARTRealtimeChannel *)channel:(NSString *)channelName { @@ -1004,17 +294,21 @@ - (void)transition:(ARTRealtimeConnectionState)state { if(![self.options.resumeKey isEqualToString:self.connectionKey] || self.options.connectionSerial != self.connectionSerial) { [self.logger warn:@"ARTRealtime connection has reconnected, but resume failed. Detaching all channels"]; for (NSString *channelName in self.allChannels) { - ARTRealtimeChannel *channel = [self.allChannels objectForKey:channelName]; ARTErrorInfo * info = [[ARTErrorInfo alloc] init]; [info setCode:80000 message:@"resume connection failed"]; - [channel detachChannel:[ARTStatus state:ARTStateConnectionDisconnected info:info]]; + + [self.logger warn:@"%@: resume connection failed", channelName]; + // FIXME: + //ARTRealtimeChannel *channel = [self.allChannels objectForKey:channelName]; + //[channel detachChannel:[ARTStatus state:ARTStateConnectionDisconnected info:info]]; } } self.options.resumeKey = nil; for (NSString *channelName in self.allChannels) { ARTRealtimeChannel *channel = [self.allChannels objectForKey:channelName]; if([channel.presenceMap stillSyncing]) { - [channel requestContinueSync]; + // FIXME: + //[channel requestContinueSync]; } } } @@ -1060,13 +354,16 @@ - (void)transition:(ARTRealtimeConnectionState)state { //do nothing. Closed state is coming. } else if(state == ARTRealtimeClosed) { - [channel setClosed:[self defaultError]]; + // FIXME: + //[channel setClosed:[self defaultError]]; } else if(state == ARTRealtimeSuspended) { - [channel detachChannel:[self defaultError]]; + // FIXME: + //[channel detachChannel:[self defaultError]]; } else { - [channel setFailed:[self defaultError]]; + // FIXME: + //[channel setFailed:[self defaultError]]; } } else { @@ -1568,8 +865,7 @@ +(NSString *) protocolStr:(ARTProtocolMessageAction ) action { } } -+(NSString *) ARTRealtimeStateToStr:(ARTRealtimeConnectionState) state -{ ++ (NSString *) ARTRealtimeStateToStr:(ARTRealtimeConnectionState) state { switch(state) { case ARTRealtimeInitialized: @@ -1590,151 +886,7 @@ +(NSString *) ARTRealtimeStateToStr:(ARTRealtimeConnectionState) state return @"ARTRealtimeFailed"; default: return @"unknown connectionstate"; - - } -} - -@end - - -#pragma mark - ARTPresence - -@implementation ARTPresence - --(instancetype) initWithChannel:(ARTRealtimeChannel *) channel { - self = [super init]; - if(self) { - _channel = channel; - self.logger = channel.logger; - } - return self; -} - -- (void)get:(ARTDataQuery *)query callback:(void (^)(ARTStatus *, ARTPaginatedResult *))callback { - [self.channel throwOnDisconnectedOrFailed]; - [self.channel.restChannel.presence get:query callback:callback]; -} - -- (void)history:(ARTDataQuery *)query callback:(void (^)(ARTStatus *, ARTPaginatedResult *))callback { - [self.channel throwOnDisconnectedOrFailed]; - [self.channel.restChannel.presence history:query callback:callback]; -} - -- (void)enter:(id)data cb:(ARTStatusCallback)cb { - [self enterClient:self.channel.clientId data:data cb:cb]; -} - -- (void) enterClient:(NSString *) clientId data:(id) data cb:(ARTStatusCallback) cb { - if(!clientId) { - [NSException raise:@"Cannot publish presence without a clientId" format:@""]; - } - ARTPresenceMessage *msg = [[ARTPresenceMessage alloc] init]; - msg.action = ARTPresenceMessageEnter; - msg.clientId = clientId; - if(data) { - msg.payload = [ARTPayload payloadWithPayload:data encoding:@""]; - } - - msg.connectionId = self.channel.realtime.connectionId; - [self.channel publishPresence:msg cb:cb]; - -} - -- (void)update:(id)data cb:(ARTStatusCallback)cb { - [self updateClient:self.channel.clientId data:data cb:cb]; -} - -- (void)updateClient:(NSString *) clientId data:(id) data cb:(ARTStatusCallback) cb { - ARTPresenceMessage *msg = [[ARTPresenceMessage alloc] init]; - msg.action = ARTPresenceMessageUpdate; - msg.clientId = clientId; - if(!msg.clientId) { - cb([ARTStatus state:ARTStateNoClientId]); - return; - } - if(data) { - msg.payload = [ARTPayload payloadWithPayload:data encoding:@""]; - } - msg.connectionId = self.channel.realtime.connectionId; - - [self.channel publishPresence:msg cb:cb]; - -} - -- (void)leave:(id) data cb:(ARTStatusCallback)cb { - [self leaveClient:self.channel.clientId data:data cb:cb]; -} - -- (void) leaveClient:(NSString *) clientId data:(id) data cb:(ARTStatusCallback) cb { - - if([clientId isEqualToString:self.channel.clientId]) { - if(self.channel.lastPresenceAction != ARTPresenceMessageEnter && self.channel.lastPresenceAction != ARTPresenceMessageUpdate) { - [NSException raise:@"Cannot leave a channel before you've entered it" format:@""]; - } } - ARTPresenceMessage *msg = [[ARTPresenceMessage alloc] init]; - msg.action = ARTPresenceMessageLeave; - - if(data) { - msg.payload= [ARTPayload payloadWithPayload:data encoding:@""]; - } - msg.clientId = clientId; - msg.connectionId = self.channel.realtime.connectionId; - if(!msg.clientId) { - cb([ARTStatus state:ARTStateNoClientId]); - return; - } - [self.channel publishPresence:msg cb:cb]; - -} - -- (BOOL)isSyncComplete { - return [self.channel.presenceMap isSyncComplete]; -} - -- (id)subscribe:(ARTRealtimeChannelPresenceCb)cb { - ARTRealtimeChannelPresenceSubscription *subscription = [[ARTRealtimeChannelPresenceSubscription alloc] initWithChannel:self.channel cb:cb]; - [self.channel.presenceSubscriptions addObject:subscription]; - [self.channel attach]; - return subscription; -} - -- (id)subscribe:(ARTPresenceMessageAction) action cb:(ARTRealtimeChannelPresenceCb)cb { - ARTRealtimeChannelPresenceSubscription *subscription = (ARTRealtimeChannelPresenceSubscription *) [self subscribe:cb]; - [subscription excludeAllActionsExcept:action]; - return subscription; -} - -- (void)unsubscribe:(id)subscription action:(ARTPresenceMessageAction) action { - ARTRealtimeChannelPresenceSubscription * s = (ARTRealtimeChannelPresenceSubscription *) subscription; - [s excludeAction:action]; -} - -- (void)unsubscribe:(ARTRealtimeChannelPresenceSubscription *)subscription { - ARTRealtimeChannelPresenceSubscription *s = (ARTRealtimeChannelPresenceSubscription *) subscription; - [self.channel.presenceSubscriptions removeObject:s]; -} - -@end - - -#pragma mark - ARTEventEmitter - -@implementation ARTEventEmitter - --(instancetype) initWithRealtime:(ARTRealtime *) realtime { - self = [super init]; - if(self) { - _realtime = realtime; - } - return self; -} - -- (id)on:(ARTRealtimeConnectionStateCb)cb { - ARTRealtimeConnectionStateSubscription *subscription = [[ARTRealtimeConnectionStateSubscription alloc] initWithRealtime:self.realtime cb:cb]; - [self.realtime.stateSubscriptions addObject:subscription]; - cb(self.realtime.state); - return subscription; } @end diff --git a/ably-ios/ARTRealtimeChannel+Private.h b/ably-ios/ARTRealtimeChannel+Private.h new file mode 100644 index 000000000..87eeacaa5 --- /dev/null +++ b/ably-ios/ARTRealtimeChannel+Private.h @@ -0,0 +1,18 @@ + +// +// ARTRealtimeChannel+Private.h +// ably-ios +// +// Created by Ricardo Pereira on 30/09/2015. +// Copyright (c) 2015 Ably. All rights reserved. +// + +#import + +#import "ARTRealtimeChannel.h" + +@interface ARTRealtimeChannel (Private) + +- (void)setFailed:(ARTStatus *)error; + +@end diff --git a/ably-ios/ARTRealtimeChannel.h b/ably-ios/ARTRealtimeChannel.h new file mode 100644 index 000000000..d2c8506cb --- /dev/null +++ b/ably-ios/ARTRealtimeChannel.h @@ -0,0 +1,92 @@ +// +// ARTRealtimeChannel.h +// ably +// +// Created by Ricardo Pereira on 30/09/2015. +// Copyright (c) 2015 Ably. All rights reserved. +// + +#import +#import "ably.h" + +#import "ARTPresenceMessage.h" + +@protocol ARTSubscription; +@protocol ARTPayloadEncoder; + +@class ARTChannel; +@class ARTRealtime; +@class ARTDataQuery; +@class ARTPresence; +@class ARTPresenceMap; +@class ARTMessage; +@class ARTPaginatedResult; +@class ARTProtocolMessage; +@class ARTRealtimeChannelSubscription; +@class ARTRealtimeChannelStateSubscription; + + +@interface ARTRealtimeChannel : NSObject + +@property (nonatomic, weak) ARTLog * logger; +@property (readonly, strong, nonatomic) ARTRealtime *realtime; +@property (readonly, strong, nonatomic) NSString *name; +@property (readonly, strong, nonatomic) ARTChannel *restChannel; //?! +@property (readwrite, assign, nonatomic) ARTRealtimeChannelState state; +@property (readwrite, strong, nonatomic) NSMutableArray *queuedMessages; +@property (readwrite, strong, nonatomic) NSString *attachSerial; +@property (readonly, strong, nonatomic) NSMutableDictionary *subscriptions; +@property (readonly, strong, nonatomic) NSMutableArray *presenceSubscriptions; +@property (readonly, strong, nonatomic) NSMutableDictionary *presenceDict; +@property (readonly, strong, nonatomic) NSString *clientId; +@property (readonly, strong, nonatomic) NSMutableArray *stateSubscriptions; +@property (readonly, strong, nonatomic) id payloadEncoder; +@property (readwrite, strong, nonatomic) ARTPresenceMap * presenceMap; +@property (readwrite, assign, nonatomic) ARTPresenceAction lastPresenceAction; + +- (instancetype)initWithRealtime:(ARTRealtime *)realtime name:(NSString *)name cipherParams:(ARTCipherParams *)cipherParams; ++ (instancetype)channelWithRealtime:(ARTRealtime *)realtime name:(NSString *)name cipherParams:(ARTCipherParams *)cipherParams; + +- (void)transition:(ARTRealtimeChannelState)state status:(ARTStatus *)status; + +- (void)onChannelMessage:(ARTProtocolMessage *)message; +- (void)publishMessages:(NSArray *)messages cb:(ARTStatusCallback)cb; +- (void)publishPresence:(ARTPresenceMessage *)pm cb:(ARTStatusCallback)cb; +- (void)publishProtocolMessage:(ARTProtocolMessage *)pm cb:(ARTStatusCallback)cb; + +- (void)setAttached:(ARTProtocolMessage *)message; +- (void)setDetached:(ARTProtocolMessage *)message; +- (void)onMessage:(ARTProtocolMessage *)message; +- (void)onPresence:(ARTProtocolMessage *)message; +- (void)onError:(ARTProtocolMessage *)error; +- (void)setSuspended:(ARTStatus *)error; + +- (void)sendQueuedMessages; +- (void)failQueuedMessages:(ARTStatus *)status; + +- (void)unsubscribe:(ARTRealtimeChannelSubscription *)subscription; +- (void)unsubscribeState:(ARTRealtimeChannelStateSubscription *)subscription; + +- (void)broadcastPresence:(ARTPresenceMessage *)pm; + + +- (void)publish:(id)payload withName:(NSString *)name cb:(ARTStatusCallback)cb; +- (void)publish:(id)payload cb:(ARTStatusCallback)cb; + +- (void)history:(ARTDataQuery *)query callback:(void (^)(ARTStatus *status, ARTPaginatedResult /* */ *result))callback; + +- (id)subscribe:(ARTRealtimeChannelMessageCb)cb; +- (id)subscribeToName:(NSString *)name cb:(ARTRealtimeChannelMessageCb)cb; +- (id)subscribeToNames:(NSArray *)names cb:(ARTRealtimeChannelMessageCb)cb; +- (id)subscribeToStateChanges:(ARTRealtimeChannelStateCb)cb; + +- (BOOL)attach; +- (BOOL)detach; + +- (void)releaseChannel; //ARC forbids implementation of release +- (ARTRealtimeChannelState)state; +- (ARTPresenceMap *)presenceMap; + +@property (readonly, strong, nonatomic) ARTPresence *presence; + +@end diff --git a/ably-ios/ARTRealtimeChannel.m b/ably-ios/ARTRealtimeChannel.m new file mode 100644 index 000000000..56ddb1cee --- /dev/null +++ b/ably-ios/ARTRealtimeChannel.m @@ -0,0 +1,491 @@ +// +// ARTRealtimeChannel.m +// ably +// +// Created by Ricardo Pereira on 30/09/2015. +// Copyright (c) 2015 Ably. All rights reserved. +// + +#import "ARTRealtimeChannel.h" + +#import "ARTRealtime.h" +#import "ARTPresence.h" +#import "ARTChannel.h" +#import "ARTProtocolMessage.h" +#import "ARTRealtimeChannelSubscription.h" +#import "ARTPresenceMap.h" +#import "ARTQueuedMessage.h" + +@interface ARTRealtimeChannel () + +@end + +@implementation ARTRealtimeChannel + +- (instancetype)initWithRealtime:(ARTRealtime *)realtime name:(NSString *)name cipherParams:(ARTCipherParams *)cipherParams { + self = [super init]; + if (self) { + _logger = realtime.logger; + _presence = [[ARTPresence alloc] initWithChannel:self]; + _realtime = realtime; + _name = name; + // FIXME: + //_restChannel = [realtime.rest channel:name]; + _state = ARTRealtimeChannelInitialised; + _queuedMessages = [NSMutableArray array]; + _attachSerial = nil; + _subscriptions = [NSMutableDictionary dictionary]; + _presenceSubscriptions = [NSMutableArray array]; + _stateSubscriptions = [NSMutableArray array]; + // FIXME: + //_clientId = realtime.clientId; + _payloadEncoder = [ARTPayload defaultPayloadEncoder:cipherParams]; + _presenceMap =[[ARTPresenceMap alloc] init]; + _lastPresenceAction = ARTPresenceAbsent; + } + return self; +} + ++ (instancetype)channelWithRealtime:(ARTRealtime *)realtime name:(NSString *)name cipherParams:(ARTCipherParams *)cipherParams { + return [[ARTRealtimeChannel alloc] initWithRealtime:realtime name:name cipherParams:cipherParams]; +} + +- (void)publish:(id)payload cb:(ARTStatusCallback)cb { + if([payload isKindOfClass:[NSArray class]]) { + NSArray * messages = [ARTMessage messagesWithPayloads:(NSArray *) payload]; + [self publishMessages:messages cb:cb]; + } + else { + [self publish:payload withName:nil cb:cb]; + } +} + +- (void)publish:(id)payload withName:(NSString *)name cb:(ARTStatusCallback)cb { + // FIXME: + //NSArray *messages = [NSArray arrayWithObject:[ARTMessage messageWithPayload:payload name:name]]; + NSArray *messages = nil; + [self publishMessages:messages cb:cb]; +} + +- (void)publishMessages:(NSArray *)messages cb:(ARTStatusCallback)cb { + if (self.payloadEncoder) { + messages = [messages artMap:^id(ARTMessage *message) { + ARTPayload *encodedPayload = nil; + ARTStatus * status = [self.payloadEncoder encode:message.payload output:&encodedPayload]; + if (status.state != ARTStateOk) { + [self.logger error:@"ARTRealtime: error decoding payload, status: %tu", status]; + } + // FIXME: + return nil; //[message messageWithPayload:encodedPayload]; + }]; + } + ARTProtocolMessage *msg = [[ARTProtocolMessage alloc] init]; + msg.action = ARTProtocolMessageMessage; + msg.channel = self.name; + msg.messages = messages; + [self publishProtocolMessage:msg cb:cb]; +} + +- (void) requestContinueSync { + [self.logger info:@"ARTRealtime requesting to continue sync operation after reconnect"]; + + ARTProtocolMessage * msg = [[ARTProtocolMessage alloc] init]; + msg.action = ARTProtocolMessageSync; + msg.msgSerial = self.presenceMap.syncSerial; + msg.channel = self.name; + + // FIXME: + //[self.realtime send:msg cb:^(ARTStatus *status) {}]; +} + +- (void)publishPresence:(ARTPresenceMessage *)msg cb:(ARTStatusCallback)cb { + if (!msg.clientId) { + msg.clientId = self.clientId; + } + if(!msg.clientId) { + cb([ARTStatus state:ARTStateNoClientId]); + return; + } + _lastPresenceAction = msg.action; + + if (msg.payload && self.payloadEncoder) { + ARTPayload *encodedPayload = nil; + ARTStatus * status = [self.payloadEncoder encode:msg.payload output:&encodedPayload]; + if (status.state != ARTStateOk) { + [self.logger warn:@"bad status encoding presence message %d",(int) status]; + } + msg.payload = encodedPayload; + } + + ARTProtocolMessage *pm = [[ARTProtocolMessage alloc] init]; + pm.action = ARTProtocolMessagePresence; + pm.channel = self.name; + pm.presence = @[msg]; + + [self publishProtocolMessage:pm cb:cb]; +} + +- (void)publishProtocolMessage:(ARTProtocolMessage *)pm cb:(ARTStatusCallback)cb { + switch (self.state) { + case ARTRealtimeChannelInitialised: + [self attach]; + // intentional fall-through + case ARTRealtimeChannelAttaching: + { + ARTQueuedMessage *qm = [[ARTQueuedMessage alloc] initWithProtocolMessage:pm cb:cb]; + [self.queuedMessages addObject:qm]; + break; + } + case ARTRealtimeChannelDetaching: + case ARTRealtimeChannelDetached: + case ARTRealtimeChannelFailed: + { + if (cb) { + ARTStatus *status = [ARTStatus state:ARTStateError]; + [status.errorInfo setCode:90001 message:@"invalid channel state"]; + cb(status); + } + break; + } + case ARTRealtimeChannelAttached: + { + // FIXME: + //[self.realtime send:pm cb:cb]; + break; + } + default: + NSAssert(NO, @"Invalid State"); + } +} + +- (ARTPresenceMap *) presenceMap { + return _presenceMap; +} + +- (void)throwOnDisconnectedOrFailed { + if(self.realtime.state == ARTRealtimeFailed || self.realtime.state == ARTRealtimeDisconnected) { + [NSException raise:@"realtime cannot perform action in disconnected or failed state" format:@"state: %d", (int)self.realtime.state]; + } +} + +- (void)history:(ARTDataQuery *)query callback:(void (^)(ARTStatus *, ARTPaginatedResult *))callback { + // FIXME: + //[self.restChannel history:query callback:callback]; +} + +- (id)subscribe:(ARTRealtimeChannelMessageCb)cb { + // Empty string used for blanket subscriptions + return [self subscribeToName:@"" cb:cb]; +} + +- (id)subscribeToName:(NSString *)name cb:(ARTRealtimeChannelMessageCb)cb { + return [self subscribeToNames:@[name] cb:cb]; +} + +- (id)subscribeToNames:(NSArray *)names cb:(ARTRealtimeChannelMessageCb)cb { + NSSet *nameSet = [NSSet setWithArray:names]; + + ARTRealtimeChannelSubscription *subscription = [[ARTRealtimeChannelSubscription alloc] initWithChannel:self cb:cb]; + + for (NSString *name in nameSet) { + NSMutableArray *subscriptions = [self.subscriptions objectForKey:name]; + if (!subscriptions) { + subscriptions = [NSMutableArray array]; + [self.subscriptions setValue:subscriptions forKey:name]; + } + + [subscriptions addObject:subscription]; + } + + // Trigger attach + [self attach]; + + return subscription; +} + +- (void)unsubscribe:(ARTRealtimeChannelSubscription *)subscription { + NSMutableArray *toRemove = [NSMutableArray array]; + for (NSString *name in self.subscriptions) { + NSMutableArray *subscriptions = [self.subscriptions objectForKey:name]; + [subscriptions removeObject:subscription]; + if (subscriptions.count == 0) { + [toRemove addObject:name]; + } + } + + [self.subscriptions removeObjectsForKeys:toRemove]; +} + +- (id)subscribeToStateChanges:(ARTRealtimeChannelStateCb)cb { + ARTRealtimeChannelStateSubscription *subscription = [[ARTRealtimeChannelStateSubscription alloc] initWithChannel:self cb:cb]; + [self.stateSubscriptions addObject:subscription]; + return subscription; +} + +- (void)unsubscribeState:(ARTRealtimeChannelStateSubscription *)subscription { + [self.stateSubscriptions removeObject:subscription]; +} + +- (void)transition:(ARTRealtimeChannelState)state status:(ARTStatus *)status { + self.state = state; + + for (ARTRealtimeChannelStateSubscription *subscription in self.stateSubscriptions) { + subscription.cb(state, status); + } +} + +/** + Checks that a channelSerial is the final serial in a sequence of sync messages, + by checking that there is nothing after the colon + */ +- (bool)isLastChannelSerial:(NSString *) channelSerial { + NSArray * a = [channelSerial componentsSeparatedByString:@":"]; + if([a count] >1 && ![[a objectAtIndex:1] isEqualToString:@""] ) { + return false; + } + return true; +} + +- (void)onChannelMessage:(ARTProtocolMessage *)message { + + if(message.action ==ARTProtocolMessageAttached && [message isSyncEnabled]) { + [self.presenceMap startSync]; + } + else if(message.action == ARTProtocolMessageSync || message.action == ARTProtocolMessagePresence) { + [self.logger info:@"ARTRealtime sync message received"]; + self.presenceMap.syncSerial = message.connectionSerial; + for(int i=0; i< [message.presence count]; i++) { + [self.presenceMap put:[message.presence objectAtIndex:i]]; + } + NSString * channelSerial = message.channelSerial; + if([self isLastChannelSerial:channelSerial]) { + [self.presenceMap endSync]; + } + } + + switch (message.action) { + case ARTProtocolMessageAttached: + [self setAttached:message]; + break; + case ARTProtocolMessageDetached: + [self setDetached:message]; + break; + case ARTProtocolMessageMessage: + [self onMessage:message]; + break; + case ARTProtocolMessagePresence: + [self onPresence:message]; + break; + case ARTProtocolMessageError: + [self onError:message]; + break; + case ARTProtocolMessageSync: + break; + default: + [self.logger warn:@"ARTRealtime, unknown ARTProtocolMessage action: %tu", message.action]; + break; + } + + if(message.action == ARTProtocolMessageSync) { + [self.presenceMap syncMessageProcessed]; + } +} + +- (ARTRealtimeChannelState)state { + return _state; +} + +- (void)setAttached:(ARTProtocolMessage *)message { + self.attachSerial = message.channelSerial; + [self sendQueuedMessages]; + + for (ARTPresenceMessage *pm in message.presence) { + [self.presenceDict setObject:pm forKey:pm.clientId]; + } + [self transition:ARTRealtimeChannelAttached status:ARTStateOk]; +} + +- (void)setDetached:(ARTProtocolMessage *)message { + self.attachSerial = nil; + + ARTStatus *reason = [ARTStatus state:ARTStateNotAttached info:message.error]; + [self detachChannel:reason]; +} + +- (void)releaseChannel { + [self detachChannel:ARTStateOk]; + // FIXME: + //[self.realtime.allChannels removeObjectForKey:self.name]; +} + +- (void) detachChannel:(ARTStatus *) error { + [self failQueuedMessages:error]; + [self transition:ARTRealtimeChannelDetached status:error]; +} + +-(void) setFailed:(ARTStatus *) error { + [self failQueuedMessages:error]; + [self transition:ARTRealtimeChannelFailed status:error]; +} + +-(void) setClosed:(ARTStatus *) error { + [self failQueuedMessages:error]; + [self transition:ARTRealtimeChannelClosed status:error]; +} + +- (void)setSuspended:(ARTStatus *)error { + [self failQueuedMessages:error]; + [self transition:ARTRealtimeChannelDetached status:error]; +} + +- (void)onMessage:(ARTProtocolMessage *)message { + NSArray *blanketSubscriptions = [self.subscriptions objectForKey:@""]; + + int i = 0; + id payloadEncoder = self.payloadEncoder; + for (ARTMessage *m in message.messages) { + ARTMessage *msg = m; + if (payloadEncoder) { + msg = [msg decode:payloadEncoder]; + } + + if (!msg.timestamp) { + msg.timestamp = message.timestamp; + } + if (!msg.id) { + msg.id = [NSString stringWithFormat:@"%@:%d", message.id, i]; + } + + // Notify subscribers that are interested in everything + for (ARTRealtimeChannelSubscription *subscription in blanketSubscriptions) { + subscription.cb(msg); + } + + if (msg.name && msg.name.length) { + // Notify subscribers that are interested in this message + NSArray *nameSubscriptions = [self.subscriptions objectForKey:msg.name]; + for (ARTRealtimeChannelSubscription *subscription in nameSubscriptions) { + subscription.cb(msg); + } + } + + ++i; + } +} + +- (void)onPresence:(ARTProtocolMessage *)message { + int i = 0; + id payloadEncoder = self.payloadEncoder; + for (ARTPresenceMessage *p in message.presence) { + ARTPresenceMessage *pm = p; + if (payloadEncoder) { + pm = [pm decode:payloadEncoder]; + } + + if (!pm.timestamp) { + pm.timestamp = message.timestamp; + } + + if (!pm.id) { + pm.id = [NSString stringWithFormat:@"%@:%d", message.id, i]; + } + + [self.presenceDict setObject:pm forKey:pm.clientId]; + [self broadcastPresence:pm]; + + ++i; + } +} + +- (void)broadcastPresence:(ARTPresenceMessage *)pm { + for (ARTRealtimeChannelPresenceSubscription *subscription in self.presenceSubscriptions) { + if(![[subscription excludedActions] containsObject:[NSNumber numberWithInt:(int) pm.action]]) { + subscription.cb(pm); + } + } +} + +- (void)onError:(ARTProtocolMessage *)msg { + [self failQueuedMessages:[ARTStatus state:ARTStateError info: msg.error]]; + [self transition:ARTRealtimeChannelFailed status:[ARTStatus state:ARTStateError info: msg.error]]; +} + +- (BOOL)attach { + switch (self.state) { + case ARTRealtimeChannelAttaching: + case ARTRealtimeChannelAttached: + // FIXME: + //[self.realtime.errorReason setCode:90000 message:@"Already attached"]; + return false; + default: + break; + } + + // FIXME: + /* + if (![self.realtime isActive]) { + [self.realtime.errorReason setCode:90000 message:@"Can't attach when not in an active state"]; + return false; + } + */ + + ARTProtocolMessage *attachMessage = [[ARTProtocolMessage alloc] init]; + attachMessage.action = ARTProtocolMessageAttach; + attachMessage.channel = self.name; + + // TODO should queueEvents be forced? + // FIXME: + //[self.realtime send:attachMessage cb:nil]; + + [self transition:ARTRealtimeChannelAttaching status:ARTStateOk]; + return true; +} + +- (BOOL)detach { + switch (self.state) { + case ARTRealtimeChannelInitialised: + case ARTRealtimeChannelDetaching: + case ARTRealtimeChannelDetached: + // FIXME: + //[self.realtime.errorReason setCode:90000 message:@"Can't detach when not attahed"]; + return false; + default: + break; + } + + // FIXME: + /* + if (![self.realtime isActive]) { + [self.realtime.errorReason setCode:90000 message:@"Can't detach when not in an active state"]; + return false; + } + */ + + ARTProtocolMessage *detachMessage = [[ARTProtocolMessage alloc] init]; + detachMessage.action = ARTProtocolMessageDetach; + detachMessage.channel = self.name; + + // FIXME: + //[self.realtime send:detachMessage cb:nil]; + [self transition:ARTRealtimeChannelDetaching status:ARTStateOk]; + return true; +} + +- (void)sendQueuedMessages { + //NSArray *qms = self.queuedMessages; + self.queuedMessages = [NSMutableArray array]; + // FIXME: + //for (ARTQueuedMessage *qm in qms) { + //[self.realtime send:qm.msg cb:qm.cb]; + //} +} + +- (void)failQueuedMessages:(ARTStatus *)status { + NSArray *qms = self.queuedMessages; + self.queuedMessages = [NSMutableArray array]; + for (ARTQueuedMessage *qm in qms) { + qm.cb(status); + } +} + +@end diff --git a/ably-ios/ARTRealtimeChannelSubscription.h b/ably-ios/ARTRealtimeChannelSubscription.h new file mode 100644 index 000000000..6daa218d9 --- /dev/null +++ b/ably-ios/ARTRealtimeChannelSubscription.h @@ -0,0 +1,65 @@ +// +// ARTRealtimeChannelSubscription.h +// ably +// +// Created by Ricardo Pereira on 01/10/2015. +// Copyright (c) 2015 Ably. All rights reserved. +// + +#import +#import "ably.h" + +#import "ARTSubscription.h" +#import "ARTPresenceMessage.h" + +@class ARTRealtime; +@class ARTRealtimeChannel; + +@interface ARTRealtimeChannelSubscription : NSObject + +@property (readonly, weak, nonatomic) ARTRealtimeChannel *channel; +@property (readonly, strong, nonatomic) ARTRealtimeChannelMessageCb cb; + +- (instancetype)initWithChannel:(ARTRealtimeChannel *)channel cb:(ARTRealtimeChannelMessageCb)cb; + +- (void)unsubscribe; + +@end + + +@interface ARTRealtimeChannelPresenceSubscription : NSObject + +@property (readonly, strong, nonatomic) NSMutableSet *excludedActions; +@property (readonly, assign, nonatomic) ARTPresenceAction action; +@property (readonly, weak, nonatomic) ARTRealtimeChannel *channel; +@property (readonly, strong, nonatomic) ARTRealtimeChannelPresenceCb cb; + +- (instancetype)initWithChannel:(ARTRealtimeChannel *)channel cb:(ARTRealtimeChannelPresenceCb)cb; + +- (void)unsubscribe; + +@end + + +@interface ARTRealtimeChannelStateSubscription : NSObject + +@property (readonly, weak, nonatomic) ARTRealtimeChannel *channel; +@property (readonly, strong, nonatomic) ARTRealtimeChannelStateCb cb; + +- (instancetype)initWithChannel:(ARTRealtimeChannel *)channel cb:(ARTRealtimeChannelStateCb)cb; + +- (void)unsubscribe; + +@end + + +@interface ARTRealtimeConnectionStateSubscription : NSObject + +@property (readonly, weak, nonatomic) ARTRealtime *realtime; +@property (readonly, strong, nonatomic) ARTRealtimeConnectionStateCb cb; + +- (instancetype)initWithRealtime:(ARTRealtime *)realtime cb:(ARTRealtimeConnectionStateCb)cb; + +- (void)unsubscribe; + +@end diff --git a/ably-ios/ARTRealtimeChannelSubscription.m b/ably-ios/ARTRealtimeChannelSubscription.m new file mode 100644 index 000000000..b09c5a597 --- /dev/null +++ b/ably-ios/ARTRealtimeChannelSubscription.m @@ -0,0 +1,112 @@ +// +// ARTRealtimeChannelSubscription.m +// ably +// +// Created by Ricardo Pereira on 01/10/2015. +// Copyright (c) 2015 Ably. All rights reserved. +// + +#import "ARTRealtimeChannelSubscription.h" + +#import "ARTRealtime.h" +#import "ARTRealtimeChannel.h" + +#pragma mark - ARTRealtimeChannelSubscription + +@implementation ARTRealtimeChannelSubscription + +- (instancetype)initWithChannel:(ARTRealtimeChannel *)channel cb:(ARTRealtimeChannelMessageCb)cb { + self = [super init]; + if (self) { + _channel = channel; + _cb = cb; + } + return self; +} + +- (void)unsubscribe { + // FIXME: + //[self.channel unsubscribe:self]; +} + +@end + + +#pragma mark - ARTRealtimeChannelPresenceSubscription + +@implementation ARTRealtimeChannelPresenceSubscription + +- (instancetype)initWithChannel:(ARTRealtimeChannel *)channel cb:(ARTRealtimeChannelPresenceCb)cb { + self = [super init]; + if (self) { + _channel = channel; + _cb = cb; + _action = ARTPresenceLast; + _excludedActions = [NSMutableSet set]; + } + return self; +} + +- (void)excludeAction:(ARTPresenceAction) action { + [_excludedActions addObject:[NSNumber numberWithInt:(int) action]]; +} +- (void)excludeAllActionsExcept:(ARTPresenceAction) action { + for(int i=0; i<(int) ARTPresenceLast; i++) { + if(i != (int) action) { + [_excludedActions addObject:[NSNumber numberWithInt:(int) i]]; + } + } +} + +- (void)includeAction:(ARTPresenceAction) action { + [_excludedActions removeObject:[NSNumber numberWithInt:(int) action]]; +} + +- (void)unsubscribe { + // FIXME: + //[self.channel.presence unsubscribe:self]; +} + +@end + + +#pragma mark - ARTRealtimeChannelStateSubscription + +@implementation ARTRealtimeChannelStateSubscription + +- (instancetype)initWithChannel:(ARTRealtimeChannel *)channel cb:(ARTRealtimeChannelStateCb)cb { + self = [super init]; + if (self) { + _channel = channel; + _cb = cb; + } + return self; +} + +- (void)unsubscribe { + // FIXME: + //[self.channel unsubscribeState:self]; +} + +@end + + +#pragma mark - ARTRealtimeConnectionStateSubscription + +@implementation ARTRealtimeConnectionStateSubscription + +- (instancetype)initWithRealtime:(ARTRealtime *)realtime cb:(ARTRealtimeConnectionStateCb)cb { + self = [super init]; + if (self) { + _realtime = realtime; + _cb = cb; + } + return self; +} + +- (void)unsubscribe { + // FIXME: + //[self.realtime unsubscribeState:self]; +} + +@end diff --git a/ably-ios/ARTRealtimeTransport.h b/ably-ios/ARTRealtimeTransport.h index baca6031c..ff3655a78 100644 --- a/ably-ios/ARTRealtimeTransport.h +++ b/ably-ios/ARTRealtimeTransport.h @@ -8,10 +8,11 @@ #import -#import "ARTProtocolMessage.h" - @protocol ARTRealtimeTransport; +@class ARTProtocolMessage; +@class ARTStatus; + @protocol ARTRealtimeTransportDelegate - (void)realtimeTransport:(id)transport didReceiveMessage:(ARTProtocolMessage *)message; diff --git a/ably-ios/ARTRest+Private.h b/ably-ios/ARTRest+Private.h index 06a8529de..4c93ec9a0 100644 --- a/ably-ios/ARTRest+Private.h +++ b/ably-ios/ARTRest+Private.h @@ -10,10 +10,7 @@ #import @protocol ARTEncoder; - -/** - ARTRest private methods that are used for whitebox testing. - */ +@protocol ARTHTTPExecutor; typedef NS_ENUM(NSUInteger, ARTAuthentication) { ARTAuthenticationOff, @@ -21,7 +18,8 @@ typedef NS_ENUM(NSUInteger, ARTAuthentication) { ARTAuthenticationUseBasic }; -@interface ARTRest () +/// ARTRest private methods that are used for whitebox testing +@interface ARTRest (Private) @property (readonly, strong, nonatomic) id defaultEncoder; @property (nonatomic, strong) id httpExecutor; @@ -35,12 +33,15 @@ typedef NS_ENUM(NSUInteger, ARTAuthentication) { - (id)get:(NSString *)relUrl authenticated:(BOOL)authenticated cb:(ARTHttpCb)cb; - (id)get:(NSString *)relUrl headers:(NSDictionary *)headers authenticated:(BOOL)authenticated cb:(ARTHttpCb)cb; + - (id)post:(NSString *)relUrl headers:(NSDictionary *)headers body:(NSData *)body authenticated:(ARTAuthentication)authenticated cb:(ARTHttpCb)cb; - (id)withAuthHeadersUseBasic:(BOOL) useBasic cb:(id(^)(NSDictionary *))cb; + - (id)withAuthHeaders:(id(^)(NSDictionary *authHeaders))cb; + - (id)withAuthParams:(id(^)(NSDictionary *authParams))cb; --(id) postTestStats:(NSArray *) stats cb:(void(^)(ARTStatus * status)) cb; +- (id)postTestStats:(NSArray *) stats cb:(void(^)(ARTStatus * status)) cb; @end diff --git a/ably-ios/ARTRest.h b/ably-ios/ARTRest.h index 0fdb89ea3..9e7012121 100644 --- a/ably-ios/ARTRest.h +++ b/ably-ios/ARTRest.h @@ -7,38 +7,18 @@ // #import -#import -#import -#import -#import -#import -#import -#import +#import "ably.h" -@class ARTLog; +@class ARTCancellable; +@class ARTPaginatedResult; +@class ARTStatsQuery; +@class ARTClientOptions; +@class ARTAuth; +@class ARTChannel; +@class ARTChannelCollection; NS_ASSUME_NONNULL_BEGIN -@interface ARTRestPresence : ARTPresence - -@end - -@interface ARTRestChannel : ARTChannel - -@property (nonatomic, strong, readonly) ARTRestPresence *presence; - -@end - -@interface ARTRestChannelCollection : ARTChannelCollection - -- (ARTRestChannel *)get:(NSString *)channelName; -- (ARTRestChannel *)get:(NSString *)channelName options:(ARTChannelOptions *)options; - -@end - - -# pragma mark - ARTRest - @interface ARTRest : NSObject - (instancetype)init UNAVAILABLE_ATTRIBUTE; @@ -46,16 +26,13 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithLogger:(ARTLog *)logger andOptions:(ARTClientOptions *)options; - (instancetype)initWithKey:(NSString *)key; -- (id)token:(ARTAuthTokenParams *)keyName tokenCb:(void (^)(ARTStatus * status, ARTTokenDetails *))cb; - -- (id)time:(void(^)(ARTStatus * status, NSDate *time))cb; - +- (void)time:(void(^)(NSDate *__nullable time, NSError *__nullable error))callback; - (void)stats:(nullable ARTStatsQuery *)query callback:(void (^)(ARTPaginatedResult /* */ *__nullable result, NSError *__nullable error))callback; -- (id)internetIsUp:(void (^)(bool isUp)) cb; +- (id)internetIsUp:(void (^)(bool isUp))cb; @property (nonatomic, strong, readonly) ARTLog *logger; -@property (nonatomic, strong, readonly) ARTRestChannelCollection *channels; +@property (nonatomic, strong, readonly) ARTChannelCollection *channels; @property (nonatomic, strong, readonly) ARTAuth *auth; @property (nonatomic, strong, readonly) ARTClientOptions *options; diff --git a/ably-ios/ARTRest.m b/ably-ios/ARTRest.m index ef1953371..fa67782bb 100644 --- a/ably-ios/ARTRest.m +++ b/ably-ios/ARTRest.m @@ -8,177 +8,36 @@ #import "ARTRest.h" #import "ARTRest+Private.h" -#import "ARTChannels+Private.h" -#import "ARTDataQuery+Private.h" +#import "ARTChannel+Private.h" +#import "ARTDataQuery+Private.h" +#import "ARTPaginatedResult+Private.h" #import "ARTAuth.h" #import "ARTHttp.h" #import "ARTEncoder.h" #import "ARTJsonEncoder.h" -#import "ARTMsgPackEncoder.h" #import "ARTMessage.h" -#import "ARTPaginatedResult+Private.h" -#import "ARTStats.h" - -#import "ARTNSDictionary+ARTDictionaryUtil.h" -#import "ARTNSArray+ARTFunctional.h" - -#import "ARTLog.h" +#import "ARTPresence.h" +#import "ARTPresenceMessage.h" #import "ARTHttp.h" +#import "ARTClientOptions.h" #import "ARTDefault.h" +#import "ARTStats.h" #import "ARTFallback.h" +#import "ARTNSDictionary+ARTDictionaryUtil.h" +#import "ARTNSArray+ARTFunctional.h" @interface ARTRest () @property (readonly, strong, nonatomic) ARTHttp *http; @property (strong, nonatomic) ARTAuth *auth; +@property (nonatomic, strong) NSURL *baseUrl; @property (readonly, strong, nonatomic) NSDictionary *encoders; @property (readonly, strong, nonatomic) NSString *defaultEncoding; @property (readwrite, assign, nonatomic) int fallbackCount; -- (id)makeRequestWithMethod:(NSString *)method relUrl:(NSString *)relUrl headers:(NSDictionary *)headers body:(NSData *)body authenticated:(ARTAuthentication)authenticated cb:(ARTHttpCb)cb; - -- (NSDictionary *)withAcceptHeader:(NSDictionary *)headers; -- (void)throwOnHighLimitCheck:(NSDictionary *)params; - -@end - -@interface ARTRestPresence () - -- (instancetype)initWithChannel:(ARTRestChannel *)channel; - -@end - -@implementation ARTRestChannel { -@public - __weak ARTRest *_rest; - NSString *_basePath; -} - -@dynamic presence; - -- (instancetype)initWithRest:(ARTRest *)rest name:(NSString *)name options:(ARTChannelOptions *)options { - if (self = [super initWithName:name presence:[[ARTRestPresence alloc] initWithChannel:self] options:options]) { - _logger = rest.logger; - [_logger debug:@"ARTRestChannel: instantiating under %@", name]; - _rest = rest; - _basePath = [NSString stringWithFormat:@"/channels/%@", [name stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLPathAllowedCharacterSet]]]; - } - - return self; -} - -- (void)history:(ARTDataQuery *)query callback:(void (^)(ARTPaginatedResult *__nullable, NSError *__nullable))callback { - NSParameterAssert(query.limit < 1000); - NSParameterAssert([query.start compare:query.end] != NSOrderedDescending); - - NSURLComponents *requestUrl = [NSURLComponents componentsWithString:[_basePath stringByAppendingPathComponent:@"messages"]]; - requestUrl.queryItems = [query asQueryItems]; - NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:requestUrl.URL]; - - ARTPaginatedResultResponseProcessor responseProcessor = ^(NSHTTPURLResponse *response, NSData *data) { - id encoder = [_rest.encoders objectForKey:response.MIMEType]; - return [[encoder decodeMessages:data] artMap:^(ARTMessage *message) { - return [message decode:_payloadEncoder]; - }]; - }; - - [ARTPaginatedResult executePaginatedRequest:request executor:_rest responseProcessor:responseProcessor callback:callback]; -} - -- (void)_postMessages:(id)payload callback:(ARTErrorCallback)callback { - NSData *encodedMessage = nil; - if ([payload isKindOfClass:[ARTMessage class]]) { - encodedMessage = [_rest.defaultEncoder encodeMessage:payload]; - } else if ([payload isKindOfClass:[NSArray class]]) { - encodedMessage = [_rest.defaultEncoder encodeMessages:payload]; - } - - NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[_basePath stringByAppendingPathComponent:@"messages"]]]; - request.HTTPMethod = @"POST"; - request.HTTPBody = encodedMessage; - if (_rest.defaultEncoding) { - [request setValue:_rest.defaultEncoding forHTTPHeaderField:@"Content-Type"]; - } - - [_rest executeRequest:request callback:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { - if (callback) { - callback(error); - } - }]; -} - -@end - -@implementation ARTRestChannelCollection { - __weak ARTRest *_rest; -} - -- (instancetype)initWithRest:(ARTRest *)rest { - if (self = [super init]) { - _rest = rest; - } - - return self; -} - -- (ARTChannel *)_createChannelWithName:(NSString *)name options:(ARTChannelOptions *)options { - return [[ARTRestChannel alloc] initWithRest:_rest name:name options:options]; -} - -@end - -@implementation ARTRestPresence { - __weak ARTRestChannel *_channel; -} - -- (instancetype)initWithChannel:(ARTRestChannel *)channel { - if (self = [super init]) { - _channel = channel; - } - - return self; -} - -- (void)get:(void (^)(ARTPaginatedResult *__nullable, NSError *__nullable))callback { - NSURL *requestUrl = [NSURL URLWithString:[_channel->_basePath stringByAppendingPathComponent:@"presence"]]; - NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:requestUrl]; - - ARTPaginatedResultResponseProcessor responseProcessor = ^(NSHTTPURLResponse *response, NSData *data) { - id encoder = [_channel->_rest.encoders objectForKey:response.MIMEType]; - NSArray *messages = [encoder decodePresenceMessages:data]; - return [messages artMap:^id(ARTPresenceMessage *pm) { - return [pm decode:_channel->_payloadEncoder]; - }]; - }; - - [ARTPaginatedResult executePaginatedRequest:request executor:_channel->_rest responseProcessor:responseProcessor callback:callback]; -} - -- (void)history:(nullable ARTDataQuery *)query callback:(void (^)(ARTPaginatedResult *__nullable, NSError *__nullable))callback { - NSParameterAssert(query.limit < 1000); - NSParameterAssert([query.start compare:query.end] != NSOrderedDescending); - - NSURLComponents *requestUrl = [NSURLComponents componentsWithString:[_channel->_basePath stringByAppendingPathComponent:@"presence/history"]]; - requestUrl.queryItems = [query asQueryItems]; - NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:requestUrl.URL]; - - ARTPaginatedResultResponseProcessor responseProcessor = ^(NSHTTPURLResponse *response, NSData *data) { - id encoder = [_channel->_rest.encoders objectForKey:response.MIMEType]; - NSArray *messages = [encoder decodePresenceMessages:data]; - return [messages artMap:^id(ARTPresenceMessage *pm) { - return [pm decode:_channel->_payloadEncoder]; - }]; - }; - - [ARTPaginatedResult executePaginatedRequest:request executor:_channel->_rest responseProcessor:responseProcessor callback:callback]; -} - @end - -#pragma mark - ARTRest - @implementation ARTRest - (instancetype)initWithLogger:(ARTLog *)logger andOptions:(ARTClientOptions *)options { @@ -186,6 +45,7 @@ - (instancetype)initWithLogger:(ARTLog *)logger andOptions:(ARTClientOptions *)o if (self) { NSAssert(options, @"ARTRest: No options provided"); _options = options; + //_baseUrl = [options restUrl]; if (logger) { _logger = logger; @@ -194,8 +54,21 @@ - (instancetype)initWithLogger:(ARTLog *)logger andOptions:(ARTClientOptions *)o _logger = [[ARTLog alloc] init]; } - self.baseUrl = [ARTClientOptions restUrl:self.options.restHost port:self.options.restPort]; - [self setup]; + _http = [[ARTHttp alloc] init]; + // ?! + //_httpExecutor = _http; + //_httpExecutor.logger = _logger; + + // FIXME: + //_channels = [[ARTChannelCollection alloc] initWithRest:self]; + + id defaultEncoder = [[ARTJsonEncoder alloc] init]; + _encoders = @{ + [defaultEncoder mimeType]: defaultEncoder, + }; + + _defaultEncoding = [defaultEncoder mimeType]; + _fallbackCount = 0; _auth = [[ARTAuth alloc] initWithRest:self options:options.authOptions]; } return self; @@ -206,23 +79,7 @@ - (instancetype)initWithOptions:(ARTClientOptions *)options { } - (instancetype)initWithKey:(NSString *) key { - return [self initWithOptions:[ARTClientOptions optionsWithKey:key]]; -} - -- (void)setup { - _http = [[ARTHttp alloc] init]; - _httpExecutor = _http; - _httpExecutor.logger = _logger; - - _channels = [[ARTRestChannelCollection alloc] initWithRest:self]; - - id defaultEncoder = [[ARTJsonEncoder alloc] init]; - _encoders = @{ - [defaultEncoder mimeType]: defaultEncoder, - }; - - _defaultEncoding = [defaultEncoder mimeType]; - _fallbackCount = 0; + return [self initWithOptions:[[ARTClientOptions alloc] initWithKey:key]]; } - (void)executeRequest:(NSMutableURLRequest *)request callback:(void (^)(NSHTTPURLResponse *, NSData *, NSError *))callback { @@ -231,81 +88,44 @@ - (void)executeRequest:(NSMutableURLRequest *)request callback:(void (^)(NSHTTPU NSString *accept = [[_encoders.allValues valueForKeyPath:@"mimeType"] componentsJoinedByString:@","]; [request setValue:accept forHTTPHeaderField:@"Accept"]; - [self withAuthHeaders:^id(NSDictionary *authHeaders) { - for (NSString *header in authHeaders) { - [request setValue:authHeaders[header] forHTTPHeaderField:header]; - } - - [self.httpExecutor executeRequest:request callback:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { - if (response.statusCode >= 400) { - NSError *error = [self->_encoders[response.MIMEType] decodeError:data]; - if (error.code == 40140) { - // TODO: request token or error if no token information + [self calculateAuthorization:^(NSString *authorization, NSError *error) { + if (error) { + callback(nil, nil, error); + } else { + [request setValue:authorization forHTTPHeaderField:@"Authorization"]; + + [self.httpExecutor executeRequest:request callback:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { + if (response.statusCode >= 400) { + NSError *error = [self->_encoders[response.MIMEType] decodeError:data]; + if (error.code == 40140) { + // TODO: request token or error if no token information + } else { + callback(nil, nil, error); + } } else { - callback(nil, nil, error); + callback(response, data, error); } - } else { - callback(response, data, error); - } - }]; - return nil; - }]; -} - -- (id)token:(ARTAuthTokenParams *)params tokenCb:(void (^)(ARTStatus *status, ARTTokenDetails *)) cb { - - [self.logger debug:@"ARTRest is requesting a fresh token"]; - if(![self.auth canRequestToken]) { - cb([ARTStatus state:ARTStateError], nil); - id c = nil; - return c; - } - - NSString * keyPath = [NSString stringWithFormat:@"/keys/%@/requestToken",params.keyName]; - if([self.auth getAuthOptions].authUrl) { - keyPath = [[self.auth getAuthOptions].authUrl absoluteString]; - [self.logger info:@"ARTRest is bypassing the default token request URL for this authURL:%@",keyPath]; - } - NSDictionary * paramsDict = [params asDictionary]; - - NSData * dictData = [NSJSONSerialization dataWithJSONObject:paramsDict options:0 error:nil]; - - NSDictionary *headers = @{@"Content-Type":self.defaultEncoding}; - return [self post:keyPath headers:headers body:dictData authenticated:ARTAuthenticationUseBasic cb:^(ARTHttpResponse *response) { - if(!response.body) { - cb([ARTStatus state:ARTStateError info:response.error], nil); - return; - } - NSString * str = [[NSString alloc] initWithData:response.body encoding:NSUTF8StringEncoding]; - [self.logger verbose:@"ARTRest token is %@", str]; - if(response.status == 201) { - ARTTokenDetails * token =[self.defaultEncoder decodeAccessToken:response.body]; - cb(ARTStateOk, token); - } - else { - - ARTErrorInfo * e = [self.defaultEncoder decodeError:response.body]; - [self.logger error:@"ARTRest: requestToken Error code: %d, Status %d, Message %@", e.code, e.statusCode, e.message]; - cb([ARTStatus state:ARTStateError info:e], nil); + }]; } }]; } --(ARTAuth *) auth { - return _auth; +- (void)calculateAuthorization:(void (^)(NSString *authorization, NSError *error))callback { + } -- (id)time:(void (^)(ARTStatus *, NSDate *))cb { - return [self get:@"/time" authenticated:NO cb:^(ARTHttpResponse *response) { - NSDate *date = nil; - - if (response.status == 200) { - date = [self.defaultEncoder decodeTime:response.body]; - } - if (date) { - cb([ARTStatus state:ARTStateOk], date); +- (void)time:(void(^)(NSDate *__nullable time, NSError *__nullable error))callback { + NSURL *requestUrl = [NSURL URLWithString:@"/time" relativeToURL:self.baseUrl]; + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:requestUrl]; + request.HTTPMethod = @"GET"; + NSString *accept = [[_encoders.allValues valueForKeyPath:@"mimeType"] componentsJoinedByString:@","]; + [request setValue:accept forHTTPHeaderField:@"Accept"]; + + [self.httpExecutor executeRequest:request callback:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { + if (response.statusCode >= 400) { + callback(nil, [self->_encoders[response.MIMEType] decodeError:data]); } else { - cb([ARTStatus state:ARTStateError info:response.error], nil); + callback([self->_encoders[response.MIMEType] decodeTime:data], nil); } }]; } @@ -334,171 +154,8 @@ - (void)stats:(ARTStatsQuery *)query callback:(void (^)(ARTPaginatedResult *__nu [ARTPaginatedResult executePaginatedRequest:request executor:self responseProcessor:responseProcessor callback:callback]; } -- (bool)isAnErrorStatus:(int)status { - return status >= 400; -} - -- (id)makeRequestWithMethod:(NSString *)method relUrl:(NSString *)relUrl headers:(NSDictionary *)headers body:(NSData *)body authenticated:(ARTAuthentication)authenticated fb:(ARTFallback *) fb cb:(ARTHttpCb)cb { - __weak ARTRest * weakSelf = self; - ARTHttpCb errorCheckingCb = ^(ARTHttpResponse * response) { - ARTRest * s = weakSelf; - [self.logger verbose:@"ARTRest Http response is %d", response.status]; - if([s isAnErrorStatus:response.status]) { - if(response.body) { - ARTErrorInfo * error = [s.defaultEncoder decodeError:response.body]; - response.error = error; - [self.logger info:@"ARTRest received an error: \n status %d \n code %d \n message: %@", error.statusCode, error.code, error.message]; - } - } - if([ARTFallback shouldTryFallback:response options:self.options]) { - ARTFallback * theFb = fb; - if(theFb == nil) { - theFb = [[ARTFallback alloc] init]; - } - NSString * nextFallbackHost = [theFb popFallbackHost]; - if(nextFallbackHost != nil) { - self.baseUrl = [ARTClientOptions restUrl:nextFallbackHost port:self.options.restPort]; - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ - [s makeRequestWithMethod:method relUrl:relUrl headers:headers body:body authenticated:authenticated fb:theFb cb:cb]; - }); - } - else { - [self.logger warn:@"ARTRest has no more fallback hosts to attempt. Giving up."]; - self.baseUrl = [self.options restUrl]; - cb(response); - return; - } - } - else if(response && response.error && response.error.code == 40140) { - [self.logger info:@"requesting new token"]; - if(![_auth canRequestToken]) { - cb(response); - return; - } - [_auth attemptTokenFetch:^() { - ARTRest * s = weakSelf; - if(s) { - //TODO consider counting this. enough times we give up? - [self.logger debug:@"ARTRest Token fetch complete. Now trying the same server call again with the new token"]; - [s makeRequestWithMethod:method relUrl:relUrl headers:headers body:body authenticated:authenticated cb:cb]; - } - else { - [self.logger error:@"ARTRest is nil. Can't renew token"]; - cb(response); - } - }]; - } - else { - cb(response); - } - }; - - NSURL *url = [self resolveUrl:relUrl]; - headers = [self withAcceptHeader:headers]; - - if (authenticated == ARTAuthenticationOff) { - return [self.http makeRequestWithMethod:method url:url headers:headers body:body cb:errorCheckingCb]; - } else { - bool useBasic = authenticated == ARTAuthenticationUseBasic; - return [self withAuthHeadersUseBasic:useBasic cb:^(NSDictionary *authHeaders) { - NSMutableDictionary *allHeaders = [NSMutableDictionary dictionary]; - [allHeaders addEntriesFromDictionary:headers]; - [allHeaders addEntriesFromDictionary:authHeaders]; - if(useBasic) { - return [self.http makeRequestWithMethod:method url:url headers:allHeaders body:body cb:errorCheckingCb]; - } - else { - return [self.http makeRequestWithMethod:method url:url headers:allHeaders body:body cb:errorCheckingCb]; - } - }]; - } -} - -- (id)makeRequestWithMethod:(NSString *)method relUrl:(NSString *)relUrl headers:(NSDictionary *)headers body:(NSData *)body authenticated:(ARTAuthentication)authenticated cb:(ARTHttpCb)cb { - - return [self makeRequestWithMethod:method relUrl:relUrl headers:headers body:body authenticated:authenticated fb:nil cb:cb]; -} - -- (NSDictionary *)withAcceptHeader:(NSDictionary *)headers { - NSMutableDictionary *md = [NSMutableDictionary dictionaryWithDictionary:headers]; - - NSMutableArray *mimeTypes = [NSMutableArray arrayWithObject:self.defaultEncoding]; - - for (NSString *mimeType in self.encoders) { - if (![mimeType isEqualToString:self.defaultEncoding]) { - [mimeTypes addObject:mimeType]; - } - } - md[@"Accept"] = [mimeTypes componentsJoinedByString:@","]; - - return md; -} - -@end - -@implementation ARTRest (Private) - - (id)defaultEncoder { return self.encoders[self.defaultEncoding]; } -- (NSString *)formatQueryParams:(NSDictionary *)queryParams { - NSMutableArray *encodedParams = [NSMutableArray array]; - - for (NSString *queryParamName in queryParams) { - NSString *queryParamValue = [queryParams objectForKey:queryParamName]; - NSString *encoded = [NSString stringWithFormat:@"%@=%@", [queryParamName stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]], [queryParamValue stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]]; - [encodedParams addObject:encoded]; - } - - return [encodedParams componentsJoinedByString:@"&"]; -} - -- (NSURL *)resolveUrl:(NSString *)relUrl { - if([relUrl length] ==0) { - return self.baseUrl; - } - if([[relUrl substringWithRange:NSMakeRange(0, 1)] isEqualToString:@"/"]) { - return [NSURL URLWithString:relUrl relativeToURL:self.baseUrl]; - } - [self.logger verbose:@"ARTRest is treating the relative url as the base"]; - return [NSURL URLWithString:relUrl]; - -} - -- (NSURL *)resolveUrl:(NSString *)relUrl queryParams:(NSDictionary *)queryParams { - NSString *queryString = [self formatQueryParams:queryParams]; - - if (queryString.length) { - relUrl = [NSString stringWithFormat:@"%@?%@", relUrl, queryString]; - } - - return [self resolveUrl:relUrl]; -} - -- (id)get:(NSString *)relUrl authenticated:(BOOL)authenticated cb:(ARTHttpCb)cb { - return [self get:relUrl headers:nil authenticated:authenticated cb:cb]; -} - -- (id)get:(NSString *)relUrl headers:(NSDictionary *)headers authenticated:(BOOL)authenticated cb:(ARTHttpCb)cb { - return [self makeRequestWithMethod:@"GET" relUrl:relUrl headers:headers body:nil authenticated:authenticated cb:cb]; -} - -- (id)post:(NSString *)relUrl headers:(NSDictionary *)headers body:(NSData *)body authenticated:(ARTAuthentication)authenticated cb:(ARTHttpCb)cb { - return [self makeRequestWithMethod:@"POST" relUrl:relUrl headers:headers body:body authenticated:authenticated cb:cb]; -} - -- (id)withAuthHeaders:(id(^) - (NSDictionary *))cb { - return [self withAuthHeadersUseBasic:false cb:cb]; -} - -- (id)withAuthHeadersUseBasic:(BOOL) useBasic cb:(id(^)(NSDictionary *))cb { - return [self.auth authHeadersUseBasic:useBasic cb:cb]; -} - -- (id)withAuthParams:(id(^)(NSDictionary *))cb { - return [self.auth authParams:cb]; -} - @end diff --git a/ably-ios/ARTStatus.h b/ably-ios/ARTStatus.h index 36be856ee..067fedcd3 100644 --- a/ably-ios/ARTStatus.h +++ b/ably-ios/ARTStatus.h @@ -26,8 +26,7 @@ typedef NS_ENUM(NSUInteger, ARTState) { FOUNDATION_EXPORT NSString *const ARTAblyErrorDomain; -typedef void(^ARTErrorCallback)(NSError *error); - +// FIXME: base NSError @interface ARTErrorInfo : NSObject @property (readonly, copy, nonatomic) NSString *message; @@ -43,7 +42,7 @@ typedef void(^ARTErrorCallback)(NSError *error); @interface ARTStatus : NSObject { } -@property (readonly, strong, nonatomic) ARTErrorInfo * errorInfo; +@property (readonly, strong, nonatomic) ARTErrorInfo *errorInfo; @property (nonatomic, assign) ARTState state; +(ARTStatus *) state:(ARTState) state; diff --git a/ably-ios/ARTStatus.m b/ably-ios/ARTStatus.m index a67fc5acb..e2ab7c244 100644 --- a/ably-ios/ARTStatus.m +++ b/ably-ios/ARTStatus.m @@ -7,6 +7,7 @@ // #import + #import "ARTStatus.h" NSString *const ARTAblyErrorDomain = @"ARTAblyErrorDomain"; diff --git a/ably-ios/ARTSubscription.h b/ably-ios/ARTSubscription.h new file mode 100644 index 000000000..f77e2fe56 --- /dev/null +++ b/ably-ios/ARTSubscription.h @@ -0,0 +1,18 @@ +// +// ARTSubscription.h +// ably +// +// Created by Ricardo Pereira on 30/09/2015. +// Copyright (c) 2015 Ably. All rights reserved. +// + +#ifndef ably_ARTSubscription_h +#define ably_ARTSubscription_h + +@protocol ARTSubscription + +- (void)unsubscribe; + +@end + +#endif diff --git a/ably-ios/ARTTokenDetails+Private.h b/ably-ios/ARTTokenDetails+Private.h deleted file mode 100644 index 38acb7540..000000000 --- a/ably-ios/ARTTokenDetails+Private.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// ARTTokenDetails+Private.h -// ably -// -// Created by vic on 22/05/2015. -// Copyright (c) 2015 Ably. All rights reserved. -// - -#import -#import "ARTAuth.h" -@interface ARTTokenDetails (Private) --(void) setExpiresTime:(int64_t) time; -@end - -@interface ARTAuthOptions (Private) --(void) setKeySecretTo:(NSString *) keySecret; -@end - - -@interface ARTAuth (Private) --(ARTAuthCb) getTheAuthCb; -@end \ No newline at end of file diff --git a/ably-ios/ARTTypes.h b/ably-ios/ARTTypes.h index bce2c63c6..b2ba05162 100644 --- a/ably-ios/ARTTypes.h +++ b/ably-ios/ARTTypes.h @@ -7,10 +7,48 @@ // #import -#import -typedef void (^ARTStatusCallback)(ARTStatus * status); +@class ARTStatus; +@class ARTHttpResponse; +@class ARTMessage; +@class ARTPresenceMessage; +typedef NS_ENUM(NSUInteger, ARTRealtimeConnectionState) { + ARTRealtimeInitialized, + ARTRealtimeConnecting, + ARTRealtimeConnected, + ARTRealtimeDisconnected, + ARTRealtimeSuspended, + ARTRealtimeClosing, + ARTRealtimeClosed, + ARTRealtimeFailed +}; + +typedef NS_ENUM(NSUInteger, ARTRealtimeChannelState) { + ARTRealtimeChannelInitialised, + ARTRealtimeChannelAttaching, + ARTRealtimeChannelAttached, + ARTRealtimeChannelDetaching, + ARTRealtimeChannelDetached, + ARTRealtimeChannelClosed, + ARTRealtimeChannelFailed +}; + +typedef void (^ARTRealtimeChannelMessageCb)(ARTMessage *); + +typedef void (^ARTRealtimeChannelStateCb)(ARTRealtimeChannelState, ARTStatus *); + +typedef void (^ARTStatusCallback)(ARTStatus *status); + +typedef void (^ARTHttpCb)(ARTHttpResponse *response); + +typedef void (^ARTErrorCallback)(NSError *error); + +typedef void (^ARTRealtimeConnectionStateCb)(ARTRealtimeConnectionState state); + +typedef void (^ARTRealtimeChannelPresenceCb)(ARTPresenceMessage *); + +// FIXME: @protocol ARTCancellable - (void)cancel; diff --git a/ably-ios/ARTWebSocketTransport.m b/ably-ios/ARTWebSocketTransport.m index d7bbd4cac..aff1a6331 100644 --- a/ably-ios/ARTWebSocketTransport.m +++ b/ably-ios/ARTWebSocketTransport.m @@ -9,10 +9,11 @@ #import "ARTWebSocketTransport.h" #import -#import "ARTClientOptions.h" + #import "ARTRest.h" #import "ARTRest+Private.h" -#import "ARTLog.h" +#import "ARTProtocolMessage.h" +#import "ARTClientOptions.h" enum { ARTWsNeverConnected = -1, @@ -72,17 +73,8 @@ - (instancetype)initWithRest:(ARTRest *)rest options:(ARTClientOptions *)options NSMutableDictionary *queryParams = [NSMutableDictionary dictionaryWithDictionary:authParams]; - /* - //msgpack not supported yet. - if(false || options.binary) { - queryParams[@"format"] = @"msgpack"; - } - */ - - queryParams[@"echo"] = echoMessages ? @"true" :@"false"; - - + if(options.recover) { NSArray * parts = [options.recover componentsSeparatedByString:@":"]; if([parts count] == 2) { diff --git a/ably-ios/ably.h b/ably-ios/ably.h index 0ca2ab842..afe103db9 100644 --- a/ably-ios/ably.h +++ b/ably-ios/ably.h @@ -14,16 +14,9 @@ FOUNDATION_EXPORT double ablyVersionNumber; //! Project version string for ably-ios. FOUNDATION_EXPORT const unsigned char ablyVersionString[]; -#import -#import -#import -#import -#import -#import -#import -#import -#import -#import #import -#import -#import \ No newline at end of file +#import + +#import +#import +#import diff --git a/ably-ios/ably.modulemap b/ably-ios/ably.modulemap index c87ea4f27..6e732da1a 100644 --- a/ably-ios/ably.modulemap +++ b/ably-ios/ably.modulemap @@ -5,14 +5,15 @@ framework module ably { module * { export * } explicit module Private { + header "ARTEncoder.h" + header "ARTJsonEncoder.h" + header "ARTChannels+Private.h" header "ARTDataQuery+Private.h" header "ARTPayload+Private.h" header "ARTTokenDetails+Private.h" header "ARTRest+Private.h" header "ARTClientOptions+Private.h" - header "ARTEncoder.h" - header "ARTJsonEncoder.h" header "ARTPaginatedResult+Private.h" } } diff --git a/ably.xcodeproj/project.pbxproj b/ably.xcodeproj/project.pbxproj index 9b690f4b8..9ebff51bc 100644 --- a/ably.xcodeproj/project.pbxproj +++ b/ably.xcodeproj/project.pbxproj @@ -32,10 +32,7 @@ 1C6C18A31ADFDAB100AB79E4 /* ARTLog.h in Headers */ = {isa = PBXBuildFile; fileRef = 1C6C18A11ADFDAB100AB79E4 /* ARTLog.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1C6C18A41ADFDAB100AB79E4 /* ARTLog.m in Sources */ = {isa = PBXBuildFile; fileRef = 1C6C18A21ADFDAB100AB79E4 /* ARTLog.m */; }; 1C6C18A71ADFDDBA00AB79E4 /* ARTLogTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 1C6C18A61ADFDDBA00AB79E4 /* ARTLogTest.m */; }; - 1C70CB881B020D7F003D295A /* ARTClientOptions+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 1C70CB871B020D7F003D295A /* ARTClientOptions+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; 1C8065051AE7C8FA00D49357 /* ARTPayload+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 1C8065041AE7C8FA00D49357 /* ARTPayload+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 1CA5E1C21AB72369006ADD70 /* ARTMsgPackEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 1CA5E1C01AB72369006ADD70 /* ARTMsgPackEncoder.h */; }; - 1CA5E1C31AB72369006ADD70 /* ARTMsgPackEncoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 1CA5E1C11AB72369006ADD70 /* ARTMsgPackEncoder.m */; }; 1CC3D94B1AB6FBB60005BEB0 /* ARTRealtimeChannelHistoryTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 1CC3D94A1AB6FBB60005BEB0 /* ARTRealtimeChannelHistoryTest.m */; }; 1CC3D94D1AB6FE700005BEB0 /* ARTRealtimeConnectTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 1CC3D94C1AB6FE700005BEB0 /* ARTRealtimeConnectTest.m */; }; 1CC3D94F1AB6FECC0005BEB0 /* ARTRealtimeConnectFailTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 1CC3D94E1AB6FECC0005BEB0 /* ARTRealtimeConnectFailTest.m */; }; @@ -49,7 +46,6 @@ 1CC3D95F1AB7043F0005BEB0 /* ARTRealtimeResumeTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 1CC3D95E1AB7043F0005BEB0 /* ARTRealtimeResumeTest.m */; }; 1CD8DC9F1B1C7315007EAF36 /* ARTDefault.h in Headers */ = {isa = PBXBuildFile; fileRef = 1CD8DC9D1B1C7315007EAF36 /* ARTDefault.h */; }; 1CD8DCA01B1C7315007EAF36 /* ARTDefault.m in Sources */ = {isa = PBXBuildFile; fileRef = 1CD8DC9E1B1C7315007EAF36 /* ARTDefault.m */; }; - 1CEFC60E1B0F9EF500D84463 /* ARTTokenDetails+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 1CEFC60D1B0F9EF500D84463 /* ARTTokenDetails+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; 2EBDBEEBF881A49F795F1D2E /* Pods_ablySpec.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52CD0A2BB89BAA3D8353FBF3 /* Pods_ablySpec.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; 850BFB4C1B79323C009D0ADD /* ARTPaginatedResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 850BFB4A1B79323C009D0ADD /* ARTPaginatedResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; 850BFB4D1B79323C009D0ADD /* ARTPaginatedResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 850BFB4B1B79323C009D0ADD /* ARTPaginatedResult.m */; }; @@ -91,8 +87,8 @@ 96BF615B1A35B9ED004CF2B3 /* ARTHttpTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 96BF615A1A35B9ED004CF2B3 /* ARTHttpTest.m */; }; 96BF615E1A35C1C8004CF2B3 /* ARTTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 96BF615C1A35C1C8004CF2B3 /* ARTTypes.h */; settings = {ATTRIBUTES = (Public, ); }; }; 96BF615F1A35C1C8004CF2B3 /* ARTTypes.m in Sources */ = {isa = PBXBuildFile; fileRef = 96BF615D1A35C1C8004CF2B3 /* ARTTypes.m */; }; - 96BF61641A35CDE1004CF2B3 /* ARTMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = 96BF61621A35CDE1004CF2B3 /* ARTMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 96BF61651A35CDE1004CF2B3 /* ARTMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 96BF61631A35CDE1004CF2B3 /* ARTMessage.m */; }; + 96BF61641A35CDE1004CF2B3 /* ARTBaseMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = 96BF61621A35CDE1004CF2B3 /* ARTBaseMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 96BF61651A35CDE1004CF2B3 /* ARTBaseMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 96BF61631A35CDE1004CF2B3 /* ARTBaseMessage.m */; }; 96BF61701A35FB7C004CF2B3 /* ARTAuth.h in Headers */ = {isa = PBXBuildFile; fileRef = 96BF616E1A35FB7C004CF2B3 /* ARTAuth.h */; settings = {ATTRIBUTES = (Public, ); }; }; 96BF61711A35FB7C004CF2B3 /* ARTAuth.m in Sources */ = {isa = PBXBuildFile; fileRef = 96BF616F1A35FB7C004CF2B3 /* ARTAuth.m */; }; 96E408361A38595F00087F77 /* ARTRealtimeAttachTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 96E408351A38595F00087F77 /* ARTRealtimeAttachTest.m */; }; @@ -106,14 +102,29 @@ D746AE1D1BBB5207003ECEF8 /* ARTDataQuery.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE1A1BBB5207003ECEF8 /* ARTDataQuery.h */; }; D746AE1E1BBB5207003ECEF8 /* ARTDataQuery+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE1B1BBB5207003ECEF8 /* ARTDataQuery+Private.h */; }; D746AE1F1BBB5207003ECEF8 /* ARTDataQuery.m in Sources */ = {isa = PBXBuildFile; fileRef = D746AE1C1BBB5207003ECEF8 /* ARTDataQuery.m */; }; - D746AE221BBB60EE003ECEF8 /* ARTChannels.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE201BBB60EE003ECEF8 /* ARTChannels.h */; }; - D746AE231BBB60EE003ECEF8 /* ARTChannels.m in Sources */ = {isa = PBXBuildFile; fileRef = D746AE211BBB60EE003ECEF8 /* ARTChannels.m */; }; - D746AE251BBB611C003ECEF8 /* ARTChannels+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE241BBB611C003ECEF8 /* ARTChannels+Private.h */; }; + D746AE221BBB60EE003ECEF8 /* ARTChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE201BBB60EE003ECEF8 /* ARTChannel.h */; }; + D746AE231BBB60EE003ECEF8 /* ARTChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = D746AE211BBB60EE003ECEF8 /* ARTChannel.m */; }; + D746AE251BBB611C003ECEF8 /* ARTChannel+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE241BBB611C003ECEF8 /* ARTChannel+Private.h */; }; D746AE281BBB61C9003ECEF8 /* ARTPresence.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE261BBB61C9003ECEF8 /* ARTPresence.h */; }; D746AE291BBB61C9003ECEF8 /* ARTPresence.m in Sources */ = {isa = PBXBuildFile; fileRef = D746AE271BBB61C9003ECEF8 /* ARTPresence.m */; }; D746AE2C1BBB625E003ECEF8 /* RestChannel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D746AE2A1BBB625E003ECEF8 /* RestChannel.swift */; }; D746AE2D1BBB625E003ECEF8 /* RestClient.channels.swift in Sources */ = {isa = PBXBuildFile; fileRef = D746AE2B1BBB625E003ECEF8 /* RestClient.channels.swift */; }; D746AE2F1BBBE7D7003ECEF8 /* ARTPaginatedResult+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE2E1BBBE7D7003ECEF8 /* ARTPaginatedResult+Private.h */; }; + D746AE381BBC3201003ECEF8 /* ARTMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE361BBC3201003ECEF8 /* ARTMessage.h */; }; + D746AE391BBC3201003ECEF8 /* ARTMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = D746AE371BBC3201003ECEF8 /* ARTMessage.m */; }; + D746AE3C1BBC5AE1003ECEF8 /* ARTRealtimeChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE3A1BBC5AE1003ECEF8 /* ARTRealtimeChannel.h */; }; + D746AE3D1BBC5AE1003ECEF8 /* ARTRealtimeChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = D746AE3B1BBC5AE1003ECEF8 /* ARTRealtimeChannel.m */; }; + D746AE401BBC5B14003ECEF8 /* ARTEventEmitter.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE3E1BBC5B14003ECEF8 /* ARTEventEmitter.h */; }; + D746AE411BBC5B14003ECEF8 /* ARTEventEmitter.m in Sources */ = {isa = PBXBuildFile; fileRef = D746AE3F1BBC5B14003ECEF8 /* ARTEventEmitter.m */; }; + D746AE431BBC5CD0003ECEF8 /* ARTRealtimeChannel+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE421BBC5CD0003ECEF8 /* ARTRealtimeChannel+Private.h */; }; + D746AE471BBD6FE9003ECEF8 /* ARTQueuedMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE451BBD6FE9003ECEF8 /* ARTQueuedMessage.h */; }; + D746AE481BBD6FE9003ECEF8 /* ARTQueuedMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = D746AE461BBD6FE9003ECEF8 /* ARTQueuedMessage.m */; }; + D746AE4B1BBD70F0003ECEF8 /* ARTRealtimeChannelSubscription.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE491BBD70F0003ECEF8 /* ARTRealtimeChannelSubscription.h */; }; + D746AE4C1BBD70F0003ECEF8 /* ARTRealtimeChannelSubscription.m in Sources */ = {isa = PBXBuildFile; fileRef = D746AE4A1BBD70F0003ECEF8 /* ARTRealtimeChannelSubscription.m */; }; + D746AE4F1BBD84E7003ECEF8 /* ARTChannelOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE4D1BBD84E7003ECEF8 /* ARTChannelOptions.h */; }; + D746AE501BBD84E7003ECEF8 /* ARTChannelOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = D746AE4E1BBD84E7003ECEF8 /* ARTChannelOptions.m */; }; + D746AE531BBD85C5003ECEF8 /* ARTChannelCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE511BBD85C5003ECEF8 /* ARTChannelCollection.h */; }; + D746AE541BBD85C5003ECEF8 /* ARTChannelCollection.m in Sources */ = {isa = PBXBuildFile; fileRef = D746AE521BBD85C5003ECEF8 /* ARTChannelCollection.m */; }; FC78549C1BCD5688539CCBE4 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FDA6E099E4BC04FC80F845C0 /* libPods.a */; }; /* End PBXBuildFile section */ @@ -185,10 +196,7 @@ 1C6C18A11ADFDAB100AB79E4 /* ARTLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTLog.h; sourceTree = ""; }; 1C6C18A21ADFDAB100AB79E4 /* ARTLog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTLog.m; sourceTree = ""; }; 1C6C18A61ADFDDBA00AB79E4 /* ARTLogTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTLogTest.m; sourceTree = ""; }; - 1C70CB871B020D7F003D295A /* ARTClientOptions+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ARTClientOptions+Private.h"; sourceTree = ""; }; 1C8065041AE7C8FA00D49357 /* ARTPayload+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ARTPayload+Private.h"; sourceTree = ""; }; - 1CA5E1C01AB72369006ADD70 /* ARTMsgPackEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTMsgPackEncoder.h; sourceTree = ""; }; - 1CA5E1C11AB72369006ADD70 /* ARTMsgPackEncoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTMsgPackEncoder.m; sourceTree = ""; }; 1CC3D94A1AB6FBB60005BEB0 /* ARTRealtimeChannelHistoryTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTRealtimeChannelHistoryTest.m; sourceTree = ""; }; 1CC3D94C1AB6FE700005BEB0 /* ARTRealtimeConnectTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTRealtimeConnectTest.m; sourceTree = ""; }; 1CC3D94E1AB6FECC0005BEB0 /* ARTRealtimeConnectFailTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTRealtimeConnectFailTest.m; sourceTree = ""; }; @@ -202,7 +210,6 @@ 1CC3D95E1AB7043F0005BEB0 /* ARTRealtimeResumeTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTRealtimeResumeTest.m; sourceTree = ""; }; 1CD8DC9D1B1C7315007EAF36 /* ARTDefault.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTDefault.h; sourceTree = ""; }; 1CD8DC9E1B1C7315007EAF36 /* ARTDefault.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTDefault.m; sourceTree = ""; }; - 1CEFC60D1B0F9EF500D84463 /* ARTTokenDetails+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ARTTokenDetails+Private.h"; sourceTree = ""; }; 52CD0A2BB89BAA3D8353FBF3 /* Pods_ablySpec.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ablySpec.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 850BFB4A1B79323C009D0ADD /* ARTPaginatedResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTPaginatedResult.h; sourceTree = ""; }; 850BFB4B1B79323C009D0ADD /* ARTPaginatedResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTPaginatedResult.m; sourceTree = ""; }; @@ -250,8 +257,8 @@ 96BF615A1A35B9ED004CF2B3 /* ARTHttpTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTHttpTest.m; sourceTree = ""; }; 96BF615C1A35C1C8004CF2B3 /* ARTTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTTypes.h; sourceTree = ""; }; 96BF615D1A35C1C8004CF2B3 /* ARTTypes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTTypes.m; sourceTree = ""; }; - 96BF61621A35CDE1004CF2B3 /* ARTMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTMessage.h; sourceTree = ""; }; - 96BF61631A35CDE1004CF2B3 /* ARTMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTMessage.m; sourceTree = ""; }; + 96BF61621A35CDE1004CF2B3 /* ARTBaseMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTBaseMessage.h; sourceTree = ""; }; + 96BF61631A35CDE1004CF2B3 /* ARTBaseMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTBaseMessage.m; sourceTree = ""; }; 96BF616E1A35FB7C004CF2B3 /* ARTAuth.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTAuth.h; sourceTree = ""; }; 96BF616F1A35FB7C004CF2B3 /* ARTAuth.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTAuth.m; sourceTree = ""; }; 96E408351A38595F00087F77 /* ARTRealtimeAttachTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTRealtimeAttachTest.m; sourceTree = ""; }; @@ -267,14 +274,31 @@ D746AE1A1BBB5207003ECEF8 /* ARTDataQuery.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTDataQuery.h; sourceTree = ""; }; D746AE1B1BBB5207003ECEF8 /* ARTDataQuery+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ARTDataQuery+Private.h"; sourceTree = ""; }; D746AE1C1BBB5207003ECEF8 /* ARTDataQuery.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTDataQuery.m; sourceTree = ""; }; - D746AE201BBB60EE003ECEF8 /* ARTChannels.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTChannels.h; sourceTree = ""; }; - D746AE211BBB60EE003ECEF8 /* ARTChannels.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTChannels.m; sourceTree = ""; }; - D746AE241BBB611C003ECEF8 /* ARTChannels+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ARTChannels+Private.h"; sourceTree = ""; }; + D746AE201BBB60EE003ECEF8 /* ARTChannel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTChannel.h; sourceTree = ""; }; + D746AE211BBB60EE003ECEF8 /* ARTChannel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTChannel.m; sourceTree = ""; }; + D746AE241BBB611C003ECEF8 /* ARTChannel+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ARTChannel+Private.h"; sourceTree = ""; }; D746AE261BBB61C9003ECEF8 /* ARTPresence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTPresence.h; sourceTree = ""; }; D746AE271BBB61C9003ECEF8 /* ARTPresence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTPresence.m; sourceTree = ""; }; D746AE2A1BBB625E003ECEF8 /* RestChannel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RestChannel.swift; sourceTree = ""; }; D746AE2B1BBB625E003ECEF8 /* RestClient.channels.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RestClient.channels.swift; sourceTree = ""; }; D746AE2E1BBBE7D7003ECEF8 /* ARTPaginatedResult+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ARTPaginatedResult+Private.h"; sourceTree = ""; }; + D746AE361BBC3201003ECEF8 /* ARTMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTMessage.h; sourceTree = ""; }; + D746AE371BBC3201003ECEF8 /* ARTMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTMessage.m; sourceTree = ""; }; + D746AE3A1BBC5AE1003ECEF8 /* ARTRealtimeChannel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTRealtimeChannel.h; sourceTree = ""; }; + D746AE3B1BBC5AE1003ECEF8 /* ARTRealtimeChannel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTRealtimeChannel.m; sourceTree = ""; }; + D746AE3E1BBC5B14003ECEF8 /* ARTEventEmitter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTEventEmitter.h; sourceTree = ""; }; + D746AE3F1BBC5B14003ECEF8 /* ARTEventEmitter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTEventEmitter.m; sourceTree = ""; }; + D746AE421BBC5CD0003ECEF8 /* ARTRealtimeChannel+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ARTRealtimeChannel+Private.h"; sourceTree = ""; }; + D746AE441BBC5CF9003ECEF8 /* ARTSubscription.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ARTSubscription.h; sourceTree = ""; }; + D746AE451BBD6FE9003ECEF8 /* ARTQueuedMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTQueuedMessage.h; sourceTree = ""; }; + D746AE461BBD6FE9003ECEF8 /* ARTQueuedMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTQueuedMessage.m; sourceTree = ""; }; + D746AE491BBD70F0003ECEF8 /* ARTRealtimeChannelSubscription.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTRealtimeChannelSubscription.h; sourceTree = ""; }; + D746AE4A1BBD70F0003ECEF8 /* ARTRealtimeChannelSubscription.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTRealtimeChannelSubscription.m; sourceTree = ""; }; + D746AE4D1BBD84E7003ECEF8 /* ARTChannelOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTChannelOptions.h; sourceTree = ""; }; + D746AE4E1BBD84E7003ECEF8 /* ARTChannelOptions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTChannelOptions.m; sourceTree = ""; }; + D746AE511BBD85C5003ECEF8 /* ARTChannelCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTChannelCollection.h; sourceTree = ""; }; + D746AE521BBD85C5003ECEF8 /* ARTChannelCollection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTChannelCollection.m; sourceTree = ""; }; + D746AE551BBD8622003ECEF8 /* ARTChannelCollection+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ARTChannelCollection+Private.h"; sourceTree = ""; }; DF8DF755839D59574B7B795F /* Pods-ablySpec.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ablySpec.release.xcconfig"; path = "Pods/Target Support Files/Pods-ablySpec/Pods-ablySpec.release.xcconfig"; sourceTree = ""; }; FDA6E099E4BC04FC80F845C0 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -386,73 +410,16 @@ 96BF61331A35B2AB004CF2B3 /* ably-ios */ = { isa = PBXGroup; children = ( - 85B2C2181B6FE8DE00EA5254 /* CompatibilityMacros.h */, 85F0A60A1B6D039F00EFF45A /* ably.h */, - 96BF61341A35B2AB004CF2B3 /* Supporting Files */, - 96BF61511A35B39C004CF2B3 /* ARTRest.h */, - 96BF61521A35B39C004CF2B3 /* ARTRest.m */, - 96A507BB1A3791490077CDF8 /* ARTRealtime.h */, - 96A507BC1A3791490077CDF8 /* ARTRealtime.m */, - 96E408451A3895E800087F77 /* ARTWebSocketTransport.h */, - 96E408461A3895E800087F77 /* ARTWebSocketTransport.m */, - 961343D61A42E0B7006DC822 /* ARTClientOptions.h */, - 1C70CB871B020D7F003D295A /* ARTClientOptions+Private.h */, - 961343D71A42E0B7006DC822 /* ARTClientOptions.m */, - D746AE201BBB60EE003ECEF8 /* ARTChannels.h */, - D746AE241BBB611C003ECEF8 /* ARTChannels+Private.h */, - D746AE211BBB60EE003ECEF8 /* ARTChannels.m */, - 96BF61551A35B40E004CF2B3 /* ARTStatus.h */, - 1C55427C1B148306003068DB /* ARTStatus.m */, - D746AE1A1BBB5207003ECEF8 /* ARTDataQuery.h */, - D746AE1B1BBB5207003ECEF8 /* ARTDataQuery+Private.h */, - D746AE1C1BBB5207003ECEF8 /* ARTDataQuery.m */, - 96BF61561A35B52C004CF2B3 /* ARTHttp.h */, - 96BF61571A35B52C004CF2B3 /* ARTHttp.m */, - 96BF615C1A35C1C8004CF2B3 /* ARTTypes.h */, - 96BF615D1A35C1C8004CF2B3 /* ARTTypes.m */, - 96BF61621A35CDE1004CF2B3 /* ARTMessage.h */, - 96BF61631A35CDE1004CF2B3 /* ARTMessage.m */, - 96BF616E1A35FB7C004CF2B3 /* ARTAuth.h */, - 96BF616F1A35FB7C004CF2B3 /* ARTAuth.m */, - 96A507931A370F860077CDF8 /* ARTStats.h */, - 96A507941A370F860077CDF8 /* ARTStats.m */, - 850BFB4A1B79323C009D0ADD /* ARTPaginatedResult.h */, - D746AE2E1BBBE7D7003ECEF8 /* ARTPaginatedResult+Private.h */, - 850BFB4B1B79323C009D0ADD /* ARTPaginatedResult.m */, - D746AE261BBB61C9003ECEF8 /* ARTPresence.h */, - D746AE271BBB61C9003ECEF8 /* ARTPresence.m */, - 96A5079F1A377AA50077CDF8 /* ARTPresenceMessage.h */, - 96A507A01A377AA50077CDF8 /* ARTPresenceMessage.m */, - 1C2B0FFB1B136A6D00E3633C /* ARTPresenceMap.h */, - 1C2B0FFC1B136A6D00E3633C /* ARTPresenceMap.m */, - 96A507A31A377DE90077CDF8 /* ARTNSDictionary+ARTDictionaryUtil.h */, - 96A507A41A377DE90077CDF8 /* ARTNSDictionary+ARTDictionaryUtil.m */, - 96A507A71A37806A0077CDF8 /* ARTEncoder.h */, - 96A507AB1A3780F60077CDF8 /* ARTJsonEncoder.h */, - 96A507AC1A3780F60077CDF8 /* ARTJsonEncoder.m */, - 96A507B31A37881C0077CDF8 /* ARTNSDate+ARTUtil.h */, - 96A507B41A37881C0077CDF8 /* ARTNSDate+ARTUtil.m */, - 96E4083D1A3892C700087F77 /* ARTRealtimeTransport.h */, - 96E408411A38939E00087F77 /* ARTProtocolMessage.h */, - 96E408421A38939E00087F77 /* ARTProtocolMessage.m */, - 967A431F1A39AEAF00E4CE23 /* ARTNSArray+ARTFunctional.h */, - 967A43201A39AEAF00E4CE23 /* ARTNSArray+ARTFunctional.m */, - 961343E61A432E7C006DC822 /* ARTPayload.h */, - 961343E71A432E7C006DC822 /* ARTPayload.m */, - 960D07911A45F1D800ED8C8C /* ARTCrypto.h */, - 960D07921A45F1D800ED8C8C /* ARTCrypto.m */, - 960D07951A46FFC300ED8C8C /* ARTRest+Private.h */, - 1CA5E1C01AB72369006ADD70 /* ARTMsgPackEncoder.h */, - 1CA5E1C11AB72369006ADD70 /* ARTMsgPackEncoder.m */, - 1C05CF1E1AC1D7EB00687AC9 /* ARTRealtime+Private.h */, - 1C6C18A11ADFDAB100AB79E4 /* ARTLog.h */, - 1C6C18A21ADFDAB100AB79E4 /* ARTLog.m */, - 1C8065041AE7C8FA00D49357 /* ARTPayload+Private.h */, - 1CEFC60D1B0F9EF500D84463 /* ARTTokenDetails+Private.h */, + 85B2C2181B6FE8DE00EA5254 /* CompatibilityMacros.h */, 1CD8DC9D1B1C7315007EAF36 /* ARTDefault.h */, 1CD8DC9E1B1C7315007EAF36 /* ARTDefault.m */, - 1C578E1D1B3435CA00EF46EC /* ARTFallback.h */, - 1C578E1E1B3435CA00EF46EC /* ARTFallback.m */, + D746AE301BBC299D003ECEF8 /* REST */, + D746AE311BBC29B2003ECEF8 /* Realtime */, + D746AE331BBC29FF003ECEF8 /* Types */, + D746AE351BBC2BF9003ECEF8 /* HTTP */, + D746AE341BBC2B60003ECEF8 /* Utilities */, + 96BF61341A35B2AB004CF2B3 /* Supporting Files */, ); path = "ably-ios"; sourceTree = ""; @@ -501,6 +468,124 @@ name = Pods; sourceTree = ""; }; + D746AE301BBC299D003ECEF8 /* REST */ = { + isa = PBXGroup; + children = ( + 96BF61511A35B39C004CF2B3 /* ARTRest.h */, + 960D07951A46FFC300ED8C8C /* ARTRest+Private.h */, + 96BF61521A35B39C004CF2B3 /* ARTRest.m */, + 96BF616E1A35FB7C004CF2B3 /* ARTAuth.h */, + 96BF616F1A35FB7C004CF2B3 /* ARTAuth.m */, + D746AE321BBC29EB003ECEF8 /* Transport */, + ); + name = REST; + sourceTree = ""; + }; + D746AE311BBC29B2003ECEF8 /* Realtime */ = { + isa = PBXGroup; + children = ( + 96A507BB1A3791490077CDF8 /* ARTRealtime.h */, + 1C05CF1E1AC1D7EB00687AC9 /* ARTRealtime+Private.h */, + 96A507BC1A3791490077CDF8 /* ARTRealtime.m */, + D746AE3E1BBC5B14003ECEF8 /* ARTEventEmitter.h */, + D746AE3F1BBC5B14003ECEF8 /* ARTEventEmitter.m */, + D746AE451BBD6FE9003ECEF8 /* ARTQueuedMessage.h */, + D746AE461BBD6FE9003ECEF8 /* ARTQueuedMessage.m */, + D746AE3A1BBC5AE1003ECEF8 /* ARTRealtimeChannel.h */, + D746AE421BBC5CD0003ECEF8 /* ARTRealtimeChannel+Private.h */, + D746AE3B1BBC5AE1003ECEF8 /* ARTRealtimeChannel.m */, + D746AE491BBD70F0003ECEF8 /* ARTRealtimeChannelSubscription.h */, + D746AE4A1BBD70F0003ECEF8 /* ARTRealtimeChannelSubscription.m */, + D746AE441BBC5CF9003ECEF8 /* ARTSubscription.h */, + D746AE261BBB61C9003ECEF8 /* ARTPresence.h */, + D746AE271BBB61C9003ECEF8 /* ARTPresence.m */, + 1C2B0FFB1B136A6D00E3633C /* ARTPresenceMap.h */, + 1C2B0FFC1B136A6D00E3633C /* ARTPresenceMap.m */, + ); + name = Realtime; + sourceTree = ""; + }; + D746AE321BBC29EB003ECEF8 /* Transport */ = { + isa = PBXGroup; + children = ( + 96E4083D1A3892C700087F77 /* ARTRealtimeTransport.h */, + 96E408451A3895E800087F77 /* ARTWebSocketTransport.h */, + 96E408461A3895E800087F77 /* ARTWebSocketTransport.m */, + ); + name = Transport; + sourceTree = ""; + }; + D746AE331BBC29FF003ECEF8 /* Types */ = { + isa = PBXGroup; + children = ( + 961343D61A42E0B7006DC822 /* ARTClientOptions.h */, + 961343D71A42E0B7006DC822 /* ARTClientOptions.m */, + D746AE201BBB60EE003ECEF8 /* ARTChannel.h */, + D746AE241BBB611C003ECEF8 /* ARTChannel+Private.h */, + D746AE211BBB60EE003ECEF8 /* ARTChannel.m */, + D746AE511BBD85C5003ECEF8 /* ARTChannelCollection.h */, + D746AE551BBD8622003ECEF8 /* ARTChannelCollection+Private.h */, + D746AE521BBD85C5003ECEF8 /* ARTChannelCollection.m */, + D746AE4D1BBD84E7003ECEF8 /* ARTChannelOptions.h */, + D746AE4E1BBD84E7003ECEF8 /* ARTChannelOptions.m */, + 96E408411A38939E00087F77 /* ARTProtocolMessage.h */, + 96E408421A38939E00087F77 /* ARTProtocolMessage.m */, + 96BF61621A35CDE1004CF2B3 /* ARTBaseMessage.h */, + 96BF61631A35CDE1004CF2B3 /* ARTBaseMessage.m */, + D746AE361BBC3201003ECEF8 /* ARTMessage.h */, + D746AE371BBC3201003ECEF8 /* ARTMessage.m */, + 96A5079F1A377AA50077CDF8 /* ARTPresenceMessage.h */, + 96A507A01A377AA50077CDF8 /* ARTPresenceMessage.m */, + 961343E61A432E7C006DC822 /* ARTPayload.h */, + 1C8065041AE7C8FA00D49357 /* ARTPayload+Private.h */, + 961343E71A432E7C006DC822 /* ARTPayload.m */, + 96A507931A370F860077CDF8 /* ARTStats.h */, + 96A507941A370F860077CDF8 /* ARTStats.m */, + 96BF61551A35B40E004CF2B3 /* ARTStatus.h */, + 1C55427C1B148306003068DB /* ARTStatus.m */, + 96BF615C1A35C1C8004CF2B3 /* ARTTypes.h */, + 96BF615D1A35C1C8004CF2B3 /* ARTTypes.m */, + ); + name = Types; + sourceTree = ""; + }; + D746AE341BBC2B60003ECEF8 /* Utilities */ = { + isa = PBXGroup; + children = ( + 960D07911A45F1D800ED8C8C /* ARTCrypto.h */, + 960D07921A45F1D800ED8C8C /* ARTCrypto.m */, + 96A507A71A37806A0077CDF8 /* ARTEncoder.h */, + 96A507AB1A3780F60077CDF8 /* ARTJsonEncoder.h */, + 96A507AC1A3780F60077CDF8 /* ARTJsonEncoder.m */, + 1C6C18A11ADFDAB100AB79E4 /* ARTLog.h */, + 1C6C18A21ADFDAB100AB79E4 /* ARTLog.m */, + 96A507B31A37881C0077CDF8 /* ARTNSDate+ARTUtil.h */, + 96A507B41A37881C0077CDF8 /* ARTNSDate+ARTUtil.m */, + 967A431F1A39AEAF00E4CE23 /* ARTNSArray+ARTFunctional.h */, + 967A43201A39AEAF00E4CE23 /* ARTNSArray+ARTFunctional.m */, + 96A507A31A377DE90077CDF8 /* ARTNSDictionary+ARTDictionaryUtil.h */, + 96A507A41A377DE90077CDF8 /* ARTNSDictionary+ARTDictionaryUtil.m */, + ); + name = Utilities; + sourceTree = ""; + }; + D746AE351BBC2BF9003ECEF8 /* HTTP */ = { + isa = PBXGroup; + children = ( + 96BF61561A35B52C004CF2B3 /* ARTHttp.h */, + 96BF61571A35B52C004CF2B3 /* ARTHttp.m */, + D746AE1A1BBB5207003ECEF8 /* ARTDataQuery.h */, + D746AE1B1BBB5207003ECEF8 /* ARTDataQuery+Private.h */, + D746AE1C1BBB5207003ECEF8 /* ARTDataQuery.m */, + 850BFB4A1B79323C009D0ADD /* ARTPaginatedResult.h */, + D746AE2E1BBBE7D7003ECEF8 /* ARTPaginatedResult+Private.h */, + 850BFB4B1B79323C009D0ADD /* ARTPaginatedResult.m */, + 1C578E1D1B3435CA00EF46EC /* ARTFallback.h */, + 1C578E1E1B3435CA00EF46EC /* ARTFallback.m */, + ); + name = HTTP; + sourceTree = ""; + }; FAFEC5BB8312EB69E84C28D4 /* Frameworks */ = { isa = PBXGroup; children = ( @@ -517,28 +602,31 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + D746AE4B1BBD70F0003ECEF8 /* ARTRealtimeChannelSubscription.h in Headers */, 96BF61531A35B39C004CF2B3 /* ARTRest.h in Headers */, D746AE281BBB61C9003ECEF8 /* ARTPresence.h in Headers */, 96A507BD1A3791490077CDF8 /* ARTRealtime.h in Headers */, 961343D81A42E0B7006DC822 /* ARTClientOptions.h in Headers */, 96BF615E1A35C1C8004CF2B3 /* ARTTypes.h in Headers */, D746AE2F1BBBE7D7003ECEF8 /* ARTPaginatedResult+Private.h in Headers */, - D746AE221BBB60EE003ECEF8 /* ARTChannels.h in Headers */, + D746AE221BBB60EE003ECEF8 /* ARTChannel.h in Headers */, D746AE1D1BBB5207003ECEF8 /* ARTDataQuery.h in Headers */, 1C1EC3FA1AE26A8B00AAADD7 /* ARTStatus.h in Headers */, 96E408431A38939E00087F77 /* ARTProtocolMessage.h in Headers */, + D746AE381BBC3201003ECEF8 /* ARTMessage.h in Headers */, + D746AE471BBD6FE9003ECEF8 /* ARTQueuedMessage.h in Headers */, 96BF61581A35B52C004CF2B3 /* ARTHttp.h in Headers */, - 96BF61641A35CDE1004CF2B3 /* ARTMessage.h in Headers */, + 96BF61641A35CDE1004CF2B3 /* ARTBaseMessage.h in Headers */, 96BF61701A35FB7C004CF2B3 /* ARTAuth.h in Headers */, 96A507A11A377AA50077CDF8 /* ARTPresenceMessage.h in Headers */, 1C2B0FFD1B136A6D00E3633C /* ARTPresenceMap.h in Headers */, 850BFB4C1B79323C009D0ADD /* ARTPaginatedResult.h in Headers */, 1CD8DC9F1B1C7315007EAF36 /* ARTDefault.h in Headers */, 1C8065051AE7C8FA00D49357 /* ARTPayload+Private.h in Headers */, - 1CEFC60E1B0F9EF500D84463 /* ARTTokenDetails+Private.h in Headers */, - 1C70CB881B020D7F003D295A /* ARTClientOptions+Private.h in Headers */, 85F0A60B1B6D039F00EFF45A /* ably.h in Headers */, 960D07971A46FFC300ED8C8C /* ARTRest+Private.h in Headers */, + D746AE401BBC5B14003ECEF8 /* ARTEventEmitter.h in Headers */, + D746AE531BBD85C5003ECEF8 /* ARTChannelCollection.h in Headers */, 1C05CF201AC1D7EB00687AC9 /* ARTRealtime+Private.h in Headers */, 85B2C2191B6FE8DE00EA5254 /* CompatibilityMacros.h in Headers */, 961343E81A432E7C006DC822 /* ARTPayload.h in Headers */, @@ -547,12 +635,14 @@ 96E408471A3895E800087F77 /* ARTWebSocketTransport.h in Headers */, 96E4083F1A3892C700087F77 /* ARTRealtimeTransport.h in Headers */, 96A507B51A37881C0077CDF8 /* ARTNSDate+ARTUtil.h in Headers */, + D746AE4F1BBD84E7003ECEF8 /* ARTChannelOptions.h in Headers */, 96A507A91A37806A0077CDF8 /* ARTEncoder.h in Headers */, D746AE1E1BBB5207003ECEF8 /* ARTDataQuery+Private.h in Headers */, 1C6C18A31ADFDAB100AB79E4 /* ARTLog.h in Headers */, - 1CA5E1C21AB72369006ADD70 /* ARTMsgPackEncoder.h in Headers */, 96A507A51A377DE90077CDF8 /* ARTNSDictionary+ARTDictionaryUtil.h in Headers */, - D746AE251BBB611C003ECEF8 /* ARTChannels+Private.h in Headers */, + D746AE3C1BBC5AE1003ECEF8 /* ARTRealtimeChannel.h in Headers */, + D746AE431BBC5CD0003ECEF8 /* ARTRealtimeChannel+Private.h in Headers */, + D746AE251BBB611C003ECEF8 /* ARTChannel+Private.h in Headers */, 96A507AD1A3780F60077CDF8 /* ARTJsonEncoder.h in Headers */, 96A507951A370F860077CDF8 /* ARTStats.h in Headers */, 960D07931A45F1D800ED8C8C /* ARTCrypto.h in Headers */, @@ -789,8 +879,12 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D746AE231BBB60EE003ECEF8 /* ARTChannels.m in Sources */, + D746AE391BBC3201003ECEF8 /* ARTMessage.m in Sources */, + D746AE231BBB60EE003ECEF8 /* ARTChannel.m in Sources */, + D746AE481BBD6FE9003ECEF8 /* ARTQueuedMessage.m in Sources */, + D746AE3D1BBC5AE1003ECEF8 /* ARTRealtimeChannel.m in Sources */, 96A507A21A377AA50077CDF8 /* ARTPresenceMessage.m in Sources */, + D746AE541BBD85C5003ECEF8 /* ARTChannelCollection.m in Sources */, 96BF61541A35B39C004CF2B3 /* ARTRest.m in Sources */, 961343E91A432E7C006DC822 /* ARTPayload.m in Sources */, 1C2B0FFE1B136A6D00E3633C /* ARTPresenceMap.m in Sources */, @@ -803,14 +897,16 @@ D746AE1F1BBB5207003ECEF8 /* ARTDataQuery.m in Sources */, 961343D91A42E0B7006DC822 /* ARTClientOptions.m in Sources */, 96BF615F1A35C1C8004CF2B3 /* ARTTypes.m in Sources */, + D746AE411BBC5B14003ECEF8 /* ARTEventEmitter.m in Sources */, 96A507AE1A3780F60077CDF8 /* ARTJsonEncoder.m in Sources */, 96A507961A370F860077CDF8 /* ARTStats.m in Sources */, + D746AE4C1BBD70F0003ECEF8 /* ARTRealtimeChannelSubscription.m in Sources */, 96E408481A3895E800087F77 /* ARTWebSocketTransport.m in Sources */, 96BF61711A35FB7C004CF2B3 /* ARTAuth.m in Sources */, 96E408441A38939E00087F77 /* ARTProtocolMessage.m in Sources */, - 96BF61651A35CDE1004CF2B3 /* ARTMessage.m in Sources */, - 1CA5E1C31AB72369006ADD70 /* ARTMsgPackEncoder.m in Sources */, + 96BF61651A35CDE1004CF2B3 /* ARTBaseMessage.m in Sources */, 1CD8DCA01B1C7315007EAF36 /* ARTDefault.m in Sources */, + D746AE501BBD84E7003ECEF8 /* ARTChannelOptions.m in Sources */, 1C6C18A41ADFDAB100AB79E4 /* ARTLog.m in Sources */, D746AE291BBB61C9003ECEF8 /* ARTPresence.m in Sources */, 96A507BE1A3791490077CDF8 /* ARTRealtime.m in Sources */, diff --git a/ablySpec/Auth.swift b/ablySpec/Auth.swift new file mode 100644 index 000000000..dc44ae640 --- /dev/null +++ b/ablySpec/Auth.swift @@ -0,0 +1,92 @@ +// +// Auth.swift +// ably +// +// Created by Yavor Georgiev on 28.08.15. +// Copyright (c) 2015 г. Ably. All rights reserved. +// + +import Nimble +import Quick +import ably +import ably.Private + +class Auth : QuickSpec { + override func spec() { + var mockExecutor: MockHTTPExecutor! + beforeEach { + mockExecutor = MockHTTPExecutor() + } + + describe("Basic") { + // RSA1 + it("should work over HTTPS only") { + let clientOptions = AblyTests.setupOptions(AblyTests.jsonRestOptions) + clientOptions.tls = false + + expect{ ARTRest(options: clientOptions) }.to(raiseException()) + } + + // RSA11 + it("should send the API key in the Authorization header") { + let client = ARTRest(options: AblyTests.setupOptions(AblyTests.jsonRestOptions)) + client.httpExecutor = mockExecutor + + publishTestMessage(client, failOnError: false) + + let key64 = NSString(string: "\(client.options.authOptions.keyName):\(client.options.authOptions.keySecret)") + .dataUsingEncoding(NSUTF8StringEncoding)? + .base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0)) + let Authorization = "Basic \(key64!)" + expect(mockExecutor.requests.first?.allHTTPHeaderFields?["Authorization"]).to(equal(Authorization)) + } + + // RSA2 + it("should be default when an API йеъ is set") { + let client = ARTRest(options: ARTClientOptions(key: "fake:key")) + + expect(client.auth.getAuthMethod()).to(equal(ARTAuthMethod.Basic)) + } + } + + describe("Token") { + + fit("should send the token in the Authorization header") { + let options = ARTClientOptions() + options.authOptions.token = getTestToken() + let client = ARTRest(options: options) + client.httpExecutor = mockExecutor + + publishTestMessage(client, failOnError: false) + + let token64 = NSString(string: client.options.authOptions.token) + .dataUsingEncoding(NSUTF8StringEncoding)? + .base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0)) + let Authorization = "Bearer \(token64!)" + expect(mockExecutor.requests.first?.allHTTPHeaderFields?["Authorization"]).to(equal(Authorization)) + } + + // RSA4 + context("auhentication method") { + let cases: [String: (ARTAuthOptions) -> ()] = [ + "useTokenAuth": { $0.useTokenAuth = true; $0.key = "fake:key" }, + "clientId": { $0.clientId = "clientId" }, + "authUrl": { $0.authUrl = NSURL(string: "http://test.com") }, + "authCallback": { $0.authCallback = { _ in return nil } }, + "token": { $0.token = "" } + ] + + for (caseName, caseSetter) in cases { + it("should be default when \(caseName) is set") { + let options = ARTClientOptions() + caseSetter(options.authOptions) + + let client = ARTRest(options: options) + + expect(client.auth.getAuthMethod()).to(equal(ARTAuthMethod.Token)) + } + } + } + } + } +} From 2707181058598c9bc9459c96242fcd0f6703b19c Mon Sep 17 00:00:00 2001 From: Ricardo Pereira Date: Fri, 2 Oct 2015 01:37:55 +0100 Subject: [PATCH 13/22] Implementing RSA8e --- ably-ios/ARTAuth.h | 4 +- ably-ios/ARTAuth.m | 110 +++++++++++++++++++++++++++---- ably-ios/ARTClientOptions.h | 3 +- ably-ios/ARTClientOptions.m | 2 + ably-ios/ARTRealtime.h | 2 +- ably-ios/ARTRest+Private.h | 3 +- ably-ios/ARTRest.m | 10 +-- ably-ios/ably.h | 22 +++++++ ably-ios/ably.modulemap | 9 +-- ably-iosTests/ARTRestTokenTest.m | 2 +- ably.xcodeproj/project.pbxproj | 86 ++++++++++++------------ ablySpec/RealtimeClient.swift | 3 + ablySpec/RestChannel.swift | 2 +- ablySpec/RestClient.stats.swift | 6 +- ablySpec/RestClient.swift | 23 ++++--- ablySpec/TestUtilities.swift | 9 ++- 16 files changed, 209 insertions(+), 87 deletions(-) diff --git a/ably-ios/ARTAuth.h b/ably-ios/ARTAuth.h index 55135aaa0..7fc6954c7 100644 --- a/ably-ios/ARTAuth.h +++ b/ably-ios/ARTAuth.h @@ -40,6 +40,8 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)init; +- (NSArray *)toArray; + @end @interface ARTAuthTokenRequest : ARTAuthTokenParams @@ -97,7 +99,7 @@ typedef NS_ENUM(NSUInteger, ARTAuthMethod) { - (instancetype)initWithRest:(ARTRest *)rest options:(ARTAuthOptions *)options; -- (void)requestToken:(nullable ARTAuthTokenParams *)tokenParams options:(nullable ARTAuthOptions *)options +- (void)requestToken:(nullable ARTAuthTokenParams *)tokenParams withOptions:(nullable ARTAuthOptions *)authOptions callback:(void (^)(ARTAuthTokenDetails *__nullable tokenDetails, NSError *__nullable error))callback; - (void)authorise:(nullable ARTAuthTokenParams *)tokenParams options:(nullable ARTAuthOptions *)options force:(BOOL)force diff --git a/ably-ios/ARTAuth.m b/ably-ios/ARTAuth.m index 0bcf5ab6a..3b4abe781 100644 --- a/ably-ios/ARTAuth.m +++ b/ably-ios/ARTAuth.m @@ -46,6 +46,9 @@ - (instancetype)initWithToken:(NSString *)token { @end + +#pragma mark - ARTAuthTokenParams + @implementation ARTAuthTokenParams - (instancetype)init { @@ -66,6 +69,21 @@ - (void)setTimestamp:(NSDate *)timestamp { _timestamp = timestamp; } +- (NSArray *)toArray { + NSMutableArray *params = [[NSMutableArray alloc] init]; + + if (self.clientId) + [params addObject:[NSString stringWithFormat:@"clientId=%@", self.clientId]]; + if (self.ttl > 0) + [params addObject:[NSString stringWithFormat:@"ttl=%f", self.ttl]]; + if (self.capability) + [params addObject:[NSString stringWithFormat:@"capability=%@", self.capability]]; + if (self.timestamp > 0) + [params addObject:[NSString stringWithFormat:@"timestamp=%f", [self.timestamp timeIntervalSince1970]]]; + + return params; +} + static NSString *generateNonce() { // Generate two random numbers up to 8 digits long and concatenate them to produce a 16 digit random number NSUInteger r1 = arc4random_uniform(100000000); @@ -102,6 +120,9 @@ - (ARTAuthTokenRequest *)sign:(NSString *)key { @end + +#pragma mark - ARTAuthTokenRequest + @implementation ARTAuthTokenRequest @dynamic timestamp; @@ -126,6 +147,9 @@ - (NSDictionary *)asDictionary { @end + +#pragma mark - ARTAuthOptions + @implementation ARTAuthOptions - (instancetype)initWithKey:(NSString *)key { @@ -172,6 +196,9 @@ - (void)setAuthMethod:(NSString *)authMethod { @end + +#pragma mark - ARTAuth implementation + @implementation ARTAuth { __weak ARTRest *_rest; } @@ -201,36 +228,97 @@ - (BOOL)shouldUseTokenAuth { return NO; } -- (void)requestToken:(ARTAuthTokenParams *)tokenParams options:(ARTAuthOptions *)options +/** + # (RSA8) Auth#requestToken + + Implicitly creates a `TokenRequest` if required, and requests a token from Ably if required. + + `TokenParams` and `AuthOptions` are optional. + When provided, the values supersede matching client library configured params and options. + + - Parameter tokenParams: Token params (optional). + - Parameter authOptions: Authentication options (optional). + - Parameter callback: Completion callback (ARTAuthTokenDetails, NSError). + */ +- (void)requestToken:(ARTAuthTokenParams *)tokenParams withOptions:(ARTAuthOptions *)authOptions callback:(void (^)(ARTAuthTokenDetails *, NSError *))callback { - ARTAuthOptions *mergedOptions = options; + + ARTAuthOptions *mergedOptions = authOptions; + ARTAuthTokenParams *currentTokenParams = tokenParams; + + if (!mergedOptions) { + // TODO: Merge + mergedOptions = self.options; + } + + if (!currentTokenParams) { + currentTokenParams = [[ARTAuthTokenParams alloc] init]; + // TODO: Client library configured params + NSAssert(false, @"Client library configured params not implemented"); + + /* + currentTokenParams.clientId + currentTokenParams.capability + currentTokenParams.ttl + currentTokenParams.timestamp + */ + } if (mergedOptions.authUrl) { NSURLComponents *urlComponents = [NSURLComponents componentsWithURL:mergedOptions.authUrl resolvingAgainstBaseURL:YES]; - if (mergedOptions.authParams) { - urlComponents.queryItems = [[NSArray arrayWithArray:urlComponents.queryItems] arrayByAddingObjectsFromArray:mergedOptions.authParams]; + + if (!mergedOptions.authMethod || !mergedOptions.authMethod.length) { + mergedOptions.authMethod = @"GET"; //Default + } + + if ([mergedOptions.authMethod isEqualToString:@"POST"]) { + // When POST, use body of the POST request + + // TODO + + //[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; + + //NSData.length + //[request setValue:@"" forHTTPHeaderField:@"Content-Length"]; + } + else { + // When GET, use query string params + if (mergedOptions.authParams) { + urlComponents.queryItems = [urlComponents.queryItems arrayByAddingObjectsFromArray:mergedOptions.authParams]; + } + urlComponents.queryItems = [urlComponents.queryItems arrayByAddingObjectsFromArray:[currentTokenParams toArray]]; + + //(RSA8c2) TokenParams take precedence over any configured authParams when a name conflict occurs + // TODO + + //[request setValue:@"application/json" forHTTPHeaderField:@"Accept"]; } NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:urlComponents.URL]; request.HTTPMethod = mergedOptions.authMethod; + for (NSString *key in mergedOptions.authHeaders) { [request setValue:mergedOptions.authHeaders[key] forHTTPHeaderField:key]; } - [request setValue:@"application/json" forHTTPHeaderField:@"Accept"]; + + [_rest.logger debug:@"%@ %@", request.HTTPMethod, urlComponents.URL]; [_rest.httpExecutor executeRequest:request callback:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { if (error) { callback(nil, error); } else { - // check if response is TokenRequest or TokenDetails and act accordingly + // The token retrieved is assumed by the library to be a token string if the response has Content-Type "text/plain", or taken to be a TokenRequest or TokenDetails object if the response has Content-Type "application/json". + + // TODO + NSAssert(false, @"Token string or TokenDetails object not implemented"); } }]; } else { - ARTAuthCallback tokenRequestFactory = mergedOptions.authCallback ?: ^(ARTAuthTokenParams *tokenParams, void(^callback)(ARTAuthTokenRequest *tokenRequest, NSError *error)) { - [self createTokenRequest:tokenParams options:mergedOptions callback:callback]; + ARTAuthCallback tokenRequestFactory = mergedOptions.authCallback? : ^(ARTAuthTokenParams *tokenParams, void(^callback)(ARTAuthTokenRequest *tokenRequest, NSError *error)) { + [self createTokenRequest:currentTokenParams options:mergedOptions callback:callback]; }; - tokenRequestFactory(tokenParams, ^(ARTAuthTokenRequest *tokenRequest, NSError *error) { + tokenRequestFactory(currentTokenParams, ^(ARTAuthTokenRequest *tokenRequest, NSError *error) { if (error) { callback(nil, error); } else { @@ -268,7 +356,7 @@ - (void)authorise:(ARTAuthTokenParams *)tokenParams options:(ARTAuthOptions *)op callback(self.currentToken, nil); } else { [self.logger verbose:@"ARTAuth authorise requesting new token."]; - [self requestToken:tokenParams options:options callback:^(ARTAuthTokenDetails *tokenDetails, NSError *error) { + [self requestToken:tokenParams withOptions:options callback:^(ARTAuthTokenDetails *tokenDetails, NSError *error) { if (error) { callback(nil, error); } else { @@ -302,7 +390,7 @@ - (void)createTokenRequest:(ARTAuthTokenParams *)tokenParams options:(ARTAuthOpt - (BOOL)canRequestToken { if (self.options.authCallback) { - [self.logger verbose:@"ARTAuth can request token via authCb"]; + [self.logger verbose:@"ARTAuth can request token via authCallback"]; return YES; } else if (self.options.authUrl) { [self.logger verbose:@"ARTAuth can request token via authURL"]; diff --git a/ably-ios/ARTClientOptions.h b/ably-ios/ARTClientOptions.h index df0462688..741d7cf39 100644 --- a/ably-ios/ARTClientOptions.h +++ b/ably-ios/ARTClientOptions.h @@ -8,7 +8,7 @@ #import -#import "ARTAuth.h" +@class ARTAuthOptions; @interface ARTClientOptions : NSObject @@ -37,5 +37,6 @@ - (bool)isFallbackPermitted; + (NSURL*)restUrl:(NSString *)host port:(int)port tls:(BOOL)tls; +- (NSURL *)restUrl; @end diff --git a/ably-ios/ARTClientOptions.m b/ably-ios/ARTClientOptions.m index c6abef3b0..e4ddf03f8 100644 --- a/ably-ios/ARTClientOptions.m +++ b/ably-ios/ARTClientOptions.m @@ -7,7 +7,9 @@ // #import "ARTClientOptions.h" + #import "ARTDefault.h" +#import "ARTAuth.h" @interface ARTClientOptions () diff --git a/ably-ios/ARTRealtime.h b/ably-ios/ARTRealtime.h index 5fe2882d1..0814634bd 100644 --- a/ably-ios/ARTRealtime.h +++ b/ably-ios/ARTRealtime.h @@ -23,7 +23,7 @@ @class ARTRealtimeChannelPresenceSubscription; @class ARTEventEmitter; @class ARTRealtimeChannel; - +@class ARTAuth; #pragma mark - ARTRealtime diff --git a/ably-ios/ARTRest+Private.h b/ably-ios/ARTRest+Private.h index 4c93ec9a0..87d08e65c 100644 --- a/ably-ios/ARTRest+Private.h +++ b/ably-ios/ARTRest+Private.h @@ -7,7 +7,8 @@ // #import -#import + +#import "ARTRest.h" @protocol ARTEncoder; @protocol ARTHTTPExecutor; diff --git a/ably-ios/ARTRest.m b/ably-ios/ARTRest.m index fa67782bb..bd5e5af3f 100644 --- a/ably-ios/ARTRest.m +++ b/ably-ios/ARTRest.m @@ -45,7 +45,7 @@ - (instancetype)initWithLogger:(ARTLog *)logger andOptions:(ARTClientOptions *)o if (self) { NSAssert(options, @"ARTRest: No options provided"); _options = options; - //_baseUrl = [options restUrl]; + _baseUrl = [options restUrl]; if (logger) { _logger = logger; @@ -54,10 +54,11 @@ - (instancetype)initWithLogger:(ARTLog *)logger andOptions:(ARTClientOptions *)o _logger = [[ARTLog alloc] init]; } + // FIXME: _http = [[ARTHttp alloc] init]; - // ?! - //_httpExecutor = _http; - //_httpExecutor.logger = _logger; + // Private + self.httpExecutor = _http; + self.httpExecutor.logger = _logger; // FIXME: //_channels = [[ARTChannelCollection alloc] initWithRest:self]; @@ -99,6 +100,7 @@ - (void)executeRequest:(NSMutableURLRequest *)request callback:(void (^)(NSHTTPU NSError *error = [self->_encoders[response.MIMEType] decodeError:data]; if (error.code == 40140) { // TODO: request token or error if no token information + NSAssert(false, @"Request token or error if no token information"); } else { callback(nil, nil, error); } diff --git a/ably-ios/ably.h b/ably-ios/ably.h index afe103db9..eb2dba1fe 100644 --- a/ably-ios/ably.h +++ b/ably-ios/ably.h @@ -17,6 +17,28 @@ FOUNDATION_EXPORT const unsigned char ablyVersionString[]; #import #import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + #import #import #import diff --git a/ably-ios/ably.modulemap b/ably-ios/ably.modulemap index 6e732da1a..00244abc6 100644 --- a/ably-ios/ably.modulemap +++ b/ably-ios/ably.modulemap @@ -5,15 +5,12 @@ framework module ably { module * { export * } explicit module Private { - header "ARTEncoder.h" - header "ARTJsonEncoder.h" - - header "ARTChannels+Private.h" + header "ARTChannel+Private.h" header "ARTDataQuery+Private.h" header "ARTPayload+Private.h" - header "ARTTokenDetails+Private.h" header "ARTRest+Private.h" - header "ARTClientOptions+Private.h" header "ARTPaginatedResult+Private.h" + header "ARTEncoder.h" + header "ARTJsonEncoder.h" } } diff --git a/ably-iosTests/ARTRestTokenTest.m b/ably-iosTests/ARTRestTokenTest.m index abc1e178b..7913d98f2 100644 --- a/ably-iosTests/ARTRestTokenTest.m +++ b/ably-iosTests/ARTRestTokenTest.m @@ -16,7 +16,7 @@ #import "ARTRest+Private.h" #import "ARTLog.h" #import "ARTPayload.h" -#import "ARTTokenDetails+Private.h" + @interface ARTRestTokenTest : XCTestCase { ARTRest *_rest; diff --git a/ably.xcodeproj/project.pbxproj b/ably.xcodeproj/project.pbxproj index 9ebff51bc..dcb71e2b0 100644 --- a/ably.xcodeproj/project.pbxproj +++ b/ably.xcodeproj/project.pbxproj @@ -22,10 +22,10 @@ 1C1E53021AB373C5004A690F /* ARTRealtimeChannelTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 1C1E53011AB373C5004A690F /* ARTRealtimeChannelTest.m */; }; 1C1EC3FA1AE26A8B00AAADD7 /* ARTStatus.h in Headers */ = {isa = PBXBuildFile; fileRef = 96BF61551A35B40E004CF2B3 /* ARTStatus.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1C2B0FFA1B13388500E3633C /* ARTRealtimeTokenTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 1C2B0FF91B13388500E3633C /* ARTRealtimeTokenTest.m */; }; - 1C2B0FFD1B136A6D00E3633C /* ARTPresenceMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 1C2B0FFB1B136A6D00E3633C /* ARTPresenceMap.h */; }; + 1C2B0FFD1B136A6D00E3633C /* ARTPresenceMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 1C2B0FFB1B136A6D00E3633C /* ARTPresenceMap.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1C2B0FFE1B136A6D00E3633C /* ARTPresenceMap.m in Sources */ = {isa = PBXBuildFile; fileRef = 1C2B0FFC1B136A6D00E3633C /* ARTPresenceMap.m */; }; 1C55427D1B148306003068DB /* ARTStatus.m in Sources */ = {isa = PBXBuildFile; fileRef = 1C55427C1B148306003068DB /* ARTStatus.m */; }; - 1C578E1F1B3435CA00EF46EC /* ARTFallback.h in Headers */ = {isa = PBXBuildFile; fileRef = 1C578E1D1B3435CA00EF46EC /* ARTFallback.h */; }; + 1C578E1F1B3435CA00EF46EC /* ARTFallback.h in Headers */ = {isa = PBXBuildFile; fileRef = 1C578E1D1B3435CA00EF46EC /* ARTFallback.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1C578E201B3435CA00EF46EC /* ARTFallback.m in Sources */ = {isa = PBXBuildFile; fileRef = 1C578E1E1B3435CA00EF46EC /* ARTFallback.m */; }; 1C578E221B3438F300EF46EC /* ARTFallbackTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 1C578E211B3438F300EF46EC /* ARTFallbackTest.m */; }; 1C5DA0151A6E5FA400A2B7EF /* ably.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = 96BF61311A35B2AB004CF2B3 /* ably.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -44,7 +44,7 @@ 1CC3D95B1AB702E30005BEB0 /* ARTRealtimePresenceHistoryTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 1CC3D95A1AB702E30005BEB0 /* ARTRealtimePresenceHistoryTest.m */; }; 1CC3D95D1AB704080005BEB0 /* ARTRealtimeRecoverTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 1CC3D95C1AB704080005BEB0 /* ARTRealtimeRecoverTest.m */; }; 1CC3D95F1AB7043F0005BEB0 /* ARTRealtimeResumeTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 1CC3D95E1AB7043F0005BEB0 /* ARTRealtimeResumeTest.m */; }; - 1CD8DC9F1B1C7315007EAF36 /* ARTDefault.h in Headers */ = {isa = PBXBuildFile; fileRef = 1CD8DC9D1B1C7315007EAF36 /* ARTDefault.h */; }; + 1CD8DC9F1B1C7315007EAF36 /* ARTDefault.h in Headers */ = {isa = PBXBuildFile; fileRef = 1CD8DC9D1B1C7315007EAF36 /* ARTDefault.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1CD8DCA01B1C7315007EAF36 /* ARTDefault.m in Sources */ = {isa = PBXBuildFile; fileRef = 1CD8DC9E1B1C7315007EAF36 /* ARTDefault.m */; }; 2EBDBEEBF881A49F795F1D2E /* Pods_ablySpec.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52CD0A2BB89BAA3D8353FBF3 /* Pods_ablySpec.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; 850BFB4C1B79323C009D0ADD /* ARTPaginatedResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 850BFB4A1B79323C009D0ADD /* ARTPaginatedResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -64,18 +64,18 @@ 961343D91A42E0B7006DC822 /* ARTClientOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 961343D71A42E0B7006DC822 /* ARTClientOptions.m */; }; 961343E81A432E7C006DC822 /* ARTPayload.h in Headers */ = {isa = PBXBuildFile; fileRef = 961343E61A432E7C006DC822 /* ARTPayload.h */; settings = {ATTRIBUTES = (Public, ); }; }; 961343E91A432E7C006DC822 /* ARTPayload.m in Sources */ = {isa = PBXBuildFile; fileRef = 961343E71A432E7C006DC822 /* ARTPayload.m */; }; - 967A43211A39AEAF00E4CE23 /* ARTNSArray+ARTFunctional.h in Headers */ = {isa = PBXBuildFile; fileRef = 967A431F1A39AEAF00E4CE23 /* ARTNSArray+ARTFunctional.h */; }; + 967A43211A39AEAF00E4CE23 /* ARTNSArray+ARTFunctional.h in Headers */ = {isa = PBXBuildFile; fileRef = 967A431F1A39AEAF00E4CE23 /* ARTNSArray+ARTFunctional.h */; settings = {ATTRIBUTES = (Public, ); }; }; 967A43221A39AEAF00E4CE23 /* ARTNSArray+ARTFunctional.m in Sources */ = {isa = PBXBuildFile; fileRef = 967A43201A39AEAF00E4CE23 /* ARTNSArray+ARTFunctional.m */; }; 96A507951A370F860077CDF8 /* ARTStats.h in Headers */ = {isa = PBXBuildFile; fileRef = 96A507931A370F860077CDF8 /* ARTStats.h */; settings = {ATTRIBUTES = (Public, ); }; }; 96A507961A370F860077CDF8 /* ARTStats.m in Sources */ = {isa = PBXBuildFile; fileRef = 96A507941A370F860077CDF8 /* ARTStats.m */; }; 96A507A11A377AA50077CDF8 /* ARTPresenceMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = 96A5079F1A377AA50077CDF8 /* ARTPresenceMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; 96A507A21A377AA50077CDF8 /* ARTPresenceMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 96A507A01A377AA50077CDF8 /* ARTPresenceMessage.m */; }; - 96A507A51A377DE90077CDF8 /* ARTNSDictionary+ARTDictionaryUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 96A507A31A377DE90077CDF8 /* ARTNSDictionary+ARTDictionaryUtil.h */; }; + 96A507A51A377DE90077CDF8 /* ARTNSDictionary+ARTDictionaryUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 96A507A31A377DE90077CDF8 /* ARTNSDictionary+ARTDictionaryUtil.h */; settings = {ATTRIBUTES = (Public, ); }; }; 96A507A61A377DE90077CDF8 /* ARTNSDictionary+ARTDictionaryUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 96A507A41A377DE90077CDF8 /* ARTNSDictionary+ARTDictionaryUtil.m */; }; 96A507A91A37806A0077CDF8 /* ARTEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 96A507A71A37806A0077CDF8 /* ARTEncoder.h */; settings = {ATTRIBUTES = (Private, ); }; }; 96A507AD1A3780F60077CDF8 /* ARTJsonEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 96A507AB1A3780F60077CDF8 /* ARTJsonEncoder.h */; settings = {ATTRIBUTES = (Private, ); }; }; 96A507AE1A3780F60077CDF8 /* ARTJsonEncoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 96A507AC1A3780F60077CDF8 /* ARTJsonEncoder.m */; }; - 96A507B51A37881C0077CDF8 /* ARTNSDate+ARTUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 96A507B31A37881C0077CDF8 /* ARTNSDate+ARTUtil.h */; }; + 96A507B51A37881C0077CDF8 /* ARTNSDate+ARTUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 96A507B31A37881C0077CDF8 /* ARTNSDate+ARTUtil.h */; settings = {ATTRIBUTES = (Public, ); }; }; 96A507B61A37881C0077CDF8 /* ARTNSDate+ARTUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 96A507B41A37881C0077CDF8 /* ARTNSDate+ARTUtil.m */; }; 96A507BD1A3791490077CDF8 /* ARTRealtime.h in Headers */ = {isa = PBXBuildFile; fileRef = 96A507BB1A3791490077CDF8 /* ARTRealtime.h */; settings = {ATTRIBUTES = (Public, ); }; }; 96A507BE1A3791490077CDF8 /* ARTRealtime.m in Sources */ = {isa = PBXBuildFile; fileRef = 96A507BC1A3791490077CDF8 /* ARTRealtime.m */; }; @@ -93,37 +93,37 @@ 96BF61711A35FB7C004CF2B3 /* ARTAuth.m in Sources */ = {isa = PBXBuildFile; fileRef = 96BF616F1A35FB7C004CF2B3 /* ARTAuth.m */; }; 96E408361A38595F00087F77 /* ARTRealtimeAttachTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 96E408351A38595F00087F77 /* ARTRealtimeAttachTest.m */; }; 96E4083C1A38622D00087F77 /* ARTTestUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 96E4083B1A38622D00087F77 /* ARTTestUtil.m */; }; - 96E4083F1A3892C700087F77 /* ARTRealtimeTransport.h in Headers */ = {isa = PBXBuildFile; fileRef = 96E4083D1A3892C700087F77 /* ARTRealtimeTransport.h */; }; - 96E408431A38939E00087F77 /* ARTProtocolMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = 96E408411A38939E00087F77 /* ARTProtocolMessage.h */; }; + 96E4083F1A3892C700087F77 /* ARTRealtimeTransport.h in Headers */ = {isa = PBXBuildFile; fileRef = 96E4083D1A3892C700087F77 /* ARTRealtimeTransport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 96E408431A38939E00087F77 /* ARTProtocolMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = 96E408411A38939E00087F77 /* ARTProtocolMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; 96E408441A38939E00087F77 /* ARTProtocolMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 96E408421A38939E00087F77 /* ARTProtocolMessage.m */; }; - 96E408471A3895E800087F77 /* ARTWebSocketTransport.h in Headers */ = {isa = PBXBuildFile; fileRef = 96E408451A3895E800087F77 /* ARTWebSocketTransport.h */; }; + 96E408471A3895E800087F77 /* ARTWebSocketTransport.h in Headers */ = {isa = PBXBuildFile; fileRef = 96E408451A3895E800087F77 /* ARTWebSocketTransport.h */; settings = {ATTRIBUTES = (Public, ); }; }; 96E408481A3895E800087F77 /* ARTWebSocketTransport.m in Sources */ = {isa = PBXBuildFile; fileRef = 96E408461A3895E800087F77 /* ARTWebSocketTransport.m */; }; D72304701BB72CED00F1ABDA /* RealtimeClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = D723046F1BB72CED00F1ABDA /* RealtimeClient.swift */; }; - D746AE1D1BBB5207003ECEF8 /* ARTDataQuery.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE1A1BBB5207003ECEF8 /* ARTDataQuery.h */; }; - D746AE1E1BBB5207003ECEF8 /* ARTDataQuery+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE1B1BBB5207003ECEF8 /* ARTDataQuery+Private.h */; }; + D746AE1D1BBB5207003ECEF8 /* ARTDataQuery.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE1A1BBB5207003ECEF8 /* ARTDataQuery.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D746AE1E1BBB5207003ECEF8 /* ARTDataQuery+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE1B1BBB5207003ECEF8 /* ARTDataQuery+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; D746AE1F1BBB5207003ECEF8 /* ARTDataQuery.m in Sources */ = {isa = PBXBuildFile; fileRef = D746AE1C1BBB5207003ECEF8 /* ARTDataQuery.m */; }; - D746AE221BBB60EE003ECEF8 /* ARTChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE201BBB60EE003ECEF8 /* ARTChannel.h */; }; + D746AE221BBB60EE003ECEF8 /* ARTChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE201BBB60EE003ECEF8 /* ARTChannel.h */; settings = {ATTRIBUTES = (Public, ); }; }; D746AE231BBB60EE003ECEF8 /* ARTChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = D746AE211BBB60EE003ECEF8 /* ARTChannel.m */; }; - D746AE251BBB611C003ECEF8 /* ARTChannel+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE241BBB611C003ECEF8 /* ARTChannel+Private.h */; }; - D746AE281BBB61C9003ECEF8 /* ARTPresence.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE261BBB61C9003ECEF8 /* ARTPresence.h */; }; + D746AE251BBB611C003ECEF8 /* ARTChannel+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE241BBB611C003ECEF8 /* ARTChannel+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + D746AE281BBB61C9003ECEF8 /* ARTPresence.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE261BBB61C9003ECEF8 /* ARTPresence.h */; settings = {ATTRIBUTES = (Public, ); }; }; D746AE291BBB61C9003ECEF8 /* ARTPresence.m in Sources */ = {isa = PBXBuildFile; fileRef = D746AE271BBB61C9003ECEF8 /* ARTPresence.m */; }; D746AE2C1BBB625E003ECEF8 /* RestChannel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D746AE2A1BBB625E003ECEF8 /* RestChannel.swift */; }; D746AE2D1BBB625E003ECEF8 /* RestClient.channels.swift in Sources */ = {isa = PBXBuildFile; fileRef = D746AE2B1BBB625E003ECEF8 /* RestClient.channels.swift */; }; - D746AE2F1BBBE7D7003ECEF8 /* ARTPaginatedResult+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE2E1BBBE7D7003ECEF8 /* ARTPaginatedResult+Private.h */; }; - D746AE381BBC3201003ECEF8 /* ARTMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE361BBC3201003ECEF8 /* ARTMessage.h */; }; + D746AE2F1BBBE7D7003ECEF8 /* ARTPaginatedResult+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE2E1BBBE7D7003ECEF8 /* ARTPaginatedResult+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + D746AE381BBC3201003ECEF8 /* ARTMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE361BBC3201003ECEF8 /* ARTMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; D746AE391BBC3201003ECEF8 /* ARTMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = D746AE371BBC3201003ECEF8 /* ARTMessage.m */; }; - D746AE3C1BBC5AE1003ECEF8 /* ARTRealtimeChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE3A1BBC5AE1003ECEF8 /* ARTRealtimeChannel.h */; }; + D746AE3C1BBC5AE1003ECEF8 /* ARTRealtimeChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE3A1BBC5AE1003ECEF8 /* ARTRealtimeChannel.h */; settings = {ATTRIBUTES = (Public, ); }; }; D746AE3D1BBC5AE1003ECEF8 /* ARTRealtimeChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = D746AE3B1BBC5AE1003ECEF8 /* ARTRealtimeChannel.m */; }; - D746AE401BBC5B14003ECEF8 /* ARTEventEmitter.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE3E1BBC5B14003ECEF8 /* ARTEventEmitter.h */; }; + D746AE401BBC5B14003ECEF8 /* ARTEventEmitter.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE3E1BBC5B14003ECEF8 /* ARTEventEmitter.h */; settings = {ATTRIBUTES = (Public, ); }; }; D746AE411BBC5B14003ECEF8 /* ARTEventEmitter.m in Sources */ = {isa = PBXBuildFile; fileRef = D746AE3F1BBC5B14003ECEF8 /* ARTEventEmitter.m */; }; - D746AE431BBC5CD0003ECEF8 /* ARTRealtimeChannel+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE421BBC5CD0003ECEF8 /* ARTRealtimeChannel+Private.h */; }; - D746AE471BBD6FE9003ECEF8 /* ARTQueuedMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE451BBD6FE9003ECEF8 /* ARTQueuedMessage.h */; }; + D746AE431BBC5CD0003ECEF8 /* ARTRealtimeChannel+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE421BBC5CD0003ECEF8 /* ARTRealtimeChannel+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + D746AE471BBD6FE9003ECEF8 /* ARTQueuedMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE451BBD6FE9003ECEF8 /* ARTQueuedMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; D746AE481BBD6FE9003ECEF8 /* ARTQueuedMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = D746AE461BBD6FE9003ECEF8 /* ARTQueuedMessage.m */; }; D746AE4B1BBD70F0003ECEF8 /* ARTRealtimeChannelSubscription.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE491BBD70F0003ECEF8 /* ARTRealtimeChannelSubscription.h */; }; D746AE4C1BBD70F0003ECEF8 /* ARTRealtimeChannelSubscription.m in Sources */ = {isa = PBXBuildFile; fileRef = D746AE4A1BBD70F0003ECEF8 /* ARTRealtimeChannelSubscription.m */; }; - D746AE4F1BBD84E7003ECEF8 /* ARTChannelOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE4D1BBD84E7003ECEF8 /* ARTChannelOptions.h */; }; + D746AE4F1BBD84E7003ECEF8 /* ARTChannelOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE4D1BBD84E7003ECEF8 /* ARTChannelOptions.h */; settings = {ATTRIBUTES = (Public, ); }; }; D746AE501BBD84E7003ECEF8 /* ARTChannelOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = D746AE4E1BBD84E7003ECEF8 /* ARTChannelOptions.m */; }; - D746AE531BBD85C5003ECEF8 /* ARTChannelCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE511BBD85C5003ECEF8 /* ARTChannelCollection.h */; }; + D746AE531BBD85C5003ECEF8 /* ARTChannelCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE511BBD85C5003ECEF8 /* ARTChannelCollection.h */; settings = {ATTRIBUTES = (Public, ); }; }; D746AE541BBD85C5003ECEF8 /* ARTChannelCollection.m in Sources */ = {isa = PBXBuildFile; fileRef = D746AE521BBD85C5003ECEF8 /* ARTChannelCollection.m */; }; FC78549C1BCD5688539CCBE4 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FDA6E099E4BC04FC80F845C0 /* libPods.a */; }; /* End PBXBuildFile section */ @@ -602,47 +602,47 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - D746AE4B1BBD70F0003ECEF8 /* ARTRealtimeChannelSubscription.h in Headers */, + 85F0A60B1B6D039F00EFF45A /* ably.h in Headers */, 96BF61531A35B39C004CF2B3 /* ARTRest.h in Headers */, - D746AE281BBB61C9003ECEF8 /* ARTPresence.h in Headers */, 96A507BD1A3791490077CDF8 /* ARTRealtime.h in Headers */, 961343D81A42E0B7006DC822 /* ARTClientOptions.h in Headers */, 96BF615E1A35C1C8004CF2B3 /* ARTTypes.h in Headers */, - D746AE2F1BBBE7D7003ECEF8 /* ARTPaginatedResult+Private.h in Headers */, + 1C1EC3FA1AE26A8B00AAADD7 /* ARTStatus.h in Headers */, + 96BF61581A35B52C004CF2B3 /* ARTHttp.h in Headers */, + 96BF61641A35CDE1004CF2B3 /* ARTBaseMessage.h in Headers */, D746AE221BBB60EE003ECEF8 /* ARTChannel.h in Headers */, + 96A507B51A37881C0077CDF8 /* ARTNSDate+ARTUtil.h in Headers */, + 967A43211A39AEAF00E4CE23 /* ARTNSArray+ARTFunctional.h in Headers */, + 96A507A51A377DE90077CDF8 /* ARTNSDictionary+ARTDictionaryUtil.h in Headers */, D746AE1D1BBB5207003ECEF8 /* ARTDataQuery.h in Headers */, - 1C1EC3FA1AE26A8B00AAADD7 /* ARTStatus.h in Headers */, + D746AE4B1BBD70F0003ECEF8 /* ARTRealtimeChannelSubscription.h in Headers */, + D746AE281BBB61C9003ECEF8 /* ARTPresence.h in Headers */, 96E408431A38939E00087F77 /* ARTProtocolMessage.h in Headers */, D746AE381BBC3201003ECEF8 /* ARTMessage.h in Headers */, D746AE471BBD6FE9003ECEF8 /* ARTQueuedMessage.h in Headers */, - 96BF61581A35B52C004CF2B3 /* ARTHttp.h in Headers */, - 96BF61641A35CDE1004CF2B3 /* ARTBaseMessage.h in Headers */, - 96BF61701A35FB7C004CF2B3 /* ARTAuth.h in Headers */, - 96A507A11A377AA50077CDF8 /* ARTPresenceMessage.h in Headers */, 1C2B0FFD1B136A6D00E3633C /* ARTPresenceMap.h in Headers */, - 850BFB4C1B79323C009D0ADD /* ARTPaginatedResult.h in Headers */, 1CD8DC9F1B1C7315007EAF36 /* ARTDefault.h in Headers */, - 1C8065051AE7C8FA00D49357 /* ARTPayload+Private.h in Headers */, - 85F0A60B1B6D039F00EFF45A /* ably.h in Headers */, - 960D07971A46FFC300ED8C8C /* ARTRest+Private.h in Headers */, D746AE401BBC5B14003ECEF8 /* ARTEventEmitter.h in Headers */, D746AE531BBD85C5003ECEF8 /* ARTChannelCollection.h in Headers */, - 1C05CF201AC1D7EB00687AC9 /* ARTRealtime+Private.h in Headers */, - 85B2C2191B6FE8DE00EA5254 /* CompatibilityMacros.h in Headers */, - 961343E81A432E7C006DC822 /* ARTPayload.h in Headers */, - 967A43211A39AEAF00E4CE23 /* ARTNSArray+ARTFunctional.h in Headers */, 1C578E1F1B3435CA00EF46EC /* ARTFallback.h in Headers */, 96E408471A3895E800087F77 /* ARTWebSocketTransport.h in Headers */, 96E4083F1A3892C700087F77 /* ARTRealtimeTransport.h in Headers */, - 96A507B51A37881C0077CDF8 /* ARTNSDate+ARTUtil.h in Headers */, D746AE4F1BBD84E7003ECEF8 /* ARTChannelOptions.h in Headers */, - 96A507A91A37806A0077CDF8 /* ARTEncoder.h in Headers */, - D746AE1E1BBB5207003ECEF8 /* ARTDataQuery+Private.h in Headers */, - 1C6C18A31ADFDAB100AB79E4 /* ARTLog.h in Headers */, - 96A507A51A377DE90077CDF8 /* ARTNSDictionary+ARTDictionaryUtil.h in Headers */, D746AE3C1BBC5AE1003ECEF8 /* ARTRealtimeChannel.h in Headers */, + 96BF61701A35FB7C004CF2B3 /* ARTAuth.h in Headers */, + 96A507A11A377AA50077CDF8 /* ARTPresenceMessage.h in Headers */, + 850BFB4C1B79323C009D0ADD /* ARTPaginatedResult.h in Headers */, + D746AE1E1BBB5207003ECEF8 /* ARTDataQuery+Private.h in Headers */, + D746AE2F1BBBE7D7003ECEF8 /* ARTPaginatedResult+Private.h in Headers */, D746AE431BBC5CD0003ECEF8 /* ARTRealtimeChannel+Private.h in Headers */, D746AE251BBB611C003ECEF8 /* ARTChannel+Private.h in Headers */, + 1C8065051AE7C8FA00D49357 /* ARTPayload+Private.h in Headers */, + 960D07971A46FFC300ED8C8C /* ARTRest+Private.h in Headers */, + 1C05CF201AC1D7EB00687AC9 /* ARTRealtime+Private.h in Headers */, + 85B2C2191B6FE8DE00EA5254 /* CompatibilityMacros.h in Headers */, + 961343E81A432E7C006DC822 /* ARTPayload.h in Headers */, + 96A507A91A37806A0077CDF8 /* ARTEncoder.h in Headers */, + 1C6C18A31ADFDAB100AB79E4 /* ARTLog.h in Headers */, 96A507AD1A3780F60077CDF8 /* ARTJsonEncoder.h in Headers */, 96A507951A370F860077CDF8 /* ARTStats.h in Headers */, 960D07931A45F1D800ED8C8C /* ARTCrypto.h in Headers */, diff --git a/ablySpec/RealtimeClient.swift b/ablySpec/RealtimeClient.swift index bdbb9f844..37b8812be 100644 --- a/ablySpec/RealtimeClient.swift +++ b/ablySpec/RealtimeClient.swift @@ -22,12 +22,15 @@ class RealtimeClient: QuickSpec { let expectation = self.expectationWithDescription("async") + // FIXME: ?! + /* client.eventEmitter.on { state in if state != .Connecting { expect(state).to(equal(ARTRealtimeConnectionState.Connected)) expectation.fulfill() } } + */ self.waitForExpectationsWithTimeout(10.0, handler: nil) } diff --git a/ablySpec/RestChannel.swift b/ablySpec/RestChannel.swift index d28fca0f3..06fa805cf 100644 --- a/ablySpec/RestChannel.swift +++ b/ablySpec/RestChannel.swift @@ -40,7 +40,7 @@ extension ARTPayload { class RestChannel: QuickSpec { override func spec() { var client: ARTRest! - var channel: ARTRestChannel! + var channel: ARTChannel! //ARTRestChannel beforeEach { client = ARTRest(options: AblyTests.setupOptions(AblyTests.jsonRestOptions)) diff --git a/ablySpec/RestClient.stats.swift b/ablySpec/RestClient.stats.swift index d7c136d12..dd6a6f000 100644 --- a/ablySpec/RestClient.stats.swift +++ b/ablySpec/RestClient.stats.swift @@ -14,11 +14,13 @@ import Foundation private func postTestStats(stats: JSON) -> ARTClientOptions { let options = AblyTests.setupOptions(AblyTests.jsonRestOptions); - let key = ("\(options.authOptions.keyName):\(options.authOptions.keySecret)" as NSString) + + let key = ("\(options.authOptions.key)" as NSString) .dataUsingEncoding(NSUTF8StringEncoding)! .base64EncodedStringWithOptions(NSDataBase64EncodingOptions(0)) - let request = NSMutableURLRequest(URL: NSURL(string: "https://\(restHost)/stats")!) + let request = NSMutableURLRequest(URL: NSURL(string: "https://rest.ably.io/stats")!) + request.HTTPMethod = "POST" request.HTTPBody = stats.rawData() request.setValue("application/json", forHTTPHeaderField: "Content-Type") diff --git a/ablySpec/RestClient.swift b/ablySpec/RestClient.swift index 2f03e1ee5..e54080fa6 100644 --- a/ablySpec/RestClient.swift +++ b/ablySpec/RestClient.swift @@ -33,13 +33,12 @@ func publishTestMessage(client: ARTRest, failOnError: Bool = true) -> PublishTes func getTestToken() -> String { let options = AblyTests.commonAppSetup() options.authOptions.useTokenAuth = true - options.authOptions.clientId = "testToken" let client = ARTRest(options: options) var token: String? - client.auth.requestToken() { tokenDetails in - token = tokenDetails.token - return nil + client.auth.requestToken(nil, withOptions: nil) { tokenDetails, error in + token = tokenDetails?.token + return } while token == nil { @@ -56,8 +55,9 @@ class RestClient: QuickSpec { context("initializer") { it("should accept an API key") { let options = AblyTests.commonAppSetup() - let client = ARTRest(key: "\(options.authOptions.keyName):\(options.authOptions.keySecret)") - client.baseUrl = options.restUrl + + let client = ARTRest(key: options.authOptions.key!) + client.baseUrl = options.restUrl() let publishTask = publishTestMessage(client) @@ -70,7 +70,7 @@ class RestClient: QuickSpec { it("should result in error status when provided a bad key") { let options = AblyTests.commonAppSetup() - let client = ARTRest(key: "badName:\(options.authOptions.keySecret)") + let client = ARTRest(key: "bad") let publishTask = publishTestMessage(client, failOnError: false) @@ -94,7 +94,6 @@ class RestClient: QuickSpec { let publishTask = publishTestMessage(client) - expect(client.auth.getAuthMethod()).to(equal(ARTAuthMethod.Token)) expect(publishTask.error).toEventually(beNil(), timeout: testTimeout) } @@ -106,7 +105,6 @@ class RestClient: QuickSpec { let publishTask = publishTestMessage(client, failOnError: false) - expect(client.auth.getAuthMethod()).to(equal(ARTAuthMethod.Token)) expect(publishTask.error?.domain).toEventually(equal(ARTAblyErrorDomain), timeout: testTimeout) expect(publishTask.error?.code).toEventually(equal(40005), timeout: testTimeout) } @@ -171,7 +169,7 @@ class RestClient: QuickSpec { it("should accept an options object with a host set") { let options = ARTClientOptions(key: "fake:key") - options.restHost = "fake.ably.host" + options.environment = "fake" let client = ARTRest(options: options) client.httpExecutor = mockExecutor @@ -218,7 +216,7 @@ class RestClient: QuickSpec { let options = AblyTests.setupOptions(AblyTests.jsonRestOptions) let client = ARTRest(options: options) - let authOptions = client.auth.getAuthOptions() + let authOptions = client.auth.options expect(authOptions).to(beIdenticalTo(options.authOptions)) } @@ -230,7 +228,8 @@ class RestClient: QuickSpec { let client = ARTRest(options: options) var time: NSDate? - client.time({ (status, date) in + + client.time({ date, error in time = date }) diff --git a/ablySpec/TestUtilities.swift b/ablySpec/TestUtilities.swift index cdb9885ec..4d12d9e82 100644 --- a/ablySpec/TestUtilities.swift +++ b/ablySpec/TestUtilities.swift @@ -11,6 +11,7 @@ import Foundation import XCTest import Quick import SwiftyJSON + import ably import ably.Private @@ -76,9 +77,11 @@ class AblyTests { let appId = response["appId"] let id = key["id"] - options.authOptions.keyName = "\(appId).\(id)" - options.authOptions.keySecret = key["value"].stringValue - options.authOptions.capability = key["capability"].stringValue + // FIXME: + //options.authOptions.keyName = "\(appId).\(id)" + //options.authOptions.keySecret = key["value"].stringValue + //options.authOptions.capability = key["capability"].stringValue + return options } From d878951caaffd9cb7cdb9b80501cf9f5b7c70871 Mon Sep 17 00:00:00 2001 From: Ricardo Pereira Date: Fri, 2 Oct 2015 11:18:10 +0100 Subject: [PATCH 14/22] Fixed eventEmitter from ARTRealtime --- ably-ios/ARTEventEmitter.h | 3 +++ ably-ios/ARTEventEmitter.m | 9 +++++++++ ably-ios/ARTSubscription.h | 18 ------------------ ably-ios/ARTTypes.h | 4 +++- ably.xcodeproj/project.pbxproj | 2 -- ablySpec/RealtimeClient.swift | 5 +---- 6 files changed, 16 insertions(+), 25 deletions(-) delete mode 100644 ably-ios/ARTSubscription.h diff --git a/ably-ios/ARTEventEmitter.h b/ably-ios/ARTEventEmitter.h index 9f4c2e7ef..5a84e398d 100644 --- a/ably-ios/ARTEventEmitter.h +++ b/ably-ios/ARTEventEmitter.h @@ -18,4 +18,7 @@ - (instancetype)initWithRealtime:(ARTRealtime *)realtime; - (id)on:(ARTRealtimeConnectionStateCb)cb; +- (id)on; +- (void)test; + @end diff --git a/ably-ios/ARTEventEmitter.m b/ably-ios/ARTEventEmitter.m index 1c7a70af6..324848714 100644 --- a/ably-ios/ARTEventEmitter.m +++ b/ably-ios/ARTEventEmitter.m @@ -8,6 +8,7 @@ #import "ARTEventEmitter.h" +#import "ARTSubscription.h" #import "ARTRealtimeChannelSubscription.h" @interface ARTEventEmitter () @@ -34,4 +35,12 @@ - (instancetype)initWithRealtime:(ARTRealtime *)realtime { return subscription; } +- (id)on { + return nil; +} + +- (void)test { + +} + @end diff --git a/ably-ios/ARTSubscription.h b/ably-ios/ARTSubscription.h deleted file mode 100644 index f77e2fe56..000000000 --- a/ably-ios/ARTSubscription.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// ARTSubscription.h -// ably -// -// Created by Ricardo Pereira on 30/09/2015. -// Copyright (c) 2015 Ably. All rights reserved. -// - -#ifndef ably_ARTSubscription_h -#define ably_ARTSubscription_h - -@protocol ARTSubscription - -- (void)unsubscribe; - -@end - -#endif diff --git a/ably-ios/ARTTypes.h b/ably-ios/ARTTypes.h index b2ba05162..f5ff6d4df 100644 --- a/ably-ios/ARTTypes.h +++ b/ably-ios/ARTTypes.h @@ -50,9 +50,11 @@ typedef void (^ARTRealtimeChannelPresenceCb)(ARTPresenceMessage *); // FIXME: @protocol ARTCancellable - - (void)cancel; +@end +@protocol ARTSubscription +- (void)unsubscribe; @end @interface ARTIndirectCancellable : NSObject diff --git a/ably.xcodeproj/project.pbxproj b/ably.xcodeproj/project.pbxproj index dcb71e2b0..0f00c0e6c 100644 --- a/ably.xcodeproj/project.pbxproj +++ b/ably.xcodeproj/project.pbxproj @@ -289,7 +289,6 @@ D746AE3E1BBC5B14003ECEF8 /* ARTEventEmitter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTEventEmitter.h; sourceTree = ""; }; D746AE3F1BBC5B14003ECEF8 /* ARTEventEmitter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTEventEmitter.m; sourceTree = ""; }; D746AE421BBC5CD0003ECEF8 /* ARTRealtimeChannel+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ARTRealtimeChannel+Private.h"; sourceTree = ""; }; - D746AE441BBC5CF9003ECEF8 /* ARTSubscription.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ARTSubscription.h; sourceTree = ""; }; D746AE451BBD6FE9003ECEF8 /* ARTQueuedMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTQueuedMessage.h; sourceTree = ""; }; D746AE461BBD6FE9003ECEF8 /* ARTQueuedMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTQueuedMessage.m; sourceTree = ""; }; D746AE491BBD70F0003ECEF8 /* ARTRealtimeChannelSubscription.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTRealtimeChannelSubscription.h; sourceTree = ""; }; @@ -496,7 +495,6 @@ D746AE3B1BBC5AE1003ECEF8 /* ARTRealtimeChannel.m */, D746AE491BBD70F0003ECEF8 /* ARTRealtimeChannelSubscription.h */, D746AE4A1BBD70F0003ECEF8 /* ARTRealtimeChannelSubscription.m */, - D746AE441BBC5CF9003ECEF8 /* ARTSubscription.h */, D746AE261BBB61C9003ECEF8 /* ARTPresence.h */, D746AE271BBB61C9003ECEF8 /* ARTPresence.m */, 1C2B0FFB1B136A6D00E3633C /* ARTPresenceMap.h */, diff --git a/ablySpec/RealtimeClient.swift b/ablySpec/RealtimeClient.swift index 37b8812be..93f418883 100644 --- a/ablySpec/RealtimeClient.swift +++ b/ablySpec/RealtimeClient.swift @@ -22,15 +22,12 @@ class RealtimeClient: QuickSpec { let expectation = self.expectationWithDescription("async") - // FIXME: ?! - /* client.eventEmitter.on { state in if state != .Connecting { expect(state).to(equal(ARTRealtimeConnectionState.Connected)) expectation.fulfill() } } - */ self.waitForExpectationsWithTimeout(10.0, handler: nil) } @@ -85,4 +82,4 @@ class RealtimeClient: QuickSpec { } } } -} \ No newline at end of file +} From da03161cf13351ebdad922387fc008b4556ef0ba Mon Sep 17 00:00:00 2001 From: Ricardo Pereira Date: Fri, 2 Oct 2015 19:06:38 +0100 Subject: [PATCH 15/22] Implemented spec RSA8c1, RSA8c2, RSA8c3. Testing... --- ably-ios/ARTAuth.h | 82 ++++++++++- ably-ios/ARTAuth.m | 165 +++++++++++++--------- ably-ios/ARTClientOptions.h | 10 +- ably-ios/ARTEventEmitter.m | 1 - ably-ios/ARTRealtime.m | 14 +- ably-ios/ARTRealtimeChannelSubscription.h | 1 - ably-ios/ARTRest.m | 15 +- ably.xcodeproj/project.pbxproj | 4 + ablySpec/Auth.swift | 50 +++++-- 9 files changed, 240 insertions(+), 102 deletions(-) diff --git a/ably-ios/ARTAuth.h b/ably-ios/ARTAuth.h index 7fc6954c7..fc3373166 100644 --- a/ably-ios/ARTAuth.h +++ b/ably-ios/ARTAuth.h @@ -15,6 +15,8 @@ NS_ASSUME_NONNULL_BEGIN +#pragma mark - ARTAuthTokenParams + @interface ARTAuthTokenDetails : NSObject @property (nonatomic, readonly, copy) NSString *token; @@ -31,6 +33,9 @@ NS_ASSUME_NONNULL_BEGIN @end + +#pragma mark - ARTAuthTokenParams + @interface ARTAuthTokenParams : NSObject @property (nonatomic, assign) NSTimeInterval ttl; @@ -40,10 +45,14 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)init; -- (NSArray *)toArray; +- (NSMutableArray *)toArray; //X7: NSArray +- (NSArray *)toArrayWithUnion:(NSArray *)items; //X7: NSArray @end + +#pragma mark - ARTAuthTokenRequest + @interface ARTAuthTokenRequest : ARTAuthTokenParams @property (nonatomic, readonly, copy) NSString *keyName; @@ -56,12 +65,18 @@ NS_ASSUME_NONNULL_BEGIN @end + +#pragma mark - ARTAuthTokenParams + @interface ARTAuthTokenParams(SignedRequest) - (ARTAuthTokenRequest *)sign:(NSString *)key; @end + +#pragma mark - ARTAuthOptions + typedef void (^ARTAuthCallback)(ARTAuthTokenParams *tokenParams, void(^callback)(ARTAuthTokenRequest *__nullable tokenRequest, NSError *__nullable error)); typedef NS_ENUM(NSUInteger, ARTAuthMethod) { @@ -71,25 +86,72 @@ typedef NS_ENUM(NSUInteger, ARTAuthMethod) { @interface ARTAuthOptions : NSObject +/** + Full Ably key string as obtained from dashboard. + */ @property (nonatomic, copy, nullable) NSString *key; +/** + An authentication token issued for this application against a specific key and `TokenParams`. + */ @property (nonatomic, copy, nullable) NSString *token; + +/** + An authentication token issued for this application against a specific key and `TokenParams`. + */ @property (nonatomic, strong, nullable) ARTAuthTokenDetails *tokenDetails; -@property (nonatomic, assign) BOOL useTokenAuth; +/** + A callback to call to obtain a signed token request. + + This enables a client to obtain token requests from another entity, so tokens can be renewed without the client requiring access to keys. + */ @property (nonatomic, copy, nullable) ARTAuthCallback authCallback; +/** + A URL to queryto obtain a signed token request. + + This enables a client to obtain token requests from another entity, so tokens can be renewed without the client requiring access to keys. + */ @property (nonatomic, strong, nullable) NSURL *authUrl; + +/** + The HTTP verb to be used when a request is made by the library to the authUrl. Defaults to GET, supports GET and POST. + */ @property (nonatomic, copy, null_resettable) NSString *authMethod; + +/** + Headers to be included in any request made by the library to the authURL. + */ @property (nonatomic, copy, nullable) NSDictionary *authHeaders; //X7: NSDictionary *authHeaders; + +/** + Additional params to be included in any request made by the library to the authUrl, either as query params in the case of GET or in the body in the case of POST. + */ @property (nonatomic, copy, nullable) NSArray *authParams; //X7: NSArray *authParams; +/** + This may be set in instances that the library is to sign token requests based on a given key. + If true, the library will query the Ably system for the current time instead of relying on a locally-available time of day. + */ @property (nonatomic, assign, nonatomic) BOOL queryTime; +@property (nonatomic, assign) BOOL useTokenAuth; + - (instancetype)initWithKey:(NSString *)key; +- (NSString *)description; + +- (ARTAuthOptions *)mergeWith:(ARTAuthOptions *)precedenceOptions; + +- (BOOL)isMethodGET; +- (BOOL)isMethodPOST; + @end + +#pragma mark - ARTAuth + @interface ARTAuth : NSObject @property (nonatomic, weak) ARTLog *logger; @@ -97,8 +159,20 @@ typedef NS_ENUM(NSUInteger, ARTAuthMethod) { @property (nonatomic, readonly, assign) ARTAuthMethod authMethod; @property (nonatomic, readonly, strong) ARTAuthTokenDetails *currentToken; -- (instancetype)initWithRest:(ARTRest *)rest options:(ARTAuthOptions *)options; - +- (instancetype)init:(ARTRest *)rest withOptions:(ARTAuthOptions *)options; + +/** + # (RSA8) Auth#requestToken + + Implicitly creates a `TokenRequest` if required, and requests a token from Ably if required. + + `TokenParams` and `AuthOptions` are optional. + When provided, the values supersede matching client library configured params and options. + + - Parameter tokenParams: Token params (optional). + - Parameter authOptions: Authentication options (optional). + - Parameter callback: Completion callback (ARTAuthTokenDetails, NSError). + */ - (void)requestToken:(nullable ARTAuthTokenParams *)tokenParams withOptions:(nullable ARTAuthOptions *)authOptions callback:(void (^)(ARTAuthTokenDetails *__nullable tokenDetails, NSError *__nullable error))callback; diff --git a/ably-ios/ARTAuth.m b/ably-ios/ARTAuth.m index 3b4abe781..cacfb0bf1 100644 --- a/ably-ios/ARTAuth.m +++ b/ably-ios/ARTAuth.m @@ -69,21 +69,42 @@ - (void)setTimestamp:(NSDate *)timestamp { _timestamp = timestamp; } -- (NSArray *)toArray { +- (NSMutableArray *)toArray { NSMutableArray *params = [[NSMutableArray alloc] init]; - + if (self.clientId) - [params addObject:[NSString stringWithFormat:@"clientId=%@", self.clientId]]; + [params addObject:[NSURLQueryItem queryItemWithName:@"clientId" value:self.clientId]]; if (self.ttl > 0) - [params addObject:[NSString stringWithFormat:@"ttl=%f", self.ttl]]; + [params addObject:[NSURLQueryItem queryItemWithName:@"ttl" value:[NSString stringWithFormat:@"%f", self.ttl]]]; if (self.capability) - [params addObject:[NSString stringWithFormat:@"capability=%@", self.capability]]; + [params addObject:[NSURLQueryItem queryItemWithName:@"capability" value:self.capability]]; if (self.timestamp > 0) - [params addObject:[NSString stringWithFormat:@"timestamp=%f", [self.timestamp timeIntervalSince1970]]]; + [params addObject:[NSURLQueryItem queryItemWithName:@"timestamp" value:[NSString stringWithFormat:@"%f", self.timestamp.timeIntervalSince1970]]]; return params; } +- (NSArray *)toArrayWithUnion:(NSArray *)items { + NSMutableArray *tokenParams = [self toArray]; + BOOL add = YES; + + for (NSURLQueryItem *item in items) { + for (NSURLQueryItem *param in tokenParams) { + // Check if exist + if ([param.name isEqualToString:item.name]) { + add = NO; + break; + } + } + if (add) { + [tokenParams addObject:item]; + } + add = YES; + } + + return tokenParams; +} + static NSString *generateNonce() { // Generate two random numbers up to 8 digits long and concatenate them to produce a 16 digit random number NSUInteger r1 = arc4random_uniform(100000000); @@ -152,6 +173,8 @@ - (NSDictionary *)asDictionary { @implementation ARTAuthOptions +NSString *const ARTAuthOptionsMethodDefault = @"GET"; + - (instancetype)initWithKey:(NSString *)key { self = [self init]; if (self) { @@ -159,12 +182,14 @@ - (instancetype)initWithKey:(NSString *)key { [NSException raise:@"Invalid key" format:@"%@ should be of the form :", key]; } _key = [key copy]; + _authMethod = ARTAuthOptionsMethodDefault; } return self; } - (id)copyWithZone:(NSZone *)zone { ARTAuthOptions *options = [[ARTAuthOptions allocWithZone:zone] init]; + options.key = self.key; options.token = self.token; options.useTokenAuth = self.useTokenAuth; @@ -178,6 +203,11 @@ - (id)copyWithZone:(NSZone *)zone { return options; } +- (NSString *)description { + return [NSString stringWithFormat: @"ARTAuthOptions: key=%@ token=%@ authUrl=%@ authMethod=%@ hasAuthCallback=%d", + self.key, self.token, self.authUrl, self.authMethod, self.authCallback != nil]; +} + - (NSString *)token { return self.tokenDetails.token; } @@ -187,13 +217,42 @@ - (void)setToken:(NSString *)token { } - (void)setAuthMethod:(NSString *)authMethod { - if (authMethod == nil) { - authMethod = @"GET"; + if (authMethod == nil || authMethod.length == 0) { + authMethod = ARTAuthOptionsMethodDefault; } _authMethod = [authMethod copy]; } +- (ARTAuthOptions *)mergeWith:(ARTAuthOptions *)precedenceOptions { + ARTAuthOptions *merged = [self copy]; + + if (precedenceOptions.key) + merged.key = precedenceOptions.key; + if (precedenceOptions.authCallback) + merged.authCallback = precedenceOptions.authCallback; + if (precedenceOptions.authUrl) + merged.authUrl = precedenceOptions.authUrl; + if (precedenceOptions.authMethod) + merged.authMethod = precedenceOptions.authMethod; + if (precedenceOptions.authHeaders) + merged.authHeaders = precedenceOptions.authHeaders; + if (precedenceOptions.authParams) + merged.authParams = precedenceOptions.authParams; + if (precedenceOptions.queryTime) + merged.queryTime = precedenceOptions.queryTime; + + return merged; +} + +- (BOOL)isMethodPOST { + return [_authMethod isEqualToString:@"POST"]; +} + +- (BOOL)isMethodGET { + return [_authMethod isEqualToString:@"GET"]; +} + @end @@ -203,18 +262,26 @@ @implementation ARTAuth { __weak ARTRest *_rest; } -- (instancetype)initWithRest:(ARTRest *)rest options:(ARTAuthOptions *)options { +- (instancetype)init:(ARTRest *)rest withOptions:(ARTAuthOptions *)options { if (self = [super init]) { _rest = rest; _currentToken = options.tokenDetails; _options = options; _logger = rest.logger; + // REV: options.useTokenAuth + if (options.key != nil && !options.useTokenAuth) { [self.logger debug:@"ARTAuth: setting up auth method Basic"]; _authMethod = ARTAuthMethodBasic; - } else if ([self shouldUseTokenAuth]) { - [self.logger debug:@"ARTAuth: setting up auth method Token"]; + } else if (options.tokenDetails) { + [self.logger debug:@"ARTAuth: setting up auth method Token with supplied token only"]; + _authMethod = ARTAuthMethodToken; + } else if (options.authUrl) { + [self.logger debug:@"ARTAuth: setting up auth method Token with authUrl"]; + _authMethod = ARTAuthMethodToken; + } else if (options.authCallback) { + [self.logger debug:@"ARTAuth: setting up auth method Token with authCallback"]; _authMethod = ARTAuthMethodToken; } else { [NSException raise:@"ARTAuthException" format:@"Could not setup authentication method with given options."]; @@ -224,79 +291,47 @@ - (instancetype)initWithRest:(ARTRest *)rest options:(ARTAuthOptions *)options { return self; } -- (BOOL)shouldUseTokenAuth { - return NO; -} - -/** - # (RSA8) Auth#requestToken - - Implicitly creates a `TokenRequest` if required, and requests a token from Ably if required. - - `TokenParams` and `AuthOptions` are optional. - When provided, the values supersede matching client library configured params and options. - - - Parameter tokenParams: Token params (optional). - - Parameter authOptions: Authentication options (optional). - - Parameter callback: Completion callback (ARTAuthTokenDetails, NSError). - */ - (void)requestToken:(ARTAuthTokenParams *)tokenParams withOptions:(ARTAuthOptions *)authOptions callback:(void (^)(ARTAuthTokenDetails *, NSError *))callback { - - ARTAuthOptions *mergedOptions = authOptions; - ARTAuthTokenParams *currentTokenParams = tokenParams; - if (!mergedOptions) { - // TODO: Merge - mergedOptions = self.options; - } + // The values supersede matching client library configured params and options. + ARTAuthOptions *mergedOptions = authOptions ? [self.options mergeWith:authOptions] : self.options; + ARTAuthTokenParams *currentTokenParams = tokenParams; if (!currentTokenParams) { currentTokenParams = [[ARTAuthTokenParams alloc] init]; - // TODO: Client library configured params - NSAssert(false, @"Client library configured params not implemented"); - - /* - currentTokenParams.clientId - currentTokenParams.capability - currentTokenParams.ttl - currentTokenParams.timestamp - */ } - + if (mergedOptions.authUrl) { NSURLComponents *urlComponents = [NSURLComponents componentsWithURL:mergedOptions.authUrl resolvingAgainstBaseURL:YES]; - if (!mergedOptions.authMethod || !mergedOptions.authMethod.length) { - mergedOptions.authMethod = @"GET"; //Default - } + // TokenParams take precedence over any configured authParams when a name conflict occurs + NSArray *params = [currentTokenParams toArrayWithUnion:mergedOptions.authParams]; + NSData *bodyData = nil; - if ([mergedOptions.authMethod isEqualToString:@"POST"]) { + if ([mergedOptions isMethodPOST]) { // When POST, use body of the POST request - - // TODO - - //[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; - - //NSData.length - //[request setValue:@"" forHTTPHeaderField:@"Content-Length"]; + bodyData = [NSJSONSerialization dataWithJSONObject:params options:0 error:nil]; } else { // When GET, use query string params - if (mergedOptions.authParams) { - urlComponents.queryItems = [urlComponents.queryItems arrayByAddingObjectsFromArray:mergedOptions.authParams]; - } - urlComponents.queryItems = [urlComponents.queryItems arrayByAddingObjectsFromArray:[currentTokenParams toArray]]; - - //(RSA8c2) TokenParams take precedence over any configured authParams when a name conflict occurs - // TODO - - //[request setValue:@"application/json" forHTTPHeaderField:@"Accept"]; + urlComponents.queryItems = @[]; + urlComponents.queryItems = [urlComponents.queryItems arrayByAddingObjectsFromArray:params]; } NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:urlComponents.URL]; request.HTTPMethod = mergedOptions.authMethod; + // HTTP Header Fields + if ([mergedOptions isMethodPOST] && bodyData) { + request.HTTPBody = bodyData; + [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; + [request setValue:[NSString stringWithFormat:@"%d", (unsigned int)bodyData.length] forHTTPHeaderField:@"Content-Length"]; + } + else { + [request setValue:@"application/json" forHTTPHeaderField:@"Accept"]; + } + for (NSString *key in mergedOptions.authHeaders) { [request setValue:mergedOptions.authHeaders[key] forHTTPHeaderField:key]; } @@ -328,9 +363,11 @@ - (void)requestToken:(ARTAuthTokenParams *)tokenParams withOptions:(ARTAuthOptio } } +// FIXME: need revision - (void)requestToken:(ARTAuthTokenRequest *)tokenRequest callback:(void (^)(ARTAuthTokenDetails *, NSError *))callback { NSURL *requestUrl = [NSURL URLWithString:[NSString stringWithFormat:@"/keys/%@/requestToken", tokenRequest.keyName] relativeToURL:_rest.baseUrl]; + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:requestUrl]; request.HTTPMethod = @"POST"; diff --git a/ably-ios/ARTClientOptions.h b/ably-ios/ARTClientOptions.h index 741d7cf39..8c92042bb 100644 --- a/ably-ios/ARTClientOptions.h +++ b/ably-ios/ARTClientOptions.h @@ -10,9 +10,11 @@ @class ARTAuthOptions; +NS_ASSUME_NONNULL_BEGIN + @interface ARTClientOptions : NSObject -@property (readwrite, strong, nonatomic) ARTAuthOptions *authOptions; +@property (nonnull, strong, nonatomic) ARTAuthOptions *authOptions; @property (readwrite, strong, nonatomic) NSString *clientId; @property (readonly, getter=getRestHost) NSString *restHost; @@ -28,8 +30,8 @@ @property (readwrite, assign, nonatomic) BOOL binary; @property (readwrite, assign, nonatomic) BOOL autoConnect; @property (readwrite, assign, nonatomic) int64_t connectionSerial; -@property (readwrite, copy, nonatomic) NSString *resumeKey; -@property (readwrite, copy, nonatomic) NSString *recover; +@property (nullable, readwrite, copy, nonatomic) NSString *resumeKey; +@property (nullable, readwrite, copy, nonatomic) NSString *recover; - (instancetype)init; - (instancetype)initWithKey:(NSString *)key; @@ -40,3 +42,5 @@ - (NSURL *)restUrl; @end + +NS_ASSUME_NONNULL_END diff --git a/ably-ios/ARTEventEmitter.m b/ably-ios/ARTEventEmitter.m index 324848714..c5badecf0 100644 --- a/ably-ios/ARTEventEmitter.m +++ b/ably-ios/ARTEventEmitter.m @@ -8,7 +8,6 @@ #import "ARTEventEmitter.h" -#import "ARTSubscription.h" #import "ARTRealtimeChannelSubscription.h" @interface ARTEventEmitter () diff --git a/ably-ios/ARTRealtime.m b/ably-ios/ARTRealtime.m index 936cbbfa0..46c7721c4 100644 --- a/ably-ios/ARTRealtime.m +++ b/ably-ios/ARTRealtime.m @@ -304,13 +304,13 @@ - (void)transition:(ARTRealtimeConnectionState)state { } } self.options.resumeKey = nil; - for (NSString *channelName in self.allChannels) { - ARTRealtimeChannel *channel = [self.allChannels objectForKey:channelName]; - if([channel.presenceMap stillSyncing]) { - // FIXME: - //[channel requestContinueSync]; - } - } + for (NSString *channelName in self.allChannels) { + ARTRealtimeChannel *channel = [self.allChannels objectForKey:channelName]; + if([channel.presenceMap stillSyncing]) { + // FIXME: + //[channel requestContinueSync]; + } + } } self.msgSerial = 0; [self cancelSuspendTimer]; diff --git a/ably-ios/ARTRealtimeChannelSubscription.h b/ably-ios/ARTRealtimeChannelSubscription.h index 6daa218d9..4e68564c0 100644 --- a/ably-ios/ARTRealtimeChannelSubscription.h +++ b/ably-ios/ARTRealtimeChannelSubscription.h @@ -9,7 +9,6 @@ #import #import "ably.h" -#import "ARTSubscription.h" #import "ARTPresenceMessage.h" @class ARTRealtime; diff --git a/ably-ios/ARTRest.m b/ably-ios/ARTRest.m index bd5e5af3f..3efe69fd4 100644 --- a/ably-ios/ARTRest.m +++ b/ably-ios/ARTRest.m @@ -56,21 +56,20 @@ - (instancetype)initWithLogger:(ARTLog *)logger andOptions:(ARTClientOptions *)o // FIXME: _http = [[ARTHttp alloc] init]; - // Private - self.httpExecutor = _http; - self.httpExecutor.logger = _logger; + // Private ?! + //self.httpExecutor = _http; + //self.httpExecutor.logger = _logger; // FIXME: //_channels = [[ARTChannelCollection alloc] initWithRest:self]; id defaultEncoder = [[ARTJsonEncoder alloc] init]; - _encoders = @{ - [defaultEncoder mimeType]: defaultEncoder, - }; - + _encoders = @{ [defaultEncoder mimeType]: defaultEncoder }; _defaultEncoding = [defaultEncoder mimeType]; + _fallbackCount = 0; - _auth = [[ARTAuth alloc] initWithRest:self options:options.authOptions]; + + _auth = [[ARTAuth alloc] init:self withOptions:options.authOptions]; } return self; } diff --git a/ably.xcodeproj/project.pbxproj b/ably.xcodeproj/project.pbxproj index 0f00c0e6c..87f9434c9 100644 --- a/ably.xcodeproj/project.pbxproj +++ b/ably.xcodeproj/project.pbxproj @@ -125,6 +125,7 @@ D746AE501BBD84E7003ECEF8 /* ARTChannelOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = D746AE4E1BBD84E7003ECEF8 /* ARTChannelOptions.m */; }; D746AE531BBD85C5003ECEF8 /* ARTChannelCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE511BBD85C5003ECEF8 /* ARTChannelCollection.h */; settings = {ATTRIBUTES = (Public, ); }; }; D746AE541BBD85C5003ECEF8 /* ARTChannelCollection.m in Sources */ = {isa = PBXBuildFile; fileRef = D746AE521BBD85C5003ECEF8 /* ARTChannelCollection.m */; }; + D7C1B8771BBEA81A0087B55F /* Auth.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C1B8761BBEA81A0087B55F /* Auth.swift */; }; FC78549C1BCD5688539CCBE4 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FDA6E099E4BC04FC80F845C0 /* libPods.a */; }; /* End PBXBuildFile section */ @@ -298,6 +299,7 @@ D746AE511BBD85C5003ECEF8 /* ARTChannelCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTChannelCollection.h; sourceTree = ""; }; D746AE521BBD85C5003ECEF8 /* ARTChannelCollection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTChannelCollection.m; sourceTree = ""; }; D746AE551BBD8622003ECEF8 /* ARTChannelCollection+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ARTChannelCollection+Private.h"; sourceTree = ""; }; + D7C1B8761BBEA81A0087B55F /* Auth.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Auth.swift; sourceTree = ""; }; DF8DF755839D59574B7B795F /* Pods-ablySpec.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ablySpec.release.xcconfig"; path = "Pods/Target Support Files/Pods-ablySpec/Pods-ablySpec.release.xcconfig"; sourceTree = ""; }; FDA6E099E4BC04FC80F845C0 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -374,6 +376,7 @@ 856AAC8E1B6E304B00B07119 /* Info.plist */, 856AAC961B6E30C800B07119 /* TestUtilities.swift */, 856AAC951B6E30C800B07119 /* ablySpec-Bridging-Header.h */, + D7C1B8761BBEA81A0087B55F /* Auth.swift */, 856AAC981B6E312F00B07119 /* RestClient.swift */, 853ED7C31B7A1A3C006F1C6F /* RestClient.stats.swift */, D746AE2A1BBB625E003ECEF8 /* RestChannel.swift */, @@ -870,6 +873,7 @@ 851674EF1B7BA5CD00D35169 /* Stats.swift in Sources */, D72304701BB72CED00F1ABDA /* RealtimeClient.swift in Sources */, D746AE2D1BBB625E003ECEF8 /* RestClient.channels.swift in Sources */, + D7C1B8771BBEA81A0087B55F /* Auth.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ablySpec/Auth.swift b/ablySpec/Auth.swift index dc44ae640..3d6cf9bea 100644 --- a/ablySpec/Auth.swift +++ b/ablySpec/Auth.swift @@ -34,45 +34,67 @@ class Auth : QuickSpec { publishTestMessage(client, failOnError: false) - let key64 = NSString(string: "\(client.options.authOptions.keyName):\(client.options.authOptions.keySecret)") + let key64 = NSString(string: "\(client.options.authOptions.key)") .dataUsingEncoding(NSUTF8StringEncoding)? .base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0)) let Authorization = "Basic \(key64!)" - expect(mockExecutor.requests.first?.allHTTPHeaderFields?["Authorization"]).to(equal(Authorization)) + + // X7 + //expect(mockExecutor.requests.first?.allHTTPHeaderFields?["Authorization"]).to(equal(Authorization)) } // RSA2 it("should be default when an API йеъ is set") { let client = ARTRest(options: ARTClientOptions(key: "fake:key")) - expect(client.auth.getAuthMethod()).to(equal(ARTAuthMethod.Basic)) + expect(client.auth.authMethod).to(equal(ARTAuthMethod.Basic)) } } describe("Token") { + + fit("implicitly creates a TokenRequest") { + // WIP + let options = ARTClientOptions(key: "6p6USg.CNwGdA:uwJU1qsSf_Qe9VDH") + // Test + options.authOptions.authUrl = NSURL(string: "http://auth.ably.io") + options.authOptions.authParams = [NSURLQueryItem(name: "ttl", value: "aaa")] + options.authOptions.authMethod = "POST" + + let rest = ARTRest(options: options) + + rest.auth.requestToken(nil, withOptions: nil, callback: { tokenDetails, error in + + }) + } - fit("should send the token in the Authorization header") { + it("should send the token in the Authorization header") { let options = ARTClientOptions() options.authOptions.token = getTestToken() let client = ARTRest(options: options) client.httpExecutor = mockExecutor publishTestMessage(client, failOnError: false) - - let token64 = NSString(string: client.options.authOptions.token) - .dataUsingEncoding(NSUTF8StringEncoding)? - .base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0)) - let Authorization = "Bearer \(token64!)" - expect(mockExecutor.requests.first?.allHTTPHeaderFields?["Authorization"]).to(equal(Authorization)) + + expect(client.options.authOptions.token).toNot(beNil()) + + if let currentToken = client.options.authOptions.token { + let token64 = NSString(string: currentToken) + .dataUsingEncoding(NSUTF8StringEncoding)? + .base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0)) + let Authorization = "Bearer \(token64!)" + + // X7 + //expect(mockExecutor.requests.first?.allHTTPHeaderFields?["Authorization"]).to(equal(Authorization)) + } } // RSA4 - context("auhentication method") { + context("authentication method") { let cases: [String: (ARTAuthOptions) -> ()] = [ "useTokenAuth": { $0.useTokenAuth = true; $0.key = "fake:key" }, - "clientId": { $0.clientId = "clientId" }, "authUrl": { $0.authUrl = NSURL(string: "http://test.com") }, - "authCallback": { $0.authCallback = { _ in return nil } }, + "authCallback": { $0.authCallback = { _, _ in return } }, "token": { $0.token = "" } ] @@ -83,7 +105,7 @@ class Auth : QuickSpec { let client = ARTRest(options: options) - expect(client.auth.getAuthMethod()).to(equal(ARTAuthMethod.Token)) + expect(client.auth.authMethod).to(equal(ARTAuthMethod.Token)) } } } From 5d440dd457ca984711431d3b2d130b9dd59a9445 Mon Sep 17 00:00:00 2001 From: Ricardo Pereira Date: Sat, 3 Oct 2015 03:29:27 +0100 Subject: [PATCH 16/22] Test spec RSA8e, RSA8c1a, RSA8c1b, RSA8c3 --- ably-ios/ARTAuth+Private.h | 27 +++++++ ably-ios/ARTAuth.h | 1 + ably-ios/ARTAuth.m | 136 +++++++++++++++++++++++---------- ably-ios/ably.modulemap | 1 + ably.xcodeproj/project.pbxproj | 4 + ablySpec/Auth.swift | 121 ++++++++++++++++++++++++----- 6 files changed, 233 insertions(+), 57 deletions(-) create mode 100644 ably-ios/ARTAuth+Private.h diff --git a/ably-ios/ARTAuth+Private.h b/ably-ios/ARTAuth+Private.h new file mode 100644 index 000000000..9ccf08176 --- /dev/null +++ b/ably-ios/ARTAuth+Private.h @@ -0,0 +1,27 @@ +// +// ARTAuth+Private.h +// ably +// +// Created by Ricardo Pereira on 03/10/2015. +// Copyright (c) 2015 Ably. All rights reserved. +// + +#import + +#import "ARTAuth.h" + +@class ARTAuthOptions; + +NS_ASSUME_NONNULL_BEGIN + +@interface ARTAuth (Private) + +- (ARTAuthOptions *)mergeOptions:(nonnull ARTAuthOptions *)customOptions; +- (ARTAuthTokenParams *)mergeParams:(nonnull ARTAuthTokenParams *)customParams; + +- (NSURL *)buildURL:(nonnull ARTAuthOptions *)options withParams:(nonnull ARTAuthTokenParams *)params; +- (NSMutableURLRequest *)buildRequest:(nonnull ARTAuthOptions *)options withParams:(nonnull ARTAuthTokenParams *)params; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ably-ios/ARTAuth.h b/ably-ios/ARTAuth.h index fc3373166..e584a18a3 100644 --- a/ably-ios/ARTAuth.h +++ b/ably-ios/ARTAuth.h @@ -47,6 +47,7 @@ NS_ASSUME_NONNULL_BEGIN - (NSMutableArray *)toArray; //X7: NSArray - (NSArray *)toArrayWithUnion:(NSArray *)items; //X7: NSArray +- (NSDictionary *)toDictionaryWithUnion:(NSArray *)items; @end diff --git a/ably-ios/ARTAuth.m b/ably-ios/ARTAuth.m index cacfb0bf1..392245fbc 100644 --- a/ably-ios/ARTAuth.m +++ b/ably-ios/ARTAuth.m @@ -84,6 +84,21 @@ - (NSMutableArray *)toArray { return params; } +- (NSMutableDictionary *)toDictionary { + NSMutableDictionary *params = [[NSMutableDictionary alloc] init]; + + if (self.clientId) + params[@"clientId"] = self.clientId; + if (self.ttl > 0) + params[@"ttl"] = [NSString stringWithFormat:@"%f", self.ttl]; + if (self.capability) + params[@"capability"] = self.capability; + if (self.timestamp > 0) + params[@"timestamp"] = [NSString stringWithFormat:@"%f", self.timestamp.timeIntervalSince1970]; + + return params; +} + - (NSArray *)toArrayWithUnion:(NSArray *)items { NSMutableArray *tokenParams = [self toArray]; BOOL add = YES; @@ -105,6 +120,27 @@ - (NSArray *)toArrayWithUnion:(NSArray *)items { return tokenParams; } +- (NSDictionary *)toDictionaryWithUnion:(NSArray *)items { + NSMutableDictionary *tokenParams = [self toDictionary]; + BOOL add = YES; + + for (NSURLQueryItem *item in items) { + for (NSString *key in tokenParams.allKeys) { + // Check if exist + if ([key isEqualToString:item.name]) { + add = NO; + break; + } + } + if (add) { + tokenParams[item.name] = item.value; + } + add = YES; + } + + return tokenParams; +} + static NSString *generateNonce() { // Generate two random numbers up to 8 digits long and concatenate them to produce a 16 digit random number NSUInteger r1 = arc4random_uniform(100000000); @@ -175,6 +211,14 @@ @implementation ARTAuthOptions NSString *const ARTAuthOptionsMethodDefault = @"GET"; +- (instancetype)init { + self = [super init]; + if (self) { + _authMethod = ARTAuthOptionsMethodDefault; + } + return self; +} + - (instancetype)initWithKey:(NSString *)key { self = [self init]; if (self) { @@ -182,7 +226,6 @@ - (instancetype)initWithKey:(NSString *)key { [NSException raise:@"Invalid key" format:@"%@ should be of the form :", key]; } _key = [key copy]; - _authMethod = ARTAuthOptionsMethodDefault; } return self; } @@ -291,52 +334,65 @@ - (instancetype)init:(ARTRest *)rest withOptions:(ARTAuthOptions *)options { return self; } +- (ARTAuthOptions *)mergeOptions:(ARTAuthOptions *)customOptions { + return customOptions ? [self.options mergeWith:customOptions] : self.options; +} + +- (ARTAuthTokenParams *)mergeParams:(ARTAuthTokenParams *)customParams { + return customParams ? customParams : [[ARTAuthTokenParams alloc] init]; +} + +- (NSURL *)buildURL:(ARTAuthOptions *)options withParams:(ARTAuthTokenParams *)params { + NSURLComponents *urlComponents = [NSURLComponents componentsWithURL:options.authUrl resolvingAgainstBaseURL:YES]; + + if ([options isMethodGET]) { + // TokenParams take precedence over any configured authParams when a name conflict occurs + NSArray *unitedParams = [params toArrayWithUnion:options.authParams]; + // When GET, use query string params + urlComponents.queryItems = @[]; + urlComponents.queryItems = [urlComponents.queryItems arrayByAddingObjectsFromArray:unitedParams]; + } + + return urlComponents.URL; +} + +- (NSMutableURLRequest *)buildRequest:(ARTAuthOptions *)options withParams:(ARTAuthTokenParams *)params { + NSURL *url = [self buildURL:options withParams:params]; + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; + request.HTTPMethod = options.authMethod; + + // HTTP Header Fields + if ([options isMethodPOST]) { + // TokenParams take precedence over any configured authParams when a name conflict occurs + NSDictionary *unitedParams = [params toDictionaryWithUnion:options.authParams]; + // When POST, use body of the POST request + NSData *bodyData = [NSJSONSerialization dataWithJSONObject:unitedParams options:0 error:nil]; + request.HTTPBody = bodyData; + [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; + [request setValue:[NSString stringWithFormat:@"%d", (unsigned int)bodyData.length] forHTTPHeaderField:@"Content-Length"]; + } + else { + [request setValue:@"application/json" forHTTPHeaderField:@"Accept"]; + } + + for (NSString *key in options.authHeaders) { + [request setValue:options.authHeaders[key] forHTTPHeaderField:key]; + } + + return request; +} + - (void)requestToken:(ARTAuthTokenParams *)tokenParams withOptions:(ARTAuthOptions *)authOptions callback:(void (^)(ARTAuthTokenDetails *, NSError *))callback { // The values supersede matching client library configured params and options. - ARTAuthOptions *mergedOptions = authOptions ? [self.options mergeWith:authOptions] : self.options; - ARTAuthTokenParams *currentTokenParams = tokenParams; + ARTAuthOptions *mergedOptions = [self mergeOptions:authOptions]; + ARTAuthTokenParams *currentTokenParams = [self mergeParams:tokenParams]; - if (!currentTokenParams) { - currentTokenParams = [[ARTAuthTokenParams alloc] init]; - } - if (mergedOptions.authUrl) { - NSURLComponents *urlComponents = [NSURLComponents componentsWithURL:mergedOptions.authUrl resolvingAgainstBaseURL:YES]; - - // TokenParams take precedence over any configured authParams when a name conflict occurs - NSArray *params = [currentTokenParams toArrayWithUnion:mergedOptions.authParams]; - NSData *bodyData = nil; - - if ([mergedOptions isMethodPOST]) { - // When POST, use body of the POST request - bodyData = [NSJSONSerialization dataWithJSONObject:params options:0 error:nil]; - } - else { - // When GET, use query string params - urlComponents.queryItems = @[]; - urlComponents.queryItems = [urlComponents.queryItems arrayByAddingObjectsFromArray:params]; - } - - NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:urlComponents.URL]; - request.HTTPMethod = mergedOptions.authMethod; - - // HTTP Header Fields - if ([mergedOptions isMethodPOST] && bodyData) { - request.HTTPBody = bodyData; - [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; - [request setValue:[NSString stringWithFormat:@"%d", (unsigned int)bodyData.length] forHTTPHeaderField:@"Content-Length"]; - } - else { - [request setValue:@"application/json" forHTTPHeaderField:@"Accept"]; - } - - for (NSString *key in mergedOptions.authHeaders) { - [request setValue:mergedOptions.authHeaders[key] forHTTPHeaderField:key]; - } + NSMutableURLRequest *request = [self buildRequest:mergedOptions withParams:currentTokenParams]; - [_rest.logger debug:@"%@ %@", request.HTTPMethod, urlComponents.URL]; + [_rest.logger debug:@"%@ %@", request.HTTPMethod, request.URL]; [_rest.httpExecutor executeRequest:request callback:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { if (error) { diff --git a/ably-ios/ably.modulemap b/ably-ios/ably.modulemap index 00244abc6..29aac8d38 100644 --- a/ably-ios/ably.modulemap +++ b/ably-ios/ably.modulemap @@ -9,6 +9,7 @@ framework module ably { header "ARTDataQuery+Private.h" header "ARTPayload+Private.h" header "ARTRest+Private.h" + header "ARTAuth+Private.h" header "ARTPaginatedResult+Private.h" header "ARTEncoder.h" header "ARTJsonEncoder.h" diff --git a/ably.xcodeproj/project.pbxproj b/ably.xcodeproj/project.pbxproj index 87f9434c9..ea6d434ee 100644 --- a/ably.xcodeproj/project.pbxproj +++ b/ably.xcodeproj/project.pbxproj @@ -126,6 +126,7 @@ D746AE531BBD85C5003ECEF8 /* ARTChannelCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE511BBD85C5003ECEF8 /* ARTChannelCollection.h */; settings = {ATTRIBUTES = (Public, ); }; }; D746AE541BBD85C5003ECEF8 /* ARTChannelCollection.m in Sources */ = {isa = PBXBuildFile; fileRef = D746AE521BBD85C5003ECEF8 /* ARTChannelCollection.m */; }; D7C1B8771BBEA81A0087B55F /* Auth.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C1B8761BBEA81A0087B55F /* Auth.swift */; }; + D7C1B8791BBF5F810087B55F /* ARTAuth+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = D7C1B8781BBF5F460087B55F /* ARTAuth+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; FC78549C1BCD5688539CCBE4 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FDA6E099E4BC04FC80F845C0 /* libPods.a */; }; /* End PBXBuildFile section */ @@ -300,6 +301,7 @@ D746AE521BBD85C5003ECEF8 /* ARTChannelCollection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTChannelCollection.m; sourceTree = ""; }; D746AE551BBD8622003ECEF8 /* ARTChannelCollection+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ARTChannelCollection+Private.h"; sourceTree = ""; }; D7C1B8761BBEA81A0087B55F /* Auth.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Auth.swift; sourceTree = ""; }; + D7C1B8781BBF5F460087B55F /* ARTAuth+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ARTAuth+Private.h"; sourceTree = ""; }; DF8DF755839D59574B7B795F /* Pods-ablySpec.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ablySpec.release.xcconfig"; path = "Pods/Target Support Files/Pods-ablySpec/Pods-ablySpec.release.xcconfig"; sourceTree = ""; }; FDA6E099E4BC04FC80F845C0 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -477,6 +479,7 @@ 960D07951A46FFC300ED8C8C /* ARTRest+Private.h */, 96BF61521A35B39C004CF2B3 /* ARTRest.m */, 96BF616E1A35FB7C004CF2B3 /* ARTAuth.h */, + D7C1B8781BBF5F460087B55F /* ARTAuth+Private.h */, 96BF616F1A35FB7C004CF2B3 /* ARTAuth.m */, D746AE321BBC29EB003ECEF8 /* Transport */, ); @@ -614,6 +617,7 @@ D746AE221BBB60EE003ECEF8 /* ARTChannel.h in Headers */, 96A507B51A37881C0077CDF8 /* ARTNSDate+ARTUtil.h in Headers */, 967A43211A39AEAF00E4CE23 /* ARTNSArray+ARTFunctional.h in Headers */, + D7C1B8791BBF5F810087B55F /* ARTAuth+Private.h in Headers */, 96A507A51A377DE90077CDF8 /* ARTNSDictionary+ARTDictionaryUtil.h in Headers */, D746AE1D1BBB5207003ECEF8 /* ARTDataQuery.h in Headers */, D746AE4B1BBD70F0003ECEF8 /* ARTRealtimeChannelSubscription.h in Headers */, diff --git a/ablySpec/Auth.swift b/ablySpec/Auth.swift index 3d6cf9bea..3941cacf8 100644 --- a/ablySpec/Auth.swift +++ b/ablySpec/Auth.swift @@ -17,7 +17,7 @@ class Auth : QuickSpec { beforeEach { mockExecutor = MockHTTPExecutor() } - + describe("Basic") { // RSA1 it("should work over HTTPS only") { @@ -44,7 +44,7 @@ class Auth : QuickSpec { } // RSA2 - it("should be default when an API йеъ is set") { + it("should be default when an API key is set") { let client = ARTRest(options: ARTClientOptions(key: "fake:key")) expect(client.auth.authMethod).to(equal(ARTAuthMethod.Basic)) @@ -53,21 +53,6 @@ class Auth : QuickSpec { describe("Token") { - fit("implicitly creates a TokenRequest") { - // WIP - let options = ARTClientOptions(key: "6p6USg.CNwGdA:uwJU1qsSf_Qe9VDH") - // Test - options.authOptions.authUrl = NSURL(string: "http://auth.ably.io") - options.authOptions.authParams = [NSURLQueryItem(name: "ttl", value: "aaa")] - options.authOptions.authMethod = "POST" - - let rest = ARTRest(options: options) - - rest.auth.requestToken(nil, withOptions: nil, callback: { tokenDetails, error in - - }) - } - it("should send the token in the Authorization header") { let options = ARTClientOptions() options.authOptions.token = getTestToken() @@ -110,5 +95,107 @@ class Auth : QuickSpec { } } } + + // RSA8 + describe("requestToken") { + context("arguments") { + // RSA8e + it("sould supersede matching client library configured params and options") { + let clientOptions = ARTClientOptions() + clientOptions.authOptions.authUrl = NSURL(string: "http://auth.ably.io") + + let rest = ARTRest(options: clientOptions) + + let authOptions = ARTAuthOptions() + authOptions.authUrl = NSURL(string: "http://test.ably.io") + authOptions.authMethod = "POST" + let tokenParams = ARTAuthTokenParams() + tokenParams.ttl = 30.0 + + // AuthOptions + let mergedOptions = rest.auth.mergeOptions(authOptions) + expect(mergedOptions.authUrl) == NSURL(string: "http://test.ably.io") + expect(mergedOptions.authMethod) == "POST" + // TokenParams + let mergedParams = rest.auth.mergeParams(tokenParams) + expect(mergedParams.ttl) == 30.0 + } + + // TODO: RSA8b + } + + // RSA8c + context("authUrl") { + context("parameters") { + // RSA8c1a + it("should be added to the URL when auth method is GET") { + let clientOptions = ARTClientOptions() + clientOptions.authOptions.authUrl = NSURL(string: "http://auth.ably.io") + let tokenParams = ARTAuthTokenParams() + + let rest = ARTRest(options: clientOptions) + + let url = rest.auth.buildURL(clientOptions.authOptions, withParams: tokenParams) + expect(url) == NSURL(string: "http://auth.ably.io/") + } + + // RSA8c1b + it("should added on the body request when auth method is POST") { + let clientOptions = ARTClientOptions() + clientOptions.authOptions.authUrl = NSURL(string: "http://auth.ably.io") + let tokenParams = ARTAuthTokenParams() + + let rest = ARTRest(options: clientOptions) + + let request = rest.auth.buildRequest(clientOptions.authOptions, withParams: tokenParams) + + let expectedJSON = "{\"ttl\":\"3600.000000\",\"capability\":\"{ \"*\": [ \"*\" ] }\",\"timestamp\":\"1443810028.362290\"}" + let expectedData = NSString(string: expectedJSON).dataUsingEncoding(NSUTF8StringEncoding) + + // TODO: not passing + + expect(request.HTTPBody) == expectedData + } + } + + // RSA8c3 + fit("should override previously configured parameters") { + let clientOptions = ARTClientOptions() + clientOptions.authOptions.authUrl = NSURL(string: "http://auth.ably.io") + let rest = ARTRest(options: clientOptions) + + let authOptions = ARTAuthOptions() + authOptions.authUrl = NSURL(string: "http://auth.ably.io") + authOptions.authParams = [NSURLQueryItem(name: "ttl", value: "invalid")] + authOptions.authParams = [NSURLQueryItem(name: "test", value: "1")] + + let url = rest.auth.buildURL(authOptions, withParams: ARTAuthTokenParams()) + expect(url) == NSURL(string: "http://auth.ably.io/") + } + } + + // RSA8d + context("authCallback") { + it("") { + // TODO + } + } + + // RSA8a + it("implicitly creates a TokenRequest") { + let options = ARTClientOptions(key: "6p6USg.CNwGdA:uwJU1qsSf_Qe9VDH") + // Test + options.authOptions.authUrl = NSURL(string: "http://auth.ably.io") + options.authOptions.authParams = [NSURLQueryItem(name: "ttl", value: "aaa")] + options.authOptions.authParams = [NSURLQueryItem(name: "rp", value: "true")] + options.authOptions.authMethod = "POST" + + let rest = ARTRest(options: options) + + rest.auth.requestToken(nil, withOptions: nil, callback: { tokenDetails, error in + + }) + } + } } } From 71630765fc87dbbe24e2174e2d746bc46fdb864f Mon Sep 17 00:00:00 2001 From: Ricardo Pereira Date: Mon, 5 Oct 2015 12:22:35 +0100 Subject: [PATCH 17/22] Comparing JSON data: HTTPBody == expectedJSON --- Podfile | 2 + Podfile.lock | 3 ++ ably.xcodeproj/project.pbxproj | 4 ++ ablySpec/Auth.swift | 14 ++++--- ablySpec/Helpers.swift | 74 ++++++++++++++++++++++++++++++++++ 5 files changed, 91 insertions(+), 6 deletions(-) create mode 100644 ablySpec/Helpers.swift diff --git a/Podfile b/Podfile index 23b88c5a4..82242b04d 100644 --- a/Podfile +++ b/Podfile @@ -7,5 +7,7 @@ target 'ablySpec' do pod 'Quick', '~> 0.3.0' pod 'Nimble', '~> 1.0.0' + # Helpers + pod 'Runes', '~> 2.0.0' pod 'SwiftyJSON', '~> 2.2.1' end \ No newline at end of file diff --git a/Podfile.lock b/Podfile.lock index e3b9284d1..8fc66b5ae 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,18 +1,21 @@ PODS: - Nimble (1.0.0) - Quick (0.3.1) + - Runes (2.0.0) - SocketRocket (0.3.1-beta2) - SwiftyJSON (2.2.1) DEPENDENCIES: - Nimble (~> 1.0.0) - Quick (~> 0.3.0) + - Runes (~> 2.0.0) - SocketRocket (~> 0.3.1-beta2) - SwiftyJSON (~> 2.2.1) SPEC CHECKSUMS: Nimble: 8bee528e5fcc403653076545db562d2b5db7bb87 Quick: 824572d3d198d51e52cf4aa722cebf7e59952a35 + Runes: 4fe81355f4620b76b02176222d264b33e60dba51 SocketRocket: 7284ab9370a06c99aba92b2fe3a32aedd0f9a6fa SwiftyJSON: ae2d0a3d68025d136602a33c4ee215091ced3e33 diff --git a/ably.xcodeproj/project.pbxproj b/ably.xcodeproj/project.pbxproj index ea6d434ee..340b5e0a3 100644 --- a/ably.xcodeproj/project.pbxproj +++ b/ably.xcodeproj/project.pbxproj @@ -127,6 +127,7 @@ D746AE541BBD85C5003ECEF8 /* ARTChannelCollection.m in Sources */ = {isa = PBXBuildFile; fileRef = D746AE521BBD85C5003ECEF8 /* ARTChannelCollection.m */; }; D7C1B8771BBEA81A0087B55F /* Auth.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C1B8761BBEA81A0087B55F /* Auth.swift */; }; D7C1B8791BBF5F810087B55F /* ARTAuth+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = D7C1B8781BBF5F460087B55F /* ARTAuth+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + D7D8F81E1BC28FF0009718F2 /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D8F81D1BC28FF0009718F2 /* Helpers.swift */; }; FC78549C1BCD5688539CCBE4 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FDA6E099E4BC04FC80F845C0 /* libPods.a */; }; /* End PBXBuildFile section */ @@ -302,6 +303,7 @@ D746AE551BBD8622003ECEF8 /* ARTChannelCollection+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ARTChannelCollection+Private.h"; sourceTree = ""; }; D7C1B8761BBEA81A0087B55F /* Auth.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Auth.swift; sourceTree = ""; }; D7C1B8781BBF5F460087B55F /* ARTAuth+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ARTAuth+Private.h"; sourceTree = ""; }; + D7D8F81D1BC28FF0009718F2 /* Helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Helpers.swift; sourceTree = ""; }; DF8DF755839D59574B7B795F /* Pods-ablySpec.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ablySpec.release.xcconfig"; path = "Pods/Target Support Files/Pods-ablySpec/Pods-ablySpec.release.xcconfig"; sourceTree = ""; }; FDA6E099E4BC04FC80F845C0 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -385,6 +387,7 @@ D746AE2B1BBB625E003ECEF8 /* RestClient.channels.swift */, D723046F1BB72CED00F1ABDA /* RealtimeClient.swift */, 851674EE1B7BA5CD00D35169 /* Stats.swift */, + D7D8F81D1BC28FF0009718F2 /* Helpers.swift */, ); path = ablySpec; sourceTree = ""; @@ -874,6 +877,7 @@ D746AE2C1BBB625E003ECEF8 /* RestChannel.swift in Sources */, 856AAC971B6E30C800B07119 /* TestUtilities.swift in Sources */, 853ED7C41B7A1A3C006F1C6F /* RestClient.stats.swift in Sources */, + D7D8F81E1BC28FF0009718F2 /* Helpers.swift in Sources */, 851674EF1B7BA5CD00D35169 /* Stats.swift in Sources */, D72304701BB72CED00F1ABDA /* RealtimeClient.swift in Sources */, D746AE2D1BBB625E003ECEF8 /* RestClient.channels.swift in Sources */, diff --git a/ablySpec/Auth.swift b/ablySpec/Auth.swift index 3941cacf8..37201a6b0 100644 --- a/ablySpec/Auth.swift +++ b/ablySpec/Auth.swift @@ -8,6 +8,8 @@ import Nimble import Quick +import Runes + import ably import ably.Private @@ -140,26 +142,26 @@ class Auth : QuickSpec { } // RSA8c1b - it("should added on the body request when auth method is POST") { + fit("should added on the body request when auth method is POST") { let clientOptions = ARTClientOptions() clientOptions.authOptions.authUrl = NSURL(string: "http://auth.ably.io") + clientOptions.authOptions.authMethod = "POST" let tokenParams = ARTAuthTokenParams() let rest = ARTRest(options: clientOptions) let request = rest.auth.buildRequest(clientOptions.authOptions, withParams: tokenParams) - let expectedJSON = "{\"ttl\":\"3600.000000\",\"capability\":\"{ \"*\": [ \"*\" ] }\",\"timestamp\":\"1443810028.362290\"}" - let expectedData = NSString(string: expectedJSON).dataUsingEncoding(NSUTF8StringEncoding) + let expectedJSON = ["ttl":60*60, "capability":"{ \"*\": [ \"*\" ] }", "timestamp":NSDate().timeIntervalSince1970] - // TODO: not passing + // TODO: not passing, timestamp seconds... - expect(request.HTTPBody) == expectedData + expect(request.HTTPBody >>- JSONToDictionary) == expectedJSON } } // RSA8c3 - fit("should override previously configured parameters") { + it("should override previously configured parameters") { let clientOptions = ARTClientOptions() clientOptions.authOptions.authUrl = NSURL(string: "http://auth.ably.io") let rest = ARTRest(options: clientOptions) diff --git a/ablySpec/Helpers.swift b/ablySpec/Helpers.swift new file mode 100644 index 000000000..e3579e163 --- /dev/null +++ b/ablySpec/Helpers.swift @@ -0,0 +1,74 @@ +// +// FunctionHelpers.swift +// Ably +// +// Created by Ricardo Pereira on 01/09/2015. +// Copyright (c) 2015 Ricardo Pereira. All rights reserved. +// + +import Foundation +import Runes + + +// MARK: - Global Functions + +/** +**Public function** – Load JSON from a file +*/ +func JSONFromFile(fileName: String) -> AnyObject? { + return NSBundle.mainBundle().pathForResource(fileName, ofType: "json") + >>- { NSData(contentsOfFile: $0) } + >>- { NSJSONSerialization.JSONObjectWithData($0, options: NSJSONReadingOptions(0), error: nil) } +} + + +// MARK: - Monads + +let arrayToJSONData: NSArray -> NSData? = { + NSJSONSerialization.dataWithJSONObject($0, options: NSJSONWritingOptions.allZeros, error: nil) +} + +let dictionaryToJSONData: NSDictionary -> NSData? = { + NSJSONSerialization.dataWithJSONObject($0, options: NSJSONWritingOptions.allZeros, error: nil) +} + +let arrayToPrettyJSONData: NSArray -> NSData? = { + NSJSONSerialization.dataWithJSONObject($0, options: NSJSONWritingOptions.PrettyPrinted, error: nil) +} + +let dictionaryToPrettyJSONData: NSDictionary -> NSData? = { + NSJSONSerialization.dataWithJSONObject($0, options: NSJSONWritingOptions.PrettyPrinted, error: nil) +} + +let dataToJSONString: NSData -> NSString? = { + NSString(data: $0, encoding: NSUTF8StringEncoding) +} + +let JSONStringToData: String -> NSData? = { + NSString(string: $0).dataUsingEncoding(NSUTF8StringEncoding) +} + +let JSONDataToAny: NSData -> AnyObject? = { + NSJSONSerialization.JSONObjectWithData($0, options: .MutableLeaves, error: nil) +} + +/** +**Monad** – Convert array to JSON +*/ +let arrayToJSONString: NSArray -> String? = { + $0 >>- arrayToJSONData >>- dataToJSONString >>- { $0 as String } +} + +/** +**Monad** – Convert JSON to dictionary +*/ +let JSONToDictionary: NSData -> NSDictionary? = { + $0 >>- JSONDataToAny >>- { $0 as? NSDictionary ?? nil } +} + +/** +**Monad** – Convert JSON to array +*/ +let JSONToArray: NSData -> NSArray? = { + $0 >>- JSONDataToAny >>- { $0 as? NSArray ?? nil } +} From e2fcd8e65707e7d8a4bd64c062cb883fb7b1a94f Mon Sep 17 00:00:00 2001 From: Ricardo Pereira Date: Mon, 5 Oct 2015 17:05:25 +0100 Subject: [PATCH 18/22] =?UTF-8?q?RSA1:=20=E2=9C=85.=20Fixed=20ARTClientOpt?= =?UTF-8?q?ions.=20Now=20extends=20from=20ARTAuthOptions.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ably-ios/ARTAuth+Private.h | 2 - ably-ios/ARTAuth.h | 147 +--------- ably-ios/ARTAuth.m | 319 ++-------------------- ably-ios/ARTAuthOptions.h | 83 ++++++ ably-ios/ARTAuthOptions.m | 115 ++++++++ ably-ios/ARTAuthTokenDetails.h | 29 ++ ably-ios/ARTAuthTokenDetails.m | 33 +++ ably-ios/ARTAuthTokenParams.h | 36 +++ ably-ios/ARTAuthTokenParams.m | 149 ++++++++++ ably-ios/ARTAuthTokenRequest.h | 23 ++ ably-ios/ARTAuthTokenRequest.m | 35 +++ ably-ios/ARTClientOptions.h | 8 +- ably-ios/ARTClientOptions.m | 44 +-- ably-ios/ARTHttp.m | 14 +- ably-ios/ARTJsonEncoder.m | 2 +- ably-ios/ARTRest.m | 3 +- ably-ios/ARTTypes.h | 14 +- ably-ios/ably.h | 4 + ably.xcodeproj/project.pbxproj | 32 +++ ably.xcworkspace/contents.xcworkspacedata | 10 + ablySpec/Auth.swift | 49 ++-- ablySpec/RestClient.stats.swift | 2 +- ablySpec/RestClient.swift | 16 +- ablySpec/TestUtilities.swift | 8 +- 24 files changed, 649 insertions(+), 528 deletions(-) create mode 100644 ably-ios/ARTAuthOptions.h create mode 100644 ably-ios/ARTAuthOptions.m create mode 100644 ably-ios/ARTAuthTokenDetails.h create mode 100644 ably-ios/ARTAuthTokenDetails.m create mode 100644 ably-ios/ARTAuthTokenParams.h create mode 100644 ably-ios/ARTAuthTokenParams.m create mode 100644 ably-ios/ARTAuthTokenRequest.h create mode 100644 ably-ios/ARTAuthTokenRequest.m create mode 100644 ably.xcworkspace/contents.xcworkspacedata diff --git a/ably-ios/ARTAuth+Private.h b/ably-ios/ARTAuth+Private.h index 9ccf08176..f5e2fab80 100644 --- a/ably-ios/ARTAuth+Private.h +++ b/ably-ios/ARTAuth+Private.h @@ -10,8 +10,6 @@ #import "ARTAuth.h" -@class ARTAuthOptions; - NS_ASSUME_NONNULL_BEGIN @interface ARTAuth (Private) diff --git a/ably-ios/ARTAuth.h b/ably-ios/ARTAuth.h index e584a18a3..694727983 100644 --- a/ably-ios/ARTAuth.h +++ b/ably-ios/ARTAuth.h @@ -7,160 +7,33 @@ // #import -#import -#import +#import "ably.h" @class ARTRest; @class ARTLog; +@class ARTClientOptions; +@class ARTAuthOptions; +@class ARTAuthTokenParams; +@class ARTAuthTokenDetails; +@class ARTAuthTokenRequest; NS_ASSUME_NONNULL_BEGIN -#pragma mark - ARTAuthTokenParams - -@interface ARTAuthTokenDetails : NSObject - -@property (nonatomic, readonly, copy) NSString *token; -@property (nonatomic, readonly, strong, nullable) NSDate *expires; -@property (nonatomic, readonly, strong, nullable) NSDate *issued; -@property (nonatomic, readonly, copy, nullable) NSString *capability; -@property (nonatomic, readonly, copy, nullable) NSString *clientId; - -- (instancetype)init UNAVAILABLE_ATTRIBUTE; - -- (instancetype)initWithToken:(NSString *)token; - -- (instancetype)initWithToken:(NSString *)token expires:(NSDate *)expires issued:(NSDate *)issued capability:(NSString *)capability clientId:(NSString *)clientId; - -@end - - -#pragma mark - ARTAuthTokenParams - -@interface ARTAuthTokenParams : NSObject - -@property (nonatomic, assign) NSTimeInterval ttl; -@property (nonatomic, copy) NSString *capability; -@property (nonatomic, copy) NSString *clientId; -@property (nonatomic, strong, null_resettable) NSDate *timestamp; - -- (instancetype)init; - -- (NSMutableArray *)toArray; //X7: NSArray -- (NSArray *)toArrayWithUnion:(NSArray *)items; //X7: NSArray -- (NSDictionary *)toDictionaryWithUnion:(NSArray *)items; - -@end - - -#pragma mark - ARTAuthTokenRequest - -@interface ARTAuthTokenRequest : ARTAuthTokenParams - -@property (nonatomic, readonly, copy) NSString *keyName; -@property (nonatomic, readonly, copy) NSString *nonce; -@property (nonatomic, readonly, copy) NSString *mac; - -- (instancetype)init UNAVAILABLE_ATTRIBUTE; - -- (instancetype)initWithTokenParams:(ARTAuthTokenParams *)tokenParams keyName:(NSString *)keyName nonce:(NSString *)nonce mac:(NSString *)mac; - -@end - - -#pragma mark - ARTAuthTokenParams - -@interface ARTAuthTokenParams(SignedRequest) - -- (ARTAuthTokenRequest *)sign:(NSString *)key; - -@end - - -#pragma mark - ARTAuthOptions - -typedef void (^ARTAuthCallback)(ARTAuthTokenParams *tokenParams, void(^callback)(ARTAuthTokenRequest *__nullable tokenRequest, NSError *__nullable error)); +#pragma mark - ARTAuth typedef NS_ENUM(NSUInteger, ARTAuthMethod) { ARTAuthMethodBasic, ARTAuthMethodToken }; -@interface ARTAuthOptions : NSObject - -/** - Full Ably key string as obtained from dashboard. - */ -@property (nonatomic, copy, nullable) NSString *key; - -/** - An authentication token issued for this application against a specific key and `TokenParams`. - */ -@property (nonatomic, copy, nullable) NSString *token; - -/** - An authentication token issued for this application against a specific key and `TokenParams`. - */ -@property (nonatomic, strong, nullable) ARTAuthTokenDetails *tokenDetails; - -/** - A callback to call to obtain a signed token request. - - This enables a client to obtain token requests from another entity, so tokens can be renewed without the client requiring access to keys. - */ -@property (nonatomic, copy, nullable) ARTAuthCallback authCallback; - -/** - A URL to queryto obtain a signed token request. - - This enables a client to obtain token requests from another entity, so tokens can be renewed without the client requiring access to keys. - */ -@property (nonatomic, strong, nullable) NSURL *authUrl; - -/** - The HTTP verb to be used when a request is made by the library to the authUrl. Defaults to GET, supports GET and POST. - */ -@property (nonatomic, copy, null_resettable) NSString *authMethod; - -/** - Headers to be included in any request made by the library to the authURL. - */ -@property (nonatomic, copy, nullable) NSDictionary *authHeaders; //X7: NSDictionary *authHeaders; - -/** - Additional params to be included in any request made by the library to the authUrl, either as query params in the case of GET or in the body in the case of POST. - */ -@property (nonatomic, copy, nullable) NSArray *authParams; //X7: NSArray *authParams; - -/** - This may be set in instances that the library is to sign token requests based on a given key. - If true, the library will query the Ably system for the current time instead of relying on a locally-available time of day. - */ -@property (nonatomic, assign, nonatomic) BOOL queryTime; - -@property (nonatomic, assign) BOOL useTokenAuth; - -- (instancetype)initWithKey:(NSString *)key; - -- (NSString *)description; - -- (ARTAuthOptions *)mergeWith:(ARTAuthOptions *)precedenceOptions; - -- (BOOL)isMethodGET; -- (BOOL)isMethodPOST; - -@end - - -#pragma mark - ARTAuth - @interface ARTAuth : NSObject @property (nonatomic, weak) ARTLog *logger; @property (nonatomic, readonly, strong) ARTAuthOptions *options; -@property (nonatomic, readonly, assign) ARTAuthMethod authMethod; -@property (nonatomic, readonly, strong) ARTAuthTokenDetails *currentToken; +@property (nonatomic, readonly, assign) ARTAuthMethod method; +@property (nonatomic, readonly, strong) ARTAuthTokenDetails *tokenDetails; -- (instancetype)init:(ARTRest *)rest withOptions:(ARTAuthOptions *)options; +- (instancetype)init:(ARTRest *)rest withOptions:(ARTClientOptions *)options; /** # (RSA8) Auth#requestToken diff --git a/ably-ios/ARTAuth.m b/ably-ios/ARTAuth.m index 392245fbc..64aae8934 100644 --- a/ably-ios/ARTAuth.m +++ b/ably-ios/ARTAuth.m @@ -8,324 +8,43 @@ #import "ARTAuth.h" -#include -#include - #import "ARTRest.h" #import "ARTRest+Private.h" -#import "ARTEncoder.h" -#import "ARTLog.h" -#import "ARTPayload.h" - -//X7: NSArray -static NSArray *decomposeKey(NSString *key) { - return [key componentsSeparatedByString:@":"]; -} - -@implementation ARTAuthTokenDetails - -- (instancetype)initWithToken:(NSString *)token expires:(NSDate *)expires issued:(NSDate *)issued capability:(NSString *)capability clientId:(NSString *)clientId { - if (self = [super init]) { - _token = [token copy]; - _expires = expires; - _issued = issued; - _capability = [capability copy]; - _clientId = [clientId copy]; - } - - return self; -} - -- (instancetype)initWithToken:(NSString *)token { - if (self = [super init]) { - _token = [token copy]; - } - - return self; -} - -@end - - -#pragma mark - ARTAuthTokenParams - -@implementation ARTAuthTokenParams - -- (instancetype)init { - if (self = [super init]) { - _ttl = 60 * 60; - _timestamp = [NSDate date]; - _capability = @"{ \"*\": [ \"*\" ] }"; // allow all - } - - return self; -} - -- (void)setTimestamp:(NSDate *)timestamp { - if (timestamp == nil) { - timestamp = [NSDate date]; - } - - _timestamp = timestamp; -} - -- (NSMutableArray *)toArray { - NSMutableArray *params = [[NSMutableArray alloc] init]; - - if (self.clientId) - [params addObject:[NSURLQueryItem queryItemWithName:@"clientId" value:self.clientId]]; - if (self.ttl > 0) - [params addObject:[NSURLQueryItem queryItemWithName:@"ttl" value:[NSString stringWithFormat:@"%f", self.ttl]]]; - if (self.capability) - [params addObject:[NSURLQueryItem queryItemWithName:@"capability" value:self.capability]]; - if (self.timestamp > 0) - [params addObject:[NSURLQueryItem queryItemWithName:@"timestamp" value:[NSString stringWithFormat:@"%f", self.timestamp.timeIntervalSince1970]]]; - - return params; -} - -- (NSMutableDictionary *)toDictionary { - NSMutableDictionary *params = [[NSMutableDictionary alloc] init]; - - if (self.clientId) - params[@"clientId"] = self.clientId; - if (self.ttl > 0) - params[@"ttl"] = [NSString stringWithFormat:@"%f", self.ttl]; - if (self.capability) - params[@"capability"] = self.capability; - if (self.timestamp > 0) - params[@"timestamp"] = [NSString stringWithFormat:@"%f", self.timestamp.timeIntervalSince1970]; - - return params; -} - -- (NSArray *)toArrayWithUnion:(NSArray *)items { - NSMutableArray *tokenParams = [self toArray]; - BOOL add = YES; - - for (NSURLQueryItem *item in items) { - for (NSURLQueryItem *param in tokenParams) { - // Check if exist - if ([param.name isEqualToString:item.name]) { - add = NO; - break; - } - } - if (add) { - [tokenParams addObject:item]; - } - add = YES; - } - - return tokenParams; -} - -- (NSDictionary *)toDictionaryWithUnion:(NSArray *)items { - NSMutableDictionary *tokenParams = [self toDictionary]; - BOOL add = YES; - - for (NSURLQueryItem *item in items) { - for (NSString *key in tokenParams.allKeys) { - // Check if exist - if ([key isEqualToString:item.name]) { - add = NO; - break; - } - } - if (add) { - tokenParams[item.name] = item.value; - } - add = YES; - } - - return tokenParams; -} - -static NSString *generateNonce() { - // Generate two random numbers up to 8 digits long and concatenate them to produce a 16 digit random number - NSUInteger r1 = arc4random_uniform(100000000); - NSUInteger r2 = arc4random_uniform(100000000); - return [NSString stringWithFormat:@"%08lu%08lu", (long)r1, (long)r2]; -} - -static NSString *hmacForDataAndKey(NSData *data, NSData *key) { - const void *cKey = [key bytes]; - const void *cData = [data bytes]; - size_t keyLen = [key length]; - size_t dataLen = [data length]; - - unsigned char hmac[CC_SHA256_DIGEST_LENGTH]; - - CCHmac(kCCHmacAlgSHA256, cKey, keyLen, cData, dataLen, hmac); - NSData *mac = [[NSData alloc] initWithBytes:hmac length:sizeof(hmac)]; - NSString *str = [ARTBase64PayloadEncoder toBase64:mac]; - return str; -} - -- (ARTAuthTokenRequest *)sign:(NSString *)key { - //X7: NSArray - NSArray *keyComponents = decomposeKey(key); - NSString *keyName = keyComponents[0]; - NSString *keySecret = keyComponents[1]; - NSString *nonce = generateNonce(); - - NSString *signText = [NSString stringWithFormat:@"%@\n%lld\n%@\n%@\n%lld\n%@\n", keyName, (int64_t)(self.ttl * 1000), self.capability, self.clientId, (int64_t)(self.timestamp.timeIntervalSince1970 * 1000), nonce]; - NSString *mac = hmacForDataAndKey([signText dataUsingEncoding:NSUTF8StringEncoding], [keySecret dataUsingEncoding:NSUTF8StringEncoding]); - - return [[ARTAuthTokenRequest alloc] initWithTokenParams:self keyName:keyName nonce:nonce mac:mac]; -} - -@end - - -#pragma mark - ARTAuthTokenRequest - -@implementation ARTAuthTokenRequest - -@dynamic timestamp; - -- (instancetype)initWithTokenParams:(ARTAuthTokenParams *)tokenParams keyName:(NSString *)keyName nonce:(NSString *)nonce mac:(NSString *)mac { - if (self = [super init]) { - self.ttl = tokenParams.ttl; - self.capability = tokenParams.capability; - self.clientId = tokenParams.clientId; - self.timestamp = tokenParams.timestamp; - _keyName = [keyName copy]; - _nonce = [nonce copy]; - _mac = [mac copy]; - } - - return self; -} - -- (NSDictionary *)asDictionary { - return nil; -} - -@end - - -#pragma mark - ARTAuthOptions - -@implementation ARTAuthOptions - -NSString *const ARTAuthOptionsMethodDefault = @"GET"; - -- (instancetype)init { - self = [super init]; - if (self) { - _authMethod = ARTAuthOptionsMethodDefault; - } - return self; -} - -- (instancetype)initWithKey:(NSString *)key { - self = [self init]; - if (self) { - if (decomposeKey(key).count != 2) { - [NSException raise:@"Invalid key" format:@"%@ should be of the form :", key]; - } - _key = [key copy]; - } - return self; -} - -- (id)copyWithZone:(NSZone *)zone { - ARTAuthOptions *options = [[ARTAuthOptions allocWithZone:zone] init]; - - options.key = self.key; - options.token = self.token; - options.useTokenAuth = self.useTokenAuth; - options.authCallback = self.authCallback; - options.authUrl = self.authUrl; - options.authMethod = self.authMethod; - options.authHeaders = self.authHeaders; - options.authParams = self.authParams; - options.queryTime = self.queryTime; - - return options; -} - -- (NSString *)description { - return [NSString stringWithFormat: @"ARTAuthOptions: key=%@ token=%@ authUrl=%@ authMethod=%@ hasAuthCallback=%d", - self.key, self.token, self.authUrl, self.authMethod, self.authCallback != nil]; -} - -- (NSString *)token { - return self.tokenDetails.token; -} - -- (void)setToken:(NSString *)token { - self.tokenDetails = [[ARTAuthTokenDetails alloc] initWithToken:token]; -} - -- (void)setAuthMethod:(NSString *)authMethod { - if (authMethod == nil || authMethod.length == 0) { - authMethod = ARTAuthOptionsMethodDefault; - } - - _authMethod = [authMethod copy]; -} - -- (ARTAuthOptions *)mergeWith:(ARTAuthOptions *)precedenceOptions { - ARTAuthOptions *merged = [self copy]; - - if (precedenceOptions.key) - merged.key = precedenceOptions.key; - if (precedenceOptions.authCallback) - merged.authCallback = precedenceOptions.authCallback; - if (precedenceOptions.authUrl) - merged.authUrl = precedenceOptions.authUrl; - if (precedenceOptions.authMethod) - merged.authMethod = precedenceOptions.authMethod; - if (precedenceOptions.authHeaders) - merged.authHeaders = precedenceOptions.authHeaders; - if (precedenceOptions.authParams) - merged.authParams = precedenceOptions.authParams; - if (precedenceOptions.queryTime) - merged.queryTime = precedenceOptions.queryTime; - - return merged; -} - -- (BOOL)isMethodPOST { - return [_authMethod isEqualToString:@"POST"]; -} - -- (BOOL)isMethodGET { - return [_authMethod isEqualToString:@"GET"]; -} - -@end - - -#pragma mark - ARTAuth implementation +#import "ARTHttp.h" +#import "ARTClientOptions.h" +#import "ARTAuthOptions.h" +#import "ARTAuthTokenDetails.h" +#import "ARTAuthTokenParams.h" +#import "ARTAuthTokenRequest.h" @implementation ARTAuth { __weak ARTRest *_rest; } -- (instancetype)init:(ARTRest *)rest withOptions:(ARTAuthOptions *)options { +- (instancetype)init:(ARTRest *)rest withOptions:(ARTClientOptions *)options { if (self = [super init]) { _rest = rest; - _currentToken = options.tokenDetails; + _tokenDetails = options.tokenDetails; _options = options; _logger = rest.logger; // REV: options.useTokenAuth if (options.key != nil && !options.useTokenAuth) { + if (!options.tls) { + [NSException raise:@"ARTAuthException" format:@"Basic authentication only connects over HTTPS (tls)."]; + } [self.logger debug:@"ARTAuth: setting up auth method Basic"]; - _authMethod = ARTAuthMethodBasic; + _method = ARTAuthMethodBasic; } else if (options.tokenDetails) { [self.logger debug:@"ARTAuth: setting up auth method Token with supplied token only"]; - _authMethod = ARTAuthMethodToken; + _method = ARTAuthMethodToken; } else if (options.authUrl) { [self.logger debug:@"ARTAuth: setting up auth method Token with authUrl"]; - _authMethod = ARTAuthMethodToken; + _method = ARTAuthMethodToken; } else if (options.authCallback) { [self.logger debug:@"ARTAuth: setting up auth method Token with authCallback"]; - _authMethod = ARTAuthMethodToken; + _method = ARTAuthMethodToken; } else { [NSException raise:@"ARTAuthException" format:@"Could not setup authentication method with given options."]; } @@ -444,17 +163,17 @@ - (void)requestToken:(ARTAuthTokenRequest *)tokenRequest callback:(void (^)(ARTA - (void)authorise:(ARTAuthTokenParams *)tokenParams options:(ARTAuthOptions *)options force:(BOOL)force callback:(void (^)(ARTAuthTokenDetails *, NSError *))callback { - if (!force && self.currentToken && [self.currentToken.expires timeIntervalSinceNow] > 0) { + if (!force && self.tokenDetails && [self.tokenDetails.expires timeIntervalSinceNow] > 0) { [self.logger verbose:@"ARTAuth authorise not forced and current token is not expired yet, reuse current token."]; - callback(self.currentToken, nil); + callback(self.tokenDetails, nil); } else { [self.logger verbose:@"ARTAuth authorise requesting new token."]; [self requestToken:tokenParams withOptions:options callback:^(ARTAuthTokenDetails *tokenDetails, NSError *error) { if (error) { callback(nil, error); } else { - _currentToken = tokenDetails; - _authMethod = ARTAuthMethodToken; + _tokenDetails = tokenDetails; + _method = ARTAuthMethodToken; callback(tokenDetails, nil); } }]; diff --git a/ably-ios/ARTAuthOptions.h b/ably-ios/ARTAuthOptions.h new file mode 100644 index 000000000..1c74e5975 --- /dev/null +++ b/ably-ios/ARTAuthOptions.h @@ -0,0 +1,83 @@ +// +// ARTAuthOptions.h +// ably-ios +// +// Created by Ricardo Pereira on 05/10/2015. +// Copyright (c) 2015 Ably. All rights reserved. +// + +#import +#import + +@class ARTAuthTokenDetails; + +NS_ASSUME_NONNULL_BEGIN + +@interface ARTAuthOptions : NSObject + +/** + Full Ably key string as obtained from dashboard. + */ +@property (nonatomic, copy, nullable) NSString *key; + +/** + An authentication token issued for this application against a specific key and `TokenParams`. + */ +@property (nonatomic, copy, nullable) NSString *token; + +/** + An authentication token issued for this application against a specific key and `TokenParams`. + */ +@property (nonatomic, strong, nullable) ARTAuthTokenDetails *tokenDetails; + +/** + A callback to call to obtain a signed token request. + + This enables a client to obtain token requests from another entity, so tokens can be renewed without the client requiring access to keys. + */ +@property (nonatomic, copy, nullable) ARTAuthCallback authCallback; + +/** + A URL to queryto obtain a signed token request. + + This enables a client to obtain token requests from another entity, so tokens can be renewed without the client requiring access to keys. + */ +@property (nonatomic, strong, nullable) NSURL *authUrl; + +/** + The HTTP verb to be used when a request is made by the library to the authUrl. Defaults to GET, supports GET and POST. + */ +@property (nonatomic, copy, null_resettable) NSString *authMethod; + +/** + Headers to be included in any request made by the library to the authURL. + */ +@property (nonatomic, copy, nullable) NSDictionary *authHeaders; //X7: NSDictionary *authHeaders; + +/** + Additional params to be included in any request made by the library to the authUrl, either as query params in the case of GET or in the body in the case of POST. + */ +@property (nonatomic, copy, nullable) NSArray *authParams; //X7: NSArray *authParams; + +/** + This may be set in instances that the library is to sign token requests based on a given key. + If true, the library will query the Ably system for the current time instead of relying on a locally-available time of day. + */ +@property (nonatomic, assign, nonatomic) BOOL queryTime; + +@property (nonatomic, assign) BOOL useTokenAuth; + +- (instancetype)init; +- (instancetype)initWithKey:(NSString *)key; +- (instancetype)initDefaults; + +- (NSString *)description; + +- (ARTAuthOptions *)mergeWith:(ARTAuthOptions *)precedenceOptions; + +- (BOOL)isMethodGET; +- (BOOL)isMethodPOST; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ably-ios/ARTAuthOptions.m b/ably-ios/ARTAuthOptions.m new file mode 100644 index 000000000..f0d56ce25 --- /dev/null +++ b/ably-ios/ARTAuthOptions.m @@ -0,0 +1,115 @@ +// +// ARTAuthOptions.m +// ably-ios +// +// Created by Ricardo Pereira on 05/10/2015. +// Copyright (c) 2015 Ably. All rights reserved. +// + +#import "ARTAuthOptions.h" + +#import "ARTAuthTokenDetails.h" + +//X7: NSArray +static NSArray *decomposeKey(NSString *key) { + return [key componentsSeparatedByString:@":"]; +} + +@implementation ARTAuthOptions + +NSString *const ARTAuthOptionsMethodDefault = @"GET"; + +- (instancetype)init { + self = [super init]; + if (self) { + return [self initDefaults]; + } + return self; +} + +- (instancetype)initWithKey:(NSString *)key { + self = [super init]; + if (self) { + if (key != nil && decomposeKey(key).count != 2) { + [NSException raise:@"Invalid key" format:@"%@ should be of the form :", key]; + } + else if (key != nil) { + _key = [key copy]; + } + return [self initDefaults]; + } + return self; +} + +- (instancetype)initDefaults { + _authMethod = ARTAuthOptionsMethodDefault; + return self; +} + +- (id)copyWithZone:(NSZone *)zone { + ARTAuthOptions *options = [[[self class] allocWithZone:zone] init]; + + options.key = self.key; + options.token = self.token; + options.useTokenAuth = self.useTokenAuth; + options.authCallback = self.authCallback; + options.authUrl = self.authUrl; + options.authMethod = self.authMethod; + options.authHeaders = self.authHeaders; + options.authParams = self.authParams; + options.queryTime = self.queryTime; + + return options; +} + +- (NSString *)description { + return [NSString stringWithFormat: @"ARTAuthOptions: key=%@ token=%@ authUrl=%@ authMethod=%@ hasAuthCallback=%d", + self.key, self.token, self.authUrl, self.authMethod, self.authCallback != nil]; +} + +- (NSString *)token { + return self.tokenDetails.token; +} + +- (void)setToken:(NSString *)token { + self.tokenDetails = [[ARTAuthTokenDetails alloc] initWithToken:token]; +} + +- (void)setAuthMethod:(NSString *)authMethod { + if (authMethod == nil || authMethod.length == 0) { + authMethod = ARTAuthOptionsMethodDefault; + } + + _authMethod = [authMethod copy]; +} + +- (ARTAuthOptions *)mergeWith:(ARTAuthOptions *)precedenceOptions { + ARTAuthOptions *merged = [self copy]; + + if (precedenceOptions.key) + merged.key = precedenceOptions.key; + if (precedenceOptions.authCallback) + merged.authCallback = precedenceOptions.authCallback; + if (precedenceOptions.authUrl) + merged.authUrl = precedenceOptions.authUrl; + if (precedenceOptions.authMethod) + merged.authMethod = precedenceOptions.authMethod; + if (precedenceOptions.authHeaders) + merged.authHeaders = precedenceOptions.authHeaders; + if (precedenceOptions.authParams) + merged.authParams = precedenceOptions.authParams; + if (precedenceOptions.queryTime) + merged.queryTime = precedenceOptions.queryTime; + + return merged; +} + +- (BOOL)isMethodPOST { + return [_authMethod isEqualToString:@"POST"]; +} + +- (BOOL)isMethodGET { + return [_authMethod isEqualToString:@"GET"]; +} + +@end diff --git a/ably-ios/ARTAuthTokenDetails.h b/ably-ios/ARTAuthTokenDetails.h new file mode 100644 index 000000000..96f58120d --- /dev/null +++ b/ably-ios/ARTAuthTokenDetails.h @@ -0,0 +1,29 @@ +// +// ARTAuthTokenDetails.h +// ably-ios +// +// Created by Ricardo Pereira on 05/10/2015. +// Copyright (c) 2015 Ably. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface ARTAuthTokenDetails : NSObject + +@property (nonatomic, readonly, copy) NSString *token; +@property (nonatomic, readonly, strong, nullable) NSDate *expires; +@property (nonatomic, readonly, strong, nullable) NSDate *issued; +@property (nonatomic, readonly, copy, nullable) NSString *capability; +@property (nonatomic, readonly, copy, nullable) NSString *clientId; + +- (instancetype)init UNAVAILABLE_ATTRIBUTE; + +- (instancetype)initWithToken:(NSString *)token; + +- (instancetype)initWithToken:(NSString *)token expires:(NSDate *)expires issued:(NSDate *)issued capability:(NSString *)capability clientId:(NSString *)clientId; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ably-ios/ARTAuthTokenDetails.m b/ably-ios/ARTAuthTokenDetails.m new file mode 100644 index 000000000..d5d0e967b --- /dev/null +++ b/ably-ios/ARTAuthTokenDetails.m @@ -0,0 +1,33 @@ +// +// ARTAuthTokenDetails.m +// ably-ios +// +// Created by Ricardo Pereira on 05/10/2015. +// Copyright (c) 2015 Ably. All rights reserved. +// + +#import "ARTAuthTokenDetails.h" + +@implementation ARTAuthTokenDetails + +- (instancetype)initWithToken:(NSString *)token expires:(NSDate *)expires issued:(NSDate *)issued capability:(NSString *)capability clientId:(NSString *)clientId { + if (self = [super init]) { + _token = [token copy]; + _expires = expires; + _issued = issued; + _capability = [capability copy]; + _clientId = [clientId copy]; + } + + return self; +} + +- (instancetype)initWithToken:(NSString *)token { + if (self = [super init]) { + _token = [token copy]; + } + + return self; +} + +@end diff --git a/ably-ios/ARTAuthTokenParams.h b/ably-ios/ARTAuthTokenParams.h new file mode 100644 index 000000000..b8f71b55f --- /dev/null +++ b/ably-ios/ARTAuthTokenParams.h @@ -0,0 +1,36 @@ +// +// ARTAuthTokenParams.h +// ably-ios +// +// Created by Ricardo Pereira on 05/10/2015. +// Copyright (c) 2015 Ably. All rights reserved. +// + +#import + +@class ARTAuthTokenRequest; + +NS_ASSUME_NONNULL_BEGIN + +@interface ARTAuthTokenParams : NSObject + +@property (nonatomic, assign) NSTimeInterval ttl; +@property (nonatomic, copy) NSString *capability; +@property (nonatomic, copy) NSString *clientId; +@property (nonatomic, strong, null_resettable) NSDate *timestamp; + +- (instancetype)init; + +- (NSMutableArray *)toArray; //X7: NSArray +- (NSArray *)toArrayWithUnion:(NSArray *)items; //X7: NSArray +- (NSDictionary *)toDictionaryWithUnion:(NSArray *)items; + +@end + +@interface ARTAuthTokenParams(SignedRequest) + +- (ARTAuthTokenRequest *)sign:(NSString *)key; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ably-ios/ARTAuthTokenParams.m b/ably-ios/ARTAuthTokenParams.m new file mode 100644 index 000000000..4c9d1918d --- /dev/null +++ b/ably-ios/ARTAuthTokenParams.m @@ -0,0 +1,149 @@ +// +// ARTAuthTokenParams.h +// ably-ios +// +// Created by Ricardo Pereira on 05/10/2015. +// Copyright (c) 2015 Ably. All rights reserved. +// + +#import "ARTAuthTokenParams.h" + +#include +#include + +#import "ARTEncoder.h" +#import "ARTPayload.h" +#import "ARTAuthTokenRequest.h" + +//X7: NSArray +static NSArray *decomposeKey(NSString *key) { + return [key componentsSeparatedByString:@":"]; +} + +@implementation ARTAuthTokenParams + +- (instancetype)init { + if (self = [super init]) { + _ttl = 60 * 60; + _timestamp = [NSDate date]; + _capability = @"{ \"*\": [ \"*\" ] }"; // allow all + } + + return self; +} + +- (void)setTimestamp:(NSDate *)timestamp { + if (timestamp == nil) { + timestamp = [NSDate date]; + } + + _timestamp = timestamp; +} + +- (NSMutableArray *)toArray { + NSMutableArray *params = [[NSMutableArray alloc] init]; + + if (self.clientId) + [params addObject:[NSURLQueryItem queryItemWithName:@"clientId" value:self.clientId]]; + if (self.ttl > 0) + [params addObject:[NSURLQueryItem queryItemWithName:@"ttl" value:[NSString stringWithFormat:@"%f", self.ttl]]]; + if (self.capability) + [params addObject:[NSURLQueryItem queryItemWithName:@"capability" value:self.capability]]; + if (self.timestamp > 0) + [params addObject:[NSURLQueryItem queryItemWithName:@"timestamp" value:[NSString stringWithFormat:@"%f", self.timestamp.timeIntervalSince1970]]]; + + return params; +} + +- (NSMutableDictionary *)toDictionary { + NSMutableDictionary *params = [[NSMutableDictionary alloc] init]; + + if (self.clientId) + params[@"clientId"] = self.clientId; + if (self.ttl > 0) + params[@"ttl"] = [NSString stringWithFormat:@"%f", self.ttl]; + if (self.capability) + params[@"capability"] = self.capability; + if (self.timestamp > 0) + params[@"timestamp"] = [NSString stringWithFormat:@"%f", self.timestamp.timeIntervalSince1970]; + + return params; +} + +- (NSArray *)toArrayWithUnion:(NSArray *)items { + NSMutableArray *tokenParams = [self toArray]; + BOOL add = YES; + + for (NSURLQueryItem *item in items) { + for (NSURLQueryItem *param in tokenParams) { + // Check if exist + if ([param.name isEqualToString:item.name]) { + add = NO; + break; + } + } + if (add) { + [tokenParams addObject:item]; + } + add = YES; + } + + return tokenParams; +} + +- (NSDictionary *)toDictionaryWithUnion:(NSArray *)items { + NSMutableDictionary *tokenParams = [self toDictionary]; + BOOL add = YES; + + for (NSURLQueryItem *item in items) { + for (NSString *key in tokenParams.allKeys) { + // Check if exist + if ([key isEqualToString:item.name]) { + add = NO; + break; + } + } + if (add) { + tokenParams[item.name] = item.value; + } + add = YES; + } + + return tokenParams; +} + +static NSString *generateNonce() { + // Generate two random numbers up to 8 digits long and concatenate them to produce a 16 digit random number + NSUInteger r1 = arc4random_uniform(100000000); + NSUInteger r2 = arc4random_uniform(100000000); + return [NSString stringWithFormat:@"%08lu%08lu", (long)r1, (long)r2]; +} + +static NSString *hmacForDataAndKey(NSData *data, NSData *key) { + const void *cKey = [key bytes]; + const void *cData = [data bytes]; + size_t keyLen = [key length]; + size_t dataLen = [data length]; + + unsigned char hmac[CC_SHA256_DIGEST_LENGTH]; + + CCHmac(kCCHmacAlgSHA256, cKey, keyLen, cData, dataLen, hmac); + NSData *mac = [[NSData alloc] initWithBytes:hmac length:sizeof(hmac)]; + NSString *str = [ARTBase64PayloadEncoder toBase64:mac]; + return str; +} + +- (ARTAuthTokenRequest *)sign:(NSString *)key { + //X7: NSArray + NSArray *keyComponents = decomposeKey(key); + NSString *keyName = keyComponents[0]; + NSString *keySecret = keyComponents[1]; + NSString *nonce = generateNonce(); + + NSString *signText = [NSString stringWithFormat:@"%@\n%lld\n%@\n%@\n%lld\n%@\n", keyName, (int64_t)(self.ttl * 1000), self.capability, self.clientId, (int64_t)(self.timestamp.timeIntervalSince1970 * 1000), nonce]; + NSString *mac = hmacForDataAndKey([signText dataUsingEncoding:NSUTF8StringEncoding], [keySecret dataUsingEncoding:NSUTF8StringEncoding]); + + return [[ARTAuthTokenRequest alloc] initWithTokenParams:self keyName:keyName nonce:nonce mac:mac]; +} + +@end diff --git a/ably-ios/ARTAuthTokenRequest.h b/ably-ios/ARTAuthTokenRequest.h new file mode 100644 index 000000000..0917ff098 --- /dev/null +++ b/ably-ios/ARTAuthTokenRequest.h @@ -0,0 +1,23 @@ +// +// ARTAuthTokenRequest.h +// ably-ios +// +// Created by Ricardo Pereira on 05/10/2015. +// Copyright (c) 2015 Ably. All rights reserved. +// + +#import + +#import "ARTAuthTokenParams.h" + +@interface ARTAuthTokenRequest : ARTAuthTokenParams + +@property (nonatomic, readonly, copy) NSString *keyName; +@property (nonatomic, readonly, copy) NSString *nonce; +@property (nonatomic, readonly, copy) NSString *mac; + +- (instancetype)init UNAVAILABLE_ATTRIBUTE; + +- (instancetype)initWithTokenParams:(ARTAuthTokenParams *)tokenParams keyName:(NSString *)keyName nonce:(NSString *)nonce mac:(NSString *)mac; + +@end diff --git a/ably-ios/ARTAuthTokenRequest.m b/ably-ios/ARTAuthTokenRequest.m new file mode 100644 index 000000000..6af3c9270 --- /dev/null +++ b/ably-ios/ARTAuthTokenRequest.m @@ -0,0 +1,35 @@ +// +// ARTAuthTokenRequest.m +// ably-ios +// +// Created by Ricardo Pereira on 05/10/2015. +// Copyright (c) 2015 Ably. All rights reserved. +// + +#import "ARTAuthTokenRequest.h" + +#import "ARTAuthTokenParams.h" + +@implementation ARTAuthTokenRequest + +@dynamic timestamp; + +- (instancetype)initWithTokenParams:(ARTAuthTokenParams *)tokenParams keyName:(NSString *)keyName nonce:(NSString *)nonce mac:(NSString *)mac { + if (self = [super init]) { + self.ttl = tokenParams.ttl; + self.capability = tokenParams.capability; + self.clientId = tokenParams.clientId; + self.timestamp = tokenParams.timestamp; + _keyName = [keyName copy]; + _nonce = [nonce copy]; + _mac = [mac copy]; + } + + return self; +} + +- (NSDictionary *)asDictionary { + return nil; +} + +@end diff --git a/ably-ios/ARTClientOptions.h b/ably-ios/ARTClientOptions.h index 8c92042bb..b2da368aa 100644 --- a/ably-ios/ARTClientOptions.h +++ b/ably-ios/ARTClientOptions.h @@ -8,13 +8,12 @@ #import -@class ARTAuthOptions; +#import "ARTAuthOptions.h" NS_ASSUME_NONNULL_BEGIN -@interface ARTClientOptions : NSObject +@interface ARTClientOptions : ARTAuthOptions -@property (nonnull, strong, nonatomic) ARTAuthOptions *authOptions; @property (readwrite, strong, nonatomic) NSString *clientId; @property (readonly, getter=getRestHost) NSString *restHost; @@ -33,9 +32,6 @@ NS_ASSUME_NONNULL_BEGIN @property (nullable, readwrite, copy, nonatomic) NSString *resumeKey; @property (nullable, readwrite, copy, nonatomic) NSString *recover; -- (instancetype)init; -- (instancetype)initWithKey:(NSString *)key; - - (bool)isFallbackPermitted; + (NSURL*)restUrl:(NSString *)host port:(int)port tls:(BOOL)tls; diff --git a/ably-ios/ARTClientOptions.m b/ably-ios/ARTClientOptions.m index e4ddf03f8..ee707446d 100644 --- a/ably-ios/ARTClientOptions.m +++ b/ably-ios/ARTClientOptions.m @@ -9,7 +9,6 @@ #import "ARTClientOptions.h" #import "ARTDefault.h" -#import "ARTAuth.h" @interface ARTClientOptions () @@ -19,40 +18,16 @@ - (instancetype)initDefaults; @implementation ARTClientOptions -- (instancetype)init { - self = [super init]; - if (self) { - _authOptions = [[ARTAuthOptions alloc] init]; - if (!_authOptions) { - self = nil; - } - self = [self initDefaults]; - } - return self; -} - - (instancetype)initWithKey:(NSString *)key { - self = [super init]; + self = [super initWithKey:key]; if (self) { - _authOptions = [[ARTAuthOptions alloc] initWithKey:key]; - - if (!_authOptions) { - self = nil; - } self = [self initDefaults]; } return self; } -- (NSString*)getRestHost { - return _environment ? [NSString stringWithFormat:@"%@-%@", _environment, [ARTDefault restHost]] : [ARTDefault restHost]; -} - -- (NSString*)getRealtimeHost { - return _environment ? [NSString stringWithFormat:@"%@-%@", _environment, [ARTDefault realtimeHost]] : [ARTDefault realtimeHost]; -} - - (instancetype)initDefaults { + self = [super initDefaults]; _clientId = nil; _restPort = [ARTDefault TLSPort]; _realtimePort = [ARTDefault TLSPort]; @@ -68,12 +43,12 @@ - (instancetype)initDefaults { return self; } -+ (instancetype)options { - return [[ARTClientOptions alloc] init]; +- (NSString*)getRestHost { + return _environment ? [NSString stringWithFormat:@"%@-%@", _environment, [ARTDefault restHost]] : [ARTDefault restHost]; } -+ (instancetype)optionsWithKey:(NSString *)key { - return [[ARTClientOptions alloc] initWithKey:key]; +- (NSString*)getRealtimeHost { + return _environment ? [NSString stringWithFormat:@"%@-%@", _environment, [ARTDefault realtimeHost]] : [ARTDefault realtimeHost]; } + (NSURL*)restUrl:(NSString *)host port:(int)port tls:(BOOL)tls { @@ -94,12 +69,7 @@ - (bool)isFallbackPermitted { } - (id)copyWithZone:(NSZone *)zone { - ARTClientOptions *options = [[ARTClientOptions allocWithZone:zone] init]; - - options.authOptions = [self.authOptions copy]; - if (!options.authOptions) { - return nil; - } + ARTClientOptions *options = [super copyWithZone:zone]; options.clientId = self.clientId; options.restPort = self.restPort; diff --git a/ably-ios/ARTHttp.m b/ably-ios/ARTHttp.m index 36884dfc0..7b3aec00a 100644 --- a/ably-ios/ARTHttp.m +++ b/ably-ios/ARTHttp.m @@ -24,6 +24,9 @@ + (instancetype)requestHandleWithDataTask:(NSURLSessionDataTask *)dataTask; @end + +#pragma mark - ARTHttpRequest + @implementation ARTHttpRequest - (instancetype)initWithMethod:(NSString *)method url:(NSURL *)url headers:(NSDictionary *)headers body:(NSData *)body { @@ -47,12 +50,14 @@ - (ARTHttpRequest *)requestWithRelativeUrl:(NSString *)relUrl { @end + +#pragma mark - ARTHttpResponse + @implementation ARTHttpResponse - (instancetype)init { self = [super init]; if (self) { - _status = 0; _error = nil; _headers = nil; @@ -117,6 +122,9 @@ - (NSDictionary *)links { @end + +#pragma mark - ARTHttpRequestHandle + @implementation ARTHttpRequestHandle - (instancetype)initWithDataTask:(NSURLSessionDataTask *)dataTask { @@ -137,6 +145,9 @@ - (void)cancel { @end + +#pragma mark - ARTHttp + @implementation ARTHttp - (instancetype)init { @@ -232,7 +243,6 @@ - (void)executeRequest:(NSMutableURLRequest *)request callback:(void (^)(NSHTTPU [task resume]; return [ARTHttpRequestHandle requestHandleWithDataTask:task]; - } + (NSDictionary *)getErrorDictionary { diff --git a/ably-ios/ARTJsonEncoder.m b/ably-ios/ARTJsonEncoder.m index bb9c09586..525593505 100644 --- a/ably-ios/ARTJsonEncoder.m +++ b/ably-ios/ARTJsonEncoder.m @@ -16,9 +16,9 @@ #import "ARTNSDictionary+ARTDictionaryUtil.h" #import "ARTNSDate+ARTUtil.h" #import "ARTLog.h" -#import "ARTAuth.h" #import "ARTHttp.h" #import "ARTStatus.h" +#import "ARTAuthTokenDetails.h" @interface ARTJsonEncoder () diff --git a/ably-ios/ARTRest.m b/ably-ios/ARTRest.m index 3efe69fd4..90feca7f3 100644 --- a/ably-ios/ARTRest.m +++ b/ably-ios/ARTRest.m @@ -66,10 +66,9 @@ - (instancetype)initWithLogger:(ARTLog *)logger andOptions:(ARTClientOptions *)o id defaultEncoder = [[ARTJsonEncoder alloc] init]; _encoders = @{ [defaultEncoder mimeType]: defaultEncoder }; _defaultEncoding = [defaultEncoder mimeType]; - _fallbackCount = 0; - _auth = [[ARTAuth alloc] init:self withOptions:options.authOptions]; + _auth = [[ARTAuth alloc] init:self withOptions:options]; } return self; } diff --git a/ably-ios/ARTTypes.h b/ably-ios/ARTTypes.h index f5ff6d4df..fe7cb0b6f 100644 --- a/ably-ios/ARTTypes.h +++ b/ably-ios/ARTTypes.h @@ -12,6 +12,8 @@ @class ARTHttpResponse; @class ARTMessage; @class ARTPresenceMessage; +@class ARTAuthTokenParams; +@class ARTAuthTokenRequest; typedef NS_ENUM(NSUInteger, ARTRealtimeConnectionState) { ARTRealtimeInitialized, @@ -34,19 +36,23 @@ typedef NS_ENUM(NSUInteger, ARTRealtimeChannelState) { ARTRealtimeChannelFailed }; +NS_ASSUME_NONNULL_BEGIN + typedef void (^ARTRealtimeChannelMessageCb)(ARTMessage *); typedef void (^ARTRealtimeChannelStateCb)(ARTRealtimeChannelState, ARTStatus *); +typedef void (^ARTRealtimeConnectionStateCb)(ARTRealtimeConnectionState state); + +typedef void (^ARTRealtimeChannelPresenceCb)(ARTPresenceMessage *); + typedef void (^ARTStatusCallback)(ARTStatus *status); typedef void (^ARTHttpCb)(ARTHttpResponse *response); typedef void (^ARTErrorCallback)(NSError *error); -typedef void (^ARTRealtimeConnectionStateCb)(ARTRealtimeConnectionState state); - -typedef void (^ARTRealtimeChannelPresenceCb)(ARTPresenceMessage *); +typedef void (^ARTAuthCallback)(ARTAuthTokenParams *tokenParams, void(^callback)(ARTAuthTokenRequest *__nullable tokenRequest, NSError *__nullable error)); // FIXME: @protocol ARTCancellable @@ -67,3 +73,5 @@ typedef void (^ARTRealtimeChannelPresenceCb)(ARTPresenceMessage *); - (void)cancel; @end + +NS_ASSUME_NONNULL_END diff --git a/ably-ios/ably.h b/ably-ios/ably.h index eb2dba1fe..84cc2a8f8 100644 --- a/ably-ios/ably.h +++ b/ably-ios/ably.h @@ -21,6 +21,10 @@ FOUNDATION_EXPORT const unsigned char ablyVersionString[]; #import #import #import +#import +#import +#import +#import #import #import #import diff --git a/ably.xcodeproj/project.pbxproj b/ably.xcodeproj/project.pbxproj index 340b5e0a3..0e8c4f295 100644 --- a/ably.xcodeproj/project.pbxproj +++ b/ably.xcodeproj/project.pbxproj @@ -128,6 +128,14 @@ D7C1B8771BBEA81A0087B55F /* Auth.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C1B8761BBEA81A0087B55F /* Auth.swift */; }; D7C1B8791BBF5F810087B55F /* ARTAuth+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = D7C1B8781BBF5F460087B55F /* ARTAuth+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; D7D8F81E1BC28FF0009718F2 /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D8F81D1BC28FF0009718F2 /* Helpers.swift */; }; + D7D8F8211BC2BE16009718F2 /* ARTAuthOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = D7D8F81F1BC2BE15009718F2 /* ARTAuthOptions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D7D8F8221BC2BE16009718F2 /* ARTAuthOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = D7D8F8201BC2BE15009718F2 /* ARTAuthOptions.m */; }; + D7D8F8251BC2C691009718F2 /* ARTAuthTokenDetails.h in Headers */ = {isa = PBXBuildFile; fileRef = D7D8F8231BC2C691009718F2 /* ARTAuthTokenDetails.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D7D8F8261BC2C691009718F2 /* ARTAuthTokenDetails.m in Sources */ = {isa = PBXBuildFile; fileRef = D7D8F8241BC2C691009718F2 /* ARTAuthTokenDetails.m */; }; + D7D8F82B1BC2C706009718F2 /* ARTAuthTokenRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = D7D8F8271BC2C706009718F2 /* ARTAuthTokenRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D7D8F82C1BC2C706009718F2 /* ARTAuthTokenRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = D7D8F8281BC2C706009718F2 /* ARTAuthTokenRequest.m */; }; + D7D8F82D1BC2C706009718F2 /* ARTAuthTokenParams.h in Headers */ = {isa = PBXBuildFile; fileRef = D7D8F8291BC2C706009718F2 /* ARTAuthTokenParams.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D7D8F82E1BC2C706009718F2 /* ARTAuthTokenParams.m in Sources */ = {isa = PBXBuildFile; fileRef = D7D8F82A1BC2C706009718F2 /* ARTAuthTokenParams.m */; }; FC78549C1BCD5688539CCBE4 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FDA6E099E4BC04FC80F845C0 /* libPods.a */; }; /* End PBXBuildFile section */ @@ -304,6 +312,14 @@ D7C1B8761BBEA81A0087B55F /* Auth.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Auth.swift; sourceTree = ""; }; D7C1B8781BBF5F460087B55F /* ARTAuth+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ARTAuth+Private.h"; sourceTree = ""; }; D7D8F81D1BC28FF0009718F2 /* Helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Helpers.swift; sourceTree = ""; }; + D7D8F81F1BC2BE15009718F2 /* ARTAuthOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTAuthOptions.h; sourceTree = ""; }; + D7D8F8201BC2BE15009718F2 /* ARTAuthOptions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTAuthOptions.m; sourceTree = ""; }; + D7D8F8231BC2C691009718F2 /* ARTAuthTokenDetails.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTAuthTokenDetails.h; sourceTree = ""; }; + D7D8F8241BC2C691009718F2 /* ARTAuthTokenDetails.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTAuthTokenDetails.m; sourceTree = ""; }; + D7D8F8271BC2C706009718F2 /* ARTAuthTokenRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTAuthTokenRequest.h; sourceTree = ""; }; + D7D8F8281BC2C706009718F2 /* ARTAuthTokenRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTAuthTokenRequest.m; sourceTree = ""; }; + D7D8F8291BC2C706009718F2 /* ARTAuthTokenParams.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTAuthTokenParams.h; sourceTree = ""; }; + D7D8F82A1BC2C706009718F2 /* ARTAuthTokenParams.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTAuthTokenParams.m; sourceTree = ""; }; DF8DF755839D59574B7B795F /* Pods-ablySpec.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ablySpec.release.xcconfig"; path = "Pods/Target Support Files/Pods-ablySpec/Pods-ablySpec.release.xcconfig"; sourceTree = ""; }; FDA6E099E4BC04FC80F845C0 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -525,6 +541,14 @@ D746AE331BBC29FF003ECEF8 /* Types */ = { isa = PBXGroup; children = ( + D7D8F81F1BC2BE15009718F2 /* ARTAuthOptions.h */, + D7D8F8201BC2BE15009718F2 /* ARTAuthOptions.m */, + D7D8F8271BC2C706009718F2 /* ARTAuthTokenRequest.h */, + D7D8F8281BC2C706009718F2 /* ARTAuthTokenRequest.m */, + D7D8F8291BC2C706009718F2 /* ARTAuthTokenParams.h */, + D7D8F82A1BC2C706009718F2 /* ARTAuthTokenParams.m */, + D7D8F8231BC2C691009718F2 /* ARTAuthTokenDetails.h */, + D7D8F8241BC2C691009718F2 /* ARTAuthTokenDetails.m */, 961343D61A42E0B7006DC822 /* ARTClientOptions.h */, 961343D71A42E0B7006DC822 /* ARTClientOptions.m */, D746AE201BBB60EE003ECEF8 /* ARTChannel.h */, @@ -630,8 +654,10 @@ D746AE471BBD6FE9003ECEF8 /* ARTQueuedMessage.h in Headers */, 1C2B0FFD1B136A6D00E3633C /* ARTPresenceMap.h in Headers */, 1CD8DC9F1B1C7315007EAF36 /* ARTDefault.h in Headers */, + D7D8F8211BC2BE16009718F2 /* ARTAuthOptions.h in Headers */, D746AE401BBC5B14003ECEF8 /* ARTEventEmitter.h in Headers */, D746AE531BBD85C5003ECEF8 /* ARTChannelCollection.h in Headers */, + D7D8F82D1BC2C706009718F2 /* ARTAuthTokenParams.h in Headers */, 1C578E1F1B3435CA00EF46EC /* ARTFallback.h in Headers */, 96E408471A3895E800087F77 /* ARTWebSocketTransport.h in Headers */, 96E4083F1A3892C700087F77 /* ARTRealtimeTransport.h in Headers */, @@ -653,6 +679,8 @@ 1C6C18A31ADFDAB100AB79E4 /* ARTLog.h in Headers */, 96A507AD1A3780F60077CDF8 /* ARTJsonEncoder.h in Headers */, 96A507951A370F860077CDF8 /* ARTStats.h in Headers */, + D7D8F8251BC2C691009718F2 /* ARTAuthTokenDetails.h in Headers */, + D7D8F82B1BC2C706009718F2 /* ARTAuthTokenRequest.h in Headers */, 960D07931A45F1D800ED8C8C /* ARTCrypto.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -896,9 +924,12 @@ 96A507A21A377AA50077CDF8 /* ARTPresenceMessage.m in Sources */, D746AE541BBD85C5003ECEF8 /* ARTChannelCollection.m in Sources */, 96BF61541A35B39C004CF2B3 /* ARTRest.m in Sources */, + D7D8F82C1BC2C706009718F2 /* ARTAuthTokenRequest.m in Sources */, + D7D8F8261BC2C691009718F2 /* ARTAuthTokenDetails.m in Sources */, 961343E91A432E7C006DC822 /* ARTPayload.m in Sources */, 1C2B0FFE1B136A6D00E3633C /* ARTPresenceMap.m in Sources */, 960D07941A45F1D800ED8C8C /* ARTCrypto.m in Sources */, + D7D8F8221BC2BE16009718F2 /* ARTAuthOptions.m in Sources */, 1C55427D1B148306003068DB /* ARTStatus.m in Sources */, 96BF61591A35B52C004CF2B3 /* ARTHttp.m in Sources */, 1C578E201B3435CA00EF46EC /* ARTFallback.m in Sources */, @@ -907,6 +938,7 @@ D746AE1F1BBB5207003ECEF8 /* ARTDataQuery.m in Sources */, 961343D91A42E0B7006DC822 /* ARTClientOptions.m in Sources */, 96BF615F1A35C1C8004CF2B3 /* ARTTypes.m in Sources */, + D7D8F82E1BC2C706009718F2 /* ARTAuthTokenParams.m in Sources */, D746AE411BBC5B14003ECEF8 /* ARTEventEmitter.m in Sources */, 96A507AE1A3780F60077CDF8 /* ARTJsonEncoder.m in Sources */, 96A507961A370F860077CDF8 /* ARTStats.m in Sources */, diff --git a/ably.xcworkspace/contents.xcworkspacedata b/ably.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..1ff071305 --- /dev/null +++ b/ably.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/ablySpec/Auth.swift b/ablySpec/Auth.swift index 37201a6b0..f2445eb79 100644 --- a/ablySpec/Auth.swift +++ b/ablySpec/Auth.swift @@ -22,7 +22,7 @@ class Auth : QuickSpec { describe("Basic") { // RSA1 - it("should work over HTTPS only") { + fit("should work over HTTPS only") { let clientOptions = AblyTests.setupOptions(AblyTests.jsonRestOptions) clientOptions.tls = false @@ -36,7 +36,7 @@ class Auth : QuickSpec { publishTestMessage(client, failOnError: false) - let key64 = NSString(string: "\(client.options.authOptions.key)") + let key64 = NSString(string: "\(client.options.key)") .dataUsingEncoding(NSUTF8StringEncoding)? .base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0)) let Authorization = "Basic \(key64!)" @@ -49,7 +49,7 @@ class Auth : QuickSpec { it("should be default when an API key is set") { let client = ARTRest(options: ARTClientOptions(key: "fake:key")) - expect(client.auth.authMethod).to(equal(ARTAuthMethod.Basic)) + expect(client.auth.method).to(equal(ARTAuthMethod.Basic)) } } @@ -57,15 +57,15 @@ class Auth : QuickSpec { it("should send the token in the Authorization header") { let options = ARTClientOptions() - options.authOptions.token = getTestToken() + options.token = getTestToken() let client = ARTRest(options: options) client.httpExecutor = mockExecutor publishTestMessage(client, failOnError: false) - expect(client.options.authOptions.token).toNot(beNil()) + expect(client.options.token).toNot(beNil()) - if let currentToken = client.options.authOptions.token { + if let currentToken = client.options.token { let token64 = NSString(string: currentToken) .dataUsingEncoding(NSUTF8StringEncoding)? .base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0)) @@ -88,11 +88,11 @@ class Auth : QuickSpec { for (caseName, caseSetter) in cases { it("should be default when \(caseName) is set") { let options = ARTClientOptions() - caseSetter(options.authOptions) + caseSetter(options) let client = ARTRest(options: options) - expect(client.auth.authMethod).to(equal(ARTAuthMethod.Token)) + expect(client.auth.method).to(equal(ARTAuthMethod.Token)) } } } @@ -104,7 +104,7 @@ class Auth : QuickSpec { // RSA8e it("sould supersede matching client library configured params and options") { let clientOptions = ARTClientOptions() - clientOptions.authOptions.authUrl = NSURL(string: "http://auth.ably.io") + clientOptions.authUrl = NSURL(string: "http://auth.ably.io") let rest = ARTRest(options: clientOptions) @@ -132,38 +132,41 @@ class Auth : QuickSpec { // RSA8c1a it("should be added to the URL when auth method is GET") { let clientOptions = ARTClientOptions() - clientOptions.authOptions.authUrl = NSURL(string: "http://auth.ably.io") + clientOptions.authUrl = NSURL(string: "http://auth.ably.io") let tokenParams = ARTAuthTokenParams() let rest = ARTRest(options: clientOptions) - let url = rest.auth.buildURL(clientOptions.authOptions, withParams: tokenParams) + let url = rest.auth.buildURL(clientOptions, withParams: tokenParams) expect(url) == NSURL(string: "http://auth.ably.io/") } // RSA8c1b - fit("should added on the body request when auth method is POST") { + it("should added on the body request when auth method is POST") { let clientOptions = ARTClientOptions() - clientOptions.authOptions.authUrl = NSURL(string: "http://auth.ably.io") - clientOptions.authOptions.authMethod = "POST" + clientOptions.authUrl = NSURL(string: "http://auth.ably.io") + clientOptions.authMethod = "POST" let tokenParams = ARTAuthTokenParams() let rest = ARTRest(options: clientOptions) - let request = rest.auth.buildRequest(clientOptions.authOptions, withParams: tokenParams) + let request = rest.auth.buildRequest(clientOptions, withParams: tokenParams) - let expectedJSON = ["ttl":60*60, "capability":"{ \"*\": [ \"*\" ] }", "timestamp":NSDate().timeIntervalSince1970] + let httpBodyJSON = request.HTTPBody >>- JSONToDictionary - // TODO: not passing, timestamp seconds... + expect(httpBodyJSON).toNot(beNil()) + expect(httpBodyJSON!["timestamp"]).toNot(beNil()) - expect(request.HTTPBody >>- JSONToDictionary) == expectedJSON + let expectedJSON = ["ttl":NSString(format: "%f", CGFloat(60*60)), "capability":"{ \"*\": [ \"*\" ] }", "timestamp":httpBodyJSON!["timestamp"]!] + + expect(httpBodyJSON) == expectedJSON } } // RSA8c3 it("should override previously configured parameters") { let clientOptions = ARTClientOptions() - clientOptions.authOptions.authUrl = NSURL(string: "http://auth.ably.io") + clientOptions.authUrl = NSURL(string: "http://auth.ably.io") let rest = ARTRest(options: clientOptions) let authOptions = ARTAuthOptions() @@ -187,10 +190,10 @@ class Auth : QuickSpec { it("implicitly creates a TokenRequest") { let options = ARTClientOptions(key: "6p6USg.CNwGdA:uwJU1qsSf_Qe9VDH") // Test - options.authOptions.authUrl = NSURL(string: "http://auth.ably.io") - options.authOptions.authParams = [NSURLQueryItem(name: "ttl", value: "aaa")] - options.authOptions.authParams = [NSURLQueryItem(name: "rp", value: "true")] - options.authOptions.authMethod = "POST" + options.authUrl = NSURL(string: "http://auth.ably.io") + options.authParams = [NSURLQueryItem(name: "ttl", value: "aaa")] + options.authParams = [NSURLQueryItem(name: "rp", value: "true")] + options.authMethod = "POST" let rest = ARTRest(options: options) diff --git a/ablySpec/RestClient.stats.swift b/ablySpec/RestClient.stats.swift index dd6a6f000..2c27b772e 100644 --- a/ablySpec/RestClient.stats.swift +++ b/ablySpec/RestClient.stats.swift @@ -15,7 +15,7 @@ import Foundation private func postTestStats(stats: JSON) -> ARTClientOptions { let options = AblyTests.setupOptions(AblyTests.jsonRestOptions); - let key = ("\(options.authOptions.key)" as NSString) + let key = ("\(options.key)" as NSString) .dataUsingEncoding(NSUTF8StringEncoding)! .base64EncodedStringWithOptions(NSDataBase64EncodingOptions(0)) diff --git a/ablySpec/RestClient.swift b/ablySpec/RestClient.swift index e54080fa6..35f7fe910 100644 --- a/ablySpec/RestClient.swift +++ b/ablySpec/RestClient.swift @@ -19,8 +19,8 @@ class PublishTestMessage { client.channels.get("test").publish("message") { error in self.error = error - if failOnError && error != nil { - XCTFail("Got error '\(error!)'") + if failOnError { + XCTFail("Got error '\(error)'") } } } @@ -32,7 +32,7 @@ func publishTestMessage(client: ARTRest, failOnError: Bool = true) -> PublishTes func getTestToken() -> String { let options = AblyTests.commonAppSetup() - options.authOptions.useTokenAuth = true + options.useTokenAuth = true let client = ARTRest(options: options) var token: String? @@ -56,7 +56,7 @@ class RestClient: QuickSpec { it("should accept an API key") { let options = AblyTests.commonAppSetup() - let client = ARTRest(key: options.authOptions.key!) + let client = ARTRest(key: options.key!) client.baseUrl = options.restUrl() let publishTask = publishTestMessage(client) @@ -89,7 +89,7 @@ class RestClient: QuickSpec { it("should accept an options object with token authentication") { let options = AblyTests.commonAppSetup() - options.authOptions.token = getTestToken() + options.token = getTestToken() let client = ARTRest(options: options) let publishTask = publishTestMessage(client) @@ -99,8 +99,8 @@ class RestClient: QuickSpec { it("should result in error status when provided a bad token") { let options = AblyTests.commonAppSetup() - options.authOptions.useTokenAuth = true - options.authOptions.token = "invalid_token" + options.useTokenAuth = true + options.token = "invalid_token" let client = ARTRest(options: options) let publishTask = publishTestMessage(client, failOnError: false) @@ -218,7 +218,7 @@ class RestClient: QuickSpec { let authOptions = client.auth.options - expect(authOptions).to(beIdenticalTo(options.authOptions)) + expect(authOptions).to(beIdenticalTo(options)) } // RSC16 diff --git a/ablySpec/TestUtilities.swift b/ablySpec/TestUtilities.swift index 4d12d9e82..234e3cdc0 100644 --- a/ablySpec/TestUtilities.swift +++ b/ablySpec/TestUtilities.swift @@ -73,14 +73,10 @@ class AblyTests { XCTFail(error.localizedDescription) } else if let data = responseData { let response = JSON(data: data) + let key = response["keys"][0] - let appId = response["appId"] - let id = key["id"] - // FIXME: - //options.authOptions.keyName = "\(appId).\(id)" - //options.authOptions.keySecret = key["value"].stringValue - //options.authOptions.capability = key["capability"].stringValue + options.key = key["keyStr"].stringValue return options } From 07143356998ae7cdcadfc624fb5ce64af6d95f93 Mon Sep 17 00:00:00 2001 From: Ricardo Pereira Date: Tue, 6 Oct 2015 11:59:04 +0100 Subject: [PATCH 19/22] RSA11 is failing. Resolved import cycles and compability for Xcode 6 (nullability). --- ably-ios/ARTAuth+Private.h | 14 ++-- ably-ios/ARTAuth.h | 19 +++--- ably-ios/ARTAuth.m | 1 + ably-ios/ARTAuthOptions.h | 20 +++--- ably-ios/ARTAuthTokenDetails.h | 13 ++-- ably-ios/ARTAuthTokenParams.h | 5 +- ably-ios/ARTAuthTokenRequest.h | 1 - ably-ios/ARTBaseMessage.h | 2 +- ably-ios/ARTChannel+Private.h | 11 +-- ably-ios/ARTChannel.h | 18 +++-- ably-ios/ARTChannel.m | 8 +-- ably-ios/ARTChannelCollection+Private.h | 7 +- ably-ios/ARTChannelCollection.h | 4 ++ ably-ios/ARTChannelCollection.m | 15 +++-- ably-ios/ARTChannelOptions.h | 7 +- ably-ios/ARTClientOptions.h | 9 ++- ably-ios/ARTCrypto.h | 6 +- ably-ios/ARTDataQuery+Private.h | 6 +- ably-ios/ARTDataQuery.h | 9 +-- ably-ios/ARTDataQuery.m | 1 - ably-ios/ARTEventEmitter.h | 2 +- ably-ios/ARTHttp.h | 3 +- ably-ios/ARTJsonEncoder.h | 1 - ably-ios/ARTLog.h | 6 +- ably-ios/ARTPaginatedResult+Private.h | 5 +- ably-ios/ARTPaginatedResult.h | 9 +-- ably-ios/ARTPaginatedResult.m | 2 + ably-ios/ARTPayload+Private.h | 2 +- ably-ios/ARTPayload.h | 2 +- ably-ios/ARTPayload.m | 5 +- ably-ios/ARTPresence.h | 11 ++- ably-ios/ARTPresence.m | 4 +- ably-ios/ARTQueuedMessage.h | 2 +- ably-ios/ARTRealtime+Private.h | 2 - ably-ios/ARTRealtime.h | 23 ++++--- ably-ios/ARTRealtime.m | 2 +- ably-ios/ARTRealtimeChannel+Private.h | 2 - ably-ios/ARTRealtimeChannel.h | 6 +- ably-ios/ARTRealtimeChannel.m | 2 + ably-ios/ARTRealtimeChannelSubscription.h | 3 +- ably-ios/ARTRest+Private.h | 5 +- ably-ios/ARTRest.h | 20 +++--- ably-ios/ARTRest.m | 25 ++++--- ably-ios/ARTRestChannel.h | 26 +++++++ ably-ios/ARTRestChannel.m | 82 +++++++++++++++++++++++ ably-ios/ARTStats.h | 2 +- ably-ios/ARTTypes.h | 7 +- ably-ios/ARTWebSocketTransport.h | 1 - ably-ios/CompatibilityMacros.h | 24 ++++--- ably-ios/ably.h | 3 + ably.xcodeproj/project.pbxproj | 8 +++ ablySpec/Auth.swift | 12 ++-- ablySpec/RestClient.swift | 2 +- ablySpec/TestUtilities.swift | 2 +- 54 files changed, 317 insertions(+), 172 deletions(-) create mode 100644 ably-ios/ARTRestChannel.h create mode 100644 ably-ios/ARTRestChannel.m diff --git a/ably-ios/ARTAuth+Private.h b/ably-ios/ARTAuth+Private.h index f5e2fab80..10ee5098a 100644 --- a/ably-ios/ARTAuth+Private.h +++ b/ably-ios/ARTAuth+Private.h @@ -6,20 +6,18 @@ // Copyright (c) 2015 Ably. All rights reserved. // -#import - #import "ARTAuth.h" -NS_ASSUME_NONNULL_BEGIN +ART_ASSUME_NONNULL_BEGIN @interface ARTAuth (Private) -- (ARTAuthOptions *)mergeOptions:(nonnull ARTAuthOptions *)customOptions; -- (ARTAuthTokenParams *)mergeParams:(nonnull ARTAuthTokenParams *)customParams; +- (ARTAuthOptions *)mergeOptions:(ARTAuthOptions *)customOptions; +- (ARTAuthTokenParams *)mergeParams:(ARTAuthTokenParams *)customParams; -- (NSURL *)buildURL:(nonnull ARTAuthOptions *)options withParams:(nonnull ARTAuthTokenParams *)params; -- (NSMutableURLRequest *)buildRequest:(nonnull ARTAuthOptions *)options withParams:(nonnull ARTAuthTokenParams *)params; +- (NSURL *)buildURL:(ARTAuthOptions *)options withParams:(ARTAuthTokenParams *)params; +- (NSMutableURLRequest *)buildRequest:(ARTAuthOptions *)options withParams:(ARTAuthTokenParams *)params; @end -NS_ASSUME_NONNULL_END +ART_ASSUME_NONNULL_END diff --git a/ably-ios/ARTAuth.h b/ably-ios/ARTAuth.h index 694727983..3ef965ea1 100644 --- a/ably-ios/ARTAuth.h +++ b/ably-ios/ARTAuth.h @@ -7,7 +7,8 @@ // #import -#import "ably.h" +#import "ARTTypes.h" +#import "ARTLog.h" @class ARTRest; @class ARTLog; @@ -17,7 +18,7 @@ @class ARTAuthTokenDetails; @class ARTAuthTokenRequest; -NS_ASSUME_NONNULL_BEGIN +ART_ASSUME_NONNULL_BEGIN #pragma mark - ARTAuth @@ -47,15 +48,15 @@ typedef NS_ENUM(NSUInteger, ARTAuthMethod) { - Parameter authOptions: Authentication options (optional). - Parameter callback: Completion callback (ARTAuthTokenDetails, NSError). */ -- (void)requestToken:(nullable ARTAuthTokenParams *)tokenParams withOptions:(nullable ARTAuthOptions *)authOptions - callback:(void (^)(ARTAuthTokenDetails *__nullable tokenDetails, NSError *__nullable error))callback; +- (void)requestToken:(art_nullable ARTAuthTokenParams *)tokenParams withOptions:(art_nullable ARTAuthOptions *)authOptions + callback:(void (^)(ARTAuthTokenDetails *__art_nullable tokenDetails, NSError *__art_nullable error))callback; -- (void)authorise:(nullable ARTAuthTokenParams *)tokenParams options:(nullable ARTAuthOptions *)options force:(BOOL)force - callback:(void (^)(ARTAuthTokenDetails *__nullable tokenDetails, NSError *__nullable error))callback; +- (void)authorise:(art_nullable ARTAuthTokenParams *)tokenParams options:(art_nullable ARTAuthOptions *)options force:(BOOL)force + callback:(void (^)(ARTAuthTokenDetails *__art_nullable tokenDetails, NSError *__art_nullable error))callback; -- (void)createTokenRequest:(nullable ARTAuthTokenParams *)tokenParams options:(nullable ARTAuthOptions *)options - callback:(void (^)(ARTAuthTokenRequest *__nullable tokenRequest, NSError *__nullable error))callback; +- (void)createTokenRequest:(art_nullable ARTAuthTokenParams *)tokenParams options:(art_nullable ARTAuthOptions *)options + callback:(void (^)(ARTAuthTokenRequest *__art_nullable tokenRequest, NSError *__art_nullable error))callback; @end -NS_ASSUME_NONNULL_END +ART_ASSUME_NONNULL_END diff --git a/ably-ios/ARTAuth.m b/ably-ios/ARTAuth.m index 64aae8934..2632bbdf1 100644 --- a/ably-ios/ARTAuth.m +++ b/ably-ios/ARTAuth.m @@ -16,6 +16,7 @@ #import "ARTAuthTokenDetails.h" #import "ARTAuthTokenParams.h" #import "ARTAuthTokenRequest.h" +#import "ARTEncoder.h" @implementation ARTAuth { __weak ARTRest *_rest; diff --git a/ably-ios/ARTAuthOptions.h b/ably-ios/ARTAuthOptions.h index 1c74e5975..96941b4d6 100644 --- a/ably-ios/ARTAuthOptions.h +++ b/ably-ios/ARTAuthOptions.h @@ -11,53 +11,53 @@ @class ARTAuthTokenDetails; -NS_ASSUME_NONNULL_BEGIN +ART_ASSUME_NONNULL_BEGIN @interface ARTAuthOptions : NSObject /** Full Ably key string as obtained from dashboard. */ -@property (nonatomic, copy, nullable) NSString *key; +@property (nonatomic, copy, art_nullable) NSString *key; /** An authentication token issued for this application against a specific key and `TokenParams`. */ -@property (nonatomic, copy, nullable) NSString *token; +@property (nonatomic, copy, art_nullable) NSString *token; /** An authentication token issued for this application against a specific key and `TokenParams`. */ -@property (nonatomic, strong, nullable) ARTAuthTokenDetails *tokenDetails; +@property (nonatomic, strong, art_nullable) ARTAuthTokenDetails *tokenDetails; /** A callback to call to obtain a signed token request. This enables a client to obtain token requests from another entity, so tokens can be renewed without the client requiring access to keys. */ -@property (nonatomic, copy, nullable) ARTAuthCallback authCallback; +@property (nonatomic, copy, art_nullable) ARTAuthCallback authCallback; /** A URL to queryto obtain a signed token request. This enables a client to obtain token requests from another entity, so tokens can be renewed without the client requiring access to keys. */ -@property (nonatomic, strong, nullable) NSURL *authUrl; +@property (nonatomic, strong, art_nullable) NSURL *authUrl; /** The HTTP verb to be used when a request is made by the library to the authUrl. Defaults to GET, supports GET and POST. */ -@property (nonatomic, copy, null_resettable) NSString *authMethod; +@property (nonatomic, copy, art_null_resettable) NSString *authMethod; /** Headers to be included in any request made by the library to the authURL. */ -@property (nonatomic, copy, nullable) NSDictionary *authHeaders; //X7: NSDictionary *authHeaders; +@property (nonatomic, copy, art_nullable) NSDictionary *authHeaders; //X7: NSDictionary *authHeaders; /** Additional params to be included in any request made by the library to the authUrl, either as query params in the case of GET or in the body in the case of POST. */ -@property (nonatomic, copy, nullable) NSArray *authParams; //X7: NSArray *authParams; +@property (nonatomic, copy, art_nullable) NSArray *authParams; //X7: NSArray *authParams; /** This may be set in instances that the library is to sign token requests based on a given key. @@ -80,4 +80,4 @@ NS_ASSUME_NONNULL_BEGIN @end -NS_ASSUME_NONNULL_END +ART_ASSUME_NONNULL_END diff --git a/ably-ios/ARTAuthTokenDetails.h b/ably-ios/ARTAuthTokenDetails.h index 96f58120d..91e092b43 100644 --- a/ably-ios/ARTAuthTokenDetails.h +++ b/ably-ios/ARTAuthTokenDetails.h @@ -7,16 +7,17 @@ // #import +#import "ARTTypes.h" -NS_ASSUME_NONNULL_BEGIN +ART_ASSUME_NONNULL_BEGIN @interface ARTAuthTokenDetails : NSObject @property (nonatomic, readonly, copy) NSString *token; -@property (nonatomic, readonly, strong, nullable) NSDate *expires; -@property (nonatomic, readonly, strong, nullable) NSDate *issued; -@property (nonatomic, readonly, copy, nullable) NSString *capability; -@property (nonatomic, readonly, copy, nullable) NSString *clientId; +@property (nonatomic, readonly, strong, art_nullable) NSDate *expires; +@property (nonatomic, readonly, strong, art_nullable) NSDate *issued; +@property (nonatomic, readonly, copy, art_nullable) NSString *capability; +@property (nonatomic, readonly, copy, art_nullable) NSString *clientId; - (instancetype)init UNAVAILABLE_ATTRIBUTE; @@ -26,4 +27,4 @@ NS_ASSUME_NONNULL_BEGIN @end -NS_ASSUME_NONNULL_END +ART_ASSUME_NONNULL_END diff --git a/ably-ios/ARTAuthTokenParams.h b/ably-ios/ARTAuthTokenParams.h index b8f71b55f..3fa013a12 100644 --- a/ably-ios/ARTAuthTokenParams.h +++ b/ably-ios/ARTAuthTokenParams.h @@ -7,10 +7,11 @@ // #import +#import "ARTTypes.h" @class ARTAuthTokenRequest; -NS_ASSUME_NONNULL_BEGIN +ART_ASSUME_NONNULL_BEGIN @interface ARTAuthTokenParams : NSObject @@ -33,4 +34,4 @@ NS_ASSUME_NONNULL_BEGIN @end -NS_ASSUME_NONNULL_END +ART_ASSUME_NONNULL_END diff --git a/ably-ios/ARTAuthTokenRequest.h b/ably-ios/ARTAuthTokenRequest.h index 0917ff098..c0617deb0 100644 --- a/ably-ios/ARTAuthTokenRequest.h +++ b/ably-ios/ARTAuthTokenRequest.h @@ -7,7 +7,6 @@ // #import - #import "ARTAuthTokenParams.h" @interface ARTAuthTokenRequest : ARTAuthTokenParams diff --git a/ably-ios/ARTBaseMessage.h b/ably-ios/ARTBaseMessage.h index 21254db36..ae4ebaa78 100644 --- a/ably-ios/ARTBaseMessage.h +++ b/ably-ios/ARTBaseMessage.h @@ -7,7 +7,7 @@ // #import -#import +#import "ARTPayload.h" @interface ARTBaseMessage : NSObject diff --git a/ably-ios/ARTChannel+Private.h b/ably-ios/ARTChannel+Private.h index de66a3df5..c2ee74a9e 100644 --- a/ably-ios/ARTChannel+Private.h +++ b/ably-ios/ARTChannel+Private.h @@ -6,11 +6,12 @@ // Copyright (c) 2015 г. Ably. All rights reserved. // -#import +#import "ARTChannel.h" +#import "ARTLog.h" @protocol ARTPayloadEncoder; -NS_ASSUME_NONNULL_BEGIN +ART_ASSUME_NONNULL_BEGIN @interface ARTChannel() { @public @@ -20,10 +21,10 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, strong, null_resettable) ARTChannelOptions *options; -- (instancetype)initWithName:(NSString *)name presence:(ARTPresence *)presence options:(nullable ARTChannelOptions *)options; +- (instancetype)initWithName:(NSString *)name presence:(ARTPresence *)presence options:(art_nullable ARTChannelOptions *)options; -- (void)_postMessages:(id)payload callback:(nullable ARTErrorCallback)callback; +- (void)_postMessages:(id)payload callback:(art_nullable ARTErrorCallback)callback; @end -NS_ASSUME_NONNULL_END +ART_ASSUME_NONNULL_END diff --git a/ably-ios/ARTChannel.h b/ably-ios/ARTChannel.h index eec05117d..3aa799f13 100644 --- a/ably-ios/ARTChannel.h +++ b/ably-ios/ARTChannel.h @@ -7,7 +7,7 @@ // #import -#import "ably.h" +#import "ARTTypes.h" @class ARTChannelOptions; @class ARTPresence; @@ -15,23 +15,21 @@ @class ARTPaginatedResult; @class ARTDataQuery; -NS_ASSUME_NONNULL_BEGIN +ART_ASSUME_NONNULL_BEGIN @interface ARTChannel : NSObject @property (nonatomic, strong, readonly) NSString *name; @property (nonatomic, strong, readonly) ARTPresence *presence; -- (void)publish:(nullable id)payload callback:(nullable ARTErrorCallback)callback; +- (void)publish:(art_nullable id)payload callback:(art_nullable ARTErrorCallback)callback; +- (void)publish:(art_nullable id)payload name:(art_nullable NSString *)name callback:(art_nullable ARTErrorCallback)callback; -- (void)publish:(nullable id)payload name:(nullable NSString *)name callback:(nullable ARTErrorCallback)callback; +- (void)publishMessage:(ARTMessage *)message callback:(art_nullable ARTErrorCallback)callback; +- (void)publishMessages:(NSArray /* */ *)messages callback:(art_nullable ARTErrorCallback)callback; -- (void)publishMessage:(ARTMessage *)message callback:(nullable ARTErrorCallback)callback; - -- (void)publishMessages:(NSArray /* */ *)messages callback:(nullable ARTErrorCallback)callback; - -- (void)history:(nullable ARTDataQuery *)query callback:(void(^)(ARTPaginatedResult /* */ *__nullable result, NSError *__nullable error))callback; +- (void)history:(art_nullable ARTDataQuery *)query callback:(void(^)(ARTPaginatedResult /* */ *__art_nullable result, NSError *__art_nullable error))callback; @end -NS_ASSUME_NONNULL_END +ART_ASSUME_NONNULL_END diff --git a/ably-ios/ARTChannel.m b/ably-ios/ARTChannel.m index ff8ae6b8a..f5816e8f0 100644 --- a/ably-ios/ARTChannel.m +++ b/ably-ios/ARTChannel.m @@ -16,7 +16,7 @@ @implementation ARTChannel -- (instancetype)initWithName:(NSString *)name presence:(ARTPresence *)presence options:(nullable ARTChannelOptions *)options { +- (instancetype)initWithName:(NSString *)name presence:(ARTPresence *)presence options:(ARTChannelOptions *)options { if (self = [super init]) { _name = [name copy]; _presence = presence; @@ -36,11 +36,11 @@ - (void)setOptions:(ARTChannelOptions *)options { _payloadEncoder = [ARTJsonPayloadEncoder instance]; } -- (void)publish:(nullable id)payload callback:(ARTErrorCallback)callback { +- (void)publish:(id)payload callback:(ARTErrorCallback)callback { [self publish:payload name:nil callback:callback]; } -- (void)publish:(nullable id)payload name:(NSString *)name callback:(ARTErrorCallback)callback { +- (void)publish:(id)payload name:(NSString *)name callback:(ARTErrorCallback)callback { [self publishMessage:[[ARTMessage alloc] initWithData:payload name:name] callback:callback]; } @@ -56,7 +56,7 @@ - (void)publishMessage:(ARTMessage *)message callback:(ARTErrorCallback)callback [self _postMessages:[message encode:_payloadEncoder] callback:callback]; } -- (void)history:(ARTDataQuery *)query callback:(void (^)(ARTPaginatedResult * __nullable, NSError *__nullable))callback { +- (void)history:(ARTDataQuery *)query callback:(void (^)(ARTPaginatedResult *, NSError *))callback { NSAssert(false, @"-[%@ %@] should always be overriden.", self.class, NSStringFromSelector(_cmd)); } diff --git a/ably-ios/ARTChannelCollection+Private.h b/ably-ios/ARTChannelCollection+Private.h index e6ab77d86..9f393d6d8 100644 --- a/ably-ios/ARTChannelCollection+Private.h +++ b/ably-ios/ARTChannelCollection+Private.h @@ -7,11 +7,12 @@ // #import +#import "ARTTypes.h" @class ARTChannel; @class ARTChannelOptions; -NS_ASSUME_NONNULL_BEGIN +ART_ASSUME_NONNULL_BEGIN @interface ARTChannelCollection() { @protected @@ -20,8 +21,8 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly) NSMutableDictionary *channels; -- (ARTChannel *)_createChannelWithName:(NSString *)name options:(nullable ARTChannelOptions *)options; +- (ARTChannel *)_createChannelWithName:(NSString *)name options:(art_nullable ARTChannelOptions *)options; @end -NS_ASSUME_NONNULL_END +ART_ASSUME_NONNULL_END diff --git a/ably-ios/ARTChannelCollection.h b/ably-ios/ARTChannelCollection.h index ff5dda92a..5c9b801e4 100644 --- a/ably-ios/ARTChannelCollection.h +++ b/ably-ios/ARTChannelCollection.h @@ -13,6 +13,10 @@ @interface ARTChannelCollection : NSObject +@property (readonly, nonatomic, assign) Class channelClass; + +- (instancetype)initWithChannel:(Class)channelClass; + - (BOOL)exists:(NSString *)channelName; - (ARTChannel *)get:(NSString *)channelName; - (ARTChannel *)get:(NSString *)channelName options:(ARTChannelOptions *)options; diff --git a/ably-ios/ARTChannelCollection.m b/ably-ios/ARTChannelCollection.m index fc2eacd01..97fdb80ff 100644 --- a/ably-ios/ARTChannelCollection.m +++ b/ably-ios/ARTChannelCollection.m @@ -15,8 +15,9 @@ @implementation ARTChannelCollection -- (instancetype)init { +- (instancetype)initWithChannel:(Class)channelClass { if (self = [super init]) { + _channelClass = channelClass; _channels = [[NSMutableDictionary alloc] init]; } return self; @@ -46,7 +47,7 @@ - (void)releaseChannel:(ARTChannel *)channel { [self->_channels removeObjectForKey:channel.name]; } -- (ARTChannel *)_getChannel:(NSString *)channelName options:(nullable ARTChannelOptions *)options { +- (ARTChannel *)_getChannel:(NSString *)channelName options:(ARTChannelOptions *)options { ARTChannel *channel = self->_channels[channelName]; if (!channel) { channel = [self _createChannelWithName:channelName options:options]; @@ -57,9 +58,13 @@ - (ARTChannel *)_getChannel:(NSString *)channelName options:(nullable ARTChannel return channel; } -- (ARTChannel *)_createChannelWithName:(NSString *)name options:(nullable ARTChannelOptions *)options { - NSAssert(false, @"-[%@ %@] should always be overriden.", self.class, NSStringFromSelector(_cmd)); - return 0; +- (ARTChannel *)_createChannelWithName:(NSString *)name options:(ARTChannelOptions *)options { + return [[self.channelClass alloc] init]; + + // FIXME: + //WithRest:_rest name:name options:options + + //- (instancetype)initWithName:(NSString *)name presence:(ARTPresence *)presence options:(ARTChannelOptions *)options } @end diff --git a/ably-ios/ARTChannelOptions.h b/ably-ios/ARTChannelOptions.h index 28aed5e3c..649dc0696 100644 --- a/ably-ios/ARTChannelOptions.h +++ b/ably-ios/ARTChannelOptions.h @@ -7,15 +7,16 @@ // #import +#import "ARTTypes.h" @class ARTCipherParams; -NS_ASSUME_NONNULL_BEGIN +ART_ASSUME_NONNULL_BEGIN @interface ARTChannelOptions : NSObject @property (nonatomic, assign) BOOL isEncrypted; -@property (nonatomic, strong, nullable) ARTCipherParams *cipherParams; +@property (nonatomic, strong, art_nullable) ARTCipherParams *cipherParams; + (instancetype)unencrypted; @@ -23,4 +24,4 @@ NS_ASSUME_NONNULL_BEGIN @end -NS_ASSUME_NONNULL_END +ART_ASSUME_NONNULL_END diff --git a/ably-ios/ARTClientOptions.h b/ably-ios/ARTClientOptions.h index b2da368aa..9567ae929 100644 --- a/ably-ios/ARTClientOptions.h +++ b/ably-ios/ARTClientOptions.h @@ -7,10 +7,9 @@ // #import - #import "ARTAuthOptions.h" -NS_ASSUME_NONNULL_BEGIN +ART_ASSUME_NONNULL_BEGIN @interface ARTClientOptions : ARTAuthOptions @@ -29,8 +28,8 @@ NS_ASSUME_NONNULL_BEGIN @property (readwrite, assign, nonatomic) BOOL binary; @property (readwrite, assign, nonatomic) BOOL autoConnect; @property (readwrite, assign, nonatomic) int64_t connectionSerial; -@property (nullable, readwrite, copy, nonatomic) NSString *resumeKey; -@property (nullable, readwrite, copy, nonatomic) NSString *recover; +@property (art_nullable, readwrite, copy, nonatomic) NSString *resumeKey; +@property (art_nullable, readwrite, copy, nonatomic) NSString *recover; - (bool)isFallbackPermitted; @@ -39,4 +38,4 @@ NS_ASSUME_NONNULL_BEGIN @end -NS_ASSUME_NONNULL_END +ART_ASSUME_NONNULL_END diff --git a/ably-ios/ARTCrypto.h b/ably-ios/ARTCrypto.h index 0c9d81ae2..41ba8fb35 100644 --- a/ably-ios/ARTCrypto.h +++ b/ably-ios/ARTCrypto.h @@ -7,8 +7,8 @@ // #import -#import "ably.h" - +#import "ARTTypes.h" +#import "ARTLog.h" #import "ARTStatus.h" @class ARTLog; @@ -24,7 +24,7 @@ @end @interface ARTCipherParams : NSObject -@property (nonatomic, weak) ARTLog * logger; +@property (nonatomic, weak) ARTLog *logger; @property (readonly, strong, nonatomic) NSString *algorithm; @property (readonly, strong, nonatomic) NSData *keySpec; @property (readonly, strong, nonatomic) ARTIvParameterSpec *ivSpec; diff --git a/ably-ios/ARTDataQuery+Private.h b/ably-ios/ARTDataQuery+Private.h index 041636390..ba9375ebc 100755 --- a/ably-ios/ARTDataQuery+Private.h +++ b/ably-ios/ARTDataQuery+Private.h @@ -6,9 +6,9 @@ // Copyright (c) 2015 г. Ably. All rights reserved. // -#import +#import "ARTDataQuery.h" -NS_ASSUME_NONNULL_BEGIN +ART_ASSUME_NONNULL_BEGIN @interface ARTDataQuery(Private) @@ -16,4 +16,4 @@ NS_ASSUME_NONNULL_BEGIN @end -NS_ASSUME_NONNULL_END +ART_ASSUME_NONNULL_END diff --git a/ably-ios/ARTDataQuery.h b/ably-ios/ARTDataQuery.h index e0e42bcbf..7bb646685 100755 --- a/ably-ios/ARTDataQuery.h +++ b/ably-ios/ARTDataQuery.h @@ -7,8 +7,9 @@ // #import +#import "ARTTypes.h" -NS_ASSUME_NONNULL_BEGIN +ART_ASSUME_NONNULL_BEGIN typedef NS_ENUM(NSUInteger, ARTQueryDirection) { ARTQueryDirectionForwards, @@ -17,8 +18,8 @@ typedef NS_ENUM(NSUInteger, ARTQueryDirection) { @interface ARTDataQuery : NSObject -@property (nonatomic, strong, nullable) NSDate *start; -@property (nonatomic, strong, nullable) NSDate *end; +@property (nonatomic, strong, art_nullable) NSDate *start; +@property (nonatomic, strong, art_nullable) NSDate *end; @property (nonatomic, assign) uint16_t limit; @@ -26,4 +27,4 @@ typedef NS_ENUM(NSUInteger, ARTQueryDirection) { @end -NS_ASSUME_NONNULL_END \ No newline at end of file +ART_ASSUME_NONNULL_END \ No newline at end of file diff --git a/ably-ios/ARTDataQuery.m b/ably-ios/ARTDataQuery.m index 010aaf608..c8032275f 100755 --- a/ably-ios/ARTDataQuery.m +++ b/ably-ios/ARTDataQuery.m @@ -6,7 +6,6 @@ // Copyright (c) 2015 г. Ably. All rights reserved. // -#import "ARTDataQuery.h" #import "ARTDataQuery+Private.h" @implementation ARTDataQuery diff --git a/ably-ios/ARTEventEmitter.h b/ably-ios/ARTEventEmitter.h index 5a84e398d..49366cea8 100644 --- a/ably-ios/ARTEventEmitter.h +++ b/ably-ios/ARTEventEmitter.h @@ -7,7 +7,7 @@ // #import -#import "ably.h" +#import "ARTTypes.h" @protocol ARTSubscription; diff --git a/ably-ios/ARTHttp.h b/ably-ios/ARTHttp.h index 5977158f6..bdfa7fbb4 100644 --- a/ably-ios/ARTHttp.h +++ b/ably-ios/ARTHttp.h @@ -7,7 +7,8 @@ // #import -#import "ably.h" +#import "ARTTypes.h" +#import "ARTLog.h" @class ARTErrorInfo; diff --git a/ably-ios/ARTJsonEncoder.h b/ably-ios/ARTJsonEncoder.h index a4e7438c1..9e057d59d 100644 --- a/ably-ios/ARTJsonEncoder.h +++ b/ably-ios/ARTJsonEncoder.h @@ -7,7 +7,6 @@ // #import - #import "ARTEncoder.h" @class ARTLog; diff --git a/ably-ios/ARTLog.h b/ably-ios/ARTLog.h index 0c4cb3ad6..750d250dc 100644 --- a/ably-ios/ARTLog.h +++ b/ably-ios/ARTLog.h @@ -7,9 +7,9 @@ // #import -#import +#import "ARTTypes.h" -__ART_ASSUME_NONNULL_BEGIN +ART_ASSUME_NONNULL_BEGIN typedef NS_ENUM(NSUInteger, ARTLogLevel) { ARTLogLevelVerbose, @@ -38,4 +38,4 @@ typedef NS_ENUM(NSUInteger, ARTLogLevel) { @end -__ART_ASSUME_NONNULL_END +ART_ASSUME_NONNULL_END diff --git a/ably-ios/ARTPaginatedResult+Private.h b/ably-ios/ARTPaginatedResult+Private.h index 70b6fe28f..2a33253ea 100644 --- a/ably-ios/ARTPaginatedResult+Private.h +++ b/ably-ios/ARTPaginatedResult+Private.h @@ -6,8 +6,9 @@ // Copyright (c) 2015 г. Ably. All rights reserved. // -#import -#import +#import "ARTPaginatedResult.h" + +@protocol ARTHTTPExecutor; @interface ARTPaginatedResult () diff --git a/ably-ios/ARTPaginatedResult.h b/ably-ios/ARTPaginatedResult.h index 3809f61ac..c695bc8da 100644 --- a/ably-ios/ARTPaginatedResult.h +++ b/ably-ios/ARTPaginatedResult.h @@ -7,13 +7,14 @@ // #import -#import +#import "ARTTypes.h" +#import "ARTStatus.h" -NS_ASSUME_NONNULL_BEGIN +ART_ASSUME_NONNULL_BEGIN @interface ARTPaginatedResult : NSObject -typedef void(^ARTPaginatedResultCallback)(ARTPaginatedResult *__nullable result, NSError *__nullable error); +typedef void(^ARTPaginatedResultCallback)(ARTPaginatedResult *__art_nullable result, NSError *__art_nullable error); @property (nonatomic, strong, readonly) NSArray *items; @@ -31,4 +32,4 @@ typedef void(^ARTPaginatedResultCallback)(ARTPaginatedResult *__nullable result, @end -NS_ASSUME_NONNULL_END +ART_ASSUME_NONNULL_END diff --git a/ably-ios/ARTPaginatedResult.m b/ably-ios/ARTPaginatedResult.m index 4c807c6a1..05c602968 100644 --- a/ably-ios/ARTPaginatedResult.m +++ b/ably-ios/ARTPaginatedResult.m @@ -8,6 +8,8 @@ #import "ARTPaginatedResult+Private.h" +#import "ARTHttp.h" + @implementation ARTPaginatedResult { id _executor; NSMutableURLRequest *_relFirst; diff --git a/ably-ios/ARTPayload+Private.h b/ably-ios/ARTPayload+Private.h index 20557999e..64215a1e9 100644 --- a/ably-ios/ARTPayload+Private.h +++ b/ably-ios/ARTPayload+Private.h @@ -6,7 +6,7 @@ // Copyright (c) 2015 Ably. All rights reserved. // -#import +#import "ARTPayload.h" @interface ARTPayload (Private) diff --git a/ably-ios/ARTPayload.h b/ably-ios/ARTPayload.h index 7daffe2af..ad6b14ce2 100644 --- a/ably-ios/ARTPayload.h +++ b/ably-ios/ARTPayload.h @@ -7,7 +7,7 @@ // #import -#import +#import "ARTStatus.h" @protocol ARTPayloadEncoder; diff --git a/ably-ios/ARTPayload.m b/ably-ios/ARTPayload.m index 32639c98e..6758f87e4 100644 --- a/ably-ios/ARTPayload.m +++ b/ably-ios/ARTPayload.m @@ -6,7 +6,6 @@ // Copyright (c) 2014 Ably. All rights reserved. // -#import "ARTPayload.h" #import "ARTPayload+Private.h" #import "ARTCrypto.h" @@ -21,14 +20,14 @@ + (BOOL)canDecode:(ARTPayload *)payload; @interface ARTCipherPayloadEncoder () -@property (nonatomic, weak) ARTLog * logger; +@property (nonatomic, weak) ARTLog *logger; @property (readonly, strong, nonatomic) id cipher; @end @interface ARTPayloadEncoderChain () -@property (nonatomic, weak) ARTLog * logger; +@property (nonatomic, weak) ARTLog *logger; @property (readonly, nonatomic, strong) NSArray *encoders; @end diff --git a/ably-ios/ARTPresence.h b/ably-ios/ARTPresence.h index a762795bf..e3e7d1586 100644 --- a/ably-ios/ARTPresence.h +++ b/ably-ios/ARTPresence.h @@ -7,8 +7,7 @@ // #import -#import "ably.h" - +#import "ARTTypes.h" #import "ARTPresenceMessage.h" @protocol ARTSubscription; @@ -17,7 +16,7 @@ @class ARTDataQuery; @class ARTRealtimeChannel; -NS_ASSUME_NONNULL_BEGIN +ART_ASSUME_NONNULL_BEGIN /// Provides access to presence operations and state for the associated Channel @interface ARTPresence : NSObject @@ -25,10 +24,10 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithChannel:(ARTRealtimeChannel *)channel; /// Get the presence state for one channel -- (void)get:(void (^)(ARTPaginatedResult /* */ *__nullable result, NSError *__nullable error))callback; +- (void)get:(void (^)(ARTPaginatedResult /* */ *__art_nullable result, NSError *__art_nullable error))callback; /// Obtain recent presence history for one channel -- (void)history:(nullable ARTDataQuery *)query callback:(void (^)(ARTPaginatedResult /* */ *__nullable result, NSError *__nullable error))callback; +- (void)history:(art_nullable ARTDataQuery *)query callback:(void (^)(ARTPaginatedResult /* */ *__art_nullable result, NSError *__art_nullable error))callback; - (void)enter:(id)data cb:(ARTStatusCallback)cb; - (void)update:(id)data cb:(ARTStatusCallback)cb; @@ -46,4 +45,4 @@ NS_ASSUME_NONNULL_BEGIN @end -NS_ASSUME_NONNULL_END +ART_ASSUME_NONNULL_END diff --git a/ably-ios/ARTPresence.m b/ably-ios/ARTPresence.m index 83f33b7bd..016c86345 100644 --- a/ably-ios/ARTPresence.m +++ b/ably-ios/ARTPresence.m @@ -34,13 +34,13 @@ -(instancetype) initWithChannel:(ARTRealtimeChannel *) channel { return self; } -- (void)get:(void (^)(ARTPaginatedResult /* */ *__nullable result, NSError *__nullable error))callback { +- (void)get:(void (^)(ARTPaginatedResult /* */ *result, NSError *error))callback { // FIXME: //[self.channel throwOnDisconnectedOrFailed]; //[self.channel.restChannel.presence get:query callback:callback]; } -- (void)history:(nullable ARTDataQuery *)query callback:(void (^)(ARTPaginatedResult /* */ *__nullable result, NSError *__nullable error))callback { +- (void)history:(ARTDataQuery *)query callback:(void (^)(ARTPaginatedResult /* */ *result, NSError *error))callback { // FIXME: //[self.channel throwOnDisconnectedOrFailed]; //[self.channel.restChannel.presence history:query callback:callback]; diff --git a/ably-ios/ARTQueuedMessage.h b/ably-ios/ARTQueuedMessage.h index 3494f9e4d..50cb38941 100644 --- a/ably-ios/ARTQueuedMessage.h +++ b/ably-ios/ARTQueuedMessage.h @@ -7,7 +7,7 @@ // #import -#import "ably.h" +#import "ARTTypes.h" @class ARTProtocolMessage; diff --git a/ably-ios/ARTRealtime+Private.h b/ably-ios/ARTRealtime+Private.h index 78169281f..d629bca69 100644 --- a/ably-ios/ARTRealtime+Private.h +++ b/ably-ios/ARTRealtime+Private.h @@ -6,8 +6,6 @@ // Copyright (c) 2015 Ably. All rights reserved. // -#import - #import "ARTRealtime.h" @class ARTProtocolMessage; diff --git a/ably-ios/ARTRealtime.h b/ably-ios/ARTRealtime.h index 0814634bd..f6ef4966c 100644 --- a/ably-ios/ARTRealtime.h +++ b/ably-ios/ARTRealtime.h @@ -7,17 +7,18 @@ // #import -#import "ably.h" - -#import -#import -#import -#import -#import -#import - -#define ART_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#import "ARTTypes.h" +#import "ARTLog.h" +@class ARTStatus; +@class ARTMessage; +@class ARTClientOptions; +@class ARTStatsQuery; +@class ARTRealtimeChannel; +@class ARTPresenceMessage; +@class ARTPaginatedResult; +@class ARTErrorInfo; +@class ARTCipherParams; @class ARTPresence; @class ARTPresenceMap; @class ARTRealtimeChannelPresenceSubscription; @@ -25,6 +26,8 @@ @class ARTRealtimeChannel; @class ARTAuth; +#define ART_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) + #pragma mark - ARTRealtime @interface ARTRealtime : NSObject diff --git a/ably-ios/ARTRealtime.m b/ably-ios/ARTRealtime.m index 46c7721c4..72b700635 100644 --- a/ably-ios/ARTRealtime.m +++ b/ably-ios/ARTRealtime.m @@ -7,7 +7,6 @@ // Copyright (c) 2014 Ably. All rights reserved. // -#import "ARTRealtime.h" #import "ARTRealtime+Private.h" #import "ARTRealtimeTransport.h" @@ -16,6 +15,7 @@ #import "ARTDefault.h" #import "ARTRest.h" #import "ARTMessage.h" +#import "ARTClientOptions.h" #import "ARTPresenceMessage.h" #import "ARTWebSocketTransport.h" #import "ARTNSArray+ARTFunctional.h" diff --git a/ably-ios/ARTRealtimeChannel+Private.h b/ably-ios/ARTRealtimeChannel+Private.h index 87eeacaa5..b9101d9cc 100644 --- a/ably-ios/ARTRealtimeChannel+Private.h +++ b/ably-ios/ARTRealtimeChannel+Private.h @@ -7,8 +7,6 @@ // Copyright (c) 2015 Ably. All rights reserved. // -#import - #import "ARTRealtimeChannel.h" @interface ARTRealtimeChannel (Private) diff --git a/ably-ios/ARTRealtimeChannel.h b/ably-ios/ARTRealtimeChannel.h index d2c8506cb..f7ac250f2 100644 --- a/ably-ios/ARTRealtimeChannel.h +++ b/ably-ios/ARTRealtimeChannel.h @@ -7,8 +7,8 @@ // #import -#import "ably.h" - +#import "ARTTypes.h" +#import "ARTLog.h" #import "ARTPresenceMessage.h" @protocol ARTSubscription; @@ -28,7 +28,7 @@ @interface ARTRealtimeChannel : NSObject -@property (nonatomic, weak) ARTLog * logger; +@property (nonatomic, weak) ARTLog *logger; @property (readonly, strong, nonatomic) ARTRealtime *realtime; @property (readonly, strong, nonatomic) NSString *name; @property (readonly, strong, nonatomic) ARTChannel *restChannel; //?! diff --git a/ably-ios/ARTRealtimeChannel.m b/ably-ios/ARTRealtimeChannel.m index 56ddb1cee..092bc567f 100644 --- a/ably-ios/ARTRealtimeChannel.m +++ b/ably-ios/ARTRealtimeChannel.m @@ -9,12 +9,14 @@ #import "ARTRealtimeChannel.h" #import "ARTRealtime.h" +#import "ARTMessage.h" #import "ARTPresence.h" #import "ARTChannel.h" #import "ARTProtocolMessage.h" #import "ARTRealtimeChannelSubscription.h" #import "ARTPresenceMap.h" #import "ARTQueuedMessage.h" +#import "ARTNSArray+ARTFunctional.h" @interface ARTRealtimeChannel () diff --git a/ably-ios/ARTRealtimeChannelSubscription.h b/ably-ios/ARTRealtimeChannelSubscription.h index 4e68564c0..3d7811d62 100644 --- a/ably-ios/ARTRealtimeChannelSubscription.h +++ b/ably-ios/ARTRealtimeChannelSubscription.h @@ -7,8 +7,7 @@ // #import -#import "ably.h" - +#import "ARTTypes.h" #import "ARTPresenceMessage.h" @class ARTRealtime; diff --git a/ably-ios/ARTRest+Private.h b/ably-ios/ARTRest+Private.h index 87d08e65c..b61e174b5 100644 --- a/ably-ios/ARTRest+Private.h +++ b/ably-ios/ARTRest+Private.h @@ -6,8 +6,6 @@ // Copyright (c) 2014 Ably. All rights reserved. // -#import - #import "ARTRest.h" @protocol ARTEncoder; @@ -24,6 +22,7 @@ typedef NS_ENUM(NSUInteger, ARTAuthentication) { @property (readonly, strong, nonatomic) id defaultEncoder; @property (nonatomic, strong) id httpExecutor; + @property (nonatomic, strong) NSURL *baseUrl; - (NSURL *)getBaseURL; @@ -38,9 +37,7 @@ typedef NS_ENUM(NSUInteger, ARTAuthentication) { - (id)post:(NSString *)relUrl headers:(NSDictionary *)headers body:(NSData *)body authenticated:(ARTAuthentication)authenticated cb:(ARTHttpCb)cb; - (id)withAuthHeadersUseBasic:(BOOL) useBasic cb:(id(^)(NSDictionary *))cb; - - (id)withAuthHeaders:(id(^)(NSDictionary *authHeaders))cb; - - (id)withAuthParams:(id(^)(NSDictionary *authParams))cb; - (id)postTestStats:(NSArray *) stats cb:(void(^)(ARTStatus * status)) cb; diff --git a/ably-ios/ARTRest.h b/ably-ios/ARTRest.h index 9e7012121..ed25a5b29 100644 --- a/ably-ios/ARTRest.h +++ b/ably-ios/ARTRest.h @@ -7,17 +7,19 @@ // #import -#import "ably.h" +#import "ARTTypes.h" +#import "ARTLog.h" +@protocol ARTHTTPExecutor; + +@class ARTChannelCollection; +@class ARTClientOptions; +@class ARTAuth; @class ARTCancellable; @class ARTPaginatedResult; @class ARTStatsQuery; -@class ARTClientOptions; -@class ARTAuth; -@class ARTChannel; -@class ARTChannelCollection; -NS_ASSUME_NONNULL_BEGIN +ART_ASSUME_NONNULL_BEGIN @interface ARTRest : NSObject @@ -26,8 +28,8 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithLogger:(ARTLog *)logger andOptions:(ARTClientOptions *)options; - (instancetype)initWithKey:(NSString *)key; -- (void)time:(void(^)(NSDate *__nullable time, NSError *__nullable error))callback; -- (void)stats:(nullable ARTStatsQuery *)query callback:(void (^)(ARTPaginatedResult /* */ *__nullable result, NSError *__nullable error))callback; +- (void)time:(void(^)(NSDate *__art_nullable time, NSError *__art_nullable error))callback; +- (void)stats:(art_nullable ARTStatsQuery *)query callback:(void (^)(ARTPaginatedResult /* */ *__art_nullable result, NSError *__art_nullable error))callback; - (id)internetIsUp:(void (^)(bool isUp))cb; @@ -38,4 +40,4 @@ NS_ASSUME_NONNULL_BEGIN @end -NS_ASSUME_NONNULL_END +ART_ASSUME_NONNULL_END diff --git a/ably-ios/ARTRest.m b/ably-ios/ARTRest.m index 90feca7f3..6addedde8 100644 --- a/ably-ios/ARTRest.m +++ b/ably-ios/ARTRest.m @@ -10,6 +10,7 @@ #import "ARTRest+Private.h" #import "ARTChannel+Private.h" +#import "ARTChannelCollection.h" #import "ARTDataQuery+Private.h" #import "ARTPaginatedResult+Private.h" #import "ARTAuth.h" @@ -26,12 +27,15 @@ #import "ARTFallback.h" #import "ARTNSDictionary+ARTDictionaryUtil.h" #import "ARTNSArray+ARTFunctional.h" +#import "ARTRestChannel.h" -@interface ARTRest () +@interface ARTRest () + +@property (nonatomic, strong) NSURL *baseUrl; +@property (nonatomic, strong) id httpExecutor; @property (readonly, strong, nonatomic) ARTHttp *http; @property (strong, nonatomic) ARTAuth *auth; -@property (nonatomic, strong) NSURL *baseUrl; @property (readonly, strong, nonatomic) NSDictionary *encoders; @property (readonly, strong, nonatomic) NSString *defaultEncoding; @property (readwrite, assign, nonatomic) int fallbackCount; @@ -54,14 +58,10 @@ - (instancetype)initWithLogger:(ARTLog *)logger andOptions:(ARTClientOptions *)o _logger = [[ARTLog alloc] init]; } - // FIXME: _http = [[ARTHttp alloc] init]; - // Private ?! - //self.httpExecutor = _http; - //self.httpExecutor.logger = _logger; - - // FIXME: - //_channels = [[ARTChannelCollection alloc] initWithRest:self]; + _httpExecutor = _http; + _httpExecutor.logger = _logger; + _channels = [[ARTChannelCollection alloc] initWithChannel:[ARTRestChannel class]]; id defaultEncoder = [[ARTJsonEncoder alloc] init]; _encoders = @{ [defaultEncoder mimeType]: defaultEncoder }; @@ -114,7 +114,7 @@ - (void)calculateAuthorization:(void (^)(NSString *authorization, NSError *error } -- (void)time:(void(^)(NSDate *__nullable time, NSError *__nullable error))callback { +- (void)time:(void(^)(NSDate *time, NSError *error))callback { NSURL *requestUrl = [NSURL URLWithString:@"/time" relativeToURL:self.baseUrl]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:requestUrl]; request.HTTPMethod = @"GET"; @@ -138,10 +138,12 @@ - (void)time:(void(^)(NSDate *__nullable time, NSError *__nullable error))callba return nil; } -- (void)stats:(ARTStatsQuery *)query callback:(void (^)(ARTPaginatedResult *__nullable, NSError *__nullable))callback { +- (void)stats:(ARTStatsQuery *)query callback:(void (^)(ARTPaginatedResult *, NSError *))callback { NSParameterAssert(query.limit < 1000); NSParameterAssert([query.start compare:query.end] != NSOrderedDescending); + // FIXME: + /* NSURLComponents *requestUrl = [NSURLComponents componentsWithString:@"/stats"]; requestUrl.queryItems = [query asQueryItems]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[requestUrl URLRelativeToURL:self.baseUrl]]; @@ -152,6 +154,7 @@ - (void)stats:(ARTStatsQuery *)query callback:(void (^)(ARTPaginatedResult *__nu }; [ARTPaginatedResult executePaginatedRequest:request executor:self responseProcessor:responseProcessor callback:callback]; + */ } - (id)defaultEncoder { diff --git a/ably-ios/ARTRestChannel.h b/ably-ios/ARTRestChannel.h new file mode 100644 index 000000000..a072abab2 --- /dev/null +++ b/ably-ios/ARTRestChannel.h @@ -0,0 +1,26 @@ +// +// ARTRestChannel.h +// ably-ios +// +// Created by Ricardo Pereira on 05/10/2015. +// Copyright (c) 2015 Ably. All rights reserved. +// + +#import +#import "ARTChannel.h" + +@class ARTRest; +@class ARTChannelOptions; + +ART_ASSUME_NONNULL_BEGIN + +@interface ARTRestChannel : ARTChannel + +@property (nonatomic, strong, readonly) ARTPresence *presence; +@property (nonatomic, strong) NSString *basePath; + +//- (instancetype)initWithRest:(ARTRest *)rest name:(NSString *)name options:(art_nullable ARTChannelOptions *)options; + +@end + +ART_ASSUME_NONNULL_END diff --git a/ably-ios/ARTRestChannel.m b/ably-ios/ARTRestChannel.m new file mode 100644 index 000000000..e7a187041 --- /dev/null +++ b/ably-ios/ARTRestChannel.m @@ -0,0 +1,82 @@ +// +// ARTRestChannel.m +// ably-ios +// +// Created by Ricardo Pereira on 05/10/2015. +// Copyright (c) 2015 Ably. All rights reserved. +// + +#import "ARTRestChannel.h" + +#import "ARTRest.h" +#import "ARTChannelOptions.h" +#import "ARTChannel+Private.h" +#import "ARTPaginatedResult+Private.h" +#import "ARTDataQuery+Private.h" + +@implementation ARTRestChannel + +@dynamic presence; + +- (instancetype)initWithRest:(ARTRest *)rest name:(NSString *)name options:(ARTChannelOptions *)options { + self = [super init]; + /* + if (self = [super initWithName:name presence:[[ARTRestPresence alloc] initWithChannel:self] options:options]) { + _logger = rest.logger; + [_logger debug:@"ARTRestChannel: instantiating under %@", name]; + _rest = rest; + _basePath = [NSString stringWithFormat:@"/channels/%@", [name stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLPathAllowedCharacterSet]]]; + } + */ + + return self; +} + + +- (void)history:(ARTDataQuery *)query callback:(void (^)(ARTPaginatedResult *, NSError *))callback { + NSParameterAssert(query.limit < 1000); + NSParameterAssert([query.start compare:query.end] != NSOrderedDescending); + + // FIXME: + /* + NSURLComponents *requestUrl = [NSURLComponents componentsWithString:[_basePath stringByAppendingPathComponent:@"messages"]]; + requestUrl.queryItems = [query asQueryItems]; + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:requestUrl.URL]; + + ARTPaginatedResultResponseProcessor responseProcessor = ^(NSHTTPURLResponse *response, NSData *data) { + id encoder = [_rest.encoders objectForKey:response.MIMEType]; + return [[encoder decodeMessages:data] artMap:^(ARTMessage *message) { + return [message decode:_payloadEncoder]; + }]; + }; + + [ARTPaginatedResult executePaginatedRequest:request executor:_rest responseProcessor:responseProcessor callback:callback]; + */ +} + +- (void)_postMessages:(id)payload callback:(ARTErrorCallback)callback { + //FIXME: + /* + NSData *encodedMessage = nil; + if ([payload isKindOfClass:[ARTMessage class]]) { + encodedMessage = [_rest.defaultEncoder encodeMessage:payload]; + } else if ([payload isKindOfClass:[NSArray class]]) { + encodedMessage = [_rest.defaultEncoder encodeMessages:payload]; + } + + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[_basePath stringByAppendingPathComponent:@"messages"]]]; + request.HTTPMethod = @"POST"; + request.HTTPBody = encodedMessage; + if (_rest.defaultEncoding) { + [request setValue:_rest.defaultEncoding forHTTPHeaderField:@"Content-Type"]; + } + + [_rest executeRequest:request callback:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { + if (callback) { + callback(error); + } + }]; + */ +} + +@end diff --git a/ably-ios/ARTStats.h b/ably-ios/ARTStats.h index 03984362a..938e4e2d3 100644 --- a/ably-ios/ARTStats.h +++ b/ably-ios/ARTStats.h @@ -7,7 +7,7 @@ // #import -#import +#import "ARTDataQuery.h" typedef NS_ENUM(NSUInteger, ARTStatsUnit) { ARTStatsUnitMinute, diff --git a/ably-ios/ARTTypes.h b/ably-ios/ARTTypes.h index fe7cb0b6f..75a0f4b4e 100644 --- a/ably-ios/ARTTypes.h +++ b/ably-ios/ARTTypes.h @@ -7,6 +7,7 @@ // #import +#import "CompatibilityMacros.h" @class ARTStatus; @class ARTHttpResponse; @@ -36,7 +37,7 @@ typedef NS_ENUM(NSUInteger, ARTRealtimeChannelState) { ARTRealtimeChannelFailed }; -NS_ASSUME_NONNULL_BEGIN +ART_ASSUME_NONNULL_BEGIN typedef void (^ARTRealtimeChannelMessageCb)(ARTMessage *); @@ -52,7 +53,7 @@ typedef void (^ARTHttpCb)(ARTHttpResponse *response); typedef void (^ARTErrorCallback)(NSError *error); -typedef void (^ARTAuthCallback)(ARTAuthTokenParams *tokenParams, void(^callback)(ARTAuthTokenRequest *__nullable tokenRequest, NSError *__nullable error)); +typedef void (^ARTAuthCallback)(ARTAuthTokenParams *tokenParams, void(^callback)(ARTAuthTokenRequest *__art_nullable tokenRequest, NSError *__art_nullable error)); // FIXME: @protocol ARTCancellable @@ -74,4 +75,4 @@ typedef void (^ARTAuthCallback)(ARTAuthTokenParams *tokenParams, void(^callback) @end -NS_ASSUME_NONNULL_END +ART_ASSUME_NONNULL_END diff --git a/ably-ios/ARTWebSocketTransport.h b/ably-ios/ARTWebSocketTransport.h index c88273e3a..1725abd5a 100644 --- a/ably-ios/ARTWebSocketTransport.h +++ b/ably-ios/ARTWebSocketTransport.h @@ -7,7 +7,6 @@ // #import - #import "ARTRealtimeTransport.h" #import "ARTEncoder.h" diff --git a/ably-ios/CompatibilityMacros.h b/ably-ios/CompatibilityMacros.h index 2ab029d8e..c2ac3e06f 100644 --- a/ably-ios/CompatibilityMacros.h +++ b/ably-ios/CompatibilityMacros.h @@ -10,19 +10,27 @@ #define CompatibilityMacros_h #if __has_feature(nullability) - #define __ART_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN - #define __ART_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END - #define __ART_NULLABLE nullable + #define ART_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN + #define ART_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END + #define art_nullable nullable + #define art_nonnull nonnull + #define art_null_resettable null_resettable + #define __art_nullable __nullable + #define __art_ptr_nonnull __nonnull #else - #define __ART_ASSUME_NONNULL_BEGIN - #define __ART_ASSUME_NONNULL_END - #define __ART_NULLABLE + #define ART_ASSUME_NONNULL_BEGIN + #define ART_ASSUME_NONNULL_END + #define art_nullable + #define art_nonnull + #define art_null_resettable + #define __art_nullable + #define __art_ptr_nonnull #endif #if __has_feature(objc_generics) - #define __GENERIC(class, ...) class<__VA_ARGS__> + #define __GENERIC(class, ...) class<__VA_ARGS__> #else - #define __GENERIC(class, ...) class + #define __GENERIC(class, ...) class #endif #endif /* CompatibilityMacros_h */ diff --git a/ably-ios/ably.h b/ably-ios/ably.h index 84cc2a8f8..54e2a9759 100644 --- a/ably-ios/ably.h +++ b/ably-ios/ably.h @@ -18,6 +18,7 @@ FOUNDATION_EXPORT const unsigned char ablyVersionString[]; #import #import +#import #import #import #import @@ -37,6 +38,8 @@ FOUNDATION_EXPORT const unsigned char ablyVersionString[]; #import #import #import +#import +#import #import #import #import diff --git a/ably.xcodeproj/project.pbxproj b/ably.xcodeproj/project.pbxproj index 0e8c4f295..828c76488 100644 --- a/ably.xcodeproj/project.pbxproj +++ b/ably.xcodeproj/project.pbxproj @@ -98,6 +98,8 @@ 96E408441A38939E00087F77 /* ARTProtocolMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 96E408421A38939E00087F77 /* ARTProtocolMessage.m */; }; 96E408471A3895E800087F77 /* ARTWebSocketTransport.h in Headers */ = {isa = PBXBuildFile; fileRef = 96E408451A3895E800087F77 /* ARTWebSocketTransport.h */; settings = {ATTRIBUTES = (Public, ); }; }; 96E408481A3895E800087F77 /* ARTWebSocketTransport.m in Sources */ = {isa = PBXBuildFile; fileRef = 96E408461A3895E800087F77 /* ARTWebSocketTransport.m */; }; + D70EAAED1BC3376200CD8B9E /* ARTRestChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = D70EAAEB1BC3376200CD8B9E /* ARTRestChannel.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D70EAAEE1BC3376200CD8B9E /* ARTRestChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = D70EAAEC1BC3376200CD8B9E /* ARTRestChannel.m */; }; D72304701BB72CED00F1ABDA /* RealtimeClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = D723046F1BB72CED00F1ABDA /* RealtimeClient.swift */; }; D746AE1D1BBB5207003ECEF8 /* ARTDataQuery.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE1A1BBB5207003ECEF8 /* ARTDataQuery.h */; settings = {ATTRIBUTES = (Public, ); }; }; D746AE1E1BBB5207003ECEF8 /* ARTDataQuery+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = D746AE1B1BBB5207003ECEF8 /* ARTDataQuery+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; @@ -281,6 +283,8 @@ 96E408451A3895E800087F77 /* ARTWebSocketTransport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTWebSocketTransport.h; sourceTree = ""; }; 96E408461A3895E800087F77 /* ARTWebSocketTransport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTWebSocketTransport.m; sourceTree = ""; }; CDE13941D61BC4A0690896F6 /* Pods-ablySpec.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ablySpec.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ablySpec/Pods-ablySpec.debug.xcconfig"; sourceTree = ""; }; + D70EAAEB1BC3376200CD8B9E /* ARTRestChannel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTRestChannel.h; sourceTree = ""; }; + D70EAAEC1BC3376200CD8B9E /* ARTRestChannel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTRestChannel.m; sourceTree = ""; }; D723046F1BB72CED00F1ABDA /* RealtimeClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealtimeClient.swift; sourceTree = ""; }; D746AE1A1BBB5207003ECEF8 /* ARTDataQuery.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTDataQuery.h; sourceTree = ""; }; D746AE1B1BBB5207003ECEF8 /* ARTDataQuery+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ARTDataQuery+Private.h"; sourceTree = ""; }; @@ -497,6 +501,8 @@ 96BF61511A35B39C004CF2B3 /* ARTRest.h */, 960D07951A46FFC300ED8C8C /* ARTRest+Private.h */, 96BF61521A35B39C004CF2B3 /* ARTRest.m */, + D70EAAEB1BC3376200CD8B9E /* ARTRestChannel.h */, + D70EAAEC1BC3376200CD8B9E /* ARTRestChannel.m */, 96BF616E1A35FB7C004CF2B3 /* ARTAuth.h */, D7C1B8781BBF5F460087B55F /* ARTAuth+Private.h */, 96BF616F1A35FB7C004CF2B3 /* ARTAuth.m */, @@ -639,6 +645,7 @@ 961343D81A42E0B7006DC822 /* ARTClientOptions.h in Headers */, 96BF615E1A35C1C8004CF2B3 /* ARTTypes.h in Headers */, 1C1EC3FA1AE26A8B00AAADD7 /* ARTStatus.h in Headers */, + D70EAAED1BC3376200CD8B9E /* ARTRestChannel.h in Headers */, 96BF61581A35B52C004CF2B3 /* ARTHttp.h in Headers */, 96BF61641A35CDE1004CF2B3 /* ARTBaseMessage.h in Headers */, D746AE221BBB60EE003ECEF8 /* ARTChannel.h in Headers */, @@ -950,6 +957,7 @@ 1CD8DCA01B1C7315007EAF36 /* ARTDefault.m in Sources */, D746AE501BBD84E7003ECEF8 /* ARTChannelOptions.m in Sources */, 1C6C18A41ADFDAB100AB79E4 /* ARTLog.m in Sources */, + D70EAAEE1BC3376200CD8B9E /* ARTRestChannel.m in Sources */, D746AE291BBB61C9003ECEF8 /* ARTPresence.m in Sources */, 96A507BE1A3791490077CDF8 /* ARTRealtime.m in Sources */, 967A43221A39AEAF00E4CE23 /* ARTNSArray+ARTFunctional.m in Sources */, diff --git a/ablySpec/Auth.swift b/ablySpec/Auth.swift index f2445eb79..062f95e59 100644 --- a/ablySpec/Auth.swift +++ b/ablySpec/Auth.swift @@ -15,14 +15,16 @@ import ably.Private class Auth : QuickSpec { override func spec() { + var mockExecutor: MockHTTPExecutor! + beforeEach { mockExecutor = MockHTTPExecutor() } describe("Basic") { // RSA1 - fit("should work over HTTPS only") { + it("should work over HTTPS only") { let clientOptions = AblyTests.setupOptions(AblyTests.jsonRestOptions) clientOptions.tls = false @@ -30,7 +32,7 @@ class Auth : QuickSpec { } // RSA11 - it("should send the API key in the Authorization header") { + fit("should send the API key in the Authorization header") { let client = ARTRest(options: AblyTests.setupOptions(AblyTests.jsonRestOptions)) client.httpExecutor = mockExecutor @@ -39,10 +41,12 @@ class Auth : QuickSpec { let key64 = NSString(string: "\(client.options.key)") .dataUsingEncoding(NSUTF8StringEncoding)? .base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0)) - let Authorization = "Basic \(key64!)" + let expectedAuthorization = "Basic \(key64!)" // X7 - //expect(mockExecutor.requests.first?.allHTTPHeaderFields?["Authorization"]).to(equal(Authorization)) + let Authorization = mockExecutor.requests.first?.allHTTPHeaderFields?["Authorization"] as? String ?? "" + + expect(Authorization).to(equal(expectedAuthorization)) } // RSA2 diff --git a/ablySpec/RestClient.swift b/ablySpec/RestClient.swift index 35f7fe910..b8c761277 100644 --- a/ablySpec/RestClient.swift +++ b/ablySpec/RestClient.swift @@ -16,7 +16,7 @@ class PublishTestMessage { init(client: ARTRest, failOnError: Bool) { self.error = NSError(domain: "", code: -1, userInfo: nil) - + client.channels.get("test").publish("message") { error in self.error = error if failOnError { diff --git a/ablySpec/TestUtilities.swift b/ablySpec/TestUtilities.swift index 234e3cdc0..72bac7e63 100644 --- a/ablySpec/TestUtilities.swift +++ b/ablySpec/TestUtilities.swift @@ -111,7 +111,7 @@ func querySyslog(forLogsAfter startingTime: NSDate? = nil) -> GeneratorOf Date: Tue, 6 Oct 2015 18:00:55 +0100 Subject: [PATCH 20/22] =?UTF-8?q?RSA11:=20=E2=9C=85.=20Fixed=20authorizati?= =?UTF-8?q?on=20header?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ably-ios/ARTAuth.h | 2 +- ably-ios/ARTAuth.m | 4 +-- ably-ios/ARTAuthOptions.h | 3 +-- ably-ios/ARTAuthOptions.m | 7 +++-- ably-ios/ARTChannel+Private.h | 7 +++-- ably-ios/ARTChannel.m | 13 ++++----- ably-ios/ARTChannelCollection+Private.h | 3 +-- ably-ios/ARTChannelCollection.h | 5 ++-- ably-ios/ARTChannelCollection.m | 17 ++++-------- ably-ios/ARTHttp.h | 1 + ably-ios/ARTPresence.h | 4 +-- ably-ios/ARTPresence.m | 16 +++++------ ably-ios/ARTRealtime.h | 2 +- ably-ios/ARTRealtimeChannel.h | 1 - ably-ios/ARTRest+Private.h | 9 +++++-- ably-ios/ARTRest.m | 18 ++++++++++--- ably-ios/ARTRestChannel.h | 5 ---- ably-ios/ARTRestChannel.m | 36 +++++++++++-------------- ablySpec/Auth.swift | 28 ++++++++++++------- ablySpec/RestClient.swift | 2 -- ablySpec/TestUtilities.swift | 8 ++++-- 21 files changed, 101 insertions(+), 90 deletions(-) diff --git a/ably-ios/ARTAuth.h b/ably-ios/ARTAuth.h index 3ef965ea1..0e18e4de0 100644 --- a/ably-ios/ARTAuth.h +++ b/ably-ios/ARTAuth.h @@ -31,8 +31,8 @@ typedef NS_ENUM(NSUInteger, ARTAuthMethod) { @property (nonatomic, weak) ARTLog *logger; @property (nonatomic, readonly, strong) ARTAuthOptions *options; -@property (nonatomic, readonly, assign) ARTAuthMethod method; @property (nonatomic, readonly, strong) ARTAuthTokenDetails *tokenDetails; +@property (nonatomic, readonly, assign) ARTAuthMethod method; - (instancetype)init:(ARTRest *)rest withOptions:(ARTClientOptions *)options; diff --git a/ably-ios/ARTAuth.m b/ably-ios/ARTAuth.m index 2632bbdf1..3f723d4da 100644 --- a/ably-ios/ARTAuth.m +++ b/ably-ios/ARTAuth.m @@ -29,9 +29,7 @@ - (instancetype)init:(ARTRest *)rest withOptions:(ARTClientOptions *)options { _options = options; _logger = rest.logger; - // REV: options.useTokenAuth - - if (options.key != nil && !options.useTokenAuth) { + if ([options isBasicAuth]) { if (!options.tls) { [NSException raise:@"ARTAuthException" format:@"Basic authentication only connects over HTTPS (tls)."]; } diff --git a/ably-ios/ARTAuthOptions.h b/ably-ios/ARTAuthOptions.h index 96941b4d6..6f3160782 100644 --- a/ably-ios/ARTAuthOptions.h +++ b/ably-ios/ARTAuthOptions.h @@ -65,8 +65,6 @@ ART_ASSUME_NONNULL_BEGIN */ @property (nonatomic, assign, nonatomic) BOOL queryTime; -@property (nonatomic, assign) BOOL useTokenAuth; - - (instancetype)init; - (instancetype)initWithKey:(NSString *)key; - (instancetype)initDefaults; @@ -75,6 +73,7 @@ ART_ASSUME_NONNULL_BEGIN - (ARTAuthOptions *)mergeWith:(ARTAuthOptions *)precedenceOptions; +- (BOOL)isBasicAuth; - (BOOL)isMethodGET; - (BOOL)isMethodPOST; diff --git a/ably-ios/ARTAuthOptions.m b/ably-ios/ARTAuthOptions.m index f0d56ce25..e3f1c8e4e 100644 --- a/ably-ios/ARTAuthOptions.m +++ b/ably-ios/ARTAuthOptions.m @@ -51,7 +51,6 @@ - (id)copyWithZone:(NSZone *)zone { options.key = self.key; options.token = self.token; - options.useTokenAuth = self.useTokenAuth; options.authCallback = self.authCallback; options.authUrl = self.authUrl; options.authMethod = self.authMethod; @@ -76,10 +75,10 @@ - (void)setToken:(NSString *)token { } - (void)setAuthMethod:(NSString *)authMethod { + // HTTP Method if (authMethod == nil || authMethod.length == 0) { authMethod = ARTAuthOptionsMethodDefault; } - _authMethod = [authMethod copy]; } @@ -104,6 +103,10 @@ - (ARTAuthOptions *)mergeWith:(ARTAuthOptions *)precedenceOptions { return merged; } +- (BOOL)isBasicAuth { + return self.key != nil; +} + - (BOOL)isMethodPOST { return [_authMethod isEqualToString:@"POST"]; } diff --git a/ably-ios/ARTChannel+Private.h b/ably-ios/ARTChannel+Private.h index c2ee74a9e..0077655e7 100644 --- a/ably-ios/ARTChannel+Private.h +++ b/ably-ios/ARTChannel+Private.h @@ -11,6 +11,8 @@ @protocol ARTPayloadEncoder; +@class ARTRest; + ART_ASSUME_NONNULL_BEGIN @interface ARTChannel() { @@ -19,9 +21,10 @@ ART_ASSUME_NONNULL_BEGIN __weak ARTLog *_logger; } -@property (nonatomic, strong, null_resettable) ARTChannelOptions *options; +@property (nonatomic, weak) ARTRest *rest; +@property (nonatomic, strong, art_null_resettable) ARTChannelOptions *options; -- (instancetype)initWithName:(NSString *)name presence:(ARTPresence *)presence options:(art_nullable ARTChannelOptions *)options; +- (instancetype)initWithName:(NSString *)name rest:(ARTRest *)rest options:(art_nullable ARTChannelOptions *)options; - (void)_postMessages:(id)payload callback:(art_nullable ARTErrorCallback)callback; diff --git a/ably-ios/ARTChannel.m b/ably-ios/ARTChannel.m index f5816e8f0..bff5443b9 100644 --- a/ably-ios/ARTChannel.m +++ b/ably-ios/ARTChannel.m @@ -6,23 +6,25 @@ // Copyright (c) 2015 г. Ably. All rights reserved. // -#import "ARTChannel.h" #import "ARTChannel+Private.h" -#import "ARTNSArray+ARTFunctional.h" #import "ARTPayload.h" +#import "ARTPresence.h" #import "ARTMessage.h" #import "ARTChannelOptions.h" +#import "ARTRest.h" +#import "ARTNSArray+ARTFunctional.h" @implementation ARTChannel -- (instancetype)initWithName:(NSString *)name presence:(ARTPresence *)presence options:(ARTChannelOptions *)options { +- (instancetype)initWithName:(NSString *)name rest:(ARTRest *)rest options:(ARTChannelOptions *)options { if (self = [super init]) { _name = [name copy]; - _presence = presence; + _rest = rest; + _logger = rest.logger; self.options = options; + _presence = [[ARTPresence alloc] initWithChannel:self]; } - return self; } @@ -32,7 +34,6 @@ - (void)setOptions:(ARTChannelOptions *)options { } else { _options = options; } - _payloadEncoder = [ARTJsonPayloadEncoder instance]; } diff --git a/ably-ios/ARTChannelCollection+Private.h b/ably-ios/ARTChannelCollection+Private.h index 9f393d6d8..5859b40f7 100644 --- a/ably-ios/ARTChannelCollection+Private.h +++ b/ably-ios/ARTChannelCollection+Private.h @@ -20,8 +20,7 @@ ART_ASSUME_NONNULL_BEGIN } @property (nonatomic, readonly) NSMutableDictionary *channels; - -- (ARTChannel *)_createChannelWithName:(NSString *)name options:(art_nullable ARTChannelOptions *)options; +@property (readonly, nonatomic, weak) ARTRest *rest; @end diff --git a/ably-ios/ARTChannelCollection.h b/ably-ios/ARTChannelCollection.h index 5c9b801e4..3aa4798ca 100644 --- a/ably-ios/ARTChannelCollection.h +++ b/ably-ios/ARTChannelCollection.h @@ -8,14 +8,13 @@ #import +@class ARTRest; @class ARTChannel; @class ARTChannelOptions; @interface ARTChannelCollection : NSObject -@property (readonly, nonatomic, assign) Class channelClass; - -- (instancetype)initWithChannel:(Class)channelClass; +- (instancetype)initWithRest:(ARTRest *)rest; - (BOOL)exists:(NSString *)channelName; - (ARTChannel *)get:(NSString *)channelName; diff --git a/ably-ios/ARTChannelCollection.m b/ably-ios/ARTChannelCollection.m index 97fdb80ff..32dd9255b 100644 --- a/ably-ios/ARTChannelCollection.m +++ b/ably-ios/ARTChannelCollection.m @@ -9,15 +9,17 @@ #import "ARTChannelCollection.h" #import "ARTChannelCollection+Private.h" +#import "ARTRest+Private.h" #import "ARTChannel.h" #import "ARTChannel+Private.h" #import "ARTChannelOptions.h" +#import "ARTPresence.h" @implementation ARTChannelCollection -- (instancetype)initWithChannel:(Class)channelClass { +- (instancetype)initWithRest:(ARTRest *)rest { if (self = [super init]) { - _channelClass = channelClass; + _rest = rest; _channels = [[NSMutableDictionary alloc] init]; } return self; @@ -50,7 +52,7 @@ - (void)releaseChannel:(ARTChannel *)channel { - (ARTChannel *)_getChannel:(NSString *)channelName options:(ARTChannelOptions *)options { ARTChannel *channel = self->_channels[channelName]; if (!channel) { - channel = [self _createChannelWithName:channelName options:options]; + channel = [[self.rest.channelClass alloc] initWithName:channelName rest:self.rest options:options]; [self->_channels setObject:channel forKey:channelName]; } else if (options) { channel.options = options; @@ -58,13 +60,4 @@ - (ARTChannel *)_getChannel:(NSString *)channelName options:(ARTChannelOptions * return channel; } -- (ARTChannel *)_createChannelWithName:(NSString *)name options:(ARTChannelOptions *)options { - return [[self.channelClass alloc] init]; - - // FIXME: - //WithRest:_rest name:name options:options - - //- (instancetype)initWithName:(NSString *)name presence:(ARTPresence *)presence options:(ARTChannelOptions *)options -} - @end diff --git a/ably-ios/ARTHttp.h b/ably-ios/ARTHttp.h index bdfa7fbb4..9224663a7 100644 --- a/ably-ios/ARTHttp.h +++ b/ably-ios/ARTHttp.h @@ -12,6 +12,7 @@ @class ARTErrorInfo; +// FIXME: @protocol ARTHTTPExecutor @property (nonatomic, weak) ARTLog *logger; diff --git a/ably-ios/ARTPresence.h b/ably-ios/ARTPresence.h index e3e7d1586..fc5244af4 100644 --- a/ably-ios/ARTPresence.h +++ b/ably-ios/ARTPresence.h @@ -12,16 +12,16 @@ @protocol ARTSubscription; +@class ARTChannel; @class ARTPaginatedResult; @class ARTDataQuery; -@class ARTRealtimeChannel; ART_ASSUME_NONNULL_BEGIN /// Provides access to presence operations and state for the associated Channel @interface ARTPresence : NSObject -- (instancetype)initWithChannel:(ARTRealtimeChannel *)channel; +- (instancetype)initWithChannel:(ARTChannel *)channel; /// Get the presence state for one channel - (void)get:(void (^)(ARTPaginatedResult /* */ *__art_nullable result, NSError *__art_nullable error))callback; diff --git a/ably-ios/ARTPresence.m b/ably-ios/ARTPresence.m index 016c86345..1ffcbe996 100644 --- a/ably-ios/ARTPresence.m +++ b/ably-ios/ARTPresence.m @@ -16,20 +16,21 @@ #import "ARTDataQuery+Private.h" -@interface ARTPresence () +@interface ARTPresence () { + __weak ARTLog *_logger; +} -@property (nonatomic, weak) ARTLog *logger; @property (readonly, weak, nonatomic) ARTRealtimeChannel *channel; @end @implementation ARTPresence --(instancetype) initWithChannel:(ARTRealtimeChannel *) channel { - self = [super init]; - if(self) { +// FIXME: +- (instancetype) initWithChannel:(ARTRealtimeChannel *) channel { + if (self = [super init]) { _channel = channel; - self.logger = channel.logger; + //_logger = channel.logger; } return self; } @@ -50,7 +51,7 @@ - (void)enter:(id)data cb:(ARTStatusCallback)cb { [self enterClient:self.channel.clientId data:data cb:cb]; } -- (void) enterClient:(NSString *) clientId data:(id) data cb:(ARTStatusCallback) cb { +- (void)enterClient:(NSString *) clientId data:(id) data cb:(ARTStatusCallback) cb { if(!clientId) { [NSException raise:@"Cannot publish presence without a clientId" format:@""]; } @@ -63,7 +64,6 @@ - (void) enterClient:(NSString *) clientId data:(id) data cb:(ARTStatusCallback) msg.connectionId = self.channel.realtime.connectionId; [self.channel publishPresence:msg cb:cb]; - } - (void)update:(id)data cb:(ARTStatusCallback)cb { diff --git a/ably-ios/ARTRealtime.h b/ably-ios/ARTRealtime.h index f6ef4966c..565b7fc03 100644 --- a/ably-ios/ARTRealtime.h +++ b/ably-ios/ARTRealtime.h @@ -42,7 +42,7 @@ Instance the Ably library using a key only. This is simply a convenience constru /** Instance the Ably library with the given options. -:param options: see {@link io.ably.types.ClientOptions} for options +:param options: see ARTClientOptions for options */ - (instancetype)initWithOptions:(ARTClientOptions *)options; - (instancetype)initWithLogger:(ARTLog *)logger andOptions:(ARTClientOptions *)options; diff --git a/ably-ios/ARTRealtimeChannel.h b/ably-ios/ARTRealtimeChannel.h index f7ac250f2..b7c9f6982 100644 --- a/ably-ios/ARTRealtimeChannel.h +++ b/ably-ios/ARTRealtimeChannel.h @@ -25,7 +25,6 @@ @class ARTRealtimeChannelSubscription; @class ARTRealtimeChannelStateSubscription; - @interface ARTRealtimeChannel : NSObject @property (nonatomic, weak) ARTLog *logger; diff --git a/ably-ios/ARTRest+Private.h b/ably-ios/ARTRest+Private.h index b61e174b5..e6ec59d49 100644 --- a/ably-ios/ARTRest+Private.h +++ b/ably-ios/ARTRest+Private.h @@ -21,7 +21,10 @@ typedef NS_ENUM(NSUInteger, ARTAuthentication) { @interface ARTRest (Private) @property (readonly, strong, nonatomic) id defaultEncoder; +@property (readonly, strong, nonatomic) NSString *defaultEncoding; //Content-Type + @property (nonatomic, strong) id httpExecutor; +@property (readonly, nonatomic, assign) Class channelClass; @property (nonatomic, strong) NSURL *baseUrl; @@ -36,10 +39,12 @@ typedef NS_ENUM(NSUInteger, ARTAuthentication) { - (id)post:(NSString *)relUrl headers:(NSDictionary *)headers body:(NSData *)body authenticated:(ARTAuthentication)authenticated cb:(ARTHttpCb)cb; -- (id)withAuthHeadersUseBasic:(BOOL) useBasic cb:(id(^)(NSDictionary *))cb; +- (id)withAuthHeadersUseBasic:(BOOL)useBasic cb:(id(^)(NSDictionary *))cb; - (id)withAuthHeaders:(id(^)(NSDictionary *authHeaders))cb; - (id)withAuthParams:(id(^)(NSDictionary *authParams))cb; -- (id)postTestStats:(NSArray *) stats cb:(void(^)(ARTStatus * status)) cb; +- (void)executeRequest:(NSMutableURLRequest *)request callback:(void (^)(NSHTTPURLResponse *, NSData *, NSError *))callback; + +- (id)postTestStats:(NSArray *)stats cb:(void(^)(ARTStatus * status)) cb; @end diff --git a/ably-ios/ARTRest.m b/ably-ios/ARTRest.m index 6addedde8..9bb9318be 100644 --- a/ably-ios/ARTRest.m +++ b/ably-ios/ARTRest.m @@ -6,7 +6,6 @@ // Copyright (c) 2014 Ably. All rights reserved. // -#import "ARTRest.h" #import "ARTRest+Private.h" #import "ARTChannel+Private.h" @@ -33,11 +32,12 @@ @interface ARTRest () @property (nonatomic, strong) NSURL *baseUrl; @property (nonatomic, strong) id httpExecutor; +@property (readonly, nonatomic, assign) Class channelClass; @property (readonly, strong, nonatomic) ARTHttp *http; @property (strong, nonatomic) ARTAuth *auth; @property (readonly, strong, nonatomic) NSDictionary *encoders; -@property (readonly, strong, nonatomic) NSString *defaultEncoding; +@property (readonly, strong, nonatomic) NSString *defaultEncoding; //Content-Type @property (readwrite, assign, nonatomic) int fallbackCount; @end @@ -61,7 +61,7 @@ - (instancetype)initWithLogger:(ARTLog *)logger andOptions:(ARTClientOptions *)o _http = [[ARTHttp alloc] init]; _httpExecutor = _http; _httpExecutor.logger = _logger; - _channels = [[ARTChannelCollection alloc] initWithChannel:[ARTRestChannel class]]; + _channelClass = [ARTRestChannel class]; id defaultEncoder = [[ARTJsonEncoder alloc] init]; _encoders = @{ [defaultEncoder mimeType]: defaultEncoder }; @@ -69,6 +69,7 @@ - (instancetype)initWithLogger:(ARTLog *)logger andOptions:(ARTClientOptions *)o _fallbackCount = 0; _auth = [[ARTAuth alloc] init:self withOptions:options]; + _channels = [[ARTChannelCollection alloc] initWithRest:self]; } return self; } @@ -91,6 +92,7 @@ - (void)executeRequest:(NSMutableURLRequest *)request callback:(void (^)(NSHTTPU if (error) { callback(nil, nil, error); } else { + // RFC7235 [request setValue:authorization forHTTPHeaderField:@"Authorization"]; [self.httpExecutor executeRequest:request callback:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { @@ -111,7 +113,15 @@ - (void)executeRequest:(NSMutableURLRequest *)request callback:(void (^)(NSHTTPU } - (void)calculateAuthorization:(void (^)(NSString *authorization, NSError *error))callback { - + if (self.auth.method == ARTAuthMethodBasic) { + // Include key Base64 encoded in an Authorization header (RFC7235) + NSData *keyData = [self.options.key dataUsingEncoding:NSUTF8StringEncoding]; + NSString *keyBase64 = [keyData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithCarriageReturn]; // + callback([NSString stringWithFormat:@"Basic %@", keyBase64], nil); + } + else { + // TODO: ?! + } } - (void)time:(void(^)(NSDate *time, NSError *error))callback { diff --git a/ably-ios/ARTRestChannel.h b/ably-ios/ARTRestChannel.h index a072abab2..02be35302 100644 --- a/ably-ios/ARTRestChannel.h +++ b/ably-ios/ARTRestChannel.h @@ -16,11 +16,6 @@ ART_ASSUME_NONNULL_BEGIN @interface ARTRestChannel : ARTChannel -@property (nonatomic, strong, readonly) ARTPresence *presence; -@property (nonatomic, strong) NSString *basePath; - -//- (instancetype)initWithRest:(ARTRest *)rest name:(NSString *)name options:(art_nullable ARTChannelOptions *)options; - @end ART_ASSUME_NONNULL_END diff --git a/ably-ios/ARTRestChannel.m b/ably-ios/ARTRestChannel.m index e7a187041..6a6c245d6 100644 --- a/ably-ios/ARTRestChannel.m +++ b/ably-ios/ARTRestChannel.m @@ -8,27 +8,24 @@ #import "ARTRestChannel.h" -#import "ARTRest.h" +#import "ARTRest+Private.h" #import "ARTChannelOptions.h" #import "ARTChannel+Private.h" +#import "ARTMessage.h" #import "ARTPaginatedResult+Private.h" #import "ARTDataQuery+Private.h" +#import "ARTEncoder.h" -@implementation ARTRestChannel - -@dynamic presence; +@implementation ARTRestChannel { +@public + NSString *_basePath; +} -- (instancetype)initWithRest:(ARTRest *)rest name:(NSString *)name options:(ARTChannelOptions *)options { - self = [super init]; - /* - if (self = [super initWithName:name presence:[[ARTRestPresence alloc] initWithChannel:self] options:options]) { - _logger = rest.logger; +- (instancetype)initWithName:(NSString *)name rest:(ARTRest *)rest options:(ARTChannelOptions *)options { + if (self = [super initWithName:name rest:rest options:options]) { [_logger debug:@"ARTRestChannel: instantiating under %@", name]; - _rest = rest; _basePath = [NSString stringWithFormat:@"/channels/%@", [name stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLPathAllowedCharacterSet]]]; } - */ - return self; } @@ -55,28 +52,27 @@ - (void)history:(ARTDataQuery *)query callback:(void (^)(ARTPaginatedResult *, N } - (void)_postMessages:(id)payload callback:(ARTErrorCallback)callback { - //FIXME: - /* NSData *encodedMessage = nil; + if ([payload isKindOfClass:[ARTMessage class]]) { - encodedMessage = [_rest.defaultEncoder encodeMessage:payload]; + encodedMessage = [self.rest.defaultEncoder encodeMessage:payload]; } else if ([payload isKindOfClass:[NSArray class]]) { - encodedMessage = [_rest.defaultEncoder encodeMessages:payload]; + encodedMessage = [self.rest.defaultEncoder encodeMessages:payload]; } NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[_basePath stringByAppendingPathComponent:@"messages"]]]; request.HTTPMethod = @"POST"; request.HTTPBody = encodedMessage; - if (_rest.defaultEncoding) { - [request setValue:_rest.defaultEncoding forHTTPHeaderField:@"Content-Type"]; + + if (self.rest.defaultEncoding) { + [request setValue:self.rest.defaultEncoding forHTTPHeaderField:@"Content-Type"]; } - [_rest executeRequest:request callback:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { + [self.rest executeRequest:request callback:^(NSHTTPURLResponse *response, NSData *data, NSError *error) { if (callback) { callback(error); } }]; - */ } @end diff --git a/ablySpec/Auth.swift b/ablySpec/Auth.swift index 062f95e59..5e89111e9 100644 --- a/ablySpec/Auth.swift +++ b/ablySpec/Auth.swift @@ -35,18 +35,22 @@ class Auth : QuickSpec { fit("should send the API key in the Authorization header") { let client = ARTRest(options: AblyTests.setupOptions(AblyTests.jsonRestOptions)) client.httpExecutor = mockExecutor - + publishTestMessage(client, failOnError: false) - - let key64 = NSString(string: "\(client.options.key)") + + expect(client.options.key).toNot(beNil()) + + let key64 = NSString(string: "\(client.options.key!)") .dataUsingEncoding(NSUTF8StringEncoding)? .base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0)) + let expectedAuthorization = "Basic \(key64!)" - // X7 - let Authorization = mockExecutor.requests.first?.allHTTPHeaderFields?["Authorization"] as? String ?? "" + expect(mockExecutor.requests.first).toNot(beNil()) - expect(Authorization).to(equal(expectedAuthorization)) + let authorization = mockExecutor.requests.first?.allHTTPHeaderFields?["Authorization"] as? String ?? "" + + expect(authorization).to(equal(expectedAuthorization)) } // RSA2 @@ -73,17 +77,21 @@ class Auth : QuickSpec { let token64 = NSString(string: currentToken) .dataUsingEncoding(NSUTF8StringEncoding)? .base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0)) - let Authorization = "Bearer \(token64!)" - // X7 - //expect(mockExecutor.requests.first?.allHTTPHeaderFields?["Authorization"]).to(equal(Authorization)) + let expectedAuthorization = "Bearer \(token64!)" + + expect(mockExecutor.requests.first).toNot(beNil()) + + let authorization = mockExecutor.requests.first?.allHTTPHeaderFields?["Authorization"] as? String ?? "" + + expect(authorization).to(equal(expectedAuthorization)) } } // RSA4 context("authentication method") { let cases: [String: (ARTAuthOptions) -> ()] = [ - "useTokenAuth": { $0.useTokenAuth = true; $0.key = "fake:key" }, + "useBasicAuth": { $0.key = "fake:key" }, "authUrl": { $0.authUrl = NSURL(string: "http://test.com") }, "authCallback": { $0.authCallback = { _, _ in return } }, "token": { $0.token = "" } diff --git a/ablySpec/RestClient.swift b/ablySpec/RestClient.swift index b8c761277..0d5d59c58 100644 --- a/ablySpec/RestClient.swift +++ b/ablySpec/RestClient.swift @@ -32,7 +32,6 @@ func publishTestMessage(client: ARTRest, failOnError: Bool = true) -> PublishTes func getTestToken() -> String { let options = AblyTests.commonAppSetup() - options.useTokenAuth = true let client = ARTRest(options: options) var token: String? @@ -99,7 +98,6 @@ class RestClient: QuickSpec { it("should result in error status when provided a bad token") { let options = AblyTests.commonAppSetup() - options.useTokenAuth = true options.token = "invalid_token" let client = ARTRest(options: options) diff --git a/ablySpec/TestUtilities.swift b/ablySpec/TestUtilities.swift index 72bac7e63..a431474b4 100644 --- a/ablySpec/TestUtilities.swift +++ b/ablySpec/TestUtilities.swift @@ -111,8 +111,12 @@ func querySyslog(forLogsAfter startingTime: NSDate? = nil) -> GeneratorOf Void)!) { self.requests.append(request) - self._executor.executeRequest(request, callback: callback) + self.executor.executeRequest(request, callback: callback) } } From 4e606fef1bca0d2f8a95c109ffcdf17e5cd1b4ce Mon Sep 17 00:00:00 2001 From: Ricardo Pereira Date: Wed, 7 Oct 2015 11:27:25 +0100 Subject: [PATCH 21/22] RSA3a: not passing --- ablySpec/Auth.swift | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/ablySpec/Auth.swift b/ablySpec/Auth.swift index 5e89111e9..fb4d51b61 100644 --- a/ablySpec/Auth.swift +++ b/ablySpec/Auth.swift @@ -23,6 +23,7 @@ class Auth : QuickSpec { } describe("Basic") { + // RSA1 it("should work over HTTPS only") { let clientOptions = AblyTests.setupOptions(AblyTests.jsonRestOptions) @@ -32,7 +33,7 @@ class Auth : QuickSpec { } // RSA11 - fit("should send the API key in the Authorization header") { + it("should send the API key in the Authorization header") { let client = ARTRest(options: AblyTests.setupOptions(AblyTests.jsonRestOptions)) client.httpExecutor = mockExecutor @@ -63,7 +64,28 @@ class Auth : QuickSpec { describe("Token") { - it("should send the token in the Authorization header") { + // RSA3a + fit("should work over HTTPS or HTTP") { + let options = ARTClientOptions() + options.token = getTestToken() + let client = ARTRest(options: options) + options.tls = false + + client.httpExecutor = mockExecutor + + publishTestMessage(client, failOnError: false) + + expect(mockExecutor.requests.first).toNot(beNil()) + expect(mockExecutor.requests.first?.URL).toNot(beNil()) + + // TODO: not passing + + if let request = mockExecutor.requests.first, let url = request.URL { + expect(url.scheme).to(equal("http")) + } + } + + fit("should send the token in the Authorization header") { let options = ARTClientOptions() options.token = getTestToken() let client = ARTRest(options: options) From 212f64e0726b24293180c857ebda96fe221175dc Mon Sep 17 00:00:00 2001 From: Ricardo Pereira Date: Thu, 8 Oct 2015 19:39:38 +0100 Subject: [PATCH 22/22] Fixed token request (http body, timestamp, hmac generator). --- ably-ios/ARTAuth.m | 12 ++++-- ably-ios/ARTAuthTokenDetails.m | 5 +++ ably-ios/ARTAuthTokenParams.m | 8 +++- ably-ios/ARTAuthTokenRequest.m | 5 +++ ably-ios/ARTEncoder.h | 7 +++- ably-ios/ARTHttp.m | 1 + ably-ios/ARTJsonEncoder.m | 34 ++++++++++++++-- ably-ios/ARTNSDictionary+ARTDictionaryUtil.h | 2 + ably-ios/ARTRest.m | 7 +++- ablySpec/Auth.swift | 7 ++-- ablySpec/RestClient.swift | 37 +---------------- ablySpec/Stats.swift | 12 +++--- ablySpec/TestUtilities.swift | 42 ++++++++++++++++++++ 13 files changed, 123 insertions(+), 56 deletions(-) diff --git a/ably-ios/ARTAuth.m b/ably-ios/ARTAuth.m index 3f723d4da..c285208cb 100644 --- a/ably-ios/ARTAuth.m +++ b/ably-ios/ARTAuth.m @@ -106,7 +106,7 @@ - (void)requestToken:(ARTAuthTokenParams *)tokenParams withOptions:(ARTAuthOptio // The values supersede matching client library configured params and options. ARTAuthOptions *mergedOptions = [self mergeOptions:authOptions]; ARTAuthTokenParams *currentTokenParams = [self mergeParams:tokenParams]; - + if (mergedOptions.authUrl) { NSMutableURLRequest *request = [self buildRequest:mergedOptions withParams:currentTokenParams]; @@ -137,7 +137,6 @@ - (void)requestToken:(ARTAuthTokenParams *)tokenParams withOptions:(ARTAuthOptio } } -// FIXME: need revision - (void)requestToken:(ARTAuthTokenRequest *)tokenRequest callback:(void (^)(ARTAuthTokenDetails *, NSError *))callback { NSURL *requestUrl = [NSURL URLWithString:[NSString stringWithFormat:@"/keys/%@/requestToken", tokenRequest.keyName] relativeToURL:_rest.baseUrl]; @@ -155,7 +154,13 @@ - (void)requestToken:(ARTAuthTokenRequest *)tokenRequest callback:(void (^)(ARTA if (error) { callback(nil, error); } else { - callback([defaultEncoder decodeAccessToken:data], nil); + NSError *decodeError = nil; + ARTAuthTokenDetails *tokenDetails = [defaultEncoder decodeAccessToken:data error:&decodeError]; + if (decodeError) { + callback(nil, decodeError); + } else { + callback(tokenDetails, nil); + } } }]; } @@ -181,6 +186,7 @@ - (void)authorise:(ARTAuthTokenParams *)tokenParams options:(ARTAuthOptions *)op - (void)createTokenRequest:(ARTAuthTokenParams *)tokenParams options:(ARTAuthOptions *)options callback:(void (^)(ARTAuthTokenRequest *, NSError *))callback { ARTAuthOptions *mergedOptions = options; + // FIXME: review if (mergedOptions.queryTime) { ARTAuthTokenParams *newParams = [[ARTAuthTokenParams alloc] init]; newParams.ttl = tokenParams.ttl; diff --git a/ably-ios/ARTAuthTokenDetails.m b/ably-ios/ARTAuthTokenDetails.m index d5d0e967b..9bed60ab6 100644 --- a/ably-ios/ARTAuthTokenDetails.m +++ b/ably-ios/ARTAuthTokenDetails.m @@ -30,4 +30,9 @@ - (instancetype)initWithToken:(NSString *)token { return self; } +- (NSString *)description { + return [NSString stringWithFormat: @"ARTAuthTokenDetails: token=%@ clientId=%@ issued=%@ expires=%@", + self.token, self.clientId, self.issued, self.expires]; +} + @end diff --git a/ably-ios/ARTAuthTokenParams.m b/ably-ios/ARTAuthTokenParams.m index 4c9d1918d..3e72a5578 100644 --- a/ably-ios/ARTAuthTokenParams.m +++ b/ably-ios/ARTAuthTokenParams.m @@ -32,6 +32,11 @@ - (instancetype)init { return self; } +- (NSString *)description { + return [NSString stringWithFormat: @"ARTAuthTokenParams: ttl=%f capability=%@ timestamp=%@", + self.ttl, self.capability, self.timestamp]; +} + - (void)setTimestamp:(NSDate *)timestamp { if (timestamp == nil) { timestamp = [NSDate date]; @@ -139,8 +144,9 @@ - (ARTAuthTokenRequest *)sign:(NSString *)key { NSString *keyName = keyComponents[0]; NSString *keySecret = keyComponents[1]; NSString *nonce = generateNonce(); + NSString *clientId = self.clientId ? self.clientId : @""; - NSString *signText = [NSString stringWithFormat:@"%@\n%lld\n%@\n%@\n%lld\n%@\n", keyName, (int64_t)(self.ttl * 1000), self.capability, self.clientId, (int64_t)(self.timestamp.timeIntervalSince1970 * 1000), nonce]; + NSString *signText = [NSString stringWithFormat:@"%@\n%lld\n%@\n%@\n%lld\n%@\n", keyName, (int64_t)(self.ttl * 1000), self.capability, clientId, (int64_t)(self.timestamp.timeIntervalSince1970 * 1000), nonce]; NSString *mac = hmacForDataAndKey([signText dataUsingEncoding:NSUTF8StringEncoding], [keySecret dataUsingEncoding:NSUTF8StringEncoding]); return [[ARTAuthTokenRequest alloc] initWithTokenParams:self keyName:keyName nonce:nonce mac:mac]; diff --git a/ably-ios/ARTAuthTokenRequest.m b/ably-ios/ARTAuthTokenRequest.m index 6af3c9270..c318b089e 100644 --- a/ably-ios/ARTAuthTokenRequest.m +++ b/ably-ios/ARTAuthTokenRequest.m @@ -32,4 +32,9 @@ - (NSDictionary *)asDictionary { return nil; } +- (NSString *)description { + return [NSString stringWithFormat: @"ARTAuthTokenRequest: keyName=%@ clientId=%@ nonce=%@ mac=%@ ttl=%f capability=%@ timestamp=%@", + self.keyName, self.clientId, self.nonce, self.mac, self.ttl, self.capability, self.timestamp]; +} + @end diff --git a/ably-ios/ARTEncoder.h b/ably-ios/ARTEncoder.h index 86999d061..04f239d6a 100644 --- a/ably-ios/ARTEncoder.h +++ b/ably-ios/ARTEncoder.h @@ -7,6 +7,7 @@ // #import +#import "CompatibilityMacros.h" @class ARTMessage; @class ARTPresenceMessage; @@ -14,13 +15,15 @@ @class ARTAuthTokenDetails; @class ARTAuthTokenRequest; +ART_ASSUME_NONNULL_BEGIN + @protocol ARTEncoder - (NSString *)mimeType; - (NSData *)encodeTokenRequest:(ARTAuthTokenRequest *)request; -- (ARTAuthTokenDetails *)decodeAccessToken:(NSData *)data; +- (art_nullable ARTAuthTokenDetails *)decodeAccessToken:(NSData *)data error:(NSError * __autoreleasing *)error; - (ARTMessage *)decodeMessage:(NSData *)data; - (NSArray *)decodeMessages:(NSData *)data; - (NSData *)encodeMessage:(ARTMessage *)message; @@ -39,3 +42,5 @@ - (NSArray *)decodeStats:(NSData *)data; @end + +ART_ASSUME_NONNULL_END diff --git a/ably-ios/ARTHttp.m b/ably-ios/ARTHttp.m index 7b3aec00a..9c177ecea 100644 --- a/ably-ios/ARTHttp.m +++ b/ably-ios/ARTHttp.m @@ -172,6 +172,7 @@ - (void)executeRequest:(NSMutableURLRequest *)request callback:(void (^)(NSHTTPU [self.logger verbose:@"Headers %@", request.allHTTPHeaderFields]; CFRunLoopRef currentRunloop = CFRunLoopGetCurrent(); + NSURLSessionDataTask *task = [_urlSession dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; diff --git a/ably-ios/ARTJsonEncoder.m b/ably-ios/ARTJsonEncoder.m index 525593505..acfa16b23 100644 --- a/ably-ios/ARTJsonEncoder.m +++ b/ably-ios/ARTJsonEncoder.m @@ -19,6 +19,7 @@ #import "ARTHttp.h" #import "ARTStatus.h" #import "ARTAuthTokenDetails.h" +#import "ARTAuthTokenRequest.h" @interface ARTJsonEncoder () @@ -105,8 +106,8 @@ - (ARTProtocolMessage *)decodeProtocolMessage:(NSData *)data { return [self protocolMessageFromDictionary:[self decodeDictionary:data]]; } -- (ARTAuthTokenDetails *)decodeAccessToken:(NSData *) data { - return [self tokenFromDictionary:[self decodeDictionary:data]]; +- (ARTAuthTokenDetails *)decodeAccessToken:(NSData *)data error:(NSError * __autoreleasing *)error { + return [self tokenFromDictionary:[self decodeDictionary:data] error:error]; } - (NSData *)encodeTokenRequest:(ARTAuthTokenRequest *)request { @@ -330,12 +331,28 @@ - (NSDictionary *)protocolMessageToDictionary:(ARTProtocolMessage *)message { return output; } -- (ARTAuthTokenDetails *)tokenFromDictionary:(NSDictionary *)input { +- (ARTAuthTokenDetails *)tokenFromDictionary:(NSDictionary *)input error:(NSError * __autoreleasing *)error { [self.logger verbose:@"ARTJsonEncoder: tokenFromDictionary %@", input]; + if (![input isKindOfClass:[NSDictionary class]]) { return nil; } + NSDictionary *jsonError = [input artDictionary:@"error"]; + if (jsonError) { + [self.logger error:@"ARTJsonEncoder: tokenFromDictionary error %@", jsonError]; + if (error) { + NSMutableDictionary* details = [NSMutableDictionary dictionary]; + [details setValue:[jsonError artString:@"message"] forKey:NSLocalizedDescriptionKey]; + // FIXME: domain + *error = [NSError errorWithDomain:@"Ably" + code:[jsonError artNumber:@"code"].integerValue + userInfo:details]; + } + return nil; + } + error = nil; + NSData *tokenData = [[input artString:@"token"] dataUsingEncoding:NSUTF8StringEncoding]; NSString *token = [ARTBase64PayloadEncoder toBase64:tokenData]; NSNumber *expiresTimeInterval = [input artNumber:@"expires"]; @@ -353,8 +370,17 @@ - (ARTAuthTokenDetails *)tokenFromDictionary:(NSDictionary *)input { - (NSDictionary *)tokenRequestToDictionary:(ARTAuthTokenRequest *)tokenRequest { [self.logger verbose:@"ARTJsonEncoder: tokenRequestToDictionary %@", tokenRequest]; + + NSNumber *timestamp = [NSNumber numberWithInteger:[NSNumber numberWithDouble:tokenRequest.timestamp.timeIntervalSince1970 * 1000].integerValue]; + return @{ - + @"keyName":tokenRequest.keyName, + @"ttl":[NSString stringWithFormat:@"%lld", (int64_t)(tokenRequest.ttl * 1000)], + @"capability":tokenRequest.capability, + @"clientId":tokenRequest.clientId ? tokenRequest.clientId : @"", + @"timestamp":timestamp ? timestamp : [NSNumber numberWithInteger:[NSNumber numberWithDouble:[NSDate date].timeIntervalSince1970 * 1000].integerValue], + @"nonce":tokenRequest.nonce, + @"mac":tokenRequest.mac, }; } diff --git a/ably-ios/ARTNSDictionary+ARTDictionaryUtil.h b/ably-ios/ARTNSDictionary+ARTDictionaryUtil.h index 5ee3d6482..521f75824 100644 --- a/ably-ios/ARTNSDictionary+ARTDictionaryUtil.h +++ b/ably-ios/ARTNSDictionary+ARTDictionaryUtil.h @@ -13,6 +13,8 @@ - (NSString *)artString:(id)key; - (NSNumber *)artNumber:(id)key; - (NSDate *)artDate:(id)key; +- (NSArray *)artArray:(id)key; +- (NSDictionary *)artDictionary:(id)key; - (id)artTyped:(Class)cls key:(id)key; diff --git a/ably-ios/ARTRest.m b/ably-ios/ARTRest.m index 9bb9318be..5f3e6382c 100644 --- a/ably-ios/ARTRest.m +++ b/ably-ios/ARTRest.m @@ -116,11 +116,14 @@ - (void)calculateAuthorization:(void (^)(NSString *authorization, NSError *error if (self.auth.method == ARTAuthMethodBasic) { // Include key Base64 encoded in an Authorization header (RFC7235) NSData *keyData = [self.options.key dataUsingEncoding:NSUTF8StringEncoding]; - NSString *keyBase64 = [keyData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithCarriageReturn]; // + NSString *keyBase64 = [keyData base64EncodedStringWithOptions:0]; //NSDataBase64EncodingEndLineWithCarriageReturn callback([NSString stringWithFormat:@"Basic %@", keyBase64], nil); } else { - // TODO: ?! + // TODO: Reuse + NSData *keyData = [self.options.token dataUsingEncoding:NSUTF8StringEncoding]; + NSString *keyBase64 = [keyData base64EncodedStringWithOptions:0]; + callback([NSString stringWithFormat:@"Bearer %@", keyBase64], nil); } } diff --git a/ablySpec/Auth.swift b/ablySpec/Auth.swift index fb4d51b61..2b30e5f14 100644 --- a/ablySpec/Auth.swift +++ b/ablySpec/Auth.swift @@ -68,6 +68,9 @@ class Auth : QuickSpec { fit("should work over HTTPS or HTTP") { let options = ARTClientOptions() options.token = getTestToken() + + expect(options.token!.isEmpty) == false + let client = ARTRest(options: options) options.tls = false @@ -78,14 +81,12 @@ class Auth : QuickSpec { expect(mockExecutor.requests.first).toNot(beNil()) expect(mockExecutor.requests.first?.URL).toNot(beNil()) - // TODO: not passing - if let request = mockExecutor.requests.first, let url = request.URL { expect(url.scheme).to(equal("http")) } } - fit("should send the token in the Authorization header") { + it("should send the token in the Authorization header") { let options = ARTClientOptions() options.token = getTestToken() let client = ARTRest(options: options) diff --git a/ablySpec/RestClient.swift b/ablySpec/RestClient.swift index 0d5d59c58..308facd2f 100644 --- a/ablySpec/RestClient.swift +++ b/ablySpec/RestClient.swift @@ -8,45 +8,10 @@ import Nimble import Quick + import ably import ably.Private -class PublishTestMessage { - var error: NSError? - - init(client: ARTRest, failOnError: Bool) { - self.error = NSError(domain: "", code: -1, userInfo: nil) - - client.channels.get("test").publish("message") { error in - self.error = error - if failOnError { - XCTFail("Got error '\(error)'") - } - } - } -} - -func publishTestMessage(client: ARTRest, failOnError: Bool = true) -> PublishTestMessage { - return PublishTestMessage(client: client, failOnError: failOnError) -} - -func getTestToken() -> String { - let options = AblyTests.commonAppSetup() - let client = ARTRest(options: options) - - var token: String? - client.auth.requestToken(nil, withOptions: nil) { tokenDetails, error in - token = tokenDetails?.token - return - } - - while token == nil { - CFRunLoopRunInMode(kCFRunLoopDefaultMode, CFTimeInterval(0.1), Boolean(0)) - } - - return token! -} - class RestClient: QuickSpec { override func spec() { describe("RestClient") { diff --git a/ablySpec/Stats.swift b/ablySpec/Stats.swift index a900a1732..e6be53c9a 100644 --- a/ablySpec/Stats.swift +++ b/ablySpec/Stats.swift @@ -24,7 +24,7 @@ class Stats: QuickSpec { let data: JSON = [ [ attribute: [ "messages": [ "count": 5], "all": [ "data": 10 ] ] ] ] - let stats = encoder.decodeStats(data.rawData())[0] as? ARTStats + let stats = encoder.decodeStats(data.rawData()!)[0] as? ARTStats let subject = stats?.valueForKey(attribute) as? ARTStatsMessageTypes it("should return a MessagesTypes object") { @@ -57,7 +57,7 @@ class Stats: QuickSpec { "all": [ "messages": [ "count": 25 ], "presence": [ "data": 210 ] ] ] ] ] - let stats = encoder.decodeStats(data.rawData())[0] as? ARTStats + let stats = encoder.decodeStats(data.rawData()!)[0] as? ARTStats let subject = stats?.valueForKey(direction) as? ARTStatsMessageTraffic it("should return a MessageTraffic object") { @@ -81,7 +81,7 @@ class Stats: QuickSpec { let data: JSON = [ [ "connections": [ "tls": [ "opened": 5], "all": [ "peak": 10 ] ] ] ] - let stats = encoder.decodeStats(data.rawData())[0] as? ARTStats + let stats = encoder.decodeStats(data.rawData()!)[0] as? ARTStats let subject = stats?.connections it("should return a ConnectionTypes object") { @@ -107,7 +107,7 @@ class Stats: QuickSpec { let data: JSON = [ [ "channels": [ "opened": 5, "peak": 10 ] ] ] - let stats = encoder.decodeStats(data.rawData())[0] as? ARTStats + let stats = encoder.decodeStats(data.rawData()!)[0] as? ARTStats let subject = stats?.channels it("should return a ResourceCount object") { @@ -133,7 +133,7 @@ class Stats: QuickSpec { let data: JSON = [ [ requestType: [ "succeeded": 5, "failed": 10 ] ] ] - let stats = encoder.decodeStats(data.rawData())[0] as? ARTStats + let stats = encoder.decodeStats(data.rawData()!)[0] as? ARTStats let subject = stats?.valueForKey(requestType) as? ARTStatsRequestCount context(requestType) { @@ -155,7 +155,7 @@ class Stats: QuickSpec { let data: JSON = [ [ "intervalId": "2004-02-01:05:06" ] ] - let stats = encoder.decodeStats(data.rawData())[0] as? ARTStats + let stats = encoder.decodeStats(data.rawData()!)[0] as? ARTStats it("should return a Date object representing the start of the interval") { let dateComponents = NSDateComponents() diff --git a/ablySpec/TestUtilities.swift b/ablySpec/TestUtilities.swift index a431474b4..ff6869eb8 100644 --- a/ablySpec/TestUtilities.swift +++ b/ablySpec/TestUtilities.swift @@ -110,6 +110,48 @@ func querySyslog(forLogsAfter startingTime: NSDate? = nil) -> GeneratorOf PublishTestMessage { + return PublishTestMessage(client: client, failOnError: failOnError) +} + +func getTestToken() -> String { + let options = AblyTests.setupOptions(AblyTests.jsonRestOptions) + let client = ARTRest(options: options) + + var token: String? + var error: NSError? + + client.auth.requestToken(nil, withOptions: nil) { tokenDetails, _error in + token = tokenDetails?.token + error = _error + return + } + + while token == nil && error == nil { + CFRunLoopRunInMode(kCFRunLoopDefaultMode, CFTimeInterval(0.1), Boolean(0)) + } + + return token ?? "" +} + @objc /* Records each request for test purpose.