From 276fbc0e1a1caab1628885b4dc71474f3e2ece57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toni=20C=C3=A1rdenas?= Date: Thu, 28 Jan 2016 05:49:48 +0100 Subject: [PATCH 1/4] Rename payload -> data, refactor message data encoding. Well, this took a bit of time to get right. It started just wanting to comply with the spec, and access message data just by doing `message.data` instead of `message.payload.payload`. So I wanted to remove just the amount of code necessary to do that, but ended up opening a can of worms and didn't stop until it kind of made sense. It seems like the whole ARTPayload business was designed with a use case which didn't really fit the spec at all, so it was then hacked here and there to kind of work in some (not all) cases by bypassing the design. It was also really overengineered, with a whole class hierarchy thingy just for a few predefined cases. I simplified the whole thing a lot and made it fit the spec. I could've just set up a custom `message.data` getter method that extracted the data from the ARTPayload instance to make it comply with the spec, but well, this would've hit us down the road anyway. --- ably-ios/ARTAuthTokenParams.m | 3 +- ably-ios/ARTBaseMessage.h | 12 +- ably-ios/ARTBaseMessage.m | 37 +- ably-ios/ARTChannel+Private.h | 4 +- ably-ios/ARTChannel.h | 12 +- ably-ios/ARTChannel.m | 31 +- ably-ios/ARTDataEncoder.h | 34 ++ ably-ios/ARTDataEncoder.m | 175 +++++++ ably-ios/ARTJsonEncoder.m | 53 +-- ably-ios/ARTMessage.h | 4 +- ably-ios/ARTMessage.m | 19 +- ably-ios/ARTPayload+Private.h | 17 - ably-ios/ARTPayload.h | 85 ---- ably-ios/ARTPayload.m | 463 ------------------- ably-ios/ARTRealtimeChannel.h | 6 +- ably-ios/ARTRealtimeChannel.m | 56 +-- ably-ios/ARTRealtimePresence.m | 14 +- ably-ios/ARTRestChannel.h | 1 - ably-ios/ARTRestChannel.m | 6 +- ably-ios/ARTRestPresence.m | 10 +- ably-ios/ably.h | 2 +- ably-ios/ably.modulemap | 1 - ably-iosTests/ARTRealtimeChannelTest.m | 18 - ably-iosTests/ARTRealtimeCryptoMessageTest.m | 140 ------ ably-iosTests/ARTRealtimeCryptoTest.m | 5 +- ably-iosTests/ARTRealtimeMessageTest.m | 2 - ably-iosTests/ARTRestChannelPublishTest.m | 21 - ably-iosTests/ARTRestTokenTest.m | 2 +- ably-iosTests/ARTTestUtil.h | 4 +- ably-iosTests/ARTTestUtil.m | 6 +- ably.xcodeproj/project.pbxproj | 34 +- ablySpec/RealtimeClientConnection.swift | 2 +- ablySpec/RestChannel.swift | 32 +- 33 files changed, 370 insertions(+), 941 deletions(-) create mode 100644 ably-ios/ARTDataEncoder.h create mode 100644 ably-ios/ARTDataEncoder.m delete mode 100644 ably-ios/ARTPayload+Private.h delete mode 100644 ably-ios/ARTPayload.h delete mode 100644 ably-ios/ARTPayload.m delete mode 100644 ably-iosTests/ARTRealtimeCryptoMessageTest.m diff --git a/ably-ios/ARTAuthTokenParams.m b/ably-ios/ARTAuthTokenParams.m index 436fc06a5..2734fe96a 100644 --- a/ably-ios/ARTAuthTokenParams.m +++ b/ably-ios/ARTAuthTokenParams.m @@ -13,7 +13,6 @@ #import "ARTDefault.h" #import "ARTEncoder.h" -#import "ARTPayload.h" #import "ARTAuthTokenRequest.h" @implementation ARTAuthTokenParams @@ -130,7 +129,7 @@ - (NSDictionary *)toDictionaryWithUnion:(NSArray *)items { CCHmac(kCCHmacAlgSHA256, cKey, keyLen, cData, dataLen, hmac); NSData *mac = [[NSData alloc] initWithBytes:hmac length:sizeof(hmac)]; - NSString *str = [ARTBase64PayloadEncoder toBase64:mac]; + NSString *str = [mac base64EncodedStringWithOptions:0]; return str; } diff --git a/ably-ios/ARTBaseMessage.h b/ably-ios/ARTBaseMessage.h index cebd612c0..25eaf679e 100644 --- a/ably-ios/ARTBaseMessage.h +++ b/ably-ios/ARTBaseMessage.h @@ -8,7 +8,8 @@ #import #import "CompatibilityMacros.h" -#import "ARTPayload.h" +#import "ARTDataEncoder.h" +#import "ARTStatus.h" ART_ASSUME_NONNULL_BEGIN @@ -29,16 +30,15 @@ ART_ASSUME_NONNULL_BEGIN /// Any transformation applied to the data for this message @property (strong, nonatomic) NSString *encoding; -/// The message payload. -@property (strong, nonatomic) ARTPayload *payload; +@property (strong, nonatomic) id data; -- (instancetype)decode:(id)encoder; -- (instancetype)encode:(id)encoder; +- (ARTStatus *__art_nonnull)decodeWithEncoder:(ARTDataEncoder*)encoder output:(id __art_nonnull*__art_nonnull)output; +- (ARTStatus *__art_nonnull)encodeWithEncoder:(ARTDataEncoder*)encoder output:(id __art_nonnull*__art_nonnull)output; - (id)content; - (NSString *)description; -- (instancetype)messageWithPayload:(ARTPayload *)payload; +- (instancetype)messageWithData:(id)data encoding:(NSString *)encoding; @end diff --git a/ably-ios/ARTBaseMessage.m b/ably-ios/ARTBaseMessage.m index dbabf929b..ec7653370 100644 --- a/ably-ios/ARTBaseMessage.m +++ b/ably-ios/ARTBaseMessage.m @@ -8,6 +8,7 @@ #import "ARTBaseMessage.h" #import "ARTLog.h" +#import "ARTStatus.h" @implementation ARTBaseMessage @@ -26,33 +27,41 @@ - (id)copyWithZone:(NSZone *)zone { message->_id = self.id; message->_clientId = self.clientId; message->_timestamp = self.timestamp; - message->_payload = self.payload; + message->_data = self.data; message->_connectionId = self.connectionId; message->_encoding = self.encoding; return message; } -- (instancetype)messageWithPayload:(ARTPayload *)payload { +- (instancetype)messageWithData:(id)data encoding:(NSString *)encoding { ARTBaseMessage *message = [self copy]; - message.payload = payload; + message.data = data; + message.encoding = encoding; return message; } -- (instancetype)decode:(id)encoder { - ARTPayload *payload = self.payload; - [encoder decode:payload output:&payload]; - return [self messageWithPayload:payload]; +- (ARTStatus *)decodeWithEncoder:(ARTDataEncoder*)encoder output:(id *)output { + id decoded = nil; + NSString *decodedEncoding = nil; + ARTStatus *status = [encoder decode:self.data encoding:self.encoding outputData:&decoded outputEncoding:&decodedEncoding]; + *output = [self copy]; + ((ARTBaseMessage *)*output).data = decoded; + ((ARTBaseMessage *)*output).encoding = decodedEncoding; + return status; } -- (instancetype)encode:(id)encoder { - ARTPayload *payload = self.payload; - [encoder encode:payload output:&payload]; - return [self messageWithPayload:payload]; +- (ARTStatus *)encodeWithEncoder:(ARTDataEncoder*)encoder output:(id *)output { + id encoded = nil; + NSString *encoding = nil; + ARTStatus *status = [encoder encode:self.data outputData:&encoded outputEncoding:&encoding]; + *output = [self copy]; + ((ARTBaseMessage *)*output).data = encoded; + ((ARTBaseMessage *)*output).encoding = [self.encoding artAddEncoding:encoding]; + return status; } - (id)content { - // FIXME: payload.payload - return self.payload.payload; + return self.data; } - (NSString *)description { @@ -62,7 +71,7 @@ - (NSString *)description { [description appendFormat:@" connectionId: %@,\n", self.connectionId]; [description appendFormat:@" timestamp: %@,\n", self.timestamp]; [description appendFormat:@" encoding: %@,\n", self.encoding]; - [description appendFormat:@" payload: %@\n", self.payload]; + [description appendFormat:@" data: %@\n", self.data]; [description appendFormat:@"}"]; return description; } diff --git a/ably-ios/ARTChannel+Private.h b/ably-ios/ARTChannel+Private.h index 5121f3eb9..eab8d1d40 100644 --- a/ably-ios/ARTChannel+Private.h +++ b/ably-ios/ARTChannel+Private.h @@ -9,14 +9,14 @@ #import "ARTChannel.h" #import "ARTLog.h" -@protocol ARTPayloadEncoder; - ART_ASSUME_NONNULL_BEGIN @interface ARTChannel() @property (nonatomic, strong, art_null_resettable) ARTChannelOptions *options; +@property (nonatomic, strong, readonly) ARTDataEncoder *dataEncoder; +- (ARTMessage *__art_nonnull)encodeMessageIfNeeded:(ARTMessage *__art_nonnull)message; - (void)internalPostMessages:(id)data callback:(art_nullable ARTErrorCallback)callback; @end diff --git a/ably-ios/ARTChannel.h b/ably-ios/ARTChannel.h index 5e32aaedc..b6962982c 100644 --- a/ably-ios/ARTChannel.h +++ b/ably-ios/ARTChannel.h @@ -8,8 +8,7 @@ #import #import "ARTTypes.h" - -@protocol ARTPayloadEncoder; +#import "ARTDataEncoder.h" @class ARTChannelOptions; @class ARTMessage; @@ -21,13 +20,12 @@ ART_ASSUME_NONNULL_BEGIN @interface ARTChannel : NSObject @property (nonatomic, strong, readonly) NSString *name; +@property (readonly, getter=getLogger) ARTLog *logger; -@property (readonly, strong, nonatomic) id payloadEncoder; - -- (instancetype)initWithName:(NSString *)name andOptions:(ARTChannelOptions *)options; +- (instancetype)initWithName:(NSString *)name andOptions:(ARTChannelOptions *)options andLogger:(ARTLog *)logger; -- (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:(art_nullable id)data callback:(art_nullable ARTErrorCallback)callback; +- (void)publish:(art_nullable id)data name:(art_nullable NSString *)name callback:(art_nullable ARTErrorCallback)callback; - (void)publishMessage:(ARTMessage *)message callback:(art_nullable ARTErrorCallback)callback; - (void)publishMessages:(__GENERIC(NSArray, ARTMessage *) *)messages callback:(art_nullable ARTErrorCallback)callback; diff --git a/ably-ios/ARTChannel.m b/ably-ios/ARTChannel.m index 7cb3b8982..492836cc9 100644 --- a/ably-ios/ARTChannel.m +++ b/ably-ios/ARTChannel.m @@ -8,18 +8,19 @@ #import "ARTChannel+Private.h" -#import "ARTPayload.h" +#import "ARTDataEncoder.h" #import "ARTMessage.h" #import "ARTChannelOptions.h" #import "ARTNSArray+ARTFunctional.h" @implementation ARTChannel -- (instancetype)initWithName:(NSString *)name andOptions:(ARTChannelOptions *)options { +- (instancetype)initWithName:(NSString *)name andOptions:(ARTChannelOptions *)options andLogger:(ARTLog *)logger { if (self = [super init]) { _name = name; - _options = options; - _payloadEncoder = [ARTPayload defaultPayloadEncoder:options.cipherParams]; + self.options = options; + _dataEncoder = [[ARTDataEncoder alloc] initWithCipherParams:_options.cipherParams logger:logger]; + _logger = logger; } return self; } @@ -30,8 +31,6 @@ - (void)setOptions:(ARTChannelOptions *)options { } else { _options = options; } - // FIXME: odd, always JSON?! - _payloadEncoder = [ARTJsonPayloadEncoder instance]; } - (void)publish:(id)data callback:(ARTErrorCallback)callback { @@ -43,15 +42,25 @@ - (void)publish:(id)data name:(NSString *)name callback:(ARTErrorCallback)callba } - (void)publishMessages:(NSArray *)messages callback:(ARTErrorCallback)callback { - messages = [messages artMap:^(ARTMessage *message) { - return [message encode:self.payloadEncoder]; - }]; + [self internalPostMessages:[messages artMap:^id(ARTMessage *message) { + return [self encodeMessageIfNeeded:message]; + }] callback:callback]; +} - [self internalPostMessages:messages callback:callback]; +- (ARTMessage *)encodeMessageIfNeeded:(ARTMessage *)message { + if (!self.dataEncoder) { + return message; + } + ARTStatus *status = [message encodeWithEncoder:self.dataEncoder output:&message]; + if (status.state != ARTStateOk) { + [self.logger error:@"ARTChannel: error encoding data, status: %tu", status]; + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"message encoding failed" userInfo:nil]; + } + return message; } - (void)publishMessage:(ARTMessage *)message callback:(ARTErrorCallback)callback { - [self internalPostMessages:[message encode:self.payloadEncoder] callback:callback]; + [self internalPostMessages:[self encodeMessageIfNeeded:message] callback:callback]; } - (BOOL)history:(ARTDataQuery *)query callback:(void (^)(__GENERIC(ARTPaginatedResult, ARTMessage *) *, NSError *))callback error:(NSError **)errorPtr { diff --git a/ably-ios/ARTDataEncoder.h b/ably-ios/ARTDataEncoder.h new file mode 100644 index 000000000..ff70fc8c1 --- /dev/null +++ b/ably-ios/ARTDataEncoder.h @@ -0,0 +1,34 @@ +// +// ARTDataEncoder.h +// ably-ios +// +// Created by Jason Choy on 18/12/2014. +// Copyright (c) 2014 Ably. All rights reserved. +// + +#import +#import "CompatibilityMacros.h" +#import "ARTStatus.h" +#import "ARTCrypto.h" + +@class ARTCipherParams; + +ART_ASSUME_NONNULL_BEGIN + +@interface ARTDataEncoder : NSObject + +- (instancetype)initWithCipherParams:(ARTCipherParams *)params logger:(ARTLog *)logger; +- (ARTStatus *__art_nullable)encode:(id)data outputData:(id __art_nullable *__art_nonnull)outputData outputEncoding:(NSString *__art_nullable *__art_nonnull)outputEncoding; +- (ARTStatus *__art_nullable)decode:(id)data encoding:(NSString *)encoding outputData:(id __art_nullable *__art_nonnull)outputData outputEncoding:(NSString *__art_nullable *__art_nonnull)outputEncoding; + +@end + +@interface NSString (ARTDataEncoder) + +- (NSString *)artAddEncoding:(NSString *)encoding; +- (NSString *)artLastEncoding; +- (NSString *)artRemoveLastEncoding; + +@end + +ART_ASSUME_NONNULL_END diff --git a/ably-ios/ARTDataEncoder.m b/ably-ios/ARTDataEncoder.m new file mode 100644 index 000000000..61fd2f55c --- /dev/null +++ b/ably-ios/ARTDataEncoder.m @@ -0,0 +1,175 @@ +// +// ARTDataEncoder.m +// ably-ios +// +// Created by Jason Choy on 18/12/2014. +// Copyright (c) 2014 Ably. All rights reserved. +// + +#import "ARTCrypto.h" +#import "ARTLog.h" +#import "ARTDataEncoder.h" + +@interface ARTDataEncoder () + +ART_ASSUME_NONNULL_BEGIN + +@property (readonly, nonatomic) ARTLog *logger; + +ART_ASSUME_NONNULL_END + +@end + +@implementation ARTDataEncoder { + id _cipher; + ARTLog *_logger; +} + +- (instancetype)initWithCipherParams:(ARTCipherParams *)params logger:(ARTLog *)logger { + self = [super init]; + if (self) { + _logger = logger; + if (params) { + _cipher = [ARTCrypto cipherWithParams:params]; + if (!_cipher) { + [self.logger error:@"ARTDataEncoder failed to create cipher with name %@", params.algorithm]; + } + } + } + return self; +} + +- (ARTStatus *__art_nullable)encode:(id)data outputData:(id __art_nullable *__art_nonnull)outputData outputEncoding:(NSString **)outputEncoding { + NSData *encoded = nil; + NSString *encoding = nil; + BOOL ok = false; + + if (!data) { + *outputData = nil; + *outputEncoding = nil; + return [ARTStatus state:ARTStateOk]; + } else if ([data isKindOfClass:[NSData class]]) { + encoding = @"base64"; + encoded = [[((NSData *)data) base64EncodedStringWithOptions:0] dataUsingEncoding:NSUTF8StringEncoding]; + ok = encoded != nil; + } else if ([data isKindOfClass:[NSString class]]) { + encoding = @""; + encoded = [data dataUsingEncoding:NSUTF8StringEncoding]; + ok = true; + } else if ([data isKindOfClass:[NSArray class]] || [data isKindOfClass:[NSDictionary class]]) { + encoding = @"json"; + NSError *error = nil; + encoded = [NSJSONSerialization dataWithJSONObject:data options:0 error:&error]; + ok = error == nil && encoded != nil; + } + + if (!ok) { + return [ARTStatus state:ARTStateError]; + } + + if (_cipher) { + ARTStatus *status = [_cipher encrypt:encoded output:&encoded]; + if (status.state != ARTStateOk) { + return status; + } + encoding = [encoding artAddEncoding:[self cipherEncoding]]; + + // Re-encode the encrypted bytes in base64. + encoded = [[encoded base64EncodedStringWithOptions:0] dataUsingEncoding:NSUTF8StringEncoding]; + if (encoded == nil) { + return [ARTStatus state:ARTStateError]; + } + encoding = [encoding artAddEncoding:@"base64"]; + } + + *outputData = [[NSString alloc] initWithData:encoded encoding:NSUTF8StringEncoding]; + *outputEncoding = encoding; + return [ARTStatus state:ARTStateOk]; +} + +- (ARTStatus *__art_nullable)decode:(id)data encoding:(NSString *)encoding outputData:(id __art_nullable *__art_nonnull)outputData outputEncoding:(NSString **)outputEncoding { + if (!data || !encoding ) { + *outputData = data; + *outputEncoding = encoding; + return [ARTStatus state:ARTStateOk]; + } + + BOOL ok = false; + NSArray *encodings = [encoding componentsSeparatedByString:@"/"]; + *outputEncoding = [NSString stringWithString:encoding]; + + for (NSUInteger i = [encodings count]; i > 0; i--) { + ok = false; + NSString *encoding = [encodings objectAtIndex:i-1]; + + if ([encoding isEqualToString:@"base64"]) { + if ([data isKindOfClass:[NSData class]]) { // E. g. when decrypted. + data = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + } + if ([data isKindOfClass:[NSString class]]) { + data = [[NSData alloc] initWithBase64EncodedString:(NSString *)data options:0]; + ok = data != nil; + } + } else if ([encoding isEqualToString:@""]) { + ok = [data isKindOfClass:[NSString class]]; + } else if ([encoding isEqualToString:@"json"]) { + if ([data isKindOfClass:[NSData class]]) { // E. g. when decrypted. + data = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + } + if ([data isKindOfClass:[NSString class]]) { + NSData *jsonData = [data dataUsingEncoding:NSUTF8StringEncoding]; + data = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:nil]; + ok = data != nil; + } + } else if (_cipher && [encoding isEqualToString:[self cipherEncoding]] && [data isKindOfClass:[NSData class]]) { + ARTStatus *status = [_cipher decrypt:data output:&data]; + if (i == 1) { + // Because strings have no encoding, an encoded payload whose left-most encoding + // is a cipher will be a string. + data = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + } + ok = status.state == ARTStateOk; + } + + if (ok) { + *outputEncoding = [*outputEncoding artRemoveLastEncoding]; + } else { + [self.logger error:@"ARTDataDecoded failed to decode data as '%@': (%@)%@", encoding, [data class], data]; + } + } + + *outputData = data; + if (ok) { + return [ARTStatus state:ARTStateOk]; + } else { + return [ARTStatus state:ARTStateError]; + } +} + +- (NSString *)cipherEncoding { + size_t keyLen = [_cipher keyLength]; + if (keyLen == 128) { + return @"cipher+aes-128-cbc"; + } else if (keyLen == 256) { + return @"cipher+aes-256-cbc"; + } + return nil; +} + +@end + +@implementation NSString (ARTPayload) + +- (NSString *)artAddEncoding:(NSString *)encoding { + return [self stringByAppendingPathComponent:encoding]; +} + +- (NSString *)artLastEncoding { + return [self lastPathComponent]; +} + +- (NSString *)artRemoveLastEncoding { + return [self stringByDeletingLastPathComponent]; +} + +@end diff --git a/ably-ios/ARTJsonEncoder.m b/ably-ios/ARTJsonEncoder.m index d9b2ecd65..ab2b0fec1 100644 --- a/ably-ios/ARTJsonEncoder.m +++ b/ably-ios/ARTJsonEncoder.m @@ -51,8 +51,7 @@ - (ARTStatsConnectionTypes *)statsConnectionTypesFromDictionary:(NSDictionary *) - (ARTStatsResourceCount *)statsResourceCountFromDictionary:(NSDictionary *)input; - (ARTStatsRequestCount *)statsRequestCountFromDictionary:(NSDictionary *)input; -- (ARTPayload *)payloadFromDictionary:(NSDictionary *)input; -- (void)writePayload:(ARTPayload *)payload toDictionary:(NSMutableDictionary *)output; +- (void)writeData:(id)data encoding:(NSString *)encoding toDictionary:(NSMutableDictionary *)output; - (id)decode:(NSData *)data; - (NSDictionary *)decodeDictionary:(NSData *)data; @@ -162,7 +161,8 @@ - (ARTMessage *)messageFromDictionary:(NSDictionary *)input { message.id = [input artString:@"id"]; message.name = [input artString:@"name"]; message.clientId = [input artString:@"clientId"]; - message.payload = [self payloadFromDictionary:input]; + message.data = [input objectForKey:@"data"]; + message.encoding = [input artString:@"encoding"];; message.timestamp = [input artDate:@"timestamp"]; message.connectionId = [input artString:@"connectionId"]; @@ -229,8 +229,8 @@ - (ARTPresenceMessage *)presenceMessageFromDictionary:(NSDictionary *)input { } ARTPresenceMessage *message = [[ARTPresenceMessage alloc] init]; message.id = [input artString:@"id"]; - message.payload = [self payloadFromDictionary:input]; - message.payload.encoding = [input artString:@"encoding"]; + message.data = [input objectForKey:@"data"]; + message.encoding = [input artString:@"encoding"]; message.clientId = [input artString:@"clientId"]; message.timestamp = [input artDate:@"timestamp"]; @@ -270,8 +270,8 @@ - (NSDictionary *)messageToDictionary:(ARTMessage *)message { [output setObject:message.clientId forKey:@"clientId"]; } - if (message.payload) { - [self writePayload:message.payload toDictionary:output]; + if (message.data) { + [self writeData:message.data encoding:message.encoding toDictionary:output]; } if (message.name) { @@ -306,8 +306,8 @@ - (NSDictionary *)presenceMessageToDictionary:(ARTPresenceMessage *)message { [output setObject:message.clientId forKey:@"clientId"]; } - if (message.payload) { - [self writePayload:message.payload toDictionary:output]; + if (message.data) { + [self writeData:message.data encoding:message.encoding toDictionary:output]; } if(message.connectionId) { [output setObject:message.connectionId forKey:@"connectionId"]; @@ -623,36 +623,15 @@ - (ARTStatsRequestCount *)statsRequestCountFromDictionary:(NSDictionary *)input refused:refused.doubleValue]; } -- (ARTPayload *)payloadFromDictionary:(NSDictionary *)input { - if (![input isKindOfClass:[NSDictionary class]]) { - return nil; - } - NSString *encoding = [input objectForKey:@"encoding"]; - if (!encoding) { - encoding = @""; - } - id data = [input objectForKey:@"data"]; - ARTPayload *payload = [ARTPayload payloadWithPayload:data encoding:encoding]; - ARTPayload *decoded = nil; - ARTStatus *status = [[ARTBase64PayloadEncoder instance] decode:payload output:&decoded]; - if (status.state != ARTStateOk) { - [self.logger error:@"ARTJsonEncoder failed to decode payload %@", payload]; +- (void)writeData:(id)data encoding:(NSString *)encoding toDictionary:(NSMutableDictionary *)output { + if (encoding.length) { + output[@"encoding"] = encoding; } - return decoded; -} - -- (void)writePayload:(ARTPayload *)payload toDictionary:(NSMutableDictionary *)output { - ARTPayload *encoded = nil; - ARTStatus *status = [[ARTBase64PayloadEncoder instance] encode:payload output:&encoded]; - if(status.state != ARTStateOk) { - [self.logger error:@"ARTJsonEncoder failed to encode payload"]; - } - NSAssert(status.state == ARTStateOk, @"Error encoding payload"); - - if (encoded.encoding.length) { - output[@"encoding"] = encoded.encoding; + if ([encoding isEqualToString:@"json"]) { + NSData *jsonData = [data dataUsingEncoding:NSUTF8StringEncoding]; + data = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:nil]; } - output[@"data"] = encoded.payload; + output[@"data"] = data; } - (id)decode:(NSData *)data { diff --git a/ably-ios/ARTMessage.h b/ably-ios/ARTMessage.h index 70cf80b76..7a08047a3 100644 --- a/ably-ios/ARTMessage.h +++ b/ably-ios/ARTMessage.h @@ -18,8 +18,8 @@ ART_ASSUME_NONNULL_BEGIN - (instancetype)initWithData:(id)data name:(art_nullable NSString *)name; -+ (ARTMessage *)messageWithPayload:(id)payload name:(art_nullable NSString *)name; -+ (NSArray *)messagesWithPayloads:(NSArray *)payloads; ++ (NSArray *)messagesWithData:(NSArray *)data; ++ (ARTMessage *)messageWithData:(id)data name:(art_nullable NSString *)name; @end diff --git a/ably-ios/ARTMessage.m b/ably-ios/ARTMessage.m index ef7b46c5d..2fa6031fb 100644 --- a/ably-ios/ARTMessage.m +++ b/ably-ios/ARTMessage.m @@ -14,7 +14,8 @@ - (instancetype)initWithData:(id)data name:(NSString *)name { if (self = [self init]) { _name = [name copy]; if (data) { - self.payload = [ARTPayload payloadWithPayload:data encoding:@""]; + self.data = data; + self.encoding = @""; } } return self; @@ -35,20 +36,18 @@ - (id)copyWithZone:(NSZone *)zone { return message; } -+ (ARTMessage *)messageWithPayload:(id)payload name:(NSString *)name { ++ (ARTMessage *)messageWithData:(id)data name:(NSString *)name { ARTMessage *message = [[ARTMessage alloc] init]; message.name = name; - message.payload = [ARTPayload payloadWithPayload:payload encoding:@""]; + message.data = data; + message.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]]; ++ (NSArray *)messagesWithData:(NSArray *)data { + NSMutableArray * messages =[[NSMutableArray alloc] initWithCapacity:[data count]]; + for (int i=0; i < [data count]; i++) { + [messages addObject:[ARTMessage messageWithData:[data objectAtIndex:i] name:nil]]; } return messages; } diff --git a/ably-ios/ARTPayload+Private.h b/ably-ios/ARTPayload+Private.h deleted file mode 100644 index 64215a1e9..000000000 --- a/ably-ios/ARTPayload+Private.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// ARTPayload+Private.h -// ably -// -// Created by vic on 22/04/2015. -// Copyright (c) 2015 Ably. All rights reserved. -// - -#import "ARTPayload.h" - -@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; - -@end diff --git a/ably-ios/ARTPayload.h b/ably-ios/ARTPayload.h deleted file mode 100644 index adb372c9a..000000000 --- a/ably-ios/ARTPayload.h +++ /dev/null @@ -1,85 +0,0 @@ -// -// ARTPayload.h -// ably-ios -// -// Created by Jason Choy on 18/12/2014. -// Copyright (c) 2014 Ably. All rights reserved. -// - -#import -#import "CompatibilityMacros.h" -#import "ARTStatus.h" - -@protocol ARTPayloadEncoder; - -@class ARTCipherParams; - -ART_ASSUME_NONNULL_BEGIN - -@interface ARTPayload : NSObject - -@property (art_nullable, readwrite, strong, nonatomic) id payload; -@property (readwrite, strong, nonatomic) NSString *encoding; - -- (instancetype)init; -- (instancetype)initWithPayload:(art_nullable id)payload encoding:(NSString *)encoding; - -+ (instancetype)payload; -+ (instancetype)payloadWithPayload:(art_nullable id)payload encoding:(NSString *)encoding; - -+ (id)defaultPayloadEncoder:(ARTCipherParams *)cipherParams; -+ (size_t)payloadArraySizeLimit; - -@end - -@interface NSString (ARTPayload) - -- (NSString *)artAddEncoding:(NSString *)encoding; -- (NSString *)artLastEncoding; -- (NSString *)artRemoveLastEncoding; - -@end - -@protocol ARTPayloadEncoder - -- (ARTStatus *)encode:(ARTPayload *)payload output:(ARTPayload *__autoreleasing __art_nonnull *__art_nonnull)output; -- (ARTStatus *)decode:(ARTPayload *)payload output:(ARTPayload *__autoreleasing __art_nonnull *__art_nonnull)output; -- (NSString *)name; - -@end - -@interface ARTBase64PayloadEncoder : NSObject - -+ (instancetype)instance; -+ (NSString *)toBase64:(NSData *) input; -+ (NSString *)fromBase64:(NSString *) base64; - -@end - -@interface ARTUtf8PayloadEncoder : NSObject - -+ (instancetype)instance; - -@end - -@interface ARTJsonPayloadEncoder : NSObject - -+ (instancetype)instance; - -@end - -@interface ARTCipherPayloadEncoder : NSObject - -- (instancetype)init UNAVAILABLE_ATTRIBUTE; -- (instancetype)initWithCipherParams:(ARTCipherParams *)cipherParams; - -@end - -@interface ARTPayloadEncoderChain : NSObject - -- (instancetype)init; -- (instancetype)initWithEncoders:(NSArray *)encoders; - -@end - -ART_ASSUME_NONNULL_END diff --git a/ably-ios/ARTPayload.m b/ably-ios/ARTPayload.m deleted file mode 100644 index 14780fee8..000000000 --- a/ably-ios/ARTPayload.m +++ /dev/null @@ -1,463 +0,0 @@ -// -// ARTPayload.m -// ably-ios -// -// Created by Jason Choy on 18/12/2014. -// Copyright (c) 2014 Ably. All rights reserved. -// - -#import "ARTPayload+Private.h" - -#import "ARTCrypto.h" -#import "ARTLog.h" - -@interface ARTBase64PayloadEncoder () - -+ (BOOL)canEncode:(ARTPayload *)payload; -+ (BOOL)canDecode:(ARTPayload *)payload; - -@end - -@interface ARTCipherPayloadEncoder () - -@property (nonatomic, weak) ARTLog *logger; -@property (readonly, strong, nonatomic) id cipher; - -@end - -@interface ARTPayloadEncoderChain () - -@property (nonatomic, weak) ARTLog *logger; -@property (readonly, nonatomic, strong) NSArray *encoders; - -@end - -@implementation ARTPayload - -- (instancetype)init { - return [self initWithPayload:nil encoding:@""]; -} - -- (instancetype)initWithPayload:(id)payload encoding:(NSString *)encoding { - self = [super init]; - if (self) { - _payload = payload; - _encoding = encoding; - } - return self; -} - -- (NSString *)description { - NSMutableString *description = [NSMutableString stringWithFormat:@"<%@: %p> {\n", self.class, self]; - [description appendFormat:@" payload: %@,\n", self.payload]; - [description appendFormat:@" encoding: %@\n", self.encoding]; - [description appendFormat:@"}"]; - return description; -} - -+ (instancetype)payload { - return [[ARTPayload alloc] init]; -} - -+ (instancetype)payloadWithPayload:(id)payload encoding:(NSString *)encoding { - return [[ARTPayload alloc] initWithPayload:payload encoding:encoding]; -} - -+ (id)defaultPayloadEncoder:(ARTCipherParams *)cipherParams { - if (!cipherParams) { - return [ARTJsonPayloadEncoder instance]; - } - - NSMutableArray *encoders = [NSMutableArray arrayWithObjects:[ARTJsonPayloadEncoder instance], [ARTUtf8PayloadEncoder instance], nil]; - - ARTCipherPayloadEncoder *cipherEncoder = [[ARTCipherPayloadEncoder alloc] initWithCipherParams:cipherParams]; - if (cipherEncoder) { - [encoders addObject:cipherEncoder]; - } - - return [[ARTPayloadEncoderChain alloc] initWithEncoders:encoders]; -} - -+ (id)createEncoder:(NSString *) name key:(NSData *) keySpec iv:(NSData *) iv { - if([name isEqualToString:@"json"]) { - return [ARTJsonPayloadEncoder instance]; - } - else if([name isEqualToString:@"base64"]) { - return [ARTBase64PayloadEncoder instance]; - } - else if([name isEqualToString:@"utf-8"]) { - return [ARTUtf8PayloadEncoder instance]; - } - //256 on iOS is handled by passing the keyLength into kCCAlgorithmAES128 - else if([name isEqualToString:@"cipher+aes-256-cbc"] || [name isEqualToString:@"cipher+aes-128-cbc"]){ - ARTIvParameterSpec * ivSpec = [[ARTIvParameterSpec alloc] initWithIv:iv]; - ARTCipherParams * params =[[ARTCipherParams alloc] initWithAlgorithm:@"aes" keySpec:keySpec ivSpec:ivSpec]; - return [[ARTCipherPayloadEncoder alloc] initWithCipherParams:params]; - } - return nil; -} - -+ (NSArray *)parseEncodingChain:(NSString *) encodingChain key:(NSData *) key iv:(NSData *) iv { - NSArray * strArray = [encodingChain componentsSeparatedByString:@"/"]; - NSMutableArray * encoders= [[NSMutableArray alloc] init]; - size_t l = [strArray count]; - for(int i=0;i < l; i++) { - NSString * encoderName = [strArray objectAtIndex:i]; - id encoder = [ARTPayload createEncoder:encoderName key:key iv:iv]; - if(encoder != nil) { - [encoders addObject:encoder]; - } - } - return encoders; -} - -+ (size_t) payloadArraySizeLimit { - return [ARTPayload getPayloadArraySizeLimit:0 modify:false]; -} - -+ (size_t)getPayloadArraySizeLimit:(size_t) newLimit modify:(bool) modify { - static size_t limit = SIZE_T_MAX; - if(modify) { - limit = newLimit; - } - return limit; -} - -@end - -@implementation NSString (ARTPayload) - -- (NSString *)artAddEncoding:(NSString *)encoding { - return [self stringByAppendingPathComponent:encoding]; -} - -- (NSString *)artLastEncoding { - return [self lastPathComponent]; -} - -- (NSString *)artRemoveLastEncoding { - return [self stringByDeletingLastPathComponent]; -} - -@end - - -// MARK: Base64 - -@implementation ARTBase64PayloadEncoder - -+ (instancetype)instance { - static ARTBase64PayloadEncoder *instance = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - instance = [[ARTBase64PayloadEncoder alloc] init]; - }); - return instance; -} - -+ (NSString *)toBase64:(NSData *) input { - ARTPayload * p = [[ARTPayload alloc] initWithPayload:input encoding:@"base64"]; - ARTPayload * output = nil; - ARTBase64PayloadEncoder * e = [ARTBase64PayloadEncoder instance]; - [e encode:p output:&output]; - return output.payload; -} - -+ (NSString *)fromBase64:(NSString *) base64 { - ARTPayload * p = [[ARTPayload alloc] initWithPayload:base64 encoding:@"base64"]; - ARTPayload * output = nil; - ARTBase64PayloadEncoder * e = [ARTBase64PayloadEncoder instance]; - [e decode:p output:&output]; - return [[NSString alloc] initWithData:output.payload encoding:NSUTF8StringEncoding]; -} - -+ (NSString *)getName { - return @"base64"; -} -- (NSString *)name { - return [ARTBase64PayloadEncoder getName]; -} - -+ (BOOL)canEncode:(ARTPayload *)payload { - return [payload.payload isKindOfClass:[NSData class]]; -} - -+ (BOOL)canDecode:(ARTPayload *)payload { - return [payload.encoding isEqualToString:[ARTBase64PayloadEncoder getName]]; -} - -- (ARTStatus *)encode:(ARTPayload *)payload output:(ARTPayload *__autoreleasing *)output { - if ([ARTBase64PayloadEncoder canEncode:payload]) { - NSString *encoded = [((NSData *)payload.payload) base64EncodedStringWithOptions:0]; - if (encoded) { - *output = [ARTPayload payloadWithPayload:encoded encoding:[payload.encoding artAddEncoding:[ARTBase64PayloadEncoder getName]]]; - return [ARTStatus state:ARTStateOk]; - } else { - // Set the output to be the original payload - *output = payload; - return [ARTStatus state:ARTStateError]; - } - } - *output = payload; - return [ARTStatus state:ARTStateOk]; -} - -- (ARTStatus *)decode:(ARTPayload *)payload output:(ARTPayload *__autoreleasing *)output { - if ([[payload.encoding artLastEncoding] isEqualToString:[ARTBase64PayloadEncoder getName]]) {//[ARTBase64PayloadEncoder canDecode:payload]) { - NSData *decoded = [[NSData alloc] initWithBase64EncodedString:payload.payload options:0]; - if (decoded) { - *output = [ARTPayload payloadWithPayload:decoded encoding:[payload.encoding artRemoveLastEncoding]]; - return [ARTStatus state:ARTStateOk]; - } - // Set the output to be the original payload - *output = payload; - return [ARTStatus state:ARTStateError]; - } - *output = payload; - return [ARTStatus state:ARTStateOk]; -} - -@end - - -// MARK: UTF-8 - -@implementation ARTUtf8PayloadEncoder - -+ (instancetype)instance { - static ARTUtf8PayloadEncoder *instance = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - instance = [[ARTUtf8PayloadEncoder alloc] init]; - }); - return instance; -} - -+ (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]]) { - if ([payload.payload isKindOfClass:[NSData class]]) { - NSString *decoded = [[NSString alloc] initWithData:payload.payload encoding:NSUTF8StringEncoding]; - if (decoded) { - *output = [ARTPayload payloadWithPayload:decoded encoding:[payload.encoding artRemoveLastEncoding]]; - return [ARTStatus state:ARTStateOk]; - } - } - return [ARTStatus state:ARTStateError]; - } - return [ARTStatus state:ARTStateOk]; -} - -- (ARTStatus *)encode:(ARTPayload *)payload output:(ARTPayload *__autoreleasing *)output { - *output = payload; - if ([payload isKindOfClass:[NSString class]]) { - NSData *encoded = [((NSString *)payload.payload) dataUsingEncoding:NSUTF8StringEncoding]; - if (encoded) { - *output = [ARTPayload payloadWithPayload:encoded encoding:[payload.encoding artAddEncoding:[ARTUtf8PayloadEncoder getName]]]; - return ARTStateOk; - } - return [ARTStatus state:ARTStateError]; - } - return [ARTStatus state:ARTStateOk]; -} - -@end - - -// MARK: JSON - -@implementation ARTJsonPayloadEncoder - -+ (instancetype)instance { - static ARTJsonPayloadEncoder *instance = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - instance = [[ARTJsonPayloadEncoder alloc] init]; - }); - return instance; -} - -+ (NSString *)getName { - return @"json"; -} -- (NSString *)name { - return [ARTJsonPayloadEncoder getName]; -} - -- (ARTStatus *)encode:(ARTPayload *)payload output:(ARTPayload *__autoreleasing *)output { - *output = payload; - // Handle dictionaries and arrays the same way - if ([payload.payload isKindOfClass:[NSDictionary class]] || [payload.payload isKindOfClass:[NSArray class]]) { - *output = [ARTPayload payloadWithPayload:payload.payload encoding:[payload.encoding artAddEncoding:[ARTJsonPayloadEncoder getName]]]; - } - // Otherwise do nothing besides confirm payload is NSData or NSString - else if(payload && !([payload.payload isKindOfClass:[NSData class]] || [payload.payload isKindOfClass:[NSString class]])) { - @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Must be either NSDictionary, NSArray, NSData or NSString" userInfo:nil]; - } - return [ARTStatus state:ARTStateOk]; -} - -- (ARTStatus *)decode:(ARTPayload *)payload output:(ARTPayload *__autoreleasing *)output { - *output = payload; - if ([[payload.encoding artLastEncoding] isEqualToString:[ARTJsonPayloadEncoder getName]]) { - id decoded = nil; - - if ([payload.payload isKindOfClass:[NSString class]]) { - NSData *d = [payload.payload dataUsingEncoding:NSUTF8StringEncoding]; - decoded = [NSJSONSerialization JSONObjectWithData:d options:0 error:nil]; - - if (decoded) { - *output = [ARTPayload payloadWithPayload:decoded encoding:[payload.encoding artRemoveLastEncoding]]; - return [ARTStatus state:ARTStateOk]; - } - } - else if ([payload.payload isKindOfClass:[NSData class]]) { - decoded = [NSJSONSerialization JSONObjectWithData:(NSData *)payload.payload options:0 error:nil]; - - if (decoded) { - *output = [ARTPayload payloadWithPayload:decoded encoding:[payload.encoding artRemoveLastEncoding]]; - return [ARTStatus state:ARTStateOk]; - } - } - return [ARTStatus state:ARTStateError]; - } - return [ARTStatus state:ARTStateOk]; -} - -@end - - -// MARK: Cipher - -@implementation ARTCipherPayloadEncoder - -- (instancetype)initWithCipherParams:(ARTCipherParams *)cipherParams { - self = [super init]; - if (self) { - _cipher = [ARTCrypto cipherWithParams:cipherParams]; - if (!_cipher) { - [self.logger error:@"ARTCipherPayloadEncoder failed to create cipher with name %@", cipherParams.algorithm]; - self = nil; - return nil; - } - } - return self; -} - -+ (NSString *)getName128 { - return @"cipher+aes-128-cbc"; -} - -+ (NSString *)getName256 { - return @"cipher+aes-256-cbc"; -} - -- (NSString *)name { - size_t keyLen =[self.cipher keyLength]; - if (keyLen == 128) { - return [ARTCipherPayloadEncoder getName128]; - } - else if(keyLen == 256) { - return [ARTCipherPayloadEncoder getName256]; - } - else { - [self.logger error:@"ARTPayload: keyLength is invalid %zu", keyLen]; - } - return @""; -} - -- (ARTStatus *)decode:(ARTPayload *)payload output:(ARTPayload *__autoreleasing *)output { - *output = payload; - - NSString * cipherName =[payload.encoding artLastEncoding]; - if ([payload.payload isKindOfClass:[NSData class]] && [cipherName isEqualToString:[self name]]) { - NSData *decrypted = nil; - ARTStatus * status = [self.cipher decrypt:payload.payload output:&decrypted]; - if (status.state == ARTStateOk) { - *output = [ARTPayload payloadWithPayload:decrypted encoding:[payload.encoding artRemoveLastEncoding]]; - [self.logger debug:@"cipher payload decoded successfully"]; - } - return status; - } - - return [ARTStatus state:ARTStateOk]; -} - -- (ARTStatus *)encode:(ARTPayload *)payload output:(ARTPayload *__autoreleasing *)output { - *output = payload; - if ([payload.payload isKindOfClass:[NSData class]]) { - NSData *encrypted = nil; - ARTStatus *status = [self.cipher encrypt:payload.payload output:&encrypted]; - if (status.state == ARTStateOk) { - NSString *cipherName = [self name]; - *output = [ARTPayload payloadWithPayload:encrypted encoding:[payload.encoding artAddEncoding:cipherName]]; - } - return status; - } - return [ARTStatus state:ARTStateOk]; -} - -@end - - -// MARK: Chain encoder - -@implementation ARTPayloadEncoderChain - -- (instancetype)init { - return [self initWithEncoders:@[]]; -} - -- (instancetype)initWithEncoders:(NSArray *)encoders { - self = [super init]; - if (self) { - _encoders = [NSArray arrayWithArray:encoders]; - } - return self; -} - -- (NSString *)name { - return @"chain"; //not used. -} - -- (ARTStatus *)encode:(ARTPayload *)payload output:(ARTPayload *__autoreleasing *)output { - ARTStatus *status = ARTStateOk; - *output = payload; - - for (id enc in self.encoders) { - status = [enc encode:*output output:output]; - if (status.state != ARTStateOk) { - break; - } - } - - return status; -} - -- (ARTStatus *)decode:(ARTPayload *)payload output:(ARTPayload *__autoreleasing *)output { - 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.state != ARTStateOk) { - ARTPayload * p = *output; - [self.logger error:@"ARTPayload: error in ARTPayloadEncoderChain decoding with encoder %d. Remaining decoding jobs are %@", count, p.encoding]; - break; - } - count++; - } - - return status; -} - -@end diff --git a/ably-ios/ARTRealtimeChannel.h b/ably-ios/ARTRealtimeChannel.h index 1a22fcbfe..ef0aa0c3e 100644 --- a/ably-ios/ARTRealtimeChannel.h +++ b/ably-ios/ARTRealtimeChannel.h @@ -43,7 +43,7 @@ - (void)transition:(ARTRealtimeChannelState)state status:(ARTStatus *)status; - (void)onChannelMessage:(ARTProtocolMessage *)message; -- (void)publishMessages:(NSArray *)messages cb:(ARTStatusCallback)cb; +- (void)publishMessages:(__GENERIC(NSArray, ARTMessage *) *)messages cb:(ARTStatusCallback)cb; - (void)publishPresence:(ARTPresenceMessage *)pm cb:(ARTStatusCallback)cb; - (void)publishProtocolMessage:(ARTProtocolMessage *)pm cb:(ARTStatusCallback)cb; @@ -62,8 +62,8 @@ - (void)broadcastPresence:(ARTPresenceMessage *)pm; -- (void)publish:(id)payload withName:(NSString *)name cb:(ARTStatusCallback)cb; -- (void)publish:(id)payload cb:(ARTStatusCallback)cb; +- (void)publish:(id)data withName:(NSString *)name cb:(ARTStatusCallback)cb; +- (void)publish:(id)data cb:(ARTStatusCallback)cb; - (id)subscribe:(ARTRealtimeChannelMessageCb)cb; - (id)subscribeToName:(NSString *)name cb:(ARTRealtimeChannelMessageCb)cb; diff --git a/ably-ios/ARTRealtimeChannel.m b/ably-ios/ARTRealtimeChannel.m index 9725dc555..8f676373d 100644 --- a/ably-ios/ARTRealtimeChannel.m +++ b/ably-ios/ARTRealtimeChannel.m @@ -7,6 +7,7 @@ // #import "ARTRealtimeChannel.h" +#import "ARTChannel+Private.h" #import "ARTRealtime+Private.h" #import "ARTMessage.h" @@ -20,6 +21,7 @@ #import "ARTPresenceMap.h" #import "ARTQueuedMessage.h" #import "ARTNSArray+ARTFunctional.h" +#import "ARTStatus.h" @interface ARTRealtimeChannel () { ARTRealtimePresence *_realtimePresence; @@ -56,36 +58,28 @@ - (ARTRealtimePresence *)presence { return _realtimePresence; } -- (void)publish:(id)payload cb:(ARTStatusCallback)cb { - if([payload isKindOfClass:[NSArray class]]) { - NSArray * messages = [ARTMessage messagesWithPayloads:(NSArray *) payload]; +- (void)publish:(id)data cb:(ARTStatusCallback)cb { + if([data isKindOfClass:[NSArray class]]) { + NSArray * messages = [ARTMessage messagesWithData:(NSArray *) data]; [self publishMessages:messages cb:cb]; } else { - [self publish:payload withName:nil cb:cb]; + [self publish:data withName:nil cb:cb]; } } -- (void)publish:(id)payload withName:(NSString *)name cb:(ARTStatusCallback)cb { - NSArray *messages = [NSArray arrayWithObject:[ARTMessage messageWithPayload:payload name:name]]; +- (void)publish:(id)data withName:(NSString *)name cb:(ARTStatusCallback)cb { + NSArray *messages = [NSArray arrayWithObject:[ARTMessage messageWithData:data 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; + msg.messages = [messages artMap:^id(ARTMessage *message) { + return [self encodeMessageIfNeeded:message]; + }]; [self publishProtocolMessage:msg cb:cb]; } @@ -111,13 +105,15 @@ - (void)publishPresence:(ARTPresenceMessage *)msg cb:(ARTStatusCallback)cb { } _lastPresenceAction = msg.action; - if (msg.payload && self.payloadEncoder) { - ARTPayload *encodedPayload = nil; - ARTStatus * status = [self.payloadEncoder encode:msg.payload output:&encodedPayload]; + if (msg.data && self.dataEncoder) { + id encodedData = nil; + NSString *encoding; + ARTStatus * status = [self.dataEncoder encode:msg.data outputData:&encodedData outputEncoding:&encoding]; if (status.state != ARTStateOk) { [self.logger warn:@"bad status encoding presence message %d",(int) status]; } - msg.payload = encodedPayload; + msg.data = encodedData; + msg.encoding = encoding; } ARTProtocolMessage *pm = [[ARTProtocolMessage alloc] init]; @@ -341,11 +337,14 @@ - (void)onMessage:(ARTProtocolMessage *)message { NSArray *blanketSubscriptions = [self.subscriptions objectForKey:@""]; int i = 0; - id payloadEncoder = self.payloadEncoder; + ARTDataEncoder *dataEncoder = self.dataEncoder; for (ARTMessage *m in message.messages) { ARTMessage *msg = m; - if (payloadEncoder) { - msg = [msg decode:payloadEncoder]; + if (dataEncoder) { + ARTStatus *status = [msg decodeWithEncoder:dataEncoder output:&msg]; + if (status.state != ARTStateOk) { + [self.logger error:@"ARTRealtimeChannel: error decoding data, status: %tu", status]; + } } if (!msg.timestamp) { @@ -374,11 +373,14 @@ - (void)onMessage:(ARTProtocolMessage *)message { - (void)onPresence:(ARTProtocolMessage *)message { int i = 0; - id payloadEncoder = self.payloadEncoder; + ARTDataEncoder *dataEncoder = self.dataEncoder; for (ARTPresenceMessage *p in message.presence) { ARTPresenceMessage *pm = p; - if (payloadEncoder) { - pm = [pm decode:payloadEncoder]; + if (dataEncoder) { + ARTStatus *status = [pm decodeWithEncoder:dataEncoder output:&pm]; + if (status.state != ARTStateOk) { + [self.logger error:@"ARTRealtimeChannel: error decoding data, status: %tu", status]; + } } if (!pm.timestamp) { diff --git a/ably-ios/ARTRealtimePresence.m b/ably-ios/ARTRealtimePresence.m index 36002b561..f63294970 100644 --- a/ably-ios/ARTRealtimePresence.m +++ b/ably-ios/ARTRealtimePresence.m @@ -13,6 +13,7 @@ #import "ARTPresenceMap.h" #import "ARTPresenceMessage.h" #import "ARTRealtimeChannelSubscription.h" +#import "ARTStatus.h" @implementation ARTRealtimePresence @@ -45,9 +46,7 @@ - (void)enterClient:(NSString *)clientId data:(id)data cb:(ARTStatusCallback)cb ARTPresenceMessage *msg = [[ARTPresenceMessage alloc] init]; msg.action = ARTPresenceEnter; msg.clientId = clientId; - if(data) { - msg.payload = [ARTPayload payloadWithPayload:data encoding:@""]; - } + msg.data = data; msg.connectionId = [self channel].realtime.connectionId; [[self channel] publishPresence:msg cb:cb]; @@ -65,9 +64,7 @@ - (void)updateClient:(NSString *)clientId data:(id)data cb:(ARTStatusCallback)cb cb([ARTStatus state:ARTStateNoClientId]); return; } - if(data) { - msg.payload = [ARTPayload payloadWithPayload:data encoding:@""]; - } + msg.data = data; msg.connectionId = [self channel].realtime.connectionId; [[self channel] publishPresence:msg cb:cb]; @@ -86,10 +83,7 @@ - (void) leaveClient:(NSString *) clientId data:(id) data cb:(ARTStatusCallback) } ARTPresenceMessage *msg = [[ARTPresenceMessage alloc] init]; msg.action = ARTPresenceLeave; - - if(data) { - msg.payload= [ARTPayload payloadWithPayload:data encoding:@""]; - } + msg.data = data; msg.clientId = clientId; msg.connectionId = [self channel].realtime.connectionId; if(!msg.clientId) { diff --git a/ably-ios/ARTRestChannel.h b/ably-ios/ARTRestChannel.h index bab1e68b6..0e5dda7d2 100644 --- a/ably-ios/ARTRestChannel.h +++ b/ably-ios/ARTRestChannel.h @@ -18,7 +18,6 @@ ART_ASSUME_NONNULL_BEGIN @interface ARTRestChannel : ARTChannel @property (nonatomic, weak) ARTRest *rest; -@property (readonly, getter=getLogger) ARTLog *logger; - (instancetype)initWithName:(NSString *)name withOptions:(ARTChannelOptions *)options andRest:(ARTRest *)rest; diff --git a/ably-ios/ARTRestChannel.m b/ably-ios/ARTRestChannel.m index 2ab596531..cbdabd3bf 100644 --- a/ably-ios/ARTRestChannel.m +++ b/ably-ios/ARTRestChannel.m @@ -13,6 +13,7 @@ #import "ARTChannel+Private.h" #import "ARTChannelOptions.h" #import "ARTMessage.h" +#import "ARTBaseMessage.h" #import "ARTPaginatedResult+Private.h" #import "ARTDataQuery+Private.h" #import "ARTJsonEncoder.h" @@ -28,7 +29,7 @@ @implementation ARTRestChannel { } - (instancetype)initWithName:(NSString *)name withOptions:(ARTChannelOptions *)options andRest:(ARTRest *)rest { - if (self = [super initWithName:name andOptions:options]) { + if (self = [super initWithName:name andOptions:options andLogger:rest.logger]) { _rest = rest; _basePath = [NSString stringWithFormat:@"/channels/%@", [name stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLPathAllowedCharacterSet]]]; [self.logger debug:__FILE__ line:__LINE__ message:@"%p instantiating under '%@'", self, name]; @@ -77,7 +78,8 @@ - (BOOL)history:(ARTDataQuery *)query callback:(void(^)(__GENERIC(ARTPaginatedRe ARTPaginatedResultResponseProcessor responseProcessor = ^NSArray *(NSHTTPURLResponse *response, NSData *data) { id encoder = [_rest.encoders objectForKey:response.MIMEType]; return [[encoder decodeMessages:data] artMap:^(ARTMessage *message) { - return [message decode:self.payloadEncoder]; + [message decodeWithEncoder:self.dataEncoder output:&message]; + return message; }]; }; diff --git a/ably-ios/ARTRestPresence.m b/ably-ios/ARTRestPresence.m index 40409b4cd..487eebf50 100644 --- a/ably-ios/ARTRestPresence.m +++ b/ably-ios/ARTRestPresence.m @@ -32,10 +32,7 @@ - (void)get:(void (^)(__GENERIC(ARTPaginatedResult, ARTPresenceMessage *) *resul ARTPaginatedResultResponseProcessor responseProcessor = ^(NSHTTPURLResponse *response, NSData *data) { id encoder = [[self channel].rest.encoders objectForKey:response.MIMEType]; - NSArray *messages = [encoder decodePresenceMessages:data]; - return [messages artMap:^id(ARTPresenceMessage *pm) { - return [pm decode:[self channel].payloadEncoder]; - }]; + return [encoder decodePresenceMessages:data]; }; [ARTPaginatedResult executePaginated:[self channel].rest withRequest:request andResponseProcessor:responseProcessor callback:callback]; @@ -65,10 +62,7 @@ - (BOOL)history:(ARTDataQuery *)query callback:(void (^)(__GENERIC(ARTPaginatedR ARTPaginatedResultResponseProcessor responseProcessor = ^(NSHTTPURLResponse *response, NSData *data) { id encoder = [[self channel].rest.encoders objectForKey:response.MIMEType]; - NSArray *messages = [encoder decodePresenceMessages:data]; - return [messages artMap:^id(ARTPresenceMessage *pm) { - return [pm decode:[self channel].payloadEncoder]; - }]; + return [encoder decodePresenceMessages:data]; }; [ARTPaginatedResult executePaginated:[self channel].rest withRequest:request andResponseProcessor:responseProcessor callback:callback]; diff --git a/ably-ios/ably.h b/ably-ios/ably.h index d97ff8b2d..6ec81b2af 100644 --- a/ably-ios/ably.h +++ b/ably-ios/ably.h @@ -34,7 +34,7 @@ FOUNDATION_EXPORT const unsigned char ablyVersionString[]; #import #import #import -#import +#import #import #import #import diff --git a/ably-ios/ably.modulemap b/ably-ios/ably.modulemap index 959ec6dbb..e23ad9890 100644 --- a/ably-ios/ably.modulemap +++ b/ably-ios/ably.modulemap @@ -11,7 +11,6 @@ framework module ably { header "ARTRealtimeChannel+Private.h" header "ARTChannel+Private.h" header "ARTDataQuery+Private.h" - header "ARTPayload+Private.h" header "ARTAuth+Private.h" header "ARTPaginatedResult+Private.h" } diff --git a/ably-iosTests/ARTRealtimeChannelTest.m b/ably-iosTests/ARTRealtimeChannelTest.m index 2f63d4a64..6dd70dd13 100644 --- a/ably-iosTests/ARTRealtimeChannelTest.m +++ b/ably-iosTests/ARTRealtimeChannelTest.m @@ -20,7 +20,6 @@ #import "ARTEventEmitter.h" #import "ARTTestUtil.h" #import "ARTCrypto.h" -#import "ARTPayload+Private.h" @interface ARTRealtimeChannelTest : XCTestCase { ARTRealtime * _realtime; @@ -340,23 +339,6 @@ - (void)testDetachFails { [self waitForExpectationsWithTimeout:[ARTTestUtil timeout] handler:nil]; } -/** - Currently the payloadArraySizeLimit is default to INT_MAX. Here we bring that number down to 2 - To show that publishing an array over the limit throws an exception. - */ --(void) testPublishTooManyInArray { - XCTestExpectation *exp = [self expectationWithDescription:@"testPublishTooManyInArray"]; - [ARTTestUtil testRealtime:^(ARTRealtime *realtime) { - _realtime = realtime; - ARTRealtimeChannel *channel = [realtime channel:@"channel"]; - NSArray * messages = @[@"test1", @"test2", @"test3"]; - [ARTPayload getPayloadArraySizeLimit:2 modify:true]; - XCTAssertThrows([channel publish:messages cb:^(ARTStatus *status) {}]); - [exp fulfill]; - }]; - [self waitForExpectationsWithTimeout:[ARTTestUtil timeout] handler:nil]; -} - - (void) testClientIdPreserved { NSString *firstClientId = @"firstClientId"; NSString *channelName = @"channelName"; diff --git a/ably-iosTests/ARTRealtimeCryptoMessageTest.m b/ably-iosTests/ARTRealtimeCryptoMessageTest.m deleted file mode 100644 index ad7c3b9d6..000000000 --- a/ably-iosTests/ARTRealtimeCryptoMessageTest.m +++ /dev/null @@ -1,140 +0,0 @@ -// -// ARTRealtimeCryptoMessageTest.m -// ably-ios -// -// Created by vic on 16/03/2015. -// Copyright (c) 2015 Ably. All rights reserved. -// -#import -#import -#import "ARTTestUtil.h" -#import "ARTPayload.h" -#import "ARTPayload+Private.h" -#import "ARTLog.h" -@interface ARTRealtimeCryptoMessageTest : XCTestCase - -@end - -@implementation ARTRealtimeCryptoMessageTest - -- (void)setUp { - [super setUp]; -} - -- (void)tearDown { - [super tearDown]; -} - - --(void) testCBCParser { - - NSArray * encoders = [ARTPayload parseEncodingChain:@"utf-8/cipher+aes-128-cbc/base64" - key:[[NSData alloc] initWithBase64EncodedString:@"WUP6u0K7MXI5Zeo0VppPwg==" options:0] - iv:[[NSData alloc] initWithBase64EncodedString:@"HO4cYSP8LybPYBPZPHQOtg==" options:0]]; - XCTAssertEqual([encoders count], 3); - id utf8 = [encoders objectAtIndex:0]; - id cipher128 = [encoders objectAtIndex:1]; - id base64 = [encoders objectAtIndex:2]; - XCTAssertEqualObjects([utf8 name], @"utf-8"); - XCTAssertEqualObjects([cipher128 name], @"cipher+aes-128-cbc"); - XCTAssertEqualObjects([base64 name], @"base64"); - - -} - --(void) testCBCParser256 { - - NSArray * encoders = [ARTPayload parseEncodingChain:@"utf-8/cipher+aes-256-cbc/base64" - key:[[NSData alloc] initWithBase64EncodedString:@"o9qXZoPGDNla50VnRwH7cGqIrpyagTxGsRgimKJbY40=" options:0] - iv:[[NSData alloc] initWithBase64EncodedString:@"HO4cYSP8LybPYBPZPHQOtg==" options:0]]; - XCTAssertEqual([encoders count], 3); - id utf8 = [encoders objectAtIndex:0]; - id cipher128 = [encoders objectAtIndex:1]; - id base64 = [encoders objectAtIndex:2]; - XCTAssertEqualObjects([utf8 name], @"utf-8"); - XCTAssertEqualObjects([cipher128 name], @"cipher+aes-256-cbc"); - XCTAssertEqualObjects([base64 name], @"base64"); - - -} - - --(void) testCaseByFileContents:(NSString *) str { - - NSData* data = [str dataUsingEncoding:NSUTF8StringEncoding]; - NSDictionary * topLevel =[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil]; - NSString * key = [topLevel valueForKey:@"key"]; - NSString * iv = [topLevel valueForKey:@"iv"]; - NSArray * items = [topLevel valueForKey:@"items"]; - for(int i=0;i < [items count]; i++) { - - - NSDictionary * item = [items objectAtIndex:i]; - NSString * dataStr = [[item valueForKey:@"encoded"] valueForKey:@"data"]; - NSString * simpleEncoding = [[item valueForKey:@"encoded"] valueForKey:@"encoding"]; - if(!simpleEncoding) { - simpleEncoding = @""; - } - NSString * encData = [[item valueForKey:@"encrypted"] valueForKey:@"data"]; - NSString * encoding = [[item valueForKey:@"encrypted"] valueForKey:@"encoding"]; - - NSArray * encoders = [ARTPayload parseEncodingChain:encoding - key:[[NSData alloc] initWithBase64EncodedString:key options:0] - iv:[[NSData alloc] initWithBase64EncodedString:iv options:0]]; - - NSData * encodableData = [simpleEncoding isEqualToString:@"base64"] ? - [[NSData alloc] initWithBase64EncodedString:dataStr options:0] : [dataStr dataUsingEncoding:NSUTF8StringEncoding]; - - id encoderChain =[[ARTPayloadEncoderChain alloc] initWithEncoders:encoders]; - - //check encoded result matches the encoded string in the file - { - - ARTPayload * p = [[ARTPayload alloc] initWithPayload:encodableData encoding:encoding]; - ARTPayload * outputPayload = nil; - [encoderChain encode:p output:&outputPayload]; - XCTAssertEqualObjects(outputPayload.payload, encData); - } - - //check decoded result matches the decoded string in the file - { - ARTPayload * decP = [[ARTPayload alloc] initWithPayload:encData encoding:encoding]; - ARTPayload * decOutput = nil; - [encoderChain decode:decP output:&decOutput]; - - NSString * decodedStr = decOutput.payload; - if([simpleEncoding isEqualToString:@"base64"]) { - XCTAssertEqualObjects(decOutput.payload, encodableData); - } - else if([simpleEncoding isEqualToString:@"json"]) { - //we don't want to compare json as strings, but as arrays or dictionarys, so we encode and decode the json from the file. - ARTPayload * p =[[ARTPayload alloc] initWithPayload:encodableData encoding:@"json"]; - ARTJsonPayloadEncoder * e = [ARTJsonPayloadEncoder instance]; - ARTPayload* jsonOut = nil; - [e encode:p output:&jsonOut]; - ARTPayload * decodedEncoded = nil; - [e decode:jsonOut output:&decodedEncoded]; - if([decOutput.payload isKindOfClass:[NSDictionary class]]) { - XCTAssertEqualObjects(decodedEncoded.payload, decOutput.payload); - } - else if([decOutput.payload isKindOfClass:[NSArray class]]) { - XCTAssertEqualObjects(decodedEncoded.payload, decOutput.payload); - } - } - else { - XCTAssertEqualObjects(decodedStr, dataStr); - } - } - } -} - -- (void)testEncrypt_128 { - [self testCaseByFileContents:[ARTTestUtil getCrypto128Json]]; -} - -- (void)testEncrypt_256 { - [self testCaseByFileContents:[ARTTestUtil getCrypto256Json]]; -} - - -@end diff --git a/ably-iosTests/ARTRealtimeCryptoTest.m b/ably-iosTests/ARTRealtimeCryptoTest.m index a49285754..ea2f34da4 100644 --- a/ably-iosTests/ARTRealtimeCryptoTest.m +++ b/ably-iosTests/ARTRealtimeCryptoTest.m @@ -68,9 +68,8 @@ - (void)testSendEncodedMessage { XCTAssertTrue(page != nil); XCTAssertEqual([page count], 2); ARTMessage *stringMessage = [page objectAtIndex:0]; - //ARTMessage * dataMessage = [page objectAtIndex:1]; - //TODO work out why these arent equivalent - //XCTAssertEqualObjects([dataMessage content], dataPayload); + ARTMessage * dataMessage = [page objectAtIndex:1]; + XCTAssertEqualObjects([dataMessage content], dataPayload); XCTAssertEqualObjects([stringMessage content], stringPayload); [exp fulfill]; } error:nil]; diff --git a/ably-iosTests/ARTRealtimeMessageTest.m b/ably-iosTests/ARTRealtimeMessageTest.m index d3666e697..e1906c7dc 100644 --- a/ably-iosTests/ARTRealtimeMessageTest.m +++ b/ably-iosTests/ARTRealtimeMessageTest.m @@ -18,7 +18,6 @@ #import "ARTEventEmitter.h" #import "ARTDataQuery.h" #import "ARTPaginatedResult.h" -#import "ARTPayload+Private.h" #import "ARTTestUtil.h" #import "ARTLog.h" @@ -36,7 +35,6 @@ - (void)setUp { - (void)tearDown { [super tearDown]; - [ARTPayload getPayloadArraySizeLimit:SIZE_T_MAX modify:true]; if (_realtime) { [_realtime removeAllChannels]; [_realtime.eventEmitter removeEvents]; diff --git a/ably-iosTests/ARTRestChannelPublishTest.m b/ably-iosTests/ARTRestChannelPublishTest.m index b0280c2dc..14fd0ac83 100644 --- a/ably-iosTests/ARTRestChannelPublishTest.m +++ b/ably-iosTests/ARTRestChannelPublishTest.m @@ -13,7 +13,6 @@ #import "ARTPresenceMessage.h" #import "ARTRest.h" #import "ARTTestUtil.h" -#import "ARTPayload+Private.h" #import "ARTLog.h" #import "ARTRestChannel.h" #import "ARTChannelCollection.h" @@ -98,26 +97,6 @@ -(void) testPublishArray { [self waitForExpectationsWithTimeout:[ARTTestUtil timeout] handler:nil]; } - -/** - Currently the payloadArraySizeLimit is default to INT_MAX. Here we bring that number down to 2 - To show that publishing an array over the limit throws an exception. - */ --(void) testPublishTooManyInArray { - XCTestExpectation *exp = [self expectationWithDescription:@"testPublishTooManyInArray"]; - [ARTTestUtil testRest:^(ARTRest *rest) { - _rest = rest; - ARTChannel *channel = [rest.channels get:@"channel"]; - NSArray *messages = @[[[ARTMessage alloc] initWithData:@"test1" name:nil], - [[ARTMessage alloc] initWithData:@"test2" name:nil], - [[ARTMessage alloc] initWithData:@"test3" name:nil]]; - [ARTPayload getPayloadArraySizeLimit:2 modify:true]; - XCTAssertThrows([channel publish:messages callback:^(NSError *error) {}]); - [exp fulfill]; - }]; - [self waitForExpectationsWithTimeout:[ARTTestUtil timeout] handler:nil]; -} - - (void)testPublishUnJsonableType { XCTestExpectation *expectation = [self expectationWithDescription:@"testPresence"]; [ARTTestUtil testRest:^(ARTRest *rest) { diff --git a/ably-iosTests/ARTRestTokenTest.m b/ably-iosTests/ARTRestTokenTest.m index 3f8e11b4f..f98f53446 100644 --- a/ably-iosTests/ARTRestTokenTest.m +++ b/ably-iosTests/ARTRestTokenTest.m @@ -15,7 +15,7 @@ #import "ARTTestUtil.h" #import "ARTRest+Private.h" #import "ARTLog.h" -#import "ARTPayload.h" +#import "ARTDataEncoder.h" #import "ARTRestChannel.h" #import "ARTChannelCollection.h" #import "ARTAuthTokenDetails.h" diff --git a/ably-iosTests/ARTTestUtil.h b/ably-iosTests/ARTTestUtil.h index 1aafbb983..ed6290d02 100644 --- a/ably-iosTests/ARTTestUtil.h +++ b/ably-iosTests/ARTTestUtil.h @@ -16,7 +16,7 @@ @class ARTChannel; @class XCTestExpectation; @class ARTRealtimeChannel; -@class ARTCipherPayloadEncoder; +@class ARTCipherDataEncoder; void waitForWithTimeout(NSUInteger *counter, NSArray *list, NSTimeInterval timeout); @@ -30,7 +30,7 @@ typedef NS_ENUM(NSUInteger, TestAlteration) { TestAlterationRestrictCapability }; -+ (ARTCipherPayloadEncoder *)getTestCipherEncoder; ++ (ARTCipherDataEncoder *)getTestCipherEncoder; // FIXME: why is `setupApp` using a callback? hard reading... could be a blocking method (only once per test) + (void)setupApp:(ARTClientOptions *)options cb:(void(^)(ARTClientOptions *options))cb; diff --git a/ably-iosTests/ARTTestUtil.m b/ably-iosTests/ARTTestUtil.m index 1c2615b02..9c008d521 100644 --- a/ably-iosTests/ARTTestUtil.m +++ b/ably-iosTests/ARTTestUtil.m @@ -13,7 +13,7 @@ #import "ARTRealtime.h" #import "ARTRealtimeChannel.h" #import "ARTRealtimePresence.h" -#import "ARTPayload.h" +#import "ARTDataEncoder.h" #import "ARTProtocolMessage.h" #import "ARTEventEmitter.h" #import "ARTURLSessionServerTrust.h" @@ -27,8 +27,8 @@ void waitForWithTimeout(NSUInteger *counter, NSArray *list, NSTimeInterval timeo @implementation ARTTestUtil -+ (ARTCipherPayloadEncoder *)getTestCipherEncoder { - ARTCipherPayloadEncoder *e = nil; ++ (ARTCipherDataEncoder *)getTestCipherEncoder { + ARTCipherDataEncoder *e = nil; return e; } diff --git a/ably.xcodeproj/project.pbxproj b/ably.xcodeproj/project.pbxproj index 59b95a3bf..1a8566b7b 100644 --- a/ably.xcodeproj/project.pbxproj +++ b/ably.xcodeproj/project.pbxproj @@ -29,11 +29,9 @@ 1C5DA0151A6E5FA400A2B7EF /* ably.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = 96BF61311A35B2AB004CF2B3 /* ably.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 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 */; }; - 1C8065051AE7C8FA00D49357 /* ARTPayload+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 1C8065041AE7C8FA00D49357 /* ARTPayload+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; 1CC3D94B1AB6FBB60005BEB0 /* ARTRealtimeChannelHistoryTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 1CC3D94A1AB6FBB60005BEB0 /* ARTRealtimeChannelHistoryTest.m */; }; 1CC3D94D1AB6FE700005BEB0 /* ARTRealtimeConnectTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 1CC3D94C1AB6FE700005BEB0 /* ARTRealtimeConnectTest.m */; }; 1CC3D9511AB6FF480005BEB0 /* ARTRealtimeCryptoTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 1CC3D9501AB6FF480005BEB0 /* ARTRealtimeCryptoTest.m */; }; - 1CC3D9531AB7001E0005BEB0 /* ARTRealtimeCryptoMessageTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 1CC3D9521AB7001E0005BEB0 /* ARTRealtimeCryptoMessageTest.m */; }; 1CC3D9551AB700680005BEB0 /* ARTRealtimeInitTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 1CC3D9541AB700680005BEB0 /* ARTRealtimeInitTest.m */; }; 1CC3D9571AB700EC0005BEB0 /* ARTRealtimeMessageTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 1CC3D9561AB700EC0005BEB0 /* ARTRealtimeMessageTest.m */; }; 1CC3D9591AB701E50005BEB0 /* ARTRealtimePresenceTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 1CC3D9581AB701E50005BEB0 /* ARTRealtimePresenceTest.m */; }; @@ -60,8 +58,6 @@ 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, ); }; }; 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 */; 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, ); }; }; @@ -129,7 +125,7 @@ D7588AF41BFF91B800BB8279 /* ARTURLSessionServerTrust.m in Sources */ = {isa = PBXBuildFile; fileRef = D7588AF21BFF91B800BB8279 /* ARTURLSessionServerTrust.m */; settings = {ASSET_TAGS = (); }; }; D74EFAEB1C4D09B500CFF98E /* RealtimeClientChannel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74EFAEA1C4D09B500CFF98E /* RealtimeClientChannel.swift */; }; D7B17EE31C07208B00A6958E /* ARTConnectionDetails.h in Headers */ = {isa = PBXBuildFile; fileRef = D7B17EE11C07208B00A6958E /* ARTConnectionDetails.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D7B17EE41C07208B00A6958E /* ARTConnectionDetails.m in Sources */ = {isa = PBXBuildFile; fileRef = D7B17EE21C07208B00A6958E /* ARTConnectionDetails.m */; settings = {ASSET_TAGS = (); }; }; + D7B17EE41C07208B00A6958E /* ARTConnectionDetails.m in Sources */ = {isa = PBXBuildFile; fileRef = D7B17EE21C07208B00A6958E /* ARTConnectionDetails.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, ); }; }; D7D29B421BE3DEB300374295 /* ARTConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = D7D29B411BE3DEB300374295 /* ARTConnection.m */; }; @@ -148,6 +144,10 @@ D7F1D3771BF4DE72001A4B5E /* ARTRealtimePresence.h in Headers */ = {isa = PBXBuildFile; fileRef = D7F1D3751BF4DE72001A4B5E /* ARTRealtimePresence.h */; settings = {ATTRIBUTES = (Public, ); }; }; D7F1D3781BF4DE72001A4B5E /* ARTRealtimePresence.m in Sources */ = {isa = PBXBuildFile; fileRef = D7F1D3761BF4DE72001A4B5E /* ARTRealtimePresence.m */; }; D7F1D37A1BF4E33A001A4B5E /* ARTRestChannel+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = D7F1D3791BF4E33A001A4B5E /* ARTRestChannel+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + EB3239441C59AB0400892664 /* ARTDataEncoder.m in Sources */ = {isa = PBXBuildFile; fileRef = EB3239421C59AB0400892664 /* ARTDataEncoder.m */; }; + EB3239451C59AB0400892664 /* ARTDataEncoder.m in Sources */ = {isa = PBXBuildFile; fileRef = EB3239421C59AB0400892664 /* ARTDataEncoder.m */; }; + EB82F8511C59D29B00661917 /* ARTDataEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = EB3239461C59AB2C00892664 /* ARTDataEncoder.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EB82F8521C59D30500661917 /* ARTDataEncoder.m in Sources */ = {isa = PBXBuildFile; fileRef = EB3239421C59AB0400892664 /* ARTDataEncoder.m */; }; /* End PBXBuildFile section */ /* Begin PBXBuildRule section */ @@ -207,7 +207,7 @@ 1C1E52F71AB32EF0004A690F /* ARTRestTokenTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTRestTokenTest.m; sourceTree = ""; }; 1C1E52F91AB35665004A690F /* ARTRestInitTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTRestInitTest.m; sourceTree = ""; }; 1C1E53011AB373C5004A690F /* ARTRealtimeChannelTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTRealtimeChannelTest.m; sourceTree = ""; }; - 1C2B0FFB1B136A6D00E3633C /* ARTPresenceMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTPresenceMap.h; sourceTree = ""; }; + 1C2B0FFB1B136A6D00E3633C /* ARTPresenceMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ARTPresenceMap.h; path = "ably-ios/ARTPresenceMap.h"; sourceTree = SOURCE_ROOT; }; 1C2B0FFC1B136A6D00E3633C /* ARTPresenceMap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTPresenceMap.m; sourceTree = ""; }; 1C55427C1B148306003068DB /* ARTStatus.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTStatus.m; sourceTree = ""; }; 1C578E1D1B3435CA00EF46EC /* ARTFallback.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTFallback.h; sourceTree = ""; }; @@ -215,11 +215,9 @@ 1C578E211B3438F300EF46EC /* ARTFallbackTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTFallbackTest.m; sourceTree = ""; }; 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 = ""; }; - 1C8065041AE7C8FA00D49357 /* ARTPayload+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ARTPayload+Private.h"; 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 = ""; }; 1CC3D9501AB6FF480005BEB0 /* ARTRealtimeCryptoTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTRealtimeCryptoTest.m; sourceTree = ""; }; - 1CC3D9521AB7001E0005BEB0 /* ARTRealtimeCryptoMessageTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTRealtimeCryptoMessageTest.m; sourceTree = ""; }; 1CC3D9541AB700680005BEB0 /* ARTRealtimeInitTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTRealtimeInitTest.m; sourceTree = ""; }; 1CC3D9561AB700EC0005BEB0 /* ARTRealtimeMessageTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTRealtimeMessageTest.m; sourceTree = ""; }; 1CC3D9581AB701E50005BEB0 /* ARTRealtimePresenceTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTRealtimePresenceTest.m; sourceTree = ""; }; @@ -250,8 +248,6 @@ 960D07951A46FFC300ED8C8C /* ARTRest+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ARTRest+Private.h"; sourceTree = ""; }; 961343D61A42E0B7006DC822 /* ARTClientOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTClientOptions.h; sourceTree = ""; }; 961343D71A42E0B7006DC822 /* ARTClientOptions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTClientOptions.m; sourceTree = ""; }; - 961343E61A432E7C006DC822 /* ARTPayload.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTPayload.h; sourceTree = ""; }; - 961343E71A432E7C006DC822 /* ARTPayload.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTPayload.m; sourceTree = ""; }; 967A431F1A39AEAF00E4CE23 /* ARTNSArray+ARTFunctional.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ARTNSArray+ARTFunctional.h"; sourceTree = ""; }; 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 = ""; }; @@ -301,7 +297,7 @@ 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 = ""; }; + D746AE261BBB61C9003ECEF8 /* ARTPresence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ARTPresence.h; path = "ably-ios/ARTPresence.h"; sourceTree = SOURCE_ROOT; }; 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 /* RestClientChannels.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RestClientChannels.swift; sourceTree = ""; }; @@ -348,6 +344,8 @@ D7F1D3761BF4DE72001A4B5E /* ARTRealtimePresence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTRealtimePresence.m; sourceTree = ""; }; D7F1D3791BF4E33A001A4B5E /* ARTRestChannel+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ARTRestChannel+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 = ""; }; + EB3239421C59AB0400892664 /* ARTDataEncoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTDataEncoder.m; sourceTree = ""; }; + EB3239461C59AB2C00892664 /* ARTDataEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ARTDataEncoder.h; path = "ably-ios/ARTDataEncoder.h"; sourceTree = SOURCE_ROOT; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -402,7 +400,6 @@ 1C1E53011AB373C5004A690F /* ARTRealtimeChannelTest.m */, 1CC3D94C1AB6FE700005BEB0 /* ARTRealtimeConnectTest.m */, 1CC3D9501AB6FF480005BEB0 /* ARTRealtimeCryptoTest.m */, - 1CC3D9521AB7001E0005BEB0 /* ARTRealtimeCryptoMessageTest.m */, 1CC3D9541AB700680005BEB0 /* ARTRealtimeInitTest.m */, 1CC3D9561AB700EC0005BEB0 /* ARTRealtimeMessageTest.m */, 1CC3D95A1AB702E30005BEB0 /* ARTRealtimePresenceHistoryTest.m */, @@ -604,9 +601,8 @@ 96A507A01A377AA50077CDF8 /* ARTPresenceMessage.m */, 1C2B0FFB1B136A6D00E3633C /* ARTPresenceMap.h */, 1C2B0FFC1B136A6D00E3633C /* ARTPresenceMap.m */, - 961343E61A432E7C006DC822 /* ARTPayload.h */, - 1C8065041AE7C8FA00D49357 /* ARTPayload+Private.h */, - 961343E71A432E7C006DC822 /* ARTPayload.m */, + EB3239421C59AB0400892664 /* ARTDataEncoder.m */, + EB3239461C59AB2C00892664 /* ARTDataEncoder.h */, 96A507931A370F860077CDF8 /* ARTStats.h */, 96A507941A370F860077CDF8 /* ARTStats.m */, 96BF61551A35B40E004CF2B3 /* ARTStatus.h */, @@ -698,6 +694,7 @@ 1C2B0FFD1B136A6D00E3633C /* ARTPresenceMap.h in Headers */, 1CD8DC9F1B1C7315007EAF36 /* ARTDefault.h in Headers */, D7D8F8211BC2BE16009718F2 /* ARTAuthOptions.h in Headers */, + EB82F8511C59D29B00661917 /* ARTDataEncoder.h in Headers */, D746AE401BBC5B14003ECEF8 /* ARTEventEmitter.h in Headers */, D746AE531BBD85C5003ECEF8 /* ARTChannelCollection.h in Headers */, D7D8F82D1BC2C706009718F2 /* ARTAuthTokenParams.h in Headers */, @@ -717,12 +714,10 @@ D746AE251BBB611C003ECEF8 /* ARTChannel+Private.h in Headers */, D7F1D3731BF4DE07001A4B5E /* ARTRestPresence.h in Headers */, D7B17EE31C07208B00A6958E /* ARTConnectionDetails.h in Headers */, - 1C8065051AE7C8FA00D49357 /* ARTPayload+Private.h in Headers */, 960D07971A46FFC300ED8C8C /* ARTRest+Private.h in Headers */, 1C05CF201AC1D7EB00687AC9 /* ARTRealtime+Private.h in Headers */, D7F1D37A1BF4E33A001A4B5E /* ARTRestChannel+Private.h in Headers */, 85B2C2191B6FE8DE00EA5254 /* CompatibilityMacros.h in Headers */, - 961343E81A432E7C006DC822 /* ARTPayload.h in Headers */, 96A507A91A37806A0077CDF8 /* ARTEncoder.h in Headers */, 1C6C18A31ADFDAB100AB79E4 /* ARTLog.h in Headers */, D7EBE5A41BE9F6900086E675 /* ARTConnection.h in Headers */, @@ -1005,6 +1000,7 @@ 851674EF1B7BA5CD00D35169 /* Stats.swift in Sources */, D72304701BB72CED00F1ABDA /* RealtimeClient.swift in Sources */, D746AE2D1BBB625E003ECEF8 /* RestClientChannels.swift in Sources */, + EB3239451C59AB0400892664 /* ARTDataEncoder.m in Sources */, D7C1B8771BBEA81A0087B55F /* Auth.swift in Sources */, D7EBE5A31BE8391E0086E675 /* RealtimeClientConnection.swift in Sources */, ); @@ -1014,6 +1010,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + EB82F8521C59D30500661917 /* ARTDataEncoder.m in Sources */, D746AE391BBC3201003ECEF8 /* ARTMessage.m in Sources */, D746AE231BBB60EE003ECEF8 /* ARTChannel.m in Sources */, D746AE481BBD6FE9003ECEF8 /* ARTQueuedMessage.m in Sources */, @@ -1024,7 +1021,6 @@ 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 */, @@ -1075,7 +1071,6 @@ 1CC3D95B1AB702E30005BEB0 /* ARTRealtimePresenceHistoryTest.m in Sources */, 1CC3D94D1AB6FE700005BEB0 /* ARTRealtimeConnectTest.m in Sources */, 1CC3D9551AB700680005BEB0 /* ARTRealtimeInitTest.m in Sources */, - 1CC3D9531AB7001E0005BEB0 /* ARTRealtimeCryptoMessageTest.m in Sources */, 1C1E52FA1AB35665004A690F /* ARTRestInitTest.m in Sources */, 1C1E52F41AB32EDE004A690F /* ARTRestCryptoTest.m in Sources */, 1CC3D94B1AB6FBB60005BEB0 /* ARTRealtimeChannelHistoryTest.m in Sources */, @@ -1083,6 +1078,7 @@ 1C1E52F81AB32EF0004A690F /* ARTRestTokenTest.m in Sources */, 1CC3D95D1AB704080005BEB0 /* ARTRealtimeRecoverTest.m in Sources */, 1C1E52F61AB32EE6004A690F /* ARTRestPresenceTest.m in Sources */, + EB3239441C59AB0400892664 /* ARTDataEncoder.m in Sources */, 1C1E53021AB373C5004A690F /* ARTRealtimeChannelTest.m in Sources */, 1CC3D9571AB700EC0005BEB0 /* ARTRealtimeMessageTest.m in Sources */, 1C1E52F21AB32ED1004A690F /* ARTRestChannelPublishTest.m in Sources */, diff --git a/ablySpec/RealtimeClientConnection.swift b/ablySpec/RealtimeClientConnection.swift index e1bede52c..ba9f29ffe 100644 --- a/ablySpec/RealtimeClientConnection.swift +++ b/ablySpec/RealtimeClientConnection.swift @@ -366,7 +366,7 @@ class RealtimeClientConnection: QuickSpec { expect(channel.state).to(equal(ARTRealtimeChannelState.Attached)) channel.subscribe { message, errorInfo in - expect(message.payload.payload as? String).to(equal("message_string")) + expect(message.data as? String).to(equal("message_string")) TotalReach.shared++ } diff --git a/ablySpec/RestChannel.swift b/ablySpec/RestChannel.swift index f1d2823f6..580ae949f 100644 --- a/ablySpec/RestChannel.swift +++ b/ablySpec/RestChannel.swift @@ -14,21 +14,9 @@ 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 self.name == other.name && + self.encoding == other.encoding && + self.data as! NSObject == other.data as! NSObject } return super.isEqual(object) @@ -89,7 +77,7 @@ class RestChannel: QuickSpec { 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) + expect(publishedMessage?.data as? String).toEventually(equal(data), timeout: testTimeout) } } @@ -108,7 +96,7 @@ class RestChannel: QuickSpec { expect(publishError).toEventually(beNil(), timeout: testTimeout) expect(publishedMessage?.name).toEventually(equal(name), timeout: testTimeout) - expect(publishedMessage?.payload.payload).toEventually(beNil(), timeout: testTimeout) + expect(publishedMessage?.data).toEventually(beNil(), timeout: testTimeout) } } @@ -127,7 +115,7 @@ class RestChannel: QuickSpec { expect(publishError).toEventually(beNil(), timeout: testTimeout) expect(publishedMessage?.name).toEventually(beNil(), timeout: testTimeout) - expect(publishedMessage?.payload.payload as? String).toEventually(equal(data), timeout: testTimeout) + expect(publishedMessage?.data as? String).toEventually(equal(data), timeout: testTimeout) } } @@ -146,7 +134,7 @@ class RestChannel: QuickSpec { expect(publishError).toEventually(beNil(), timeout: testTimeout) expect(publishedMessage?.name).toEventually(beNil(), timeout: testTimeout) - expect(publishedMessage?.payload.payload).toEventually(beNil(), timeout: testTimeout) + expect(publishedMessage?.data).toEventually(beNil(), timeout: testTimeout) } } @@ -164,7 +152,7 @@ class RestChannel: QuickSpec { expect(publishError).toEventually(beNil(), timeout: testTimeout) expect(publishedMessage?.name).toEventually(beNil(), timeout: testTimeout) - expect(publishedMessage?.payload.payload).toEventually(beNil(), timeout: testTimeout) + expect(publishedMessage?.data).toEventually(beNil(), timeout: testTimeout) } } @@ -229,7 +217,7 @@ class RestChannel: QuickSpec { expect(message.action).to(equal(ARTPresenceAction.Present)) // skip the encrypted message for now - if message.payload.encoding.rangeOfString("cipher") == nil { + if message.encoding.rangeOfString("cipher") == nil { expect(message.content() as? NSObject).to(equal(fixtureMessage["data"].object as? NSObject)) } } @@ -428,7 +416,7 @@ class RestChannel: QuickSpec { for (index, item) in (result.items.reverse().enumerate()) { totalReceived++ - switch (item as? ARTMessage)?.payload.payload { + switch (item as? ARTMessage)?.data { case let value as NSDictionary: expect(value).to(equal(cases[index])) break From 55248789a1bd3ae92c0d3afb9c25b125a2de30ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toni=20C=C3=A1rdenas?= Date: Thu, 28 Jan 2016 05:49:48 +0100 Subject: [PATCH 2/4] Remove leftover variable. --- ably-ios/ARTDataEncoder.m | 1 - 1 file changed, 1 deletion(-) diff --git a/ably-ios/ARTDataEncoder.m b/ably-ios/ARTDataEncoder.m index 61fd2f55c..937642873 100644 --- a/ably-ios/ARTDataEncoder.m +++ b/ably-ios/ARTDataEncoder.m @@ -22,7 +22,6 @@ @interface ARTDataEncoder () @implementation ARTDataEncoder { id _cipher; - ARTLog *_logger; } - (instancetype)initWithCipherParams:(ARTCipherParams *)params logger:(ARTLog *)logger { From 588b9e069fadd02d84f230ca465b571f8d695dbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toni=20C=C3=A1rdenas?= Date: Thu, 28 Jan 2016 17:16:58 +0100 Subject: [PATCH 3/4] Fix: decode PresenceMessages when received. --- ably-ios/ARTRestPresence.m | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ably-ios/ARTRestPresence.m b/ably-ios/ARTRestPresence.m index 487eebf50..25a088d0c 100644 --- a/ably-ios/ARTRestPresence.m +++ b/ably-ios/ARTRestPresence.m @@ -15,6 +15,7 @@ #import "ARTDataQuery+Private.h" #import "ARTJsonEncoder.h" #import "ARTNSArray+ARTFunctional.h" +#import "ARTChannel+Private.h" @implementation ARTRestPresence @@ -32,7 +33,14 @@ - (void)get:(void (^)(__GENERIC(ARTPaginatedResult, ARTPresenceMessage *) *resul ARTPaginatedResultResponseProcessor responseProcessor = ^(NSHTTPURLResponse *response, NSData *data) { id encoder = [[self channel].rest.encoders objectForKey:response.MIMEType]; - return [encoder decodePresenceMessages:data]; + return [[encoder decodePresenceMessages:data] artMap:^(ARTPresenceMessage *message) { + // FIXME: This should be refactored to be done by ART{Json,...}Encoder. + // The ART{Json,...}Encoder should take a ARTDataEncoder and use it every + // time it is enc/decoding a message. This also applies for REST and Realtime + // ARTMessages. + [message decodeWithEncoder:self.channel.dataEncoder output:&message]; + return message; + }]; }; [ARTPaginatedResult executePaginated:[self channel].rest withRequest:request andResponseProcessor:responseProcessor callback:callback]; From d5719b3c602b5c327b6e2c310fcc42ca3ab74cbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toni=20C=C3=A1rdenas?= Date: Fri, 29 Jan 2016 04:20:31 +0100 Subject: [PATCH 4/4] Fix PresenceMessage decoding test. The test was expecting PresenceMessages _not_ to be decoded, which is the wrong behavior. Plus, DataEncoder has been refactored so that it doesn't take out arguments but return a custom DataEncoderOutput instance. Calling Objective-C functions with out arguments is a real pain and really unsafe. Encrypted test fixtures for presence messages are now working too. --- ably-ios/ARTBaseMessage.h | 4 +-- ably-ios/ARTBaseMessage.m | 20 +++++------ ably-ios/ARTDataEncoder.h | 14 ++++++-- ably-ios/ARTDataEncoder.m | 63 ++++++++++++++++++++++------------- ably-ios/ARTPresenceMessage.m | 2 +- ably-ios/ARTRealtimeChannel.m | 12 +++---- ablySpec/RestChannel.swift | 24 ++++++++----- 7 files changed, 82 insertions(+), 57 deletions(-) diff --git a/ably-ios/ARTBaseMessage.h b/ably-ios/ARTBaseMessage.h index 25eaf679e..298b947a5 100644 --- a/ably-ios/ARTBaseMessage.h +++ b/ably-ios/ARTBaseMessage.h @@ -28,9 +28,9 @@ ART_ASSUME_NONNULL_BEGIN @property (strong, nonatomic) NSString *connectionId; /// Any transformation applied to the data for this message -@property (strong, nonatomic) NSString *encoding; +@property (strong, nonatomic, art_nullable) NSString *encoding; -@property (strong, nonatomic) id data; +@property (strong, nonatomic, art_nullable) id data; - (ARTStatus *__art_nonnull)decodeWithEncoder:(ARTDataEncoder*)encoder output:(id __art_nonnull*__art_nonnull)output; - (ARTStatus *__art_nonnull)encodeWithEncoder:(ARTDataEncoder*)encoder output:(id __art_nonnull*__art_nonnull)output; diff --git a/ably-ios/ARTBaseMessage.m b/ably-ios/ARTBaseMessage.m index ec7653370..6068472e4 100644 --- a/ably-ios/ARTBaseMessage.m +++ b/ably-ios/ARTBaseMessage.m @@ -41,23 +41,19 @@ - (instancetype)messageWithData:(id)data encoding:(NSString *)encoding { } - (ARTStatus *)decodeWithEncoder:(ARTDataEncoder*)encoder output:(id *)output { - id decoded = nil; - NSString *decodedEncoding = nil; - ARTStatus *status = [encoder decode:self.data encoding:self.encoding outputData:&decoded outputEncoding:&decodedEncoding]; + ARTDataEncoderOutput *decoded = [encoder decode:self.data encoding:self.encoding]; *output = [self copy]; - ((ARTBaseMessage *)*output).data = decoded; - ((ARTBaseMessage *)*output).encoding = decodedEncoding; - return status; + ((ARTBaseMessage *)*output).data = decoded.data; + ((ARTBaseMessage *)*output).encoding = decoded.encoding; + return decoded.status; } - (ARTStatus *)encodeWithEncoder:(ARTDataEncoder*)encoder output:(id *)output { - id encoded = nil; - NSString *encoding = nil; - ARTStatus *status = [encoder encode:self.data outputData:&encoded outputEncoding:&encoding]; + ARTDataEncoderOutput *encoded = [encoder encode:self.data]; *output = [self copy]; - ((ARTBaseMessage *)*output).data = encoded; - ((ARTBaseMessage *)*output).encoding = [self.encoding artAddEncoding:encoding]; - return status; + ((ARTBaseMessage *)*output).data = encoded.data; + ((ARTBaseMessage *)*output).encoding = [self.encoding artAddEncoding:encoded.encoding]; + return encoded.status; } - (id)content { diff --git a/ably-ios/ARTDataEncoder.h b/ably-ios/ARTDataEncoder.h index ff70fc8c1..35658ca52 100644 --- a/ably-ios/ARTDataEncoder.h +++ b/ably-ios/ARTDataEncoder.h @@ -15,11 +15,21 @@ ART_ASSUME_NONNULL_BEGIN +@interface ARTDataEncoderOutput : NSObject + +@property (readonly, nonatomic, art_nullable) id data; +@property (readonly, nonatomic, art_nullable) NSString *encoding; +@property (readonly, nonatomic) ARTStatus *status; + +- initWithData:(id __art_nullable)data encoding:(NSString *__art_nullable)encoding status:(ARTStatus *)status; + +@end + @interface ARTDataEncoder : NSObject - (instancetype)initWithCipherParams:(ARTCipherParams *)params logger:(ARTLog *)logger; -- (ARTStatus *__art_nullable)encode:(id)data outputData:(id __art_nullable *__art_nonnull)outputData outputEncoding:(NSString *__art_nullable *__art_nonnull)outputEncoding; -- (ARTStatus *__art_nullable)decode:(id)data encoding:(NSString *)encoding outputData:(id __art_nullable *__art_nonnull)outputData outputEncoding:(NSString *__art_nullable *__art_nonnull)outputEncoding; +- (ARTDataEncoderOutput *)encode:(id __art_nullable)data; +- (ARTDataEncoderOutput *)decode:(id __art_nullable)data encoding:(NSString *__art_nullable)encoding; @end diff --git a/ably-ios/ARTDataEncoder.m b/ably-ios/ARTDataEncoder.m index 937642873..c672e1d8f 100644 --- a/ably-ios/ARTDataEncoder.m +++ b/ably-ios/ARTDataEncoder.m @@ -20,6 +20,20 @@ @interface ARTDataEncoder () @end +@implementation ARTDataEncoderOutput + +- (id)initWithData:(id)data encoding:(NSString *)encoding status:(ARTStatus *)status { + self = [super init]; + if (self) { + _data = data; + _encoding = encoding; + _status = status; + } + return self; +} + +@end + @implementation ARTDataEncoder { id _cipher; } @@ -38,15 +52,13 @@ - (instancetype)initWithCipherParams:(ARTCipherParams *)params logger:(ARTLog *) return self; } -- (ARTStatus *__art_nullable)encode:(id)data outputData:(id __art_nullable *__art_nonnull)outputData outputEncoding:(NSString **)outputEncoding { +- (ARTDataEncoderOutput *)encode:(id)data { NSData *encoded = nil; NSString *encoding = nil; BOOL ok = false; if (!data) { - *outputData = nil; - *outputEncoding = nil; - return [ARTStatus state:ARTStateOk]; + return [[ARTDataEncoderOutput alloc] initWithData:data encoding:nil status:[ARTStatus state:ARTStateOk]]; } else if ([data isKindOfClass:[NSData class]]) { encoding = @"base64"; encoded = [[((NSData *)data) base64EncodedStringWithOptions:0] dataUsingEncoding:NSUTF8StringEncoding]; @@ -63,39 +75,37 @@ - (ARTStatus *__art_nullable)encode:(id)data outputData:(id __art_nullable *__ar } if (!ok) { - return [ARTStatus state:ARTStateError]; + return [[ARTDataEncoderOutput alloc] initWithData:encoded encoding:encoding status:[ARTStatus state:ARTStateError]]; } if (_cipher) { ARTStatus *status = [_cipher encrypt:encoded output:&encoded]; if (status.state != ARTStateOk) { - return status; + return [[ARTDataEncoderOutput alloc] initWithData:encoded encoding:encoding status:status]; } encoding = [encoding artAddEncoding:[self cipherEncoding]]; // Re-encode the encrypted bytes in base64. encoded = [[encoded base64EncodedStringWithOptions:0] dataUsingEncoding:NSUTF8StringEncoding]; if (encoded == nil) { - return [ARTStatus state:ARTStateError]; + return [[ARTDataEncoderOutput alloc] initWithData:encoded encoding:encoding status:status]; } encoding = [encoding artAddEncoding:@"base64"]; } - *outputData = [[NSString alloc] initWithData:encoded encoding:NSUTF8StringEncoding]; - *outputEncoding = encoding; - return [ARTStatus state:ARTStateOk]; + return [[ARTDataEncoderOutput alloc] initWithData:[[NSString alloc] initWithData:encoded encoding:NSUTF8StringEncoding] + encoding:encoding + status:[ARTStatus state:ARTStateOk]]; } -- (ARTStatus *__art_nullable)decode:(id)data encoding:(NSString *)encoding outputData:(id __art_nullable *__art_nonnull)outputData outputEncoding:(NSString **)outputEncoding { +- (ARTDataEncoderOutput *)decode:(id)data encoding:(NSString *)encoding { if (!data || !encoding ) { - *outputData = data; - *outputEncoding = encoding; - return [ARTStatus state:ARTStateOk]; + return [[ARTDataEncoderOutput alloc] initWithData:data encoding:encoding status:[ARTStatus state:ARTStateOk]]; } BOOL ok = false; NSArray *encodings = [encoding componentsSeparatedByString:@"/"]; - *outputEncoding = [NSString stringWithString:encoding]; + NSString *outputEncoding = [NSString stringWithString:encoding]; for (NSUInteger i = [encodings count]; i > 0; i--) { ok = false; @@ -109,7 +119,10 @@ - (ARTStatus *__art_nullable)decode:(id)data encoding:(NSString *)encoding outpu data = [[NSData alloc] initWithBase64EncodedString:(NSString *)data options:0]; ok = data != nil; } - } else if ([encoding isEqualToString:@""]) { + } else if ([encoding isEqualToString:@""] || [encoding isEqualToString:@"utf-8"]) { + if ([data isKindOfClass:[NSData class]]) { // E. g. when decrypted. + data = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + } ok = [data isKindOfClass:[NSString class]]; } else if ([encoding isEqualToString:@"json"]) { if ([data isKindOfClass:[NSData class]]) { // E. g. when decrypted. @@ -131,18 +144,16 @@ - (ARTStatus *__art_nullable)decode:(id)data encoding:(NSString *)encoding outpu } if (ok) { - *outputEncoding = [*outputEncoding artRemoveLastEncoding]; + outputEncoding = [outputEncoding artRemoveLastEncoding]; } else { [self.logger error:@"ARTDataDecoded failed to decode data as '%@': (%@)%@", encoding, [data class], data]; + break; } } - *outputData = data; - if (ok) { - return [ARTStatus state:ARTStateOk]; - } else { - return [ARTStatus state:ARTStateError]; - } + return [[ARTDataEncoderOutput alloc] initWithData:data + encoding:outputEncoding + status:[ARTStatus state:(ok ? ARTStateOk : ARTStateError)]]; } - (NSString *)cipherEncoding { @@ -168,7 +179,11 @@ - (NSString *)artLastEncoding { } - (NSString *)artRemoveLastEncoding { - return [self stringByDeletingLastPathComponent]; + NSString *encoding = [self stringByDeletingLastPathComponent]; + if ([encoding length] == 0) { + return nil; + } + return encoding; } @end diff --git a/ably-ios/ARTPresenceMessage.m b/ably-ios/ARTPresenceMessage.m index 5ff469153..e218cf20b 100644 --- a/ably-ios/ARTPresenceMessage.m +++ b/ably-ios/ARTPresenceMessage.m @@ -20,7 +20,7 @@ - (instancetype)init { } - (id)copyWithZone:(NSZone *)zone { - ARTPresenceMessage *message = [super init]; + ARTPresenceMessage *message = [super copyWithZone:zone]; message->_action = self.action; return message; } diff --git a/ably-ios/ARTRealtimeChannel.m b/ably-ios/ARTRealtimeChannel.m index 8f676373d..565b10d8a 100644 --- a/ably-ios/ARTRealtimeChannel.m +++ b/ably-ios/ARTRealtimeChannel.m @@ -106,14 +106,12 @@ - (void)publishPresence:(ARTPresenceMessage *)msg cb:(ARTStatusCallback)cb { _lastPresenceAction = msg.action; if (msg.data && self.dataEncoder) { - id encodedData = nil; - NSString *encoding; - ARTStatus * status = [self.dataEncoder encode:msg.data outputData:&encodedData outputEncoding:&encoding]; - if (status.state != ARTStateOk) { - [self.logger warn:@"bad status encoding presence message %d",(int) status]; + ARTDataEncoderOutput *encoded = [self.dataEncoder encode:msg.data]; + if (encoded.status.state != ARTStateOk) { + [self.logger warn:@"bad status encoding presence message %d",(int) encoded.status]; } - msg.data = encodedData; - msg.encoding = encoding; + msg.data = encoded.data; + msg.encoding = encoded.encoding; } ARTProtocolMessage *pm = [[ARTProtocolMessage alloc] init]; diff --git a/ablySpec/RestChannel.swift b/ablySpec/RestChannel.swift index 580ae949f..8a8f7b0b7 100644 --- a/ablySpec/RestChannel.swift +++ b/ablySpec/RestChannel.swift @@ -182,9 +182,10 @@ class RestChannel: QuickSpec { 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) - + for (i, publishedMessage) in publishedMessages.reverse().enumerate() { + expect(publishedMessage.data as? NSObject).to(equal(messages[i].data as? NSObject)) + expect(publishedMessage.name).to(equal(messages[i].name)) + } expect(mockExecutor.requests.count).to(equal(1)) } } @@ -197,7 +198,12 @@ class RestChannel: QuickSpec { // RSP3 context("get") { it("should return presence fixture data") { - let channel = client.channels.get("persisted:presence_fixtures") + let cipherParams = ARTCipherParams.init( + algorithm: appSetupJson["cipher"]["algorithm"].string!, + keySpec: appSetupJson["cipher"]["key"].string!.dataUsingEncoding(NSUTF8StringEncoding), + ivSpec: ARTIvParameterSpec.ivSpecWithIv(appSetupJson["cipher"]["iv"].string!.dataUsingEncoding(NSUTF8StringEncoding)) + ) + let channel = client.channels.get("persisted:presence_fixtures", options:ARTChannelOptions.init(encrypted: cipherParams)) var presenceMessages: [ARTPresenceMessage] = [] channel.presence().get() { result, _ in @@ -208,7 +214,6 @@ class RestChannel: QuickSpec { expect(presenceMessages.count).toEventually(equal(presenceFixtures.count), timeout: testTimeout) for message in presenceMessages { - let fixtureMessage = presenceFixtures.filter({ (key, value) -> Bool in return message.clientId == value["clientId"].stringValue }).first!.1 @@ -216,10 +221,11 @@ class RestChannel: QuickSpec { expect(message.content()).toNot(beNil()) expect(message.action).to(equal(ARTPresenceAction.Present)) - // skip the encrypted message for now - if message.encoding.rangeOfString("cipher") == nil { - expect(message.content() as? NSObject).to(equal(fixtureMessage["data"].object as? NSObject)) - } + let encodedFixture = channel.dataEncoder.decode( + fixtureMessage["data"].object, + encoding:fixtureMessage.asDictionary!["encoding"] as? String + ) + expect(message.content() as? NSObject).to(equal(encodedFixture.data as? NSObject)); } } }