diff --git a/.pubnub.yml b/.pubnub.yml index 447007202..daaa17a30 100644 --- a/.pubnub.yml +++ b/.pubnub.yml @@ -1,9 +1,25 @@ --- name: objective-c scm: github.com/pubnub/objective-c -version: "4.10.1" +version: "4.11.0" schema: 1 changelog: + - + changes: + - + text: "Add Message Actions API support which allow to: add, remove and fetch previously added actions." + type: feature + - + text: "Add new method to simple interface and argument to builder pattern interface which allow to fetch previously added actions and message metadata." + type: feature + - + text: "Modify 'PNObjectEventListener' with new callback which can be used to track message actions addition / removal events." + type: feature + - + text: "Enhance publish sequence manager performance by making save only if any change has been done." + type: improvement + date: Oct 80, 19 + version: v4.11.0 - changes: - @@ -911,6 +927,9 @@ features: - STORAGE-COUNT - HISTORY-DELETE - STORAGE-MESSAGE-COUNT + - STORAGE-HISTORY-WITH-META + - STORAGE-FETCH-WITH-META + - STORAGE-FETCH-WITH-MESSAGE-ACTIONS time: - TIME-TIME subscribe: @@ -927,6 +946,7 @@ features: - SUBSCRIBE-MEMBERSHIP-LISTENER - SUBSCRIBE-SPACE-LISTENER - SUBSCRIBE-USER-LISTENER + - SUBSCRIBE-MESSAGE-ACTIONS-LISTENER unsubscribe: - UNSUBSCRIBE-ALL - UNSUBSCRIBE-SUPPRESS-LEAVE-EVENTS @@ -947,6 +967,10 @@ features: - OBJECTS-MANAGE-MEMBERSHIPS - OBJECTS-GET-MEMBERS - OBJECTS-MANAGE-MEMBERS + message-actions: + - MESSAGE-ACTIONS-GET + - MESSAGE-ACTIONS-ADD + - MESSAGE-ACTIONS-REMOVE others: - TELEMETRY supported-platforms: diff --git a/CHANGELOG b/CHANGELOG index 86588c051..3d7c13bff 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,10 @@ +08-10-2019 - baef53b - 4.11.0 +. add Message Actions API support which allow to: add, remove and fetch previously added actions. +. add new method to simple interface and argument to builder pattern interface which allow to fetch previously added actions and message metadata. +. modify 'PNObjectEventListener' with new callback which can be used to track message actions addition / removal events. +. add new argument to history builder pattern to fetch message metadata. +. enhance publish sequence manager performance by making save only if any change has been done. + 30-08-2019 - 3a15d7f - 4.10.1 . add missing import of Objects API interface to frameworks umbrella header. diff --git a/Example/PubNub Example.xcodeproj/project.pbxproj b/Example/PubNub Example.xcodeproj/project.pbxproj index 66c22c763..bccf1e7e9 100644 --- a/Example/PubNub Example.xcodeproj/project.pbxproj +++ b/Example/PubNub Example.xcodeproj/project.pbxproj @@ -252,6 +252,9 @@ 511725D31BE92F7B008F069E = { CreatedOnToolsVersion = 7.1; }; + 6003F589195388D20070C39A = { + DevelopmentTeam = LJE3GJ53Z7; + }; }; }; buildConfigurationList = 6003F585195388D10070C39A /* Build configuration list for PBXProject "PubNub Example" */; @@ -649,7 +652,7 @@ CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES; CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = LJE3GJ53Z7; ENABLE_BITCODE = NO; GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; GCC_PRECOMPILE_PREFIX_HEADER = YES; @@ -683,7 +686,7 @@ CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES; CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = LJE3GJ53Z7; ENABLE_BITCODE = NO; GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; GCC_PRECOMPILE_PREFIX_HEADER = YES; diff --git a/Example/PubNub Example.xcodeproj/xcshareddata/xcschemes/PubNub Mac Example.xcscheme b/Example/PubNub Example.xcodeproj/xcshareddata/xcschemes/PubNub Mac Example.xcscheme index b7725210f..07b42934a 100644 --- a/Example/PubNub Example.xcodeproj/xcshareddata/xcschemes/PubNub Mac Example.xcscheme +++ b/Example/PubNub Example.xcodeproj/xcshareddata/xcschemes/PubNub Mac Example.xcscheme @@ -27,8 +27,6 @@ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> - - - - + + - - - - - - + + - - - - - - CFBundleExecutable PubNub CFBundleGetInfoString - 4.10.1 + 4.11.0 CFBundleIdentifier com.pubnub.pubnub-objc CFBundleInfoDictionaryVersion @@ -17,11 +17,11 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 4.10.1 + 4.11.0 CFBundleSignature ???? CFBundleVersion - 4.10.1 + 4.11.0 NSHumanReadableCopyright © 2010 - 2019 PubNub, Inc. NSPrincipalClass diff --git a/Framework/PubNub/PubNub-Fabric-Info.plist b/Framework/PubNub/PubNub-Fabric-Info.plist index 405296ac1..fd4027bd2 100644 --- a/Framework/PubNub/PubNub-Fabric-Info.plist +++ b/Framework/PubNub/PubNub-Fabric-Info.plist @@ -7,7 +7,7 @@ CFBundleExecutable PubNub CFBundleGetInfoString - 4.10.1 + 4.11.0 CFBundleIdentifier com.pubnub.pubnub-objc CFBundleInfoDictionaryVersion @@ -17,11 +17,11 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 4.10.1 + 4.11.0 CFBundleSignature ???? CFBundleVersion - 4.10.1 + 4.11.0 NSHumanReadableCopyright © 2010 - 2019 PubNub, Inc. NSPrincipalClass diff --git a/Framework/PubNub/PubNub-iOS-Info.plist b/Framework/PubNub/PubNub-iOS-Info.plist index 405296ac1..fd4027bd2 100644 --- a/Framework/PubNub/PubNub-iOS-Info.plist +++ b/Framework/PubNub/PubNub-iOS-Info.plist @@ -7,7 +7,7 @@ CFBundleExecutable PubNub CFBundleGetInfoString - 4.10.1 + 4.11.0 CFBundleIdentifier com.pubnub.pubnub-objc CFBundleInfoDictionaryVersion @@ -17,11 +17,11 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 4.10.1 + 4.11.0 CFBundleSignature ???? CFBundleVersion - 4.10.1 + 4.11.0 NSHumanReadableCopyright © 2010 - 2019 PubNub, Inc. NSPrincipalClass diff --git a/Framework/PubNub/PubNub-tvOS-Info.plist b/Framework/PubNub/PubNub-tvOS-Info.plist index 405296ac1..fd4027bd2 100644 --- a/Framework/PubNub/PubNub-tvOS-Info.plist +++ b/Framework/PubNub/PubNub-tvOS-Info.plist @@ -7,7 +7,7 @@ CFBundleExecutable PubNub CFBundleGetInfoString - 4.10.1 + 4.11.0 CFBundleIdentifier com.pubnub.pubnub-objc CFBundleInfoDictionaryVersion @@ -17,11 +17,11 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 4.10.1 + 4.11.0 CFBundleSignature ???? CFBundleVersion - 4.10.1 + 4.11.0 NSHumanReadableCopyright © 2010 - 2019 PubNub, Inc. NSPrincipalClass diff --git a/Framework/PubNub/PubNub-watchOS-Info.plist b/Framework/PubNub/PubNub-watchOS-Info.plist index 405296ac1..fd4027bd2 100644 --- a/Framework/PubNub/PubNub-watchOS-Info.plist +++ b/Framework/PubNub/PubNub-watchOS-Info.plist @@ -7,7 +7,7 @@ CFBundleExecutable PubNub CFBundleGetInfoString - 4.10.1 + 4.11.0 CFBundleIdentifier com.pubnub.pubnub-objc CFBundleInfoDictionaryVersion @@ -17,11 +17,11 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 4.10.1 + 4.11.0 CFBundleSignature ???? CFBundleVersion - 4.10.1 + 4.11.0 NSHumanReadableCopyright © 2010 - 2019 PubNub, Inc. NSPrincipalClass diff --git a/Framework/PubNub/PubNub.h b/Framework/PubNub/PubNub.h index fca8fbf3d..9477f00b1 100644 --- a/Framework/PubNub/PubNub.h +++ b/Framework/PubNub/PubNub.h @@ -48,6 +48,7 @@ FOUNDATION_EXPORT const unsigned char PubNubVersionString[]; // API #import "PubNub+Core.h" +#import "PubNub+MessageActions.h" #import "PubNub+ChannelGroup.h" #import "PubNub+Subscribe.h" #import "PNConfiguration.h" diff --git a/PubNub.podspec b/PubNub.podspec index 3271c8fb0..7cd0f2ceb 100644 --- a/PubNub.podspec +++ b/PubNub.podspec @@ -9,7 +9,7 @@ Pod::Spec.new do |spec| spec.name = 'PubNub' - spec.version = '4.10.1' + spec.version = '4.11.0' spec.summary = 'The PubNub Real-Time Network. Build real-time apps quickly and scale them globally.' spec.homepage = 'https://github.com/pubnub/objective-c' diff --git a/PubNub/Core/PubNub+History.h b/PubNub/Core/PubNub+History.h index da739d3cb..ff2fa20cb 100644 --- a/PubNub/Core/PubNub+History.h +++ b/PubNub/Core/PubNub+History.h @@ -87,6 +87,110 @@ NS_ASSUME_NONNULL_BEGIN - (void)historyForChannel:(NSString *)channel withCompletion:(PNHistoryCompletionBlock)block NS_SWIFT_NAME(historyForChannel(_:withCompletion:)); +/** + * @brief Allow to fetch up to \b 100 events from specified \c channel's events storage including + * \c metadata which has been sent along with messages. + * + * @code + * [self.client historyForChannel:@"storage" withMetadata:YES + * completion:^(PNHistoryResult *result, PNErrorStatus *status) { + * + * if (!status.isError) { + * // Fetched data available here: + * // result.data.channels - dictionary with single key (name of requested channel) and + * // list of dictionaries as value. Each entry will include two keys: "message" - for + * // body and "metadata" for meta which has been added during message publish. + * } else { + * // Handle message history download error. Check 'category' property to find out possible + * // issue because of which request did fail. + * // + * // Request can be resent using: [status retry]; + * } + * }]; + * @endcode + * + * @param channel Name of the channel for which events should be pulled out from storage. + * @param shouldIncludeMetadata Whether event metadata should be included in response or not. + * @param block History pull completion block. + * + * @since 4.11.0 + */ +- (void)historyForChannel:(NSString *)channel + withMetadata:(BOOL)shouldIncludeMetadata + completion:(PNHistoryCompletionBlock)block + NS_SWIFT_NAME(historyForChannel(_:withMetadata:completion:)); + +/** + * @brief Allow to fetch up to \b 100 events from specified \c channel's events storage including + * \c actions which has been added to messages. + * + * @code + * [self.client historyForChannel:@"chat" withMessageActions:YES + * completion:^(PNHistoryResult *result, PNErrorStatus *status) { + * + * if (!status.isError) { + * // Fetched data available here: + * // result.data.channels - dictionary with single key (name of requested channel) and + * // list of dictionaries. Each entry will include two keys: "message" - for body and + * // "actions" for list of added actions. + * } else { + * // Handle message history download error. Check 'category' property to find out possible + * // issue because of which request did fail. + * // + * // Request can be resent using: [status retry]; + * } + * }]; + * @endcode + * + * @param channel Name of the channel for which events should be pulled out from storage. + * @param shouldIncludeMessageActions Whether event actions should be included in response + * or not. + * @param block History pull completion block. + * + * @since 4.11.0 + */ +- (void)historyForChannel:(NSString *)channel + withMessageActions:(BOOL)shouldIncludeMessageActions + completion:(PNHistoryCompletionBlock)block + NS_SWIFT_NAME(historyForChannel(_:withMessageActions:completion:)); + +/** + * @brief Allow to fetch up to \b 100 events from specified \c channel's events storage including + * message \c meta and \c actions which has been added to messages. + * + * @code + * [self.client historyForChannel:@"chat" withMetadata:YES messageActions:YES + * completion:^(PNHistoryResult *result, PNErrorStatus *status) { + * + * if (!status.isError) { + * // Fetched data available here: + * // result.data.channels - dictionary with single key (name of requested channel) and + * // list of dictionaries. Each entry will include three keys: "message" - for body, + * // "metadata" for meta which has been added during message publish and "actions" + * // for list of added actions. + * } else { + * // Handle message history download error. Check 'category' property to find out possible + * // issue because of which request did fail. + * // + * // Request can be resent using: [status retry]; + * } + * }]; + * @endcode + * + * @param channel Name of the channel for which events should be pulled out from storage. + * @param shouldIncludeMetadata Whether event metadata should be included in response or not. + * @param shouldIncludeMessageActions Whether event actions should be included in response + * or not. + * @param block History pull completion block. + * + * @since 4.11.0 + */ +- (void)historyForChannel:(NSString *)channel + withMetadata:(BOOL)shouldIncludeMetadata + messageActions:(BOOL)shouldIncludeMessageActions + completion:(PNHistoryCompletionBlock)block + NS_SWIFT_NAME(historyForChannel(_:withMetadata:messageActions:completion:)); + #pragma mark - History in specified frame @@ -129,6 +233,138 @@ NS_ASSUME_NONNULL_BEGIN withCompletion:(PNHistoryCompletionBlock)block NS_SWIFT_NAME(historyForChannel(_:start:end:withCompletion:)); +/** + * @brief Allow to fetch events from specified \c channel's history within specified time frame + * including \c metadata which has been sent along with messages. + * + * @code + * NSNumber *startDate = @([[NSDate dateWithTimeIntervalSinceNow:-(60*60)] timeIntervalSince1970]); + * NSNumber *endDate = @([[NSDate date] timeIntervalSince1970]); + * + * [self.client historyForChannel:@"storage" start:startDate end:endDate includeMetadata:YES + * withCompletion:^(PNHistoryResult *result, PNErrorStatus *status) { + * + * if (!status.isError) { + * // Fetched data available here: + * // result.data.channels - dictionary with single key (name of requested channel) and + * // list of dictionaries. Each entry will include two keys: "message" - for body and + * // "metadata" for meta which has been added during message publish. + * } else { + * // Handle message history download error. Check 'category' property to find out possible + * // issue because of which request did fail. + * // + * // Request can be resent using: [status retry]; + * } + * }]; + * @endcode + * + * @param channel Name of the channel for which events should be pulled out from storage. + * @param startDate Timetoken for oldest event starting from which next should be returned events. + * Value will be converted to required precision internally. + * @param endDate Timetoken for latest event till which events should be pulled out. + * Value will be converted to required precision internally. + * @param shouldIncludeMetadata Whether event metadata should be included in response or not. + * @param block History pull completion block. + * + * @since 4.11.0 + */ +- (void)historyForChannel:(NSString *)channel + start:(nullable NSNumber *)startDate + end:(nullable NSNumber *)endDate + includeMetadata:(BOOL)shouldIncludeMetadata + withCompletion:(PNHistoryCompletionBlock)block + NS_SWIFT_NAME(historyForChannel(_:start:end:includeMetadata:withCompletion:)); + +/** + * @brief Allow to fetch events from specified \c channel's history within specified time frame + * including \c actions which has been added to messages. + * + * @code + * NSNumber *startDate = @([[NSDate dateWithTimeIntervalSinceNow:-(60*60)] timeIntervalSince1970]); + * NSNumber *endDate = @([[NSDate date] timeIntervalSince1970]); + * + * [self.client historyForChannel:@"storage" start:startDate end:endDate includeMessageActions:YES + * withCompletion:^(PNHistoryResult *result, PNErrorStatus *status) { + * + * if (!status.isError) { + * // Fetched data available here: + * // result.data.channels - dictionary with single key (name of requested channel) and + * // list of dictionaries. Each entry will include two keys: "message" - for body and + * // "actions" for list of added actions. + * } else { + * // Handle message history download error. Check 'category' property to find out possible + * // issue because of which request did fail. + * // + * // Request can be resent using: [status retry]; + * } + * }]; + * @endcode + * + * @param channel Name of the channel for which events should be pulled out from storage. + * @param startDate Timetoken for oldest event starting from which next should be returned events. + * Value will be converted to required precision internally. + * @param endDate Timetoken for latest event till which events should be pulled out. + * Value will be converted to required precision internally. + * @param shouldIncludeMessageActions Whether event actions should be included in response + * or not. + * @param block History pull completion block. + * + * @since 4.11.0 + */ +- (void)historyForChannel:(NSString *)channel + start:(nullable NSNumber *)startDate + end:(nullable NSNumber *)endDate + includeMessageActions:(BOOL)shouldIncludeMessageActions + withCompletion:(PNHistoryCompletionBlock)block + NS_SWIFT_NAME(historyForChannel(_:start:end:includeMessageActions:withCompletion:)); + +/** + * @brief Allow to fetch events from specified \c channel's history within specified time frame + * including message \c meta and \c actions which has been added to messages. + * + * @code + * NSNumber *startDate = @([[NSDate dateWithTimeIntervalSinceNow:-(60*60)] timeIntervalSince1970]); + * NSNumber *endDate = @([[NSDate date] timeIntervalSince1970]); + * + * [self.client historyForChannel:@"storage" start:startDate end:endDate includeMetadata:YES + * includeMessageActions:YES + * withCompletion:^(PNHistoryResult *result, PNErrorStatus *status) { + * + * if (!status.isError) { + * // Fetched data available here: + * // result.data.channels - dictionary with single key (name of requested channel) and + * // list of dictionaries. Each entry will include three keys: "message" - for body, + * // "metadata" for meta which has been added during message publish and "actions" for + * // list of added actions. + * } else { + * // Handle message history download error. Check 'category' property to find out possible + * // issue because of which request did fail. + * // + * // Request can be resent using: [status retry]; + * } + * }]; + * @endcode + * + * @param channel Name of the channel for which events should be pulled out from storage. + * @param startDate Timetoken for oldest event starting from which next should be returned events. + * Value will be converted to required precision internally. + * @param endDate Timetoken for latest event till which events should be pulled out. + * Value will be converted to required precision internally. + * @param shouldIncludeMetadata Whether event metadata should be included in response or not. + * @param shouldIncludeMessageActions Whether event actions should be included in response + * or not. + * @param block History pull completion block. + * + * @since 4.11.0 + */ +- (void)historyForChannel:(NSString *)channel + start:(nullable NSNumber *)startDate + end:(nullable NSNumber *)endDate + includeMetadata:(BOOL)shouldIncludeMetadata + includeMessageActions:(BOOL)shouldIncludeMessageActions + withCompletion:(PNHistoryCompletionBlock)block + NS_SWIFT_NAME(historyForChannel(_:start:end:includeMetadata:includeMessageActions:withCompletion:)); + /** * @brief Allow to fetch events from specified \c channel's history within specified time frame. * diff --git a/PubNub/Core/PubNub+History.m b/PubNub/Core/PubNub+History.m index 9913807d4..fb40f2c14 100644 --- a/PubNub/Core/PubNub+History.m +++ b/PubNub/Core/PubNub+History.m @@ -41,6 +41,8 @@ @interface PubNub (HistoryProtected) * @param shouldReverseOrder Whether events order in response should be reversed or not. * @param shouldIncludeTimeToken Whether event dates (time tokens) should be included in response or * not. + * @param shouldIncludeMessageActions Whether event actions should be included in response or not. + * @param shouldIncludeMetadata Whether event metadata should be included in response or not. * @param queryParameters List arbitrary query parameters which should be sent along with original * API call. * @param block History pull completion block. @@ -52,8 +54,10 @@ - (void)historyForChannels:(BOOL)multipleChannels start:(nullable NSNumber *)startDate end:(nullable NSNumber *)endDate limit:(nullable NSNumber *)limit - reverse:(nullable NSNumber *)shouldReverseOrder + reverse:(nullable NSNumber *)shouldReverseOrder includeTimeToken:(nullable NSNumber *)shouldIncludeTimeToken + includeMessageActions:(nullable NSNumber *)shouldIncludeMessageActions + includeMetadata:(nullable NSNumber *)shouldIncludeMetadata queryParameters:(nullable NSDictionary *)queryParameters withCompletion:(PNHistoryCompletionBlock)block; @@ -142,16 +146,20 @@ @implementation PubNub (History) NSNumber *end = parameters[NSStringFromSelector(@selector(end))]; NSNumber *reverse = parameters[NSStringFromSelector(@selector(reverse))]; NSNumber *includeTimeToken = parameters[NSStringFromSelector(@selector(includeTimeToken))]; + NSNumber *includeMetadata = parameters[NSStringFromSelector(@selector(includeMetadata))]; + NSNumber *includeActions = parameters[NSStringFromSelector(@selector(includeMessageActions))]; NSDictionary *queryParam = parameters[@"queryParam"]; id block = parameters[@"block"]; [self historyForChannels:(channels != nil) - object:(channels?: channel) + object:(channels ?: channel) start:start end:end limit:limit reverse:reverse includeTimeToken:includeTimeToken + includeMessageActions:includeActions + includeMetadata:includeMetadata queryParameters:queryParam withCompletion:block]; }]; @@ -214,6 +222,36 @@ - (void)historyForChannel:(NSString *)channel withCompletion:(PNHistoryCompletio [self historyForChannel:channel start:nil end:nil withCompletion:block]; } +- (void)historyForChannel:(NSString *)channel + withMetadata:(BOOL)shouldIncludeMetadata + completion:(PNHistoryCompletionBlock)block { + + [self historyForChannel:channel withMetadata:shouldIncludeMetadata messageActions:NO completion:block]; +} + +- (void)historyForChannel:(NSString *)channel + withMessageActions:(BOOL)shouldIncludeMessageActions + completion:(PNHistoryCompletionBlock)block { + + [self historyForChannel:channel + withMetadata:NO + messageActions:shouldIncludeMessageActions + completion:block]; +} + +- (void)historyForChannel:(NSString *)channel + withMetadata:(BOOL)shouldIncludeMetadata + messageActions:(BOOL)shouldIncludeMessageActions + completion:(PNHistoryCompletionBlock)block { + + [self historyForChannel:channel + start:nil + end:nil + includeMetadata:shouldIncludeMetadata + includeMessageActions:shouldIncludeMessageActions + withCompletion:block]; +} + #pragma mark - History in specified frame @@ -225,6 +263,55 @@ - (void)historyForChannel:(NSString *)channel [self historyForChannel:channel start:startDate end:endDate limit:100 withCompletion:block]; } +- (void)historyForChannel:(NSString *)channel + start:(NSNumber *)startDate + end:(NSNumber *)endDate + includeMetadata:(BOOL)shouldIncludeMetadata + withCompletion:(PNHistoryCompletionBlock)block { + + [self historyForChannel:channel + start:startDate + end:endDate + includeMetadata:shouldIncludeMetadata + includeMessageActions:NO + withCompletion:block]; +} + +- (void)historyForChannel:(NSString *)channel + start:(NSNumber *)startDate + end:(NSNumber *)endDate + includeMessageActions:(BOOL)shouldIncludeMessageActions + withCompletion:(PNHistoryCompletionBlock)block { + + [self historyForChannel:channel + start:startDate + end:endDate + includeMetadata:NO + includeMessageActions:shouldIncludeMessageActions + withCompletion:block]; +} + +- (void)historyForChannel:(NSString *)channel + start:(NSNumber *)startDate + end:(NSNumber *)endDate + includeMetadata:(BOOL)shouldIncludeMetadata + includeMessageActions:(BOOL)shouldIncludeMessageActions + withCompletion:(PNHistoryCompletionBlock)block { + + [self historyForChannels:NO + object:channel + start:startDate + end:endDate + limit:nil + reverse:@NO + includeTimeToken:@NO + includeMessageActions:@(shouldIncludeMetadata) + includeMetadata:@(shouldIncludeMessageActions) + queryParameters:nil + withCompletion:block]; + +} + - (void)historyForChannel:(NSString *)channel start:(NSNumber *)startDate end:(NSNumber *)endDate @@ -303,6 +390,8 @@ - (void)historyForChannel:(NSString *)channel limit:@(limit) reverse:@(shouldReverseOrder) includeTimeToken:@(shouldIncludeTimeToken) + includeMessageActions:nil + includeMetadata:nil queryParameters:nil withCompletion:block]; } @@ -314,22 +403,52 @@ - (void)historyForChannels:(BOOL)multipleChannels limit:(NSNumber *)limit reverse:(NSNumber *)shouldReverseOrder includeTimeToken:(NSNumber *)shouldIncludeTimeToken + includeMessageActions:(NSNumber *)shouldIncludeMessageActions + includeMetadata:(NSNumber *)shouldIncludeMetadata queryParameters:(NSDictionary *)queryParameters withCompletion:(PNHistoryCompletionBlock)block { PNRequestParameters *parameters = [PNRequestParameters new]; + [parameters addQueryParameters:queryParameters]; if (startDate && endDate && [startDate compare:endDate] == NSOrderedDescending) { NSNumber *_startDate = startDate; startDate = endDate; endDate = _startDate; } + + if (!limit || limit.unsignedIntValue == 0) { + limit = nil; + } - limit = (limit?: @(multipleChannels ? 1 : 100)); unsigned int limitValue = MIN(limit.unsignedIntValue, (multipleChannels ? 25 : 100)); - - [parameters addQueryParameters:queryParameters]; + PNOperationType operation = (!multipleChannels ? PNHistoryOperation + : PNHistoryForChannelsOperation); + + if (shouldIncludeMessageActions && shouldIncludeMessageActions.boolValue) { + operation = PNHistoryWithActionsOperation; + + if (limit) { + limitValue = limit.unsignedIntValue; + } + + if (multipleChannels) { + NSArray *channels = object; + object = channels.count ? channels.firstObject : nil; + multipleChannels = NO; + + if (channels.count > 1) { + NSString *reason = @"History can return actions data for a single channel only. " + "Either pass a single channel or disable the " + "includeMessageActions flag"; + + @throw [NSException exceptionWithName:@"PNUnacceptableParametersInput" + reason:reason + userInfo:nil]; + } + } + } if (startDate) { [parameters addQueryParameter:[PNNumber timeTokenFromNumber:startDate].stringValue @@ -341,13 +460,24 @@ - (void)historyForChannels:(BOOL)multipleChannels forFieldName:@"end"]; } + if (shouldReverseOrder && shouldReverseOrder.boolValue) { + [parameters addQueryParameter:@"true" forFieldName:@"reverse"]; + } + + if (shouldIncludeMetadata && shouldIncludeMetadata.boolValue) { + [parameters addQueryParameter:@"true" forFieldName:@"include_meta"]; + } + if (!multipleChannels) { - [parameters addQueryParameter:[NSString stringWithFormat:@"%d", limitValue] - forFieldName:@"count"]; - [parameters addQueryParameter:(shouldReverseOrder.boolValue ? @"true" : @"false") - forFieldName:@"reverse"]; - [parameters addQueryParameter:(shouldIncludeTimeToken.boolValue ? @"true" : @"false") - forFieldName:@"include_token"]; + if (limit) { + [parameters addQueryParameter:[NSString stringWithFormat:@"%d", limitValue] + forFieldName:(operation == PNHistoryOperation ? @"count" : @"max")]; + } + + if (shouldIncludeTimeToken && shouldIncludeTimeToken.boolValue) { + [parameters addQueryParameter:@"true" forFieldName:@"include_token"]; + } + NSString *channel = object; if (channel.length) { @@ -359,12 +489,14 @@ - (void)historyForChannels:(BOOL)multipleChannels (shouldReverseOrder ? @"Reversed history" : @"History"), (channel?: @""), (startDate ? [NSString stringWithFormat:@" from %@", startDate] : @""), (endDate ? [NSString stringWithFormat:@" to %@", endDate] : @""), @(limitValue), - (shouldIncludeTimeToken ? @" (including message time tokens)" : @"")); + (shouldIncludeTimeToken.boolValue ? @" (including: message time tokens" : @"")); } else { NSArray *channels = object; - - [parameters addQueryParameter:[NSString stringWithFormat:@"%d", limitValue] - forFieldName:@"max"]; + + if (limit) { + [parameters addQueryParameter:[NSString stringWithFormat:@"%d", limitValue] + forFieldName:@"max"]; + } if (channels.count) { [parameters addPathComponent:[PNChannel namesForRequest:channels] @@ -376,9 +508,6 @@ - (void)historyForChannels:(BOOL)multipleChannels (startDate ? [NSString stringWithFormat:@" from %@", startDate] : @""), (endDate ? [NSString stringWithFormat:@" to %@", endDate] : @""), @(limitValue)); } - - PNOperationType operation = (!multipleChannels ? PNHistoryOperation - : PNHistoryForChannelsOperation); __weak __typeof(self) weakSelf = self; [self processOperation:operation @@ -394,6 +523,8 @@ - (void)historyForChannels:(BOOL)multipleChannels limit:limit reverse:shouldReverseOrder includeTimeToken:shouldIncludeTimeToken + includeMessageActions:shouldIncludeMessageActions + includeMetadata:shouldIncludeMetadata queryParameters:queryParameters withCompletion:block]; }; diff --git a/PubNub/Core/PubNub+MessageActions.h b/PubNub/Core/PubNub+MessageActions.h new file mode 100644 index 000000000..41c6e4156 --- /dev/null +++ b/PubNub/Core/PubNub+MessageActions.h @@ -0,0 +1,162 @@ +#import "PubNub+Core.h" + +#import "PNFetchMessageActionsRequest.h" +#import "PNRemoveMessageActionRequest.h" +#import "PNAddMessageActionRequest.h" + +#import "PNFetchMessageActionsResult.h" +#import "PNAddMessageActionStatus.h" + +#import "PNAddMessageActionAPICallBuilder.h" +#import "PNRemoveMessageActionAPICallBuilder.h" +#import "PNFetchMessagesActionsAPICallBuilder.h" + + +NS_ASSUME_NONNULL_BEGIN + +#pragma mark API group interface + +/** + * @brief \b PubNub client core class extension to provide access to 'Message Actions' API group. + * + * @discussion Set of API which allow to manage \c actions attached to particular \c message and + * fetch previous changes. + * + * @author Serhii Mamontov + * @version 4.11.0 + * @since 4.11.0 + * @copyright © 2010-2019 PubNub, Inc. + */ +@interface PubNub (MessageActions) + + +#pragma mark - Message Actions API builder support + +/** + * @brief \c Add \c message \c action API access builder block. + * + * @return API call configuration builder. + */ +@property (nonatomic, readonly, strong) PNAddMessageActionAPICallBuilder * (^addMessageAction)(void); + +/** + * @brief \c Remove \c message \c action API access builder block. + * + * @return API call configuration builder. + */ +@property (nonatomic, readonly, strong) PNRemoveMessageActionAPICallBuilder * (^removeMessageAction)(void); + +/** + * @brief \c Fetch \c message \c actions API access builder block. + * + * @return API call configuration builder. + */ +@property (nonatomic, readonly, strong) PNFetchMessagesActionsAPICallBuilder * (^fetchMessageActions)(void); + + +#pragma mark - Message actions + +/** + * @brief \c Add \c message \c action. + * + * @code + * PNAddMessageActionRequest *request = [PNAddMessageActionRequest requestWithChannel:@"PubNub" + * messageTimetoken:@(1234567890)]; + * request.type = @"reaction"; + * request.value = @"smile"; + * + * [self.client addMessageActionWithRequest:request completion:^(PNAddMessageActionStatus *status) { + * if (!status.isError) { + * // Message action successfully added. + * // Created message action information available here: status.data.action + * } else { + * if (status.statusCode == 207) { + * // Message action has been added, but event not published. + * } else { + * // Handle add message action error. Check 'category' property to find out possible + * // issue because of which request did fail. + * // + * // Request can be resent using: [status retry] + * } + * } + * }]; + * @endcode + * + * @param request \c Add \c message \c action request with all information about new + * \c message \c action which will be passed to \b PubNub service. + * @param block \c Add \c message \c action request completion block. + */ +- (void)addMessageActionWithRequest:(PNAddMessageActionRequest *)request + completion:(nullable PNAddMessageActionCompletionBlock)block; + +/** + * @brief \c Remove \c message \c action. + * + * @code + * PNRemoveMessageActionRequest *request = [PNRemoveMessageActionRequest requestWithChannel:@"chat" + * messageTimetoken:@(1234567890)]; + * request.actionTimetoken = @(1234567891); + * + * [self.client removeMessageActionWithRequest:request + * completion:^(PNAcknowledgmentStatus *status) { + * + * if (!status.isError) { + * // Message action successfully removed. + * } else { + * // Handle remove message action error. Check 'category' property to find out possible + * // issue because of which request did fail. + * // + * // Request can be resent using: [status retry] + * } + * }]; + * @endcode + * + * @param request \c Remove \c message \c action request with information about existing + * \c message \c action. + * @param block \c Remove \c message \c action request completion block. + */ +- (void)removeMessageActionWithRequest:(PNRemoveMessageActionRequest *)request + completion:(nullable PNRemoveMessageActionCompletionBlock)block; + +/** + * @brief \c Fetch \c message \c actions. + * + * @code + * PNFetchMessageActionsRequest *request = [PNFetchMessageActionsRequest requestWithChannel:@"chat"]; + * request.start = @(1234567891); + * request.limit = 200; + * + * [self.client fetchMessageActionsWithRequest:request + * completion:^(PNFetchMessageActionsResult *result, + PNErrorStatus *status) { + * + * if (!status.isError) { + * // Message actions successfully fetched. + * // Result object has following information: + * // result.data.actions - list of message action instances + * // result.data.start - fetched messages actions time range start (oldest message + * // action timetoken). + * // result.data.end - fetched messages actions time range end (newest action timetoken). + * } else { + * // Handle fetch message actions error. Check 'category' property to find out possible + * // issue because of which request did fail. + * // + * // Request can be resent using: [status retry] + * } + * }]; + * @endcode + * + * @param request \c Fetch \c message \c actions request with all information which should be used + * to fetch existing \c message \c actions. + * @param block \c Fetch \c message \c actions request completion block. + */ +- (void)fetchMessageActionsWithRequest:(PNFetchMessageActionsRequest *)request + completion:(PNFetchMessageActionsCompletionBlock)block; + +#pragma mark - + + + +@end + +NS_ASSUME_NONNULL_END diff --git a/PubNub/Core/PubNub+MessageActions.m b/PubNub/Core/PubNub+MessageActions.m new file mode 100644 index 000000000..fe791c014 --- /dev/null +++ b/PubNub/Core/PubNub+MessageActions.m @@ -0,0 +1,151 @@ +/** + * @author Serhii Mamontov + * @version 4.11.0 + * @since 4.11.0 + * @copyright © 2010-2019 PubNub, Inc. + */ +#import "PNAPICallBuilder+Private.h" +#import "PubNub+MessageActions.h" +#import "PubNub+CorePrivate.h" +#import "PNResult+Private.h" +#import "PNStatus+Private.h" + + +#pragma mark Interface implementation + +@implementation PubNub (MessageActions) + + +#pragma mark - Message Actions API builder support + +- (PNAddMessageActionAPICallBuilder * (^)(void))addMessageAction { + PNAddMessageActionAPICallBuilder *builder = nil; + __weak __typeof(self) weakSelf = self; + + builder = [PNAddMessageActionAPICallBuilder builderWithExecutionBlock:^(NSArray *flags, + NSDictionary *parameters) { + + NSNumber *timetoken = parameters[NSStringFromSelector(@selector(messageTimetoken))]; + NSString *channel = parameters[NSStringFromSelector(@selector(channel))]; + PNAddMessageActionRequest *request = nil; + + request = [PNAddMessageActionRequest requestWithChannel:channel messageTimetoken:timetoken]; + request.value = parameters[NSStringFromSelector(@selector(value))]; + request.type = parameters[NSStringFromSelector(@selector(type))]; + + [weakSelf addMessageActionWithRequest:request completion:parameters[@"block"]]; + }]; + + return ^PNAddMessageActionAPICallBuilder * { + return builder; + }; +} + +- (PNRemoveMessageActionAPICallBuilder * (^)(void))removeMessageAction { + PNRemoveMessageActionAPICallBuilder *builder = nil; + __weak __typeof(self) weakSelf = self; + + builder = [PNRemoveMessageActionAPICallBuilder builderWithExecutionBlock:^(NSArray *flags, + NSDictionary *parameters) { + + NSNumber *timetoken = parameters[NSStringFromSelector(@selector(messageTimetoken))]; + NSString *channel = parameters[NSStringFromSelector(@selector(channel))]; + PNRemoveMessageActionRequest *request = nil; + + request = [PNRemoveMessageActionRequest requestWithChannel:channel + messageTimetoken:timetoken]; + request.actionTimetoken = parameters[NSStringFromSelector(@selector(actionTimetoken))]; + + [weakSelf removeMessageActionWithRequest:request completion:parameters[@"block"]]; + }]; + + return ^PNRemoveMessageActionAPICallBuilder * { + return builder; + }; +} + +- (PNFetchMessagesActionsAPICallBuilder * (^)(void))fetchMessageActions { + PNFetchMessagesActionsAPICallBuilder *builder = nil; + __weak __typeof(self) weakSelf = self; + + builder = [PNFetchMessagesActionsAPICallBuilder builderWithExecutionBlock:^(NSArray *flags, + NSDictionary *parameters) { + + NSNumber *limit = parameters[NSStringFromSelector(@selector(limit))] ?: @(100); + NSString *channel = parameters[NSStringFromSelector(@selector(channel))]; + PNFetchMessageActionsRequest *request = nil; + + request = [PNFetchMessageActionsRequest requestWithChannel:channel]; + request.start = parameters[NSStringFromSelector(@selector(start))]; + request.end = parameters[NSStringFromSelector(@selector(end))]; + request.limit = limit.unsignedIntegerValue; + + [weakSelf fetchMessageActionsWithRequest:request completion:parameters[@"block"]]; + }]; + + return ^PNFetchMessagesActionsAPICallBuilder * { + return builder; + }; +} + + +#pragma mark - Message actions + +- (void)addMessageActionWithRequest:(PNAddMessageActionRequest *)request + completion:(nullable PNAddMessageActionCompletionBlock)block { + + __weak __typeof(self) weakSelf = self; + + [self performRequest:request withCompletion:^(PNAddMessageActionStatus *status) { + if (status.isError) { + status.retryBlock = ^{ + [weakSelf addMessageActionWithRequest:request completion:block]; + }; + } + + if (block) { + block(status); + } + }]; +} + +- (void)removeMessageActionWithRequest:(PNRemoveMessageActionRequest *)request + completion:(nullable PNRemoveMessageActionCompletionBlock)block { + + __weak __typeof(self) weakSelf = self; + + [self performRequest:request withCompletion:^(PNAcknowledgmentStatus *status) { + if (status.isError) { + status.retryBlock = ^{ + [weakSelf removeMessageActionWithRequest:request completion:block]; + }; + } + + if (block) { + block(status); + } + }]; +} + +- (void)fetchMessageActionsWithRequest:(PNFetchMessageActionsRequest *)request + completion:(PNFetchMessageActionsCompletionBlock)block { + + __weak __typeof(self) weakSelf = self; + + [self performRequest:request + withCompletion:^(PNFetchMessageActionsResult *result, PNErrorStatus *status) { + + if (status.isError) { + status.retryBlock = ^{ + [weakSelf fetchMessageActionsWithRequest:request completion:block]; + }; + } + + block(result, status); + }]; +} + +#pragma mark - + + +@end diff --git a/PubNub/Core/PubNub+Objects.h b/PubNub/Core/PubNub+Objects.h index 2977fae0b..cea2f3aaf 100644 --- a/PubNub/Core/PubNub+Objects.h +++ b/PubNub/Core/PubNub+Objects.h @@ -47,14 +47,12 @@ NS_ASSUME_NONNULL_BEGIN - -#pragma mark - API group interface +#pragma mark API group interface /** * @brief \b PubNub client core class extension to provide access to 'Objects' API group. * - * @discussion Set of API which allow to fetch events which has been moved from remote data object - * live feed to persistent storage. + * @discussion Set of API which allow to manage space / user objects and their relationships. * * @author Serhii Mamontov * @version 4.10.0 diff --git a/PubNub/Data/Builders/API Call/Actions/Message/PNAddMessageActionAPICallBuilder.h b/PubNub/Data/Builders/API Call/Actions/Message/PNAddMessageActionAPICallBuilder.h new file mode 100644 index 000000000..f5f3b2340 --- /dev/null +++ b/PubNub/Data/Builders/API Call/Actions/Message/PNAddMessageActionAPICallBuilder.h @@ -0,0 +1,89 @@ +#import "PNAPICallBuilder.h" +#import "PNStructures.h" + + +NS_ASSUME_NONNULL_BEGIN + +#pragma mark Interface declaration + +/** + * @brief \c Add \c message \c action API call builder. + * + * @author Serhii Mamontov + * @version 4.11.0 + * @since 4.11.0 + * @copyright © 2010-2019 PubNub, Inc. + */ +@interface PNAddMessageActionAPICallBuilder : PNAPICallBuilder + + +#pragma mark - Configuration + +/** + * @brief \c Message publish timetoken. + * + * @param messageTimetoken Timetoken (\b PubNub's high precision timestamp) of message for which + * \c action should be added. + * + * @return API call configuration builder. + */ +@property (nonatomic, readonly, strong) PNAddMessageActionAPICallBuilder * (^messageTimetoken)(NSNumber *messageTimetoken); + +/** + * @brief Channel with target \c message. + * + * @param channel Name of channel which store message for which \c action should be added. + * + * @return API call configuration builder. + */ +@property (nonatomic, readonly, strong) PNAddMessageActionAPICallBuilder * (^channel)(NSString *channel); + +/** + * @brief \c Message \c action value. + * + * @param value \c Value which should be stored along with \c message \c action. + * + * @return API call configuration builder. + */ +@property (nonatomic, readonly, strong) PNAddMessageActionAPICallBuilder * (^value)(NSString *value); + +/** + * @brief What feature this \c message \c action represents. + * + * @note Maximum \b 15 characters. + * + * @param type \c Message \c action type. + * + * @return API call configuration builder. + */ +@property (nonatomic, readonly, strong) PNAddMessageActionAPICallBuilder * (^type)(NSString *type); + + +#pragma mark - Execution + +/** + * @brief Perform API call. + * + * @param block \c Add \c message \c action completion handler block. + */ +@property (nonatomic, readonly, strong) void(^performWithCompletion)(PNAddMessageActionCompletionBlock block); + + +#pragma mark - Misc + +/** + * @brief Arbitrary query parameters addition block. + * + * @param params List of arbitrary percent-encoded query parameters which should be sent along with + * original API call. + * + * @return API call configuration builder. + */ +@property (nonatomic, readonly, strong) PNAddMessageActionAPICallBuilder * (^queryParam)(NSDictionary *params); + +#pragma mark - + + +@end + +NS_ASSUME_NONNULL_END diff --git a/PubNub/Data/Builders/API Call/Actions/Message/PNAddMessageActionAPICallBuilder.m b/PubNub/Data/Builders/API Call/Actions/Message/PNAddMessageActionAPICallBuilder.m new file mode 100644 index 000000000..d26b51e4a --- /dev/null +++ b/PubNub/Data/Builders/API Call/Actions/Message/PNAddMessageActionAPICallBuilder.m @@ -0,0 +1,77 @@ +/** + * @author Serhii Mamontov + * @version 4.11.0 + * @since 4.11.0 + * @copyright © 2010-2019 PubNub, Inc. + */ +#import "PNAddMessageActionAPICallBuilder.h" +#import "PNAPICallBuilder+Private.h" + + +#pragma mark Interface implementation + +@implementation PNAddMessageActionAPICallBuilder + + +#pragma mark - Information + +@dynamic queryParam; + + +#pragma mark - Configuration + +- (PNAddMessageActionAPICallBuilder * (^)(NSNumber *messageTimetoken))messageTimetoken { + return ^PNAddMessageActionAPICallBuilder * (NSNumber *messageTimetoken) { + if ([messageTimetoken isKindOfClass:[NSNumber class]] && + messageTimetoken.unsignedIntegerValue) { + + [self setValue:messageTimetoken forParameter:NSStringFromSelector(_cmd)]; + } + + return self; + }; +} + +- (PNAddMessageActionAPICallBuilder * (^)(NSString *type))type { + return ^PNAddMessageActionAPICallBuilder * (NSString *type) { + if ([type isKindOfClass:[NSString class]] && type.length) { + [self setValue:type forParameter:NSStringFromSelector(_cmd)]; + } + + return self; + }; +} + +- (PNAddMessageActionAPICallBuilder * (^)(NSString *channel))channel { + return ^PNAddMessageActionAPICallBuilder * (NSString *channel) { + if ([channel isKindOfClass:[NSString class]] && channel.length) { + [self setValue:channel forParameter:NSStringFromSelector(_cmd)]; + } + + return self; + }; +} + +- (PNAddMessageActionAPICallBuilder * _Nonnull (^)(NSString *value))value { + return ^PNAddMessageActionAPICallBuilder * (NSString *value) { + if ([value isKindOfClass:[NSString class]] && value.length) { + [self setValue:value forParameter:NSStringFromSelector(_cmd)]; + } + + return self; + }; +} + + +#pragma mark - Execution + +- (void(^)(PNAddMessageActionCompletionBlock block))performWithCompletion { + return ^(PNAddMessageActionCompletionBlock block) { + [super performWithBlock:block]; + }; +} + +#pragma mark - + + +@end diff --git a/PubNub/Data/Builders/API Call/Actions/Message/PNFetchMessagesActionsAPICallBuilder.h b/PubNub/Data/Builders/API Call/Actions/Message/PNFetchMessagesActionsAPICallBuilder.h new file mode 100644 index 000000000..655299d13 --- /dev/null +++ b/PubNub/Data/Builders/API Call/Actions/Message/PNFetchMessagesActionsAPICallBuilder.h @@ -0,0 +1,92 @@ +#import "PNAPICallBuilder.h" +#import "PNStructures.h" + + +NS_ASSUME_NONNULL_BEGIN + +#pragma mark Interface declaration + +/** + * @brief \c Fetch \c messages \c actions API call builder. + * + * @author Serhii Mamontov + * @version 4.11.0 + * @since 4.11.0 + * @copyright © 2010-2019 PubNub, Inc. + */ +@interface PNFetchMessagesActionsAPICallBuilder : PNAPICallBuilder + + +#pragma mark - Configuration + +/** + * @brief Channel with \c messages for which \c actions should be fetched. + * + * @param channel Name of channel from which list of \c messages \c actions should be retrieved. + * + * @return API call configuration builder. + */ +@property (nonatomic, readonly, strong) PNFetchMessagesActionsAPICallBuilder * (^channel)(NSString *channel); + +/** + * @brief Maximum number of \c messages \c actions to return in response. + * + * @param limit Number of \c messages \c actions to return in response. + * + * @return API call configuration builder. + */ +@property (nonatomic, readonly, strong) PNFetchMessagesActionsAPICallBuilder * (^limit)(NSUInteger limit); + +/** + * @brief \c Messages \c actions timetoken denoting the start of the range requested. + * + * @note Return values will be less than start. + * + * @param start Previously-returned \c messages \c actions timetoken denoting the start of the range + * requested. + * + * @return API call configuration builder. + */ +@property (nonatomic, readonly, strong) PNFetchMessagesActionsAPICallBuilder * (^start)(NSNumber *start); + +/** + * @brief \c Messages \c actions timetoken denoting the end of the range requested. + * + * @note Return values will be greater than or equal to end. + * + * @param end Previously-returned \c messages \c actions timetoken denoting the end of the range + * requested. + * + * @return API call configuration builder. + */ +@property (nonatomic, readonly, strong) PNFetchMessagesActionsAPICallBuilder * (^end)(NSNumber *end); + + +#pragma mark - Execution + +/** + * @brief Perform API call. + * + * @param block \c Fetch \c messages \c actions completion handler block. + */ +@property (nonatomic, readonly, strong) void(^performWithCompletion)(PNFetchMessageActionsCompletionBlock block); + + +#pragma mark - Misc + +/** + * @brief Arbitrary query parameters addition block. + * + * @param params List of arbitrary percent-encoded query parameters which should be sent along with + * original API call. + * + * @return API call configuration builder. + */ +@property (nonatomic, readonly, strong) PNFetchMessagesActionsAPICallBuilder * (^queryParam)(NSDictionary *params); + +#pragma mark - + + +@end + +NS_ASSUME_NONNULL_END diff --git a/PubNub/Data/Builders/API Call/Actions/Message/PNFetchMessagesActionsAPICallBuilder.m b/PubNub/Data/Builders/API Call/Actions/Message/PNFetchMessagesActionsAPICallBuilder.m new file mode 100644 index 000000000..ed254c105 --- /dev/null +++ b/PubNub/Data/Builders/API Call/Actions/Message/PNFetchMessagesActionsAPICallBuilder.m @@ -0,0 +1,72 @@ +/** + * @author Serhii Mamontov + * @version 4.11.0 + * @since 4.11.0 + * @copyright © 2010-2019 PubNub, Inc. + */ +#import "PNFetchMessagesActionsAPICallBuilder.h" +#import "PNAPICallBuilder+Private.h" + + +#pragma mark Interface implementation + +@implementation PNFetchMessagesActionsAPICallBuilder + + +#pragma mark - Information + +@dynamic queryParam; + + +#pragma mark - Configuration + +- (PNFetchMessagesActionsAPICallBuilder * (^)(NSString *channel))channel { + return ^PNFetchMessagesActionsAPICallBuilder * (NSString *channel) { + if ([channel isKindOfClass:[NSString class]] && channel.length) { + [self setValue:channel forParameter:NSStringFromSelector(_cmd)]; + } + + return self; + }; +} + +- (PNFetchMessagesActionsAPICallBuilder * (^)(NSUInteger limit))limit { + return ^PNFetchMessagesActionsAPICallBuilder * (NSUInteger limit) { + [self setValue:@(limit) forParameter:NSStringFromSelector(_cmd)]; + return self; + }; +} + +- (PNFetchMessagesActionsAPICallBuilder * (^)(NSNumber *start))start { + return ^PNFetchMessagesActionsAPICallBuilder * (NSNumber *start) { + if ([start isKindOfClass:[NSNumber class]] && start.unsignedIntegerValue) { + [self setValue:start forParameter:NSStringFromSelector(_cmd)]; + } + + return self; + }; +} + +- (PNFetchMessagesActionsAPICallBuilder * (^)(NSNumber *end))end { + return ^PNFetchMessagesActionsAPICallBuilder * (NSNumber *end) { + if ([end isKindOfClass:[NSNumber class]] && end.unsignedIntegerValue) { + [self setValue:end forParameter:NSStringFromSelector(_cmd)]; + } + + return self; + }; +} + + +#pragma mark - Execution + +- (void(^)(PNFetchMessageActionsCompletionBlock block))performWithCompletion { + return ^(PNFetchMessageActionsCompletionBlock block) { + [super performWithBlock:block]; + }; +} + +#pragma mark - + + +@end diff --git a/PubNub/Data/Builders/API Call/Actions/Message/PNRemoveMessageActionAPICallBuilder.h b/PubNub/Data/Builders/API Call/Actions/Message/PNRemoveMessageActionAPICallBuilder.h new file mode 100644 index 000000000..e271a4d8e --- /dev/null +++ b/PubNub/Data/Builders/API Call/Actions/Message/PNRemoveMessageActionAPICallBuilder.h @@ -0,0 +1,78 @@ +#import "PNAPICallBuilder.h" +#import "PNStructures.h" + + +NS_ASSUME_NONNULL_BEGIN + +#pragma mark Interface declaration + +/** + * @brief \c Remove \c message \c action API call builder. + * + * @author Serhii Mamontov + * @version 4.11.0 + * @since 4.11.0 + * @copyright © 2010-2019 PubNub, Inc. + */ +@interface PNRemoveMessageActionAPICallBuilder : PNAPICallBuilder + + +#pragma mark - Configuration + +/** + * @brief \c Message publish timetoken. + * + * @param messageTimetoken Timetoken (\b PubNub's high precision timestamp) of message for which + * \c action should be removed. + * + * @return API call configuration builder. + */ +@property (nonatomic, readonly, strong) PNRemoveMessageActionAPICallBuilder * (^messageTimetoken)(NSNumber *messageTimetoken); + +/** + * @brief Target \c action publish timetoken. + * + * @param actionTimetoken \c Action addition timetoken (\b PubNub's high precision timestamp). + * + * @return API call configuration builder. + */ +@property (nonatomic, readonly, strong) PNRemoveMessageActionAPICallBuilder * (^actionTimetoken)(NSNumber *actionTimetoken); + +/** + * @brief Channel with target \c message. + * + * @param channel Name of channel which store message for which \c action should be removed. + * + * @return API call configuration builder. + */ +@property (nonatomic, readonly, strong) PNRemoveMessageActionAPICallBuilder * (^channel)(NSString *channel); + + +#pragma mark - Execution + +/** + * @brief Perform API call. + * + * @param block \c Remove \c message \c action completion handler block. + */ +@property (nonatomic, readonly, strong) void(^performWithCompletion)(PNRemoveMessageActionCompletionBlock block); + + +#pragma mark - Misc + +/** + * @brief Arbitrary query parameters addition block. + * + * @param params List of arbitrary percent-encoded query parameters which should be sent along with + * original API call. + * + * @return API call configuration builder. + */ +@property (nonatomic, readonly, strong) PNRemoveMessageActionAPICallBuilder * (^queryParam)(NSDictionary *params); + +#pragma mark - + + +@end + +NS_ASSUME_NONNULL_END diff --git a/PubNub/Data/Builders/API Call/Actions/Message/PNRemoveMessageActionAPICallBuilder.m b/PubNub/Data/Builders/API Call/Actions/Message/PNRemoveMessageActionAPICallBuilder.m new file mode 100644 index 000000000..f07342ed4 --- /dev/null +++ b/PubNub/Data/Builders/API Call/Actions/Message/PNRemoveMessageActionAPICallBuilder.m @@ -0,0 +1,69 @@ +/** + * @author Serhii Mamontov + * @version 4.11.0 + * @since 4.11.0 + * @copyright © 2010-2019 PubNub, Inc. + */ +#import "PNRemoveMessageActionAPICallBuilder.h" +#import "PNAPICallBuilder+Private.h" + + +#pragma mark Interface implementation + +@implementation PNRemoveMessageActionAPICallBuilder + + +#pragma mark - Information + +@dynamic queryParam; + + +#pragma mark - Configuration + +- (PNRemoveMessageActionAPICallBuilder * (^)(NSNumber *messageTimetoken))messageTimetoken { + return ^PNRemoveMessageActionAPICallBuilder * (NSNumber *messageTimetoken) { + if ([messageTimetoken isKindOfClass:[NSNumber class]] && + messageTimetoken.unsignedIntegerValue) { + + [self setValue:messageTimetoken forParameter:NSStringFromSelector(_cmd)]; + } + + return self; + }; +} + +- (PNRemoveMessageActionAPICallBuilder * (^)(NSNumber *actionTimetoken))actionTimetoken { + return ^PNRemoveMessageActionAPICallBuilder * (NSNumber *actionTimetoken) { + if ([actionTimetoken isKindOfClass:[NSNumber class]] && + actionTimetoken.unsignedIntegerValue) { + + [self setValue:actionTimetoken forParameter:NSStringFromSelector(_cmd)]; + } + + return self; + }; +} + +- (PNRemoveMessageActionAPICallBuilder * (^)(NSString *channel))channel { + return ^PNRemoveMessageActionAPICallBuilder * (NSString *channel) { + if ([channel isKindOfClass:[NSString class]] && channel.length) { + [self setValue:channel forParameter:NSStringFromSelector(_cmd)]; + } + + return self; + }; +} + + +#pragma mark - Execution + +- (void(^)(PNRemoveMessageActionCompletionBlock block))performWithCompletion { + return ^(PNRemoveMessageActionCompletionBlock block) { + [super performWithBlock:block]; + }; +} + +#pragma mark - + + +@end diff --git a/PubNub/Data/Builders/API Call/History/PNHistoryAPICallBuilder.h b/PubNub/Data/Builders/API Call/History/PNHistoryAPICallBuilder.h index 1d6094541..d1ac9b2a6 100644 --- a/PubNub/Data/Builders/API Call/History/PNHistoryAPICallBuilder.h +++ b/PubNub/Data/Builders/API Call/History/PNHistoryAPICallBuilder.h @@ -8,8 +8,9 @@ NS_ASSUME_NONNULL_BEGIN * @brief History / storage API call builder. * * @author Serhii Mamontov + * @version 4.11.0 * @since 4.5.4 - * @copyright © 2010-2018 PubNub, Inc. + * @copyright © 2010-2019 PubNub, Inc. */ @interface PNHistoryAPICallBuilder : PNAPICallBuilder @@ -42,8 +43,6 @@ NS_ASSUME_NONNULL_BEGIN /** * @brief Search interval start timetoken addition block. * - * @note Ignored in case if \c channels is set. - * * @param start Timetoken for oldest event starting from which next should be returned events. * Value will be converted to required precision internally. * @@ -56,8 +55,6 @@ NS_ASSUME_NONNULL_BEGIN /** * @brief Search interval end timetoken addition block. * - * @note Ignored in case if \c channels is set. - * * @param end Timetoken for latest event till which events should be pulled out. * Value will be converted to required precision internally. * @@ -70,9 +67,6 @@ NS_ASSUME_NONNULL_BEGIN /** * @brief Maximum number of events addition block. * - * @note If limit addition not used it will be assigned default value depending from used - * parameters: \c 100 if \c channel is set and \c 1 if \c channels is set. - * * @param limit Maximum number of events which should be returned in response. * Maximum \c 100 if \c channel is set and \c 25 if \c channels is set. * @@ -85,7 +79,8 @@ NS_ASSUME_NONNULL_BEGIN /** * @brief Events' time tokens presence flag addition block. * - * @note Ignored in case if \c channels is set. + * @note Each fetched entry will contain published data under 'message' key and message publish + * \c timetoken will be available under 'timetoken' key. * * @param includeTimeToken Whether event dates (time tokens) should be included in response or * not. @@ -97,9 +92,37 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly, strong) PNHistoryAPICallBuilder * (^includeTimeToken)(BOOL includeTimeToken); /** - * @brief Events sorting order reverse flag addition block. + * @brief Events' metadata presence flag addition block. + * + * @note Each fetched entry will contain published data under 'message' key and published message + * \c meta will be available under 'metadata' key. + * + * @param includeMetadata Whether event metadata should be included in response or not. * - * @note Ignored in case if \c channels is set. + * @return API call configuration builder. + * + * @since 4.11.0 + */ +@property (nonatomic, readonly, strong) PNHistoryAPICallBuilder * (^includeMetadata)(BOOL includeMetadata); + +/** + * @brief Events' actions presence flag addition block. + * + * @note Each fetched entry will contain published data under 'message' key and added \c message + * \c actions will be available under 'actions' key. + * + * @throw Exception in case if API called with more than one channel. + * + * @param includeMessageActions Whether event actions should be included in response or not. + * + * @return API call configuration builder. + * + * @since 4.11.0 + */ +@property (nonatomic, readonly, strong) PNHistoryAPICallBuilder * (^includeMessageActions)(BOOL includeMessageActions); + +/** + * @brief Events sorting order reverse flag addition block. * * @param reverse Whether events order in response should be reversed or not. * diff --git a/PubNub/Data/Builders/API Call/History/PNHistoryAPICallBuilder.m b/PubNub/Data/Builders/API Call/History/PNHistoryAPICallBuilder.m index e32592766..ccd8dc8e7 100644 --- a/PubNub/Data/Builders/API Call/History/PNHistoryAPICallBuilder.m +++ b/PubNub/Data/Builders/API Call/History/PNHistoryAPICallBuilder.m @@ -1,7 +1,8 @@ /** * @author Serhii Mamontov + * @version 4.11.0 * @since 4.5.4 - * @copyright © 2010-2018 PubNub, Inc. + * @copyright © 2010-2019 PubNub, Inc. */ #import "PNHistoryAPICallBuilder.h" #import "PNAPICallBuilder+Private.h" @@ -20,7 +21,6 @@ @implementation PNHistoryAPICallBuilder #pragma mark - Configuration - (PNHistoryAPICallBuilder * (^)(NSString *channel))channel { - return ^PNHistoryAPICallBuilder * (NSString *channel) { [self setValue:channel forParameter:NSStringFromSelector(_cmd)]; return self; @@ -28,7 +28,6 @@ @implementation PNHistoryAPICallBuilder } - (PNHistoryAPICallBuilder * (^)(NSArray *))channels { - return ^PNHistoryAPICallBuilder * (NSArray *channels) { [self setValue:channels forParameter:NSStringFromSelector(_cmd)]; return self; @@ -36,7 +35,6 @@ @implementation PNHistoryAPICallBuilder } - (PNHistoryAPICallBuilder * (^)(NSNumber *start))start { - return ^PNHistoryAPICallBuilder * (NSNumber *start) { [self setValue:start forParameter:NSStringFromSelector(_cmd)]; return self; @@ -44,7 +42,6 @@ @implementation PNHistoryAPICallBuilder } - (PNHistoryAPICallBuilder * (^)(NSNumber *end))end { - return ^PNHistoryAPICallBuilder * (NSNumber *end) { [self setValue:end forParameter:NSStringFromSelector(_cmd)]; return self; @@ -52,7 +49,6 @@ @implementation PNHistoryAPICallBuilder } - (PNHistoryAPICallBuilder * (^)(NSUInteger limit))limit { - return ^PNHistoryAPICallBuilder * (NSUInteger limit) { [self setValue:@(limit) forParameter:NSStringFromSelector(_cmd)]; return self; @@ -60,15 +56,27 @@ @implementation PNHistoryAPICallBuilder } - (PNHistoryAPICallBuilder * (^)(BOOL includeTimeToken))includeTimeToken { - return ^PNHistoryAPICallBuilder * (BOOL includeTimeToken) { [self setValue:@(includeTimeToken) forParameter:NSStringFromSelector(_cmd)]; return self; }; } +- (PNHistoryAPICallBuilder * (^)(BOOL))includeMetadata { + return ^PNHistoryAPICallBuilder * (BOOL includeMetadata) { + [self setValue:@(includeMetadata) forParameter:NSStringFromSelector(_cmd)]; + return self; + }; +} + +- (PNHistoryAPICallBuilder * (^)(BOOL))includeMessageActions { + return ^PNHistoryAPICallBuilder * (BOOL includeMessageActions) { + [self setValue:@(includeMessageActions) forParameter:NSStringFromSelector(_cmd)]; + return self; + }; +} + - (PNHistoryAPICallBuilder * (^)(BOOL reverse))reverse { - return ^PNHistoryAPICallBuilder * (BOOL reverse) { [self setValue:@(reverse) forParameter:NSStringFromSelector(_cmd)]; return self; @@ -79,7 +87,6 @@ @implementation PNHistoryAPICallBuilder #pragma mark - Execution - (void(^)(PNHistoryCompletionBlock block))performWithCompletion { - return ^(PNHistoryCompletionBlock block) { [super performWithBlock:block]; }; diff --git a/PubNub/Data/Managers/PNPublishSequence.h b/PubNub/Data/Managers/PNPublishSequence.h index 9dce75233..8078d437c 100644 --- a/PubNub/Data/Managers/PNPublishSequence.h +++ b/PubNub/Data/Managers/PNPublishSequence.h @@ -8,60 +8,57 @@ NS_ASSUME_NONNULL_BEGIN +#pragma mark - Interface declaration + /** - @brief Published messages sequence tracking manager. - @discussion \b PubNub client allow to assign for each published messages it's sequence number. Manager allow - to keep track on published sequence number even after application restart. - - @author Sergey Mamontov - @since 4.5.2 - @copyright © 2010-2018 PubNub, Inc. + * @brief Published messages sequence tracking manager. + * + * @discussion \b PubNub client allow to assign for each published messages it's sequence number. + * Manager allow to keep track on published sequence number even after application restart. + * + * @author Sergey Mamontov + * @version 4.10.2 + * @since 4.5.2 + * @copyright © 2010-2019 PubNub, Inc. */ @interface PNPublishSequence : NSObject -///------------------------------------------------ -/// @name Information -///------------------------------------------------ +#pragma mark - Information /** - @brief Stores reference on sequence number which has been used for recent message publish API usage. - - @since 4.5.2 + * @brief Sequence number which has been used for recent message publish API usage. */ @property (nonatomic, readonly, assign) NSUInteger sequenceNumber; /** - @brief Retrieve sequence number for next message and update current value if requested. - - @param shouldUpdateCurrent Whether current value should be set to the one which is returned by this method. - - @return Next published message sequence number. - - @since 4.5.2 + * @brief Sequence number for next message and update current value if requested. + * + * @param shouldUpdateCurrent Whether current value should be set to the one which is returned by + * this method. + * + * @return Next published message sequence number. */ - (NSUInteger)nextSequenceNumber:(BOOL)shouldUpdateCurrent; -///------------------------------------------------ -/// @name Initialization and Configuration -///------------------------------------------------ +#pragma mark - Initialization and Configuration /** - @brief Create and configure published messages sequence manager. - @discussion If instance for same publish key already created it will be reused. - - @param client Reference on client for which published messages sequence manager should be created. - - @return Configured and ready to use client published messages sequence manager. - - @since 4.5.2 + * @brief Create and configure published messages sequence manager. + * + * @note If instance for same publish key already created it will be reused. + * + * @param client Client for which published messages sequence manager should be created. + * + * @return Configured and ready to use client published messages sequence manager. */ + (instancetype)sequenceForClient:(PubNub *)client; /** - @brief Reset all information which is related to publish message sequence. - @discussion All data will be reset and removed from \b Keychain. + * @brief Reset all information which is related to publish message sequence. + * + * @discussion All data will be reset and removed from \b Keychain. */ - (void)reset; diff --git a/PubNub/Data/Managers/PNPublishSequence.m b/PubNub/Data/Managers/PNPublishSequence.m index 0b676e5f8..d2b0a06e7 100644 --- a/PubNub/Data/Managers/PNPublishSequence.m +++ b/PubNub/Data/Managers/PNPublishSequence.m @@ -1,4 +1,9 @@ - +/** + * @author Sergey Mamontov + * @version 4.11.0 + * @since 4.5.2 + * @copyright © 2010-2019 PubNub, Inc. + */ #import "PNPublishSequence.h" #if TARGET_OS_IOS @@ -18,26 +23,21 @@ #pragma mark Static /** - @brief Stores reference on key under which in Keychain stored information about previously used sequence - number for message publish. - - @since 4.5.2 + * @brief Key under which in Keychain stored information about previously used sequence number for + * message publish. */ static NSString * const kPNPublishSequenceDataKey = @"pn_publishSequence"; /** - @brief Stores reference on maximum age of \c publish key inactiviry after which it will be removed and - count for it will be reset to \b 0. - @discussion \b Default: 30 days - - @since 4.5.2 + * @brief Maximum age of \c publish key inactivity after which it will be removed and count for it + * will be reset to \b 0. + * + * @note \b Default: 30 days */ static NSUInteger const kPNMaximumPublishSequenceDataAge = (30 * 24 * 60 * 60); /** - @brief Spin-lock which is used to protect access to shared resources from multiple threads. - - @since 4.5.2 + * @brief Spin-lock which is used to protect access to shared resources from multiple threads. */ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wpartial-availability" @@ -48,29 +48,22 @@ #pragma mark - Structures /** - @brief Describes storable sequence manager data structure. - - @since 4.5.2 + * @brief Storable sequence manager data structure. */ struct PNPublishSequenceDataStructure { /** - @brief Stores reference on key under which stored date when sequence last has been re-saved / modified. - - @since 4.5.2 + * @brief Key under which stored date when sequence last has been re-saved / modified. */ __unsafe_unretained NSString *lastSaveDate; /** - @brief Stores reference key under which stored last saved sequence number value. - - @since 4.5.2 + * @brief Key under which stored last saved sequence number value. */ __unsafe_unretained NSString *sequence; }; struct PNPublishSequenceDataStructure PNPublishSequenceData = { - .lastSaveDate = @"sd", .sequence = @"sn" }; @@ -79,12 +72,9 @@ #pragma mark - Private interface declaration @interface PNPublishSequence () { - /** - @brief Stores reference on spin-lock which is used to protect access to current message sequence number - which can be changed at any moment. - - @since 4.5.2 + * @brief Spin-lock which is used to protect access to current message sequence number which can + * be changed at any moment. */ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wpartial-availability" @@ -96,18 +86,22 @@ @interface PNPublishSequence () { #pragma mark - Information /** - @brief Weak reference on client for which state cache manager created. - - @since 4.5.2 + * @brief Client for which state cache manager created. */ @property (nonatomic, weak) PubNub *client; +/** + * @brief Sequence number which has been used for recent message publish API usage. + */ @property (nonatomic, assign) NSUInteger sequenceNumber; /** - @brief Stores reference on current \b PubNub client publish key. - - @since 4.5.2 + * @brief Whether stored sequence number for \b publishKey has been changed or not. + */ +@property (nonatomic, assign) BOOL sequenceNumberChanged; + +/** + * @brief Current \b PubNub client publish key. */ @property (nonatomic, copy) NSString *publishKey; @@ -115,23 +109,19 @@ @interface PNPublishSequence () { #pragma mark - Initialization and Configuration /** - @brief Retrieve reference on dictionary which store initialized publish sequence number managers. - - @return Dictionary where publish sequence numbers stored under publish keys which is used for \b PubNub - client configuration. - - @since 4.5.2 + * @brief Dictionary which store initialized publish sequence number managers. + * + * @return Dictionary where publish sequence numbers stored under publish keys which is used for + * \b PubNub client configuration. */ + (NSMutableDictionary *)sequenceManagers; /** - @brief Initialize published messages sequence manager. - - @param client Reference on client for which published messages sequence manager should be created. - - @return Initialized and ready to use client published messages sequence manager. - - @since 4.5.2 + * @brief Initialize published messages sequence manager. + * + * @param client Client for which published messages sequence manager should be created. + * + * @return Initialized and ready to use client published messages sequence manager. */ - (instancetype)initForClient:(PubNub *)client; @@ -139,25 +129,17 @@ - (instancetype)initForClient:(PubNub *)client; #pragma mark - Data storage /** - @brief Fetch sequence information from \b Keychain. - @discussion Use persistent data from \b Keychain to properly track messages sequence number for each publish - key. - - @since 4.5.2 + * @brief Fetch sequence information from \b Keychain. */ - (void)loadFromPersistentStorage; /** - @brief Store in-memory sequence information to \b Keychain. - - @since 4.5.2 + * @brief Store in-memory sequence information to \b Keychain. */ - (void)saveToPersistentStorage; /** - @brief Clean-up sequence information from information about \c old publish keys. - - @since 4.5.2 + * @brief Clean-up sequence information from information about \c old publish keys. */ - (void)cleanUpIfRequired; @@ -165,12 +147,9 @@ - (void)cleanUpIfRequired; #pragma mark - Handlers /** - @brief Handle application transition between different execution contexts. - - @param notification Reference on object which stored information about notification which triggered callback - and name of notification which will allow to decide further actions. - - @since 4.5.2 + * @brief Handle application transition between different execution contexts. + * + * @param notification Notification which triggered callback. */ - (void)handleContextTransition:(NSNotification *)notification; @@ -178,11 +157,10 @@ - (void)handleContextTransition:(NSNotification *)notification; #pragma mark - Misc /** - @brief Subscribe to application context change notifications. - @discussion Context change allow to react and if required save sequence data information into persistent - storage. - - @since 4.5.2 + * @brief Subscribe to application context change notifications. + * + * @discussion Context change allow to react and if required save sequence data information into + * persistent storage. */ - (void)subscribeOnNotifications; @@ -202,61 +180,74 @@ @implementation PNPublishSequence #pragma mark - Information - (NSUInteger)sequenceNumber { - __block NSUInteger sequenceNumber = 0; - pn_lock(&_lock, ^{ sequenceNumber = self->_sequenceNumber; }); + + pn_lock(&_lock, ^{ + sequenceNumber = self->_sequenceNumber; + }); return sequenceNumber; } - (NSUInteger)nextSequenceNumber:(BOOL)shouldUpdateCurrent { - __block NSUInteger sequenceNumber = 0; + pn_lock(&_lock, ^{ - sequenceNumber = (self->_sequenceNumber == NSUIntegerMax ? 1 : self->_sequenceNumber + 1); - if (shouldUpdateCurrent) { self->_sequenceNumber = sequenceNumber; } + sequenceNumber = self->_sequenceNumber == NSUIntegerMax ? 1 : self->_sequenceNumber + 1; + + if (shouldUpdateCurrent) { + self->_sequenceNumber = sequenceNumber; + self->_sequenceNumberChanged = YES; + } }); return sequenceNumber; } +- (BOOL)sequenceNumberChanged { + __block BOOL sequenceNumberChanged = 0; + + pn_lock(&_lock, ^{ + sequenceNumberChanged = self->_sequenceNumberChanged; + }); + + return sequenceNumberChanged; +} + #pragma mark - Initialization and Configuration + (instancetype)sequenceForClient:(PubNub *)client { - PNPublishSequence *manager = nil; NSMutableDictionary *sequenceManagers = [self sequenceManagers]; + @synchronized (sequenceManagers) { - manager = sequenceManagers[client.currentConfiguration.publishKey]; + if (!manager) { - manager = [[self alloc] initForClient:client]; sequenceManagers[client.currentConfiguration.publishKey] = manager; + } else { + manager.client = client; } - // In case if received manager which existed after PubNub client has been deallocated it should be set - // new reference on client. - if (manager.client == nil) { manager.client = client; } } return manager; } + (NSMutableDictionary *)sequenceManagers { - static NSMutableDictionary *_sharedSequenceManagers; static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ _sharedSequenceManagers = [NSMutableDictionary new]; }); + + dispatch_once(&onceToken, ^{ + _sharedSequenceManagers = [NSMutableDictionary new]; + }); return _sharedSequenceManagers; } - (instancetype)initForClient:(PubNub *)client { - - // Check whether initialization was successful or not. if ((self = [super init])) { - _client = client; _publishKey = client.currentConfiguration.publishKey; #pragma clang diagnostic push @@ -273,8 +264,11 @@ - (instancetype)initForClient:(PubNub *)client { } - (void)reset { + pn_lock(&_lock, ^{ + self->_sequenceNumberChanged = YES; + self->_sequenceNumber = 0; + }); - pn_lock(&_lock, ^{ self->_sequenceNumber = 0; }); [self saveToPersistentStorage]; } @@ -282,69 +276,82 @@ - (void)reset { #pragma mark - Data storage - (void)loadFromPersistentStorage { - pn_lock_async(&publishSequenceKeychainAccessLock, ^(dispatch_block_t completion) { - - [PNKeychain valueForKey:kPNPublishSequenceDataKey withCompletionBlock:^(NSDictionary *sequences) { + [PNKeychain valueForKey:kPNPublishSequenceDataKey + withCompletionBlock:^(NSDictionary *sequences) { NSMutableDictionary *mutableSequences = [(sequences?: @{}) mutableCopy]; NSMutableDictionary *sequenceData = [(mutableSequences[self.publishKey]?: @{}) mutableCopy]; NSNumber *sequenceNumber = (NSNumber *)sequenceData[PNPublishSequenceData.sequence]; sequenceData[PNPublishSequenceData.lastSaveDate] = @([NSDate date].timeIntervalSince1970); self->_sequenceNumber = sequenceNumber.unsignedIntegerValue; - [PNKeychain storeValue:mutableSequences forKey:kPNPublishSequenceDataKey - withCompletionBlock:^(BOOL stored) { completion(); }]; + + [PNKeychain storeValue:mutableSequences + forKey:kPNPublishSequenceDataKey + withCompletionBlock:^(BOOL stored) { + + completion(); + }]; }]; }); } - (void)saveToPersistentStorage { + if (!self.sequenceNumberChanged) { + return; + } + + pn_lock(&_lock, ^{ + self->_sequenceNumberChanged = NO; + }); pn_lock_async(&publishSequenceKeychainAccessLock, ^(dispatch_block_t completion) { - - // Perform data maniupulation only if PubNub client, for which manager has been created, still - // available. - if (self.client != nil) { - - [PNKeychain valueForKey:kPNPublishSequenceDataKey withCompletionBlock:^(NSDictionary *sequences) { + [PNKeychain valueForKey:kPNPublishSequenceDataKey + withCompletionBlock:^(NSDictionary *sequences) { - NSMutableDictionary *mutableSequences = [(sequences?: @{}) mutableCopy]; - NSMutableDictionary *sequenceData = [(mutableSequences[self.publishKey]?: @{}) mutableCopy]; - sequenceData[PNPublishSequenceData.sequence] = @(self.sequenceNumber); - sequenceData[PNPublishSequenceData.lastSaveDate] = @([NSDate date].timeIntervalSince1970); - mutableSequences[self.publishKey] = sequenceData; - [PNKeychain storeValue:mutableSequences forKey:kPNPublishSequenceDataKey - withCompletionBlock:^(BOOL stored) { completion(); }]; - }]; - } + NSMutableDictionary *mutableSequences = [(sequences?: @{}) mutableCopy]; + NSMutableDictionary *sequenceData = [(mutableSequences[self.publishKey]?: @{}) mutableCopy]; + sequenceData[PNPublishSequenceData.sequence] = @(self.sequenceNumber); + sequenceData[PNPublishSequenceData.lastSaveDate] = @([NSDate date].timeIntervalSince1970); + mutableSequences[self.publishKey] = sequenceData; + + [PNKeychain storeValue:mutableSequences + forKey:kPNPublishSequenceDataKey + withCompletionBlock:^(BOOL stored) { + + completion(); + }]; + }]; }); } - (void)cleanUpIfRequired { - NSTimeInterval currentTimestamp = [NSDate date].timeIntervalSince1970; + pn_lock_async(&publishSequenceKeychainAccessLock, ^(dispatch_block_t completion) { - [PNKeychain valueForKey:kPNPublishSequenceDataKey withCompletionBlock:^(NSDictionary *sequences) { NSMutableDictionary *mutableSequences = [(sequences?: @{}) mutableCopy]; - [mutableSequences enumerateKeysAndObjectsUsingBlock:^(NSString *publishKey, + [mutableSequences enumerateKeysAndObjectsUsingBlock:^(NSString *publishKey, NSDictionary *sequenceData, BOOL *sequencesEnumeratorStop) { if (![publishKey isEqualToString:self.publishKey]) { - NSNumber *lastUpdateDate = sequenceData[PNPublishSequenceData.lastSaveDate]; NSTimeInterval lastUpdateTimestamp = lastUpdateDate.doubleValue; + if (ABS(currentTimestamp - lastUpdateTimestamp) > kPNMaximumPublishSequenceDataAge) { - mutableSequences[publishKey] = nil; } } }]; - [PNKeychain storeValue:mutableSequences forKey:kPNPublishSequenceDataKey - withCompletionBlock:^(BOOL stored) { completion(); }]; + [PNKeychain storeValue:mutableSequences + forKey:kPNPublishSequenceDataKey + withCompletionBlock:^(BOOL stored) { + + completion(); + }]; }]; }); } @@ -353,7 +360,6 @@ - (void)cleanUpIfRequired { #pragma mark - Handlers - (void)handleContextTransition:(NSNotification *)notification { - [self saveToPersistentStorage]; } @@ -361,27 +367,43 @@ - (void)handleContextTransition:(NSNotification *)notification { #pragma mark - Misc - (void)subscribeOnNotifications { - #if TARGET_OS_IOS - NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; - [notificationCenter addObserver:self selector:@selector(handleContextTransition:) - name:UIApplicationWillResignActiveNotification object:nil]; - [notificationCenter addObserver:self selector:@selector(handleContextTransition:) - name:UIApplicationDidEnterBackgroundNotification object:nil]; + NSNotificationCenter *notificationCenter = NSNotificationCenter.defaultCenter; + + [notificationCenter addObserver:self + selector:@selector(handleContextTransition:) + name:UIApplicationWillResignActiveNotification + object:nil]; + [notificationCenter addObserver:self + selector:@selector(handleContextTransition:) + name:UIApplicationDidEnterBackgroundNotification + object:nil]; #elif TARGET_OS_WATCH NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; - [notificationCenter addObserver:self selector:@selector(handleContextTransition:) - name:NSExtensionHostWillResignActiveNotification object:nil]; - [notificationCenter addObserver:self selector:@selector(handleContextTransition:) - name:NSExtensionHostDidEnterBackgroundNotification object:nil]; + + [notificationCenter addObserver:self + selector:@selector(handleContextTransition:) + name:NSExtensionHostWillResignActiveNotification + object:nil]; + [notificationCenter addObserver:self + selector:@selector(handleContextTransition:) + name:NSExtensionHostDidEnterBackgroundNotification + object:nil]; #elif TARGET_OS_OSX NSNotificationCenter *notificationCenter = [[NSWorkspace sharedWorkspace] notificationCenter]; - [notificationCenter addObserver:self selector:@selector(handleContextTransition:) - name:NSWorkspaceWillSleepNotification object:nil]; - [notificationCenter addObserver:self selector:@selector(handleContextTransition:) - name:NSWorkspaceSessionDidResignActiveNotification object:nil]; - [notificationCenter addObserver:self selector:@selector(handleContextTransition:) - name:NSWorkspaceDidDeactivateApplicationNotification object:nil]; + + [notificationCenter addObserver:self + selector:@selector(handleContextTransition:) + name:NSWorkspaceWillSleepNotification + object:nil]; + [notificationCenter addObserver:self + selector:@selector(handleContextTransition:) + name:NSWorkspaceSessionDidResignActiveNotification + object:nil]; + [notificationCenter addObserver:self + selector:@selector(handleContextTransition:) + name:NSWorkspaceDidDeactivateApplicationNotification + object:nil]; #endif // TARGET_OS_OSX } diff --git a/PubNub/Data/Managers/PNStateListener.h b/PubNub/Data/Managers/PNStateListener.h index 12afdc036..29910301a 100644 --- a/PubNub/Data/Managers/PNStateListener.h +++ b/PubNub/Data/Managers/PNStateListener.h @@ -5,7 +5,7 @@ #pragma mark Class forward @class PNMembershipEventResult, PNPresenceEventResult, PNSpaceEventResult, PNUserEventResult; -@class PNMessageResult, PNSignalResult, PNErrorStatus, PubNub; +@class PNMessageActionResult, PNMessageResult, PNSignalResult, PNErrorStatus, PubNub; NS_ASSUME_NONNULL_BEGIN @@ -108,6 +108,17 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)notifySignal:(PNSignalResult *)signal; +/** + * @brief Notify all \c message \c actions listeners about new signal. + * + * @warning Method should be called within \b -notifyWithBlock: block to shift execution to private + * protected queue. + * + * @param action Event object which provide information about operation type and service response + * for it. + */ +- (void)notifyMessageAction:(PNMessageActionResult *)action; + /** * @brief Notify all presence event listeners about new event. * diff --git a/PubNub/Data/Managers/PNStateListener.m b/PubNub/Data/Managers/PNStateListener.m index 6f73999a3..76e6bbef6 100644 --- a/PubNub/Data/Managers/PNStateListener.m +++ b/PubNub/Data/Managers/PNStateListener.m @@ -26,56 +26,74 @@ @interface PNStateListener () @property (nonatomic, weak) PubNub *client; /** - * @brief Stores list of listeners which would like to be notified when new message arrive from - * remote data feed objects on which client subscribed at this moment. + * @brief List of listeners which would like to be notified when new message arrive from remote data + * feed objects on which client subscribed at this moment. * * @return Hash table with list of new message listeners. */ @property (nonatomic, strong) NSHashTable> *messageListeners; /** - * @brief Stores list of listeners which would like to be notified when new signal arrive from - * remote data feed objects on which client subscribed at this moment. + * @brief List of listeners which would like to be notified when new signal arrive from remote data + * feed objects on which client subscribed at this moment. * - * @return Hash table with list of new message listeners. + * @return Hash table with list of new \c signal listeners. + * + * @since 4.9.0 */ @property (nonatomic, strong) NSHashTable> *signalListeners; /** - * @brief Stores list of listeners which would like to be notified when new presence event arrive - * from remote data feed objects on which client subscribed at this moment. + * @brief List of listeners which would like to be notified when new \c message \c actions + * arrive from remote data feed objects on which client subscribed at this moment. + * + * @return Hash table with list of new \c action listeners. + * + * @since 4.11.0 + */ +@property (nonatomic, strong) NSHashTable> *messageActionListeners; + +/** + * @brief List of listeners which would like to be notified when new presence event arrive from + * remote data feed objects on which client subscribed at this moment. * * @return Hash table with list of presence event listeners. */ @property (nonatomic, strong) NSHashTable> *presenceEventListeners; /** - * @brief Stores list of listeners which would like to be notified when new \c membership event - * arrive from remote data feed objects on which client subscribed at this moment. + * @brief List of listeners which would like to be notified when new \c membership event arrive from + * remote data feed objects on which client subscribed at this moment. * * @return Hash table with list of \c membership event listeners. + * + * @since 4.10.0 */ @property (nonatomic, strong) NSHashTable> *membershipEventListeners; /** - * @brief Stores list of listeners which would like to be notified when new \c space event arrive - * from remote data feed objects on which client subscribed at this moment. + * @brief List of listeners which would like to be notified when new \c space event arrive from + * remote data feed objects on which client subscribed at this moment. * * @return Hash table with list of \c space event listeners. + * + * @since 4.10.0 */ @property (nonatomic, strong) NSHashTable> *spaceEventListeners; /** - * @brief Stores list of listeners which would like to be notified when new \c user event arrive - * from remote data feed objects on which client subscribed at this moment. + * @brief List of listeners which would like to be notified when new \c user event arrive from + * remote data feed objects on which client subscribed at this moment. * * @return Hash table with list of \c user event listeners. + * + * @since 4.10.0 */ @property (nonatomic, strong) NSHashTable> *userEventListeners; /** - * @brief Stores list of listeners which would like to be notified when on subscription state - * changes (connection, access rights error, disconnection and unexpected disconnection). + * @brief List of listeners which would like to be notified when on subscription state changes + * (connection, access rights error, disconnection and unexpected disconnection). */ @property (nonatomic, strong) NSHashTable> *stateListeners; @@ -142,6 +160,7 @@ - (instancetype)initForClient:(PubNub *)client { _client = client; _messageListeners = [NSHashTable weakObjectsHashTable]; _signalListeners = [NSHashTable weakObjectsHashTable]; + _messageActionListeners = [NSHashTable weakObjectsHashTable]; _presenceEventListeners = [NSHashTable weakObjectsHashTable]; _membershipEventListeners = [NSHashTable weakObjectsHashTable]; _spaceEventListeners = [NSHashTable weakObjectsHashTable]; @@ -160,6 +179,7 @@ - (void)inheritStateFromListener:(PNStateListener *)listener { NSHashTable *messageListeners = [listener listenersCopyFrom:listener.messageListeners]; NSHashTable *signalListeners = [listener listenersCopyFrom:listener.signalListeners]; + NSHashTable *actionListeners = [listener listenersCopyFrom:listener.messageActionListeners]; NSHashTable *presenceEventListeners = [listener listenersCopyFrom:listener.presenceEventListeners]; NSHashTable *membershipEventListeners = [listener listenersCopyFrom:listener.membershipEventListeners]; NSHashTable *spaceEventListeners = [listener listenersCopyFrom:listener.spaceEventListeners]; @@ -169,6 +189,7 @@ - (void)inheritStateFromListener:(PNStateListener *)listener { dispatch_async(self.resourceAccessQueue, ^{ self.messageListeners = messageListeners; self.signalListeners = signalListeners; + self.messageActionListeners = actionListeners; self.presenceEventListeners = presenceEventListeners; self.membershipEventListeners = membershipEventListeners; self.spaceEventListeners = spaceEventListeners; @@ -190,6 +211,10 @@ - (void)addListener:(id )listener { [self.signalListeners addObject:listener]; } + if ([listener respondsToSelector:@selector(client:didReceiveMessageAction:)]) { + [self.messageActionListeners addObject:listener]; + } + if ([listener respondsToSelector:@selector(client:didReceivePresenceEvent:)]) { [self.presenceEventListeners addObject:listener]; } @@ -216,6 +241,7 @@ - (void)removeListener:(id )listener { dispatch_async(self.resourceAccessQueue, ^{ [self.messageListeners removeObject:listener]; [self.signalListeners removeObject:listener]; + [self.messageActionListeners removeObject:listener]; [self.presenceEventListeners removeObject:listener]; [self.membershipEventListeners removeObject:listener]; [self.spaceEventListeners removeObject:listener]; @@ -228,6 +254,7 @@ - (void)removeAllListeners { dispatch_async(self.resourceAccessQueue, ^{ [self.messageListeners removeAllObjects]; [self.signalListeners removeAllObjects]; + [self.messageActionListeners removeAllObjects]; [self.presenceEventListeners removeAllObjects]; [self.membershipEventListeners removeAllObjects]; [self.spaceEventListeners removeAllObjects]; @@ -272,6 +299,16 @@ - (void)notifySignal:(PNSignalResult *)signal { }); } +- (void)notifyMessageAction:(PNMessageActionResult *)action { + NSArray> *listeners = self.messageActionListeners.allObjects; + + pn_dispatch_async(self.client.callbackQueue, ^{ + for (id listener in listeners) { + [listener client:self.client didReceiveMessageAction:action]; + } + }); +} + - (void)notifyPresenceEvent:(PNPresenceEventResult *)event { NSArray> *listeners = self.presenceEventListeners.allObjects; diff --git a/PubNub/Data/Managers/PNSubscriber.m b/PubNub/Data/Managers/PNSubscriber.m index 447265b22..54f3db1f6 100644 --- a/PubNub/Data/Managers/PNSubscriber.m +++ b/PubNub/Data/Managers/PNSubscriber.m @@ -375,15 +375,30 @@ - (void)handleNewMessage:(PNMessageResult *)data; * * @param data Result data which hold information about request on which this response has been * received and message itself. + * + * @since 4.9.0 */ - (void)handleNewSignal:(PNSignalResult *)data; +/** + * @brief Process \c message \c action which just has been received from \b PubNub service + * through live feed on which client subscribed at this moment. + * + * @param data Result data which hold information about request on which this response has been + * received and message itself. + * + * @since 4.11.0 + */ +- (void)handleNewMessageAction:(PNMessageActionResult *)data; + /** * @brief Process \c objects API event which just has been received from \b PubNub service through * live feed on which client subscribed at this moment. * * @param data Result data which hold information about request on which this response has been * received and message itself. + * + * @since 4.10.0 */ - (void)handleNewObjectsEvent:(PNResult *)data; @@ -1499,6 +1514,9 @@ - (void)handleLiveFeedEvents:(PNSubscribeStatus *)status forInitialSubscription: } else if (messageType == PNSignalMessageType) { object_setClass(eventResultObject, [PNSignalResult class]); [self handleNewSignal:(PNSignalResult *)eventResultObject]; + } else if (messageType == PNMessageActionType) { + object_setClass(eventResultObject, [PNMessageActionResult class]); + [self handleNewMessageAction:(PNMessageActionResult *)eventResultObject]; } } } @@ -1545,6 +1563,16 @@ - (void)handleNewSignal:(PNSignalResult *)data { [self.client.listenersManager notifySignal:(PNSignalResult *)data]; } +- (void)handleNewMessageAction:(PNMessageActionResult *)data { + if (!data) { + return; + } + + PNLogResult(self.client.logger, @" %@", [data stringifiedRepresentation]); + + [self.client.listenersManager notifyMessageAction:(PNMessageActionResult *)data]; +} + - (void)handleNewObjectsEvent:(PNResult *)data { if (!data) { return; @@ -1651,11 +1679,12 @@ - (void)deDuplicateMessages:(NSMutableArray *)events { [events enumerateObjectsUsingBlock:^(NSDictionary *event, NSUInteger eventIdx, BOOL *eventsEnumeratorStop) { PNMessageType messageType = ((PNEnvelopeInformation *)event[@"envelope"]).messageType; - BOOL isObjectEvent = messageType == PNObjectMessageType; + BOOL isMessageEvent = (messageType != PNObjectMessageType && + messageType != PNMessageActionType); BOOL isPresenceEvent = event[@"presenceEvent"] != nil; BOOL isDecryptionError = ((NSNumber *)event[@"decryptError"]).boolValue; - if (!isPresenceEvent && !isDecryptionError && !isObjectEvent && + if (!isPresenceEvent && !isDecryptionError && isMessageEvent && ![self cacheObjectIfPossible:event withMaximumCacheSize:maximumMessagesCacheSize]) { [duplicateMessagesIndices addIndex:eventIdx]; diff --git a/PubNub/Data/Managers/PNTelemetry.m b/PubNub/Data/Managers/PNTelemetry.m index bc12f6cb5..f5ac2db31 100644 --- a/PubNub/Data/Managers/PNTelemetry.m +++ b/PubNub/Data/Managers/PNTelemetry.m @@ -216,6 +216,7 @@ - (NSString *)endpointNameForOperation:(PNOperationType)operationType { break; case PNHistoryOperation: case PNHistoryForChannelsOperation: + case PNHistoryWithActionsOperation: case PNDeleteMessageOperation: operation = @"hist"; break; @@ -259,6 +260,11 @@ - (NSString *)endpointNameForOperation:(PNOperationType)operationType { case PNFetchMembersOperation: operation = @"obj"; break; + case PNAddMessageActionOperation: + case PNRemoveMessageActionOperation: + case PNFetchMessagesActionsOperation: + operation = @"msga"; + break; default: operation = @"time"; break; diff --git a/PubNub/Data/Models/PNMessageAction+Private.h b/PubNub/Data/Models/PNMessageAction+Private.h new file mode 100644 index 000000000..e4aee7b2c --- /dev/null +++ b/PubNub/Data/Models/PNMessageAction+Private.h @@ -0,0 +1,41 @@ +/** + * @author Serhii Mamontov + * @version 4.11.0 + * @since 4.11.0 + * @copyright © 2010-2019 PubNub, Inc. + */ +#import "PNMessageAction.h" + + +NS_ASSUME_NONNULL_BEGIN + +#pragma mark Private interface declaration + +/** + * @brief Private \c message \c action extension to provide ability to set data from service + * response. + * + * @author Serhii Mamontov + * @version 4.11.0 + * @since 4.11.0 + * @copyright © 2010-2019 PubNub, Inc. + */ +@interface PNMessageAction (Private) + + +#pragma mark - Initialization & Configuration + +/** + * @brief Create and configure \c message \c action data model from dictionary. + * + * @param data Dictionary with information about \c message \c action from 'Message Action' API. + * + * @return Configured and ready to use \c message \c action representation model. + */ ++ (instancetype)actionFromDictionary:(NSDictionary *)data; + +#pragma mark - + +@end + +NS_ASSUME_NONNULL_END diff --git a/PubNub/Data/Models/PNMessageAction.h b/PubNub/Data/Models/PNMessageAction.h new file mode 100644 index 000000000..55fe17b5e --- /dev/null +++ b/PubNub/Data/Models/PNMessageAction.h @@ -0,0 +1,53 @@ +#import +#import "PNStructures.h" + + +NS_ASSUME_NONNULL_BEGIN + +#pragma mark Interface declaration + +/** + * @brief Object which is used to represent \c message \c action. + * + * @author Serhii Mamontov + * @version 4.11.0 + * @since 4.11.0 + * @copyright © 2010-2019 PubNub, Inc. + */ +@interface PNMessageAction : NSObject + + +#pragma mark - Information + +/** + * @brief What feature this \c message \c action represents. + */ +@property (nonatomic, readonly, copy) NSString *type; + +/** + * @brief Timetoken (\b PubNub's high precision timestamp) of \c message for which \c action has + * been added. + */ +@property (nonatomic, readonly, strong) NSNumber *messageTimetoken; + +/** + * @brief \c Message \c action addition timetoken (\b PubNub's high precision timestamp). + */ +@property (nonatomic, readonly, strong) NSNumber *actionTimetoken; + +/** + * @brief \c Identifier of user which added this \c message \c action. + */ +@property (nonatomic, readonly, copy) NSString *uuid; + +/** + * @brief Value which has been added with \c message \c action \b type. + */ +@property (nonatomic, readonly, copy) NSString *value; + +#pragma mark - + + +@end + +NS_ASSUME_NONNULL_END diff --git a/PubNub/Data/Models/PNMessageAction.m b/PubNub/Data/Models/PNMessageAction.m new file mode 100644 index 000000000..260df071e --- /dev/null +++ b/PubNub/Data/Models/PNMessageAction.m @@ -0,0 +1,99 @@ +/** + * @author Serhii Mamontov + * @version 4.11.0 + * @since 4.11.0 + * @copyright © 2010-2019 PubNub, Inc. + */ +#import "PNMessageAction+Private.h" + + +NS_ASSUME_NONNULL_BEGIN + +#pragma mark Protected interface declaration + +@interface PNMessageAction () + + +#pragma mark - Information + +/** + * @brief What feature this \c message \c action represents. + */ +@property (nonatomic, copy) NSString *type; + +/** + * @brief Timetoken (\b PubNub's high precision timestamp) of \c message for which \c action has + * been added. + */ +@property (nonatomic, strong) NSNumber *messageTimetoken; + +/** + * @brief \c Message \c action addition timetoken (\b PubNub's high precision timestamp). + */ +@property (nonatomic, strong) NSNumber *actionTimetoken; + +/** + * @brief \c Identifier of user which added this \c message \c action. + */ +@property (nonatomic, copy) NSString *uuid; + +/** + * @brief Value which has been added with \c message \c action \b type. + */ +@property (nonatomic, copy) NSString *value; + + +#pragma mark - Misc + +/** + * @brief Translate \c message \c action data model to dictionary. + */ +- (NSDictionary *)dictionaryRepresentation; + +#pragma mark - + + +@end + +NS_ASSUME_NONNULL_END + + +#pragma mark - Interface implementation + +@implementation PNMessageAction + + +#pragma mark - Initialization & Configuration + ++ (instancetype)actionFromDictionary:(NSDictionary *)data { + PNMessageAction *action = [self new]; + action.value = data[@"value"]; + action.uuid = data[@"uuid"]; + action.actionTimetoken = data[@"actionTimetoken"]; + action.messageTimetoken = data[@"messageTimetoken"]; + action.type = data[@"type"]; + + return action; +} + + +#pragma mark - Misc + +- (NSDictionary *)dictionaryRepresentation { + return @{ + @"type": self.type, + @"uuid": self.uuid, + @"actionTimetoken": self.actionTimetoken, + @"messageTimetoken": self.messageTimetoken, + @"value": self.value + }; +} + +- (NSString *)debugDescription { + return [[self dictionaryRepresentation] description]; +} + +#pragma mark - + + +@end diff --git a/PubNub/Data/Models/PNUser.m b/PubNub/Data/Models/PNUser.m index a5bbb645c..a4146815b 100644 --- a/PubNub/Data/Models/PNUser.m +++ b/PubNub/Data/Models/PNUser.m @@ -154,7 +154,6 @@ - (NSString *)debugDescription { return [[self dictionaryRepresentation] description]; } - #pragma mark - diff --git a/PubNub/Data/Service Objects/PNAddMessageActionStatus.h b/PubNub/Data/Service Objects/PNAddMessageActionStatus.h new file mode 100644 index 000000000..0769eadb1 --- /dev/null +++ b/PubNub/Data/Service Objects/PNAddMessageActionStatus.h @@ -0,0 +1,59 @@ +#import "PNAcknowledgmentStatus.h" +#import "PNMessageAction.h" +#import "PNServiceData.h" + + +NS_ASSUME_NONNULL_BEGIN + +#pragma mark Interfaces declaration + +/** + * @brief Object which is used to represent 'Message Actions' API response for \c add \c message + * \c action request. + * + * @author Serhii Mamontov + * @version 4.11.0 + * @since 4.11.0 + * @copyright © 2010-2019 PubNub, Inc. + */ +@interface PNAddMessageActionData : PNServiceData + + +#pragma mark - Information + +/** + * @brief Added \c message \c action. + */ +@property (nonatomic, nullable, readonly, strong) PNMessageAction *action; + +#pragma mark - + + +@end + + +/** + * @brief Object which is used to provide access to processed \c add \c message \c action request + * results. + * + * @author Serhii Mamontov + * @version 4.11.0 + * @since 4.11.0 + * @copyright © 2010-2019 PubNub, Inc. + */ +@interface PNAddMessageActionStatus : PNAcknowledgmentStatus + + +#pragma mark - Information + +/** + * @brief \c Add \c message \c action request processed information. + */ +@property (nonatomic, readonly, strong) PNAddMessageActionData *data; + +#pragma mark - + + +@end + +NS_ASSUME_NONNULL_END diff --git a/PubNub/Data/Service Objects/PNAddMessageActionStatus.m b/PubNub/Data/Service Objects/PNAddMessageActionStatus.m new file mode 100644 index 000000000..9ae1cfea0 --- /dev/null +++ b/PubNub/Data/Service Objects/PNAddMessageActionStatus.m @@ -0,0 +1,86 @@ +/** + * @author Serhii Mamontov + * @version 4.11.0 + * @since 4.11.0 + * @copyright © 2010-2019 PubNub, Inc. + */ +#import "PNAddMessageActionStatus.h" +#import "PNMessageAction+Private.h" +#import "PNServiceData+Private.h" +#import "PNResult+Private.h" + + +NS_ASSUME_NONNULL_BEGIN + +#pragma mark Protected interfaces declaration + +@interface PNAddMessageActionStatus () + + +#pragma mark - Information + +@property (nonatomic, strong) PNAddMessageActionData *data; + +#pragma mark - + + +@end + + +@interface PNAddMessageActionData () + + +#pragma mark - Information + +@property (nonatomic, strong) PNMessageAction *action; + +#pragma mark - + + +@end + +NS_ASSUME_NONNULL_END + + +#pragma mark - Interfaces implementation + +@implementation PNAddMessageActionData + + +#pragma mark - Information + +- (PNMessageAction *)action { + if (!_action) { + _action = [PNMessageAction actionFromDictionary:self.serviceData[@"action"]]; + } + + return _action; +} + +#pragma mark - + + +@end + + +@implementation PNAddMessageActionStatus + + +#pragma mark - Information + +- (BOOL)isError { + return super.isError ?: ((NSNumber *)self.serviceData[@"isError"]).boolValue; +} + +- (PNAddMessageActionData *)data { + if (!_data) { + _data = [PNAddMessageActionData dataWithServiceResponse:self.serviceData]; + } + + return _data; +} + +#pragma mark - + + +@end diff --git a/PubNub/Data/Service Objects/PNFetchMessageActionsResult.h b/PubNub/Data/Service Objects/PNFetchMessageActionsResult.h new file mode 100644 index 000000000..b6a91cd8d --- /dev/null +++ b/PubNub/Data/Service Objects/PNFetchMessageActionsResult.h @@ -0,0 +1,71 @@ +#import "PNMessageAction.h" +#import "PNServiceData.h" +#import "PNResult.h" + + +NS_ASSUME_NONNULL_BEGIN + +#pragma mark Interfaces declaration + +/** + * @brief Object which is used to represent Message Action API response for \c fetch \c message + * \c actions request. + * + * @author Serhii Mamontov + * @version 4.11.0 + * @since 4.11.0 + * @copyright © 2010-2019 PubNub, Inc. + */ +@interface PNFetchMessageActionsData : PNServiceData + + +#pragma mark - Information + +/** + * @brief List of fetched \c messages \c actions. + */ +@property (nonatomic, readonly, strong) NSArray *actions; + +/** + * @brief Fetched \c message \c actions time range start (oldest \c message \c action timetoken). + * + * @note This timetoken can be used as \c start value to fetch older \c message \c actions. + */ +@property (nonatomic, readonly, strong) NSNumber *start; + +/** + * @brief Fetched \c message \c actions time range end (newest \c action timetoken). + */ +@property (nonatomic, readonly, strong) NSNumber *end; + +#pragma mark - + + +@end + + +/** + * @brief Object which is used to provide access to processed \c fetch \c message \c actions + * request results. + * + * @author Serhii Mamontov + * @version 4.11.0 + * @since 4.11.0 + * @copyright © 2010-2019 PubNub, Inc. + */ +@interface PNFetchMessageActionsResult : PNResult + + +#pragma mark - Information + +/** + * @brief \c Fetch \c message \c actions request processed information. + */ +@property (nonatomic, readonly, strong) PNFetchMessageActionsData *data; + +#pragma mark - + + +@end + +NS_ASSUME_NONNULL_END diff --git a/PubNub/Data/Service Objects/PNFetchMessageActionsResult.m b/PubNub/Data/Service Objects/PNFetchMessageActionsResult.m new file mode 100644 index 000000000..3ec33f325 --- /dev/null +++ b/PubNub/Data/Service Objects/PNFetchMessageActionsResult.m @@ -0,0 +1,96 @@ +/** + * @author Serhii Mamontov + * @version 4.11.0 + * @since 4.11.0 + * @copyright © 2010-2019 PubNub, Inc. + */ +#import "PNFetchMessageActionsResult.h" +#import "PNMessageAction+Private.h" +#import "PNServiceData+Private.h" +#import "PNResult+Private.h" + + +NS_ASSUME_NONNULL_BEGIN + +#pragma mark Protected interfaces declaration + +@interface PNFetchMessageActionsResult () + + +#pragma mark - Information + +@property (nonatomic, nonnull, strong) PNFetchMessageActionsData *data; + +#pragma mark - + + +@end + + +@interface PNFetchMessageActionsData () + + +#pragma mark - Information + +@property (nonatomic, strong) NSArray *actions; + +#pragma mark - + + +@end + +NS_ASSUME_NONNULL_END + + +#pragma mark - Interfaces implementation + +@implementation PNFetchMessageActionsData + + +#pragma mark - Information + +- (NSArray *)actions { + if (!_actions) { + NSMutableArray *actions = [NSMutableArray new]; + + for (NSDictionary *action in self.serviceData[@"actions"]) { + [actions addObject:[PNMessageAction actionFromDictionary:action]]; + } + + _actions = [actions copy]; + } + + return _actions; +} + +- (NSNumber *)start { + return self.serviceData[@"start"]; +} + +- (NSNumber *)end { + return self.serviceData[@"end"]; +} + +#pragma mark - + + +@end + + +@implementation PNFetchMessageActionsResult + + +#pragma mark - Information + +- (PNFetchMessageActionsData *)data { + if (!_data) { + _data = [PNFetchMessageActionsData dataWithServiceResponse:self.serviceData]; + } + + return _data; +} + +#pragma mark - + + +@end diff --git a/PubNub/Data/Service Objects/PNHistoryResult.h b/PubNub/Data/Service Objects/PNHistoryResult.h index b2843d99c..601b0d0e1 100644 --- a/PubNub/Data/Service Objects/PNHistoryResult.h +++ b/PubNub/Data/Service Objects/PNHistoryResult.h @@ -4,53 +4,52 @@ NS_ASSUME_NONNULL_BEGIN +#pragma mark Interfaces declaration + /** - @brief Class which allow to get access to channel history processed result. - - @author Sergey Mamontov - @since 4.0 - @copyright © 2010-2018 PubNub, Inc. + * @brief Object which is used to represent History API response for \c channel(s) request. + * + * @author Sergey Mamontov + * @version 4.11.0 + * @since 4.0.0 + * @copyright © 2010-2019 PubNub, Inc. */ @interface PNHistoryData : PNServiceData -///------------------------------------------------ -/// @name Information -///------------------------------------------------ +#pragma mark - Information /** - @brief Channel history messages. - @discussion \b Important: for \c PNHistoryForChannelsOperation operation this property always will be - \c empty array. - - @since 4.0 + * @brief Channel history messages. + * + * @note Set only for \c PNHistoryOperation operation and will be \c empty array for other operation + * types. */ @property (nonatomic, readonly, strong) NSArray *messages; /** - @brief Channels history. - @discussion Each key represent name of channel for which messages has been received and valus is list of - messages from channel's storage. - @discussion \b Important: for \c PNHistoryOperation operation this property always will be \c empty - dictionary. - - @since 4.5.6 + * @brief Channels history. + * + * @discussion Each key represent name of \c channel for which messages has been received and values + * is list of messages from channel's storage. + * + * @note For \c PNHistoryOperation operation this property always will be \c empty dictionary. + * + * @since 4.5.6 */ @property (nonatomic, readonly, strong) NSDictionary *channels; /** - @brief History time frame start time. - @discussion \b Important: for \c PNHistoryForChannelsOperation operation this property always will be \b 0. - - @since 4.0 + * @brief Fetched history time frame start time. + * + * @note Set only for \c PNHistoryOperation operation and will be \b 0 for other operation types. */ @property (nonatomic, readonly, strong) NSNumber *start; /** - @brief History time frame end time. - @discussion \b Important: for \c PNHistoryForChannelsOperation operation this property always will be \b 0. - - @since 4.0 + * @brief Fetched history time frame end time. + * + * @note Set only for \c PNHistoryOperation operation and will be \b 0 for other operation types. */ @property (nonatomic, readonly, strong) NSNumber *end; @@ -61,23 +60,18 @@ NS_ASSUME_NONNULL_BEGIN /** - @brief Class which is used to provide access to request processing results. - - @author Sergey Mamontov - @since 4.0 - @copyright © 2010-2018 PubNub, Inc. + * @brief Object which is used to provide access to processed \c fetch \c history request results. + * + * @author Serhii Mamontov + * @copyright © 2010-2019 PubNub, Inc. */ @interface PNHistoryResult : PNResult -///------------------------------------------------ -/// @name Information -///------------------------------------------------ +#pragma mark - Information /** - @brief Stores reference on channel history request processing information. - - @since 4.0 + * @brief \c Fetch \c history request processed information. */ @property (nonatomic, readonly, strong) PNHistoryData *data; diff --git a/PubNub/Data/Service Objects/PNHistoryResult.m b/PubNub/Data/Service Objects/PNHistoryResult.m index f92c95323..ee796d5b9 100644 --- a/PubNub/Data/Service Objects/PNHistoryResult.m +++ b/PubNub/Data/Service Objects/PNHistoryResult.m @@ -1,14 +1,29 @@ /** - @author Sergey Mamontov - @since 4.0 - @copyright © 2010-2018 PubNub, Inc. + * @author Serhii Mamontov + * @copyright © 2010-2019 PubNub, Inc. */ #import "PNHistoryResult.h" #import "PNServiceData+Private.h" #import "PNResult+Private.h" +#import "PNStructures.h" -#pragma mark Interface implementation +#pragma mark Private interface declaration + +@interface PNHistoryResult () + + +#pragma mark - Properties + +@property (nonatomic, nonnull, strong) PNHistoryData *data; + +#pragma mark - + + +@end + + +#pragma mark - Interfaces implementation @implementation PNHistoryData @@ -16,23 +31,19 @@ @implementation PNHistoryData #pragma mark - Information - (NSArray *)messages { - - return (self.serviceData[@"messages"]?: @[]); + return self.serviceData[@"messages"] ?: @[]; } - (NSDictionary *)channels { - - return (self.serviceData[@"channels"]?: @{}); + return self.serviceData[@"channels"] ?: @{}; } - (NSNumber *)start { - - return (self.serviceData[@"start"]?: @0); + return self.serviceData[@"start"] ?: @0; } - (NSNumber *)end { - - return (self.serviceData[@"end"]?: @0); + return self.serviceData[@"end"] ?: @0; } #pragma mark - @@ -41,31 +52,16 @@ - (NSNumber *)end { @end -#pragma mark - Private interface declaration - -@interface PNHistoryResult () - - -#pragma mark - Properties - -@property (nonatomic, nonnull, strong) PNHistoryData *data; - -#pragma mark - - - -@end - - -#pragma mark - Interface implementation - @implementation PNHistoryResult #pragma mark - Information - (PNHistoryData *)data { + if (!_data) { + _data = [PNHistoryData dataWithServiceResponse:self.serviceData]; + } - if (!_data) { _data = [PNHistoryData dataWithServiceResponse:self.serviceData]; } return _data; } diff --git a/PubNub/Data/Service Objects/PNStatus.m b/PubNub/Data/Service Objects/PNStatus.m index 73fa87f4c..799ef59ba 100644 --- a/PubNub/Data/Service Objects/PNStatus.m +++ b/PubNub/Data/Service Objects/PNStatus.m @@ -152,14 +152,14 @@ - (instancetype)initForOperation:(PNOperationType)operation if ((self = [super initForOperation:operation completedWithTask:task processedData:processedData processingError:error])) { - _error = (error != nil || self.statusCode != 200); + _error = (error != nil || self.statusCode >= 400); if (_error && ![self.serviceData count]) { [self updateData:[self dataFromError:error]]; } // Check whether status should represent acknowledgment or not. - if (self.statusCode == 200 && !_error) { + if (self.statusCode < 400 && !_error) { _category = PNAcknowledgmentCategory; } else if (_category == PNUnknownCategory) { // Try extract category basing on response status codes. diff --git a/PubNub/Data/Service Objects/PNSubscriberResults.h b/PubNub/Data/Service Objects/PNSubscriberResults.h index 2c025ca2e..b2c068fc2 100644 --- a/PubNub/Data/Service Objects/PNSubscriberResults.h +++ b/PubNub/Data/Service Objects/PNSubscriberResults.h @@ -3,6 +3,12 @@ #import "PNResult.h" +#pragma mark Class forward + +@class PNMessageAction; + + + NS_ASSUME_NONNULL_BEGIN /** @@ -176,6 +182,36 @@ NS_ASSUME_NONNULL_BEGIN @end +/** + * @brief Class which allow to get access to \c action body received from remote object live feed. + * + * @author Serhii Mamontov + * @version 4.11.0 + * @since 4.11.0 + * @copyright © 2010-2019 PubNub, Inc. + */ +@interface PNMessageActionData : PNSubscriberData + + +#pragma mark - Information + +/** + * @brief \c Action for which event has been received. + */ +@property (nonatomic, readonly, strong) PNMessageAction *action; + +/** + * @brief Name of action for which \c message \c action event has been sent. + */ +@property (nonatomic, readonly, copy) NSString *event; + + +#pragma mark - + + +@end + + /** * @brief Class which allow to get access to \c membership event body received from remote object * live feed. @@ -438,6 +474,30 @@ NS_ASSUME_NONNULL_BEGIN @end +/** + * @brief Class which is used to provide access to subscribe request processing results. + * + * @author Serhii Mamontov + * @version 4.11.0 + * @since 4.11.0 + * @copyright © 2010-2019 PubNub, Inc. + */ +@interface PNMessageActionResult : PNResult + + +#pragma mark - Information + +/** + * @brief \c Message \c action object from live feed. + */ +@property (nonatomic, readonly, strong) PNMessageActionData *data; + +#pragma mark - + + +@end + + /** * @brief Class which is used to provide access to request processing results. * diff --git a/PubNub/Data/Service Objects/PNSubscriberResults.m b/PubNub/Data/Service Objects/PNSubscriberResults.m index 8ca5abd18..5e13cd6f0 100644 --- a/PubNub/Data/Service Objects/PNSubscriberResults.m +++ b/PubNub/Data/Service Objects/PNSubscriberResults.m @@ -6,6 +6,7 @@ */ #import "NSDateFormatter+PNCacheable.h" #import "PNSubscribeStatus+Private.h" +#import "PNMessageAction+Private.h" #import "PNServiceData+Private.h" #import "PNSubscriberResults.h" #import "PNResult+Private.h" @@ -39,6 +40,19 @@ @interface PNSignalResult () @end +@interface PNMessageActionResult () + + +#pragma mark - Properties + +@property (nonatomic, strong) PNMessageActionData *data; + +#pragma mark - + + +@end + + @interface PNPresenceEventResult () @@ -106,15 +120,15 @@ - (NSString *)uuid { return self.serviceData[@"uuid"]; } -- (NSString *)join { +- (NSArray *)join { return self.serviceData[@"join"]; } -- (NSString *)leave { +- (NSArray *)leave { return self.serviceData[@"leave"]; } -- (NSString *)timeout { +- (NSArray *)timeout { return self.serviceData[@"timeout"]; } @@ -177,6 +191,25 @@ @implementation PNSignalData @end +@implementation PNMessageActionData + + +#pragma mark - Information + +- (PNMessageAction *)action { + return [PNMessageAction actionFromDictionary:self.serviceData[@"action"]]; +} + +- (NSString *)event { + return self.serviceData[@"event"]; +} + +#pragma mark - + + +@end + + @implementation PNMembershipEventData @@ -368,6 +401,25 @@ - (PNSignalData *)data { @end +@implementation PNMessageActionResult + + +#pragma mark - Information + +- (PNMessageActionData *)data { + if (!_data) { + _data = [PNMessageActionData dataWithServiceResponse:self.serviceData]; + } + + return _data; +} + +#pragma mark - + + +@end + + @implementation PNPresenceEventResult diff --git a/PubNub/Misc/Helpers/PNJSON.m b/PubNub/Misc/Helpers/PNJSON.m index 5c60e222b..1dfcd576a 100644 --- a/PubNub/Misc/Helpers/PNJSON.m +++ b/PubNub/Misc/Helpers/PNJSON.m @@ -56,10 +56,10 @@ + (id)JSONObjectFrom:(NSString *)object withError:(NSError *__autoreleasing *)er JSONObject = [object stringByTrimmingCharactersInSet:trimCharSet]; } else { - + NSJSONReadingOptions options = NSJSONReadingAllowFragments|NSJSONReadingMutableContainers; NSData *JSONData = [object dataUsingEncoding:NSUTF8StringEncoding]; JSONObject = [NSJSONSerialization JSONObjectWithData:JSONData - options:NSJSONReadingAllowFragments + options:options error:&parsingError]; } diff --git a/PubNub/Misc/Helpers/PNNumber.h b/PubNub/Misc/Helpers/PNNumber.h index 71fe8735e..2efefe56f 100644 --- a/PubNub/Misc/Helpers/PNNumber.h +++ b/PubNub/Misc/Helpers/PNNumber.h @@ -1,29 +1,29 @@ #import +NS_ASSUME_NONNULL_BEGIN + /** - @brief Useful NSNumber additions collection. - - @author Sergey Mamontov - @since 4.2.0 - @copyright © 2010-2018 PubNub, Inc. + * @brief Useful NSNumber additions collection. + * + * @author Serhii Mamontov + * @version 4.11.0 + * @since 4.2.0 + * @copyright © 2010-2019 PubNub, Inc. */ @interface PNNumber : NSObject -///------------------------------------------------ -/// @name Conversion -///------------------------------------------------ +#pragma mark - Conversion /** - @brief Construct time token object which has higher precision and can be used inside of \b PubNub service - during requests. - - @param number Reference on original number which should be normalized and returned with new object. - - @return Normalized to \b PunNub service time token. - - @since 4.2.0 + * @brief Create higher precision timetoken which can be used inside of \b PubNub service during + * requests. + * + * @param number Reference on original number which should be normalized and returned with new + * object. + * + * @return Normalized to \b PunNub service time token. */ + (NSNumber *)timeTokenFromNumber:(NSNumber *)number; @@ -31,3 +31,5 @@ @end + +NS_ASSUME_NONNULL_END diff --git a/PubNub/Misc/Helpers/PNNumber.m b/PubNub/Misc/Helpers/PNNumber.m index f0fd86d67..6545fd302 100644 --- a/PubNub/Misc/Helpers/PNNumber.m +++ b/PubNub/Misc/Helpers/PNNumber.m @@ -1,7 +1,6 @@ /** - @author Sergey Mamontov - @since 4.2 - @copyright © 2010-2018 PubNub, Inc. + * @author Serhii Mamontov + * @copyright © 2010-2019 PubNub, Inc. */ #import "PNNumber.h" @@ -9,7 +8,7 @@ #pragma mark Static /** - @brief Stores how many digits is expected from number to be accepted by \b PubNub service. + * @brief Stores how many digits is expected from number to be accepted by \b PubNub service. */ static NSUInteger const kPNRequiredTimeTokenPrecision = 17; @@ -24,35 +23,30 @@ @interface PNNumber () #pragma mark - Misc /** - @brief Convert number to required type (taking into account type). - @discussion If number has been created from float or double, value will be adjusted to shift - floating point away. - - @return Normalized number instance. - - @since 4.2.0 + * @brief Convert number to required type (taking into account type). + * + * @discussion If number has been created from float or double, value will be adjusted to shift + * floating point away. + * + * @return Normalized number instance. */ + (NSNumber *)normalizeValue:(NSNumber *)number; /** - @brief Retrieve passed object precision information. - - @param number Reference on number for which precision should be calculated. - - @return Passed number precision. - - @since 4.2.0 + * @brief Retrieve passed object precision information. + * + * @param number Reference on number for which precision should be calculated. + * + * @return Passed number precision. */ + (NSUInteger)numberPrecision:(NSNumber *)number; /** - @brief Calculate required multiplier to adjust passed number precision to required one. - - @param precision Passed value precision. - - @return Multiplier on which original value should be multiplied during timetoken initialization. - - @since 4.2.0 + * @brief Calculate required multiplier to adjust passed number precision to required one. + * + * @param precision Passed value precision. + * + * @return Multiplier on which original value should be multiplied during timetoken initialization. */ + (NSUInteger)correctionForPrecision:(NSUInteger)precision; @@ -72,10 +66,9 @@ @implementation PNNumber #pragma mark - Conversion + (NSNumber *)timeTokenFromNumber:(NSNumber *)number { - NSNumber *timeToken = nil; + if (number) { - NSNumber *value = [self normalizeValue:number]; timeToken = @(value.unsignedLongLongValue * [self correctionForPrecision:[self numberPrecision:value]]); } @@ -87,8 +80,8 @@ + (NSNumber *)timeTokenFromNumber:(NSNumber *)number { #pragma mark - Misc + (NSNumber *)normalizeValue:(NSNumber *)number { - NSNumber *normalizeValue = number; + if (strcmp(number.objCType, @encode(float)) == 0 || strcmp(number.objCType, @encode(double)) == 0) { @@ -101,17 +94,15 @@ + (NSNumber *)normalizeValue:(NSNumber *)number { } + (NSUInteger)numberPrecision:(NSNumber *)number { - return ([number unsignedLongLongValue] > 0 ? ((NSUInteger)floor(log10(number.unsignedLongLongValue)) + 1) : 0); } + (NSUInteger)correctionForPrecision:(NSUInteger)precision { - NSUInteger precisionDiff = (precision > 0 ? (kPNRequiredTimeTokenPrecision - precision) : 0); NSUInteger correctionMultiplier = 1; + while (precisionDiff != 0) { - correctionMultiplier *= 10; precisionDiff--; } diff --git a/PubNub/Misc/PNConstants.h b/PubNub/Misc/PNConstants.h index dea42d1d6..d7d10da81 100644 --- a/PubNub/Misc/PNConstants.h +++ b/PubNub/Misc/PNConstants.h @@ -15,10 +15,10 @@ #pragma mark General information constants // Stores client library version number -static NSString * const kPNLibraryVersion = @"4.10.1"; +static NSString * const kPNLibraryVersion = @"4.11.0"; // Stores information about SDK codebase -static NSString * const kPNCommit = @"3a15d7fc9215f9f0802752ee670aa5fcb9baa3c4"; +static NSString * const kPNCommit = @"baef53be7ba1d16cabf3c7f23e91f18bc1a72b3c"; /** @brief Stores reference on unique identifier which is used to identify \b PubNub client among other diff --git a/PubNub/Misc/PNPrivateStructures.h b/PubNub/Misc/PNPrivateStructures.h index a48804b82..6647d0900 100644 --- a/PubNub/Misc/PNPrivateStructures.h +++ b/PubNub/Misc/PNPrivateStructures.h @@ -35,7 +35,12 @@ typedef NS_OPTIONS(NSUInteger, PNMessageType) { /** @brief Type which represent \c user / \c space / \c membership object. */ - PNObjectMessageType = 2 + PNObjectMessageType = 2, + + /** + @brief Type which represent \c message \c action object. + */ + PNMessageActionType = 3 }; /** @@ -43,13 +48,17 @@ typedef NS_OPTIONS(NSUInteger, PNMessageType) { @since 4.0 */ -static NSString * const PNOperationTypeStrings[41] = { +static NSString * const PNOperationTypeStrings[45] = { [PNSubscribeOperation] = @"Subscribe", [PNUnsubscribeOperation] = @"Unsubscribe", [PNPublishOperation] = @"Publish", [PNSignalOperation] = @"Signal", + [PNAddMessageActionOperation] = @"Add Message Action", + [PNRemoveMessageActionOperation] = @"Remove Message Action", + [PNFetchMessagesActionsOperation] = @"Fetch Messages Actions", [PNHistoryOperation] = @"History", [PNHistoryForChannelsOperation] = @"History for Channels", + [PNHistoryWithActionsOperation] = @"History with Actions", [PNDeleteMessageOperation] = @"Delete message from History", [PNMessageCountOperation] = @"Message count for Channels", [PNWhereNowOperation] = @"Where Now", @@ -87,9 +96,10 @@ static NSString * const PNOperationTypeStrings[41] = { [PNTimeOperation] = @"Time", }; -static NSString * const PNOperationResultClasses[41] = { +static NSString * const PNOperationResultClasses[45] = { [PNHistoryOperation] = @"PNHistoryResult", [PNHistoryForChannelsOperation] = @"PNHistoryResult", + [PNHistoryWithActionsOperation] = @"PNHistoryResult", [PNMessageCountOperation] = @"PNMessageCountResult", [PNWhereNowOperation] = @"PNPresenceWhereNowResult", [PNHereNowGlobalOperation] = @"PNPresenceGlobalHereNowResult", @@ -101,6 +111,7 @@ static NSString * const PNOperationResultClasses[41] = { [PNChannelGroupsOperation] = @"PNChannelGroupsResult", [PNChannelsForGroupOperation] = @"PNChannelGroupChannelsResult", [PNPushNotificationEnabledChannelsOperation] = @"PNAPNSEnabledChannelsResult", + [PNFetchMessagesActionsOperation] = @"PNFetchMessageActionsResult", [PNFetchUserOperation] = @"PNFetchUserResult", [PNFetchUsersOperation] = @"PNFetchUsersResult", [PNFetchSpaceOperation] = @"PNFetchSpaceResult", @@ -110,13 +121,17 @@ static NSString * const PNOperationResultClasses[41] = { [PNTimeOperation] = @"PNTimeResult", }; -static NSString * const PNOperationStatusClasses[41] = { +static NSString * const PNOperationStatusClasses[45] = { [PNSubscribeOperation] = @"PNSubscribeStatus", [PNUnsubscribeOperation] = @"PNAcknowledgmentStatus", [PNPublishOperation] = @"PNPublishStatus", [PNSignalOperation] = @"PNSignalStatus", + [PNAddMessageActionOperation] = @"PNAddMessageActionStatus", + [PNRemoveMessageActionOperation] = @"PNAcknowledgmentStatus", + [PNFetchMessagesActionsOperation] = @"PNErrorStatus", [PNHistoryOperation] = @"PNErrorStatus", [PNHistoryForChannelsOperation] = @"PNErrorStatus", + [PNHistoryWithActionsOperation] = @"PNErrorStatus", [PNDeleteMessageOperation] = @"PNAcknowledgmentStatus", [PNMessageCountOperation] = @"PNErrorStatus", [PNWhereNowOperation] = @"PNErrorStatus", diff --git a/PubNub/Misc/PNStructures.h b/PubNub/Misc/PNStructures.h index b5bf9ee37..13482d5ce 100644 --- a/PubNub/Misc/PNStructures.h +++ b/PubNub/Misc/PNStructures.h @@ -19,6 +19,7 @@ @class PNCreateUserStatus, PNUpdateUserStatus, PNFetchUserResult, PNFetchUsersResult; @class PNCreateSpaceStatus, PNUpdateSpaceStatus, PNFetchSpaceResult, PNFetchSpacesResult; @class PNManageMembershipsStatus, PNFetchMembershipsResult, PNManageMembersStatus, PNFetchMembersResult; +@class PNAddMessageActionStatus, PNFetchMessageActionsResult; #ifndef PNStructures_h #define PNStructures_h @@ -143,8 +144,10 @@ typedef void(^PNMessageCountCompletionBlock)(PNMessageCountResult * _Nullable re /** * @brief \c Create \c user completion handler block. * - * @param status Object with information about \c reate \c user request results and service + * @param status Object with information about \c create \c user request results and service * response. + * + * @since 4.10.0 */ typedef void(^PNCreateUserCompletionBlock)(PNCreateUserStatus *status); @@ -153,6 +156,8 @@ typedef void(^PNCreateUserCompletionBlock)(PNCreateUserStatus *status); * * @param status Object with information about \c update \c user request results and service * response. + * + * @since 4.10.0 */ typedef void(^PNUpdateUserCompletionBlock)(PNUpdateUserStatus *status); @@ -160,6 +165,8 @@ typedef void(^PNUpdateUserCompletionBlock)(PNUpdateUserStatus *status); * @brief \c Delete \c user completion handler block. * * @param status Object with information about \c delete \c user request results. + * + * @since 4.10.0 */ typedef void(^PNDeleteUserCompletionBlock)(PNAcknowledgmentStatus *status); @@ -168,22 +175,30 @@ typedef void(^PNDeleteUserCompletionBlock)(PNAcknowledgmentStatus *status); * * @param result Object with information about \c fetch \c user request results. * @param status Object with information about \c fetch \c user request error. + * + * @since 4.10.0 */ -typedef void(^PNFetchUserCompletionBlock)(PNFetchUserResult *result, PNErrorStatus *status); +typedef void(^PNFetchUserCompletionBlock)(PNFetchUserResult * _Nullable result, + PNErrorStatus * _Nullable status); /** * @brief \c Fetch \c all \c users completion handler block. * * @param result Object with information about \c fetch \c all \c users request results. * @param status Object with information about \c fetch \c all \c users request error. + * + * @since 4.10.0 */ -typedef void(^PNFetchUsersCompletionBlock)(PNFetchUsersResult *result, PNErrorStatus *status); +typedef void(^PNFetchUsersCompletionBlock)(PNFetchUsersResult * _Nullable result, + PNErrorStatus * _Nullable status); /** * @brief \c Create \c space completion handler block. * - * @param status Object with information about \c reate \c space request results and service + * @param status Object with information about \c create \c space request results and service * response. + * + * @since 4.10.0 */ typedef void(^PNCreateSpaceCompletionBlock)(PNCreateSpaceStatus *status); @@ -192,6 +207,8 @@ typedef void(^PNCreateSpaceCompletionBlock)(PNCreateSpaceStatus *status); * * @param status Object with information about \c update \c space request results and service * response. + * + * @since 4.10.0 */ typedef void(^PNUpdateSpaceCompletionBlock)(PNUpdateSpaceStatus *status); @@ -199,6 +216,8 @@ typedef void(^PNUpdateSpaceCompletionBlock)(PNUpdateSpaceStatus *status); * @brief \c Delete \c space completion handler block. * * @param status Object with information about \c delete \c space request results. + * + * @since 4.10.0 */ typedef void(^PNDeleteSpaceCompletionBlock)(PNAcknowledgmentStatus *status); @@ -207,22 +226,30 @@ typedef void(^PNDeleteSpaceCompletionBlock)(PNAcknowledgmentStatus *status); * * @param result Object with information about \c fetch \c space request results. * @param status Object with information about \c fetch \c user request error. + * + * @since 4.10.0 */ -typedef void(^PNFetchSpaceCompletionBlock)(PNFetchSpaceResult *result, PNErrorStatus *status); +typedef void(^PNFetchSpaceCompletionBlock)(PNFetchSpaceResult * _Nullable result, + PNErrorStatus * _Nullable status); /** * @brief \c Fetch \c all \c spaces completion handler block. * * @param result Object with information about \c fetch \c all \c spaces request results. * @param status Object with information about \c fetch \c all \c spaces request error. + * + * @since 4.10.0 */ -typedef void(^PNFetchSpacesCompletionBlock)(PNFetchSpacesResult *result, PNErrorStatus *status); +typedef void(^PNFetchSpacesCompletionBlock)(PNFetchSpacesResult * _Nullable result, + PNErrorStatus * _Nullable status); /** * @brief \c Manage \c memberships completion handler block. * * @param status Object with information about \c manage \c memberships request results and service * response. + * + * @since 4.10.0 */ typedef void(^PNManageMembershipsCompletionBlock)(PNManageMembershipsStatus *status); @@ -231,15 +258,19 @@ typedef void(^PNManageMembershipsCompletionBlock)(PNManageMembershipsStatus *sta * * @param result Object with information about \c fetch \c memberships request results. * @param status Object with information about \c fetch \c memberships request error. + * + * @since 4.10.0 */ -typedef void(^PNFetchMembershipsCompletionBlock)(PNFetchMembershipsResult *result, - PNErrorStatus *status); +typedef void(^PNFetchMembershipsCompletionBlock)(PNFetchMembershipsResult * _Nullable result, + PNErrorStatus * _Nullable status); /** * @brief \c Manage \c members completion handler block. * * @param status Object with information about \c manage \c members request results and service * response. + * + * @since 4.10.0 */ typedef void(^PNManageMembersCompletionBlock)(PNManageMembersStatus *status); @@ -248,8 +279,11 @@ typedef void(^PNManageMembersCompletionBlock)(PNManageMembersStatus *status); * * @param result Object with information about \c fetch \c members request results. * @param status Object with information about \c fetch \c members request error. + * + * @since 4.10.0 */ -typedef void(^PNFetchMembersCompletionBlock)(PNFetchMembersResult *result, PNErrorStatus *status); +typedef void(^PNFetchMembersCompletionBlock)(PNFetchMembersResult * _Nullable result, + PNErrorStatus * _Nullable status); #pragma mark - Completion blocks :: Presence @@ -356,6 +390,39 @@ typedef void(^PNChannelGroupStateCompletionBlock)(PNChannelGroupClientStateResul PNErrorStatus * _Nullable status); +#pragma mark - Completion blocks :: Message action + +/** + * @brief \c Add \c message \c action completion handler block. + * + * @param status Object with information about \c add \c message \c action request results and + * service response. + * + * @since 4.11.0 + */ +typedef void(^PNAddMessageActionCompletionBlock)(PNAddMessageActionStatus *status); + +/** + * @brief \c Remove \c message \c action completion handler block. + * + * @param status Object with information about \c remove \c message \c action request results. + * + * @since 4.11.0 + */ +typedef void(^PNRemoveMessageActionCompletionBlock)(PNAcknowledgmentStatus *status); + +/** + * @brief \c Fetch \c messages \c actions completion handler block. + * + * @param result Object with information about \c fetch \c messages \c actions request results. + * @param status Object with information about \c fetch \c messages \c actions request error. + * + * @since 4.11.0 + */ +typedef void(^PNFetchMessageActionsCompletionBlock)(PNFetchMessageActionsResult * _Nullable result, + PNErrorStatus * _Nullable status); + + #pragma mark - Completion blocks :: Time /** @@ -369,6 +436,7 @@ typedef void(^PNTimeCompletionBlock)(PNTimeResult * _Nullable result, NS_ASSUME_NONNULL_END + /** * @brief Options with possible additional \c space / \c membership fields which can be included to * response. @@ -496,7 +564,6 @@ typedef NS_ENUM(NSUInteger, PNObjectActionType) { PNDeleteObjectAction, }; - /** * @brief \b PubNub client logging levels available for manipulations. */ @@ -593,8 +660,12 @@ typedef NS_ENUM(NSInteger, PNOperationType){ PNUnsubscribeOperation, PNPublishOperation, PNSignalOperation, + PNAddMessageActionOperation, + PNRemoveMessageActionOperation, + PNFetchMessagesActionsOperation, PNHistoryOperation, PNHistoryForChannelsOperation, + PNHistoryWithActionsOperation, PNDeleteMessageOperation, PNMessageCountOperation, PNWhereNowOperation, diff --git a/PubNub/Misc/Protocols/PNObjectEventListener.h b/PubNub/Misc/Protocols/PNObjectEventListener.h index 47094b73f..6d4b29ee7 100644 --- a/PubNub/Misc/Protocols/PNObjectEventListener.h +++ b/PubNub/Misc/Protocols/PNObjectEventListener.h @@ -5,7 +5,7 @@ #pragma mark Class forward @class PNPresenceEventResult, PNSubscribeStatus, PNMessageResult, PNSignalResult, PNErrorStatus; -@class PNMembershipEventResult, PNSpaceEventResult, PNUserEventResult; +@class PNMembershipEventResult, PNMessageActionResult, PNSpaceEventResult, PNUserEventResult; NS_ASSUME_NONNULL_BEGIN @@ -24,7 +24,7 @@ NS_ASSUME_NONNULL_BEGIN @optional -#pragma mark - Message, Signals and Events handler callbacks +#pragma mark - Message, Actions, Signals and Events handler callbacks /** * @brief Notify listener about new message which arrived from one of remote data object's live feed @@ -45,6 +45,15 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)client:(PubNub *)client didReceiveSignal:(PNSignalResult *)signal; +/** + * @brief Notify listener about new \c action which arrived from one of remote data object's live + * feed on which client subscribed at this moment. + * + * @param client \b PubNub client which triggered this callback method call. + * @param action Instance which store \c action information in \c data property. + */ +- (void)client:(PubNub *)client didReceiveMessageAction:(PNMessageActionResult *)action; + /** * @brief Notify listener about new presence events which arrived from one of remote data object's * presence live feed on which client subscribed at this moment. @@ -82,7 +91,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)client:(PubNub *)client didReceiveMembershipEvent:(PNMembershipEventResult *)event; -#pragma mark - Status change handler. +#pragma mark - Status change handler. /** * @brief Notify listener about subscription state changes. diff --git a/PubNub/Network/PNNetwork.m b/PubNub/Network/PNNetwork.m index abc606e2b..0b3499526 100644 --- a/PubNub/Network/PNNetwork.m +++ b/PubNub/Network/PNNetwork.m @@ -779,16 +779,18 @@ - (BOOL)operationExpectResult:(PNOperationType)operation { dispatch_once(&onceToken, ^{ _resultExpectingOperations = @[ - @(PNHistoryOperation), @(PNHistoryForChannelsOperation), - @(PNMessageCountOperation), @(PNWhereNowOperation), - @(PNHereNowGlobalOperation), @(PNHereNowForChannelOperation), - @(PNHereNowForChannelGroupOperation), @(PNGetStateOperation), - @(PNStateForChannelOperation), @(PNStateForChannelGroupOperation), - @(PNChannelGroupsOperation), @(PNChannelsForGroupOperation), - @(PNPushNotificationEnabledChannelsOperation), @(PNTimeOperation), - @(PNFetchMembershipsOperation), @(PNFetchSpaceOperation), - @(PNFetchSpacesOperation), @(PNFetchUserOperation), @(PNFetchUsersOperation), - @(PNFetchMembersOperation)]; + @(PNHistoryOperation), @(PNHistoryForChannelsOperation), + @(PNHistoryWithActionsOperation), @(PNMessageCountOperation), + @(PNWhereNowOperation), @(PNHereNowGlobalOperation), + @(PNHereNowForChannelOperation), @(PNHereNowForChannelGroupOperation), + @(PNGetStateOperation), @(PNStateForChannelOperation), + @(PNStateForChannelGroupOperation), @(PNChannelGroupsOperation), + @(PNChannelsForGroupOperation), @(PNPushNotificationEnabledChannelsOperation), + @(PNTimeOperation), @(PNFetchMessagesActionsOperation), + @(PNFetchMembershipsOperation), @(PNFetchSpaceOperation), + @(PNFetchSpacesOperation), @(PNFetchUserOperation), + @(PNFetchUsersOperation), @(PNFetchMembersOperation) + ]; }); return [_resultExpectingOperations containsObject:@(operation)]; @@ -808,7 +810,9 @@ - (BOOL)operationExpectResult:(PNOperationType)operation { @"PNPushNotificationsAuditParser", @"PNPushNotificationsStateModificationParser", @"PNSubscribeParser",@"PNTimeParser", @"PNSpaceDataChangeParser", @"PNUserDataChangeParser", @"PNObjectsDeleteParser", @"PNMembershipsParser", - @"PNFetchSpacesParser", @"PNFetchUsersParser", @"PNMembersParser"]; + @"PNFetchSpacesParser", @"PNFetchUsersParser", @"PNMembersParser", + @"PNAddMessageActionParser", @"PNRemoveMessageActionParser", + @"PNFetchMessagesActionsParser"]; NSMutableDictionary *parsers = [NSMutableDictionary new]; for (NSString *className in parserNames) { @@ -1240,7 +1244,7 @@ - (void)handleOperation:(PNOperationType)operation if (errorData) { errorDetails = [NSJSONSerialization JSONObjectWithData:errorData - options:(NSJSONReadingOptions)0 + options:NSJSONReadingMutableContainers error:NULL]; } diff --git a/PubNub/Network/PNNetworkResponseSerializer.m b/PubNub/Network/PNNetworkResponseSerializer.m index 3189d4f0e..2a7864cd8 100644 --- a/PubNub/Network/PNNetworkResponseSerializer.m +++ b/PubNub/Network/PNNetworkResponseSerializer.m @@ -86,7 +86,7 @@ - (id)serializedResponse:(NSHTTPURLResponse *)response withData:(NSData *)data NSError *JSONSerializationError = nil; serializedResponse = [NSJSONSerialization JSONObjectWithData:data - options:(NSJSONReadingOptions)0 + options:NSJSONReadingMutableContainers error:&JSONSerializationError]; } } @@ -116,7 +116,7 @@ - (BOOL)getProcessingError:(NSError **)error ifUnableToHandleResponse:(NSHTTPURL description = [NSString stringWithFormat:@"Request completed but unexpected data type " "received in response: %@", [response MIMEType]]; statusCode = NSURLErrorCannotDecodeContentData; - } else if (response.statusCode != 200) { + } else if (response.statusCode >= 400) { // Construct error description. description = [NSString stringWithFormat:@"Request failed: %@ (%ld)", diff --git a/PubNub/Network/PNURLBuilder.m b/PubNub/Network/PNURLBuilder.m index 8e282a445..44d5b8444 100644 --- a/PubNub/Network/PNURLBuilder.m +++ b/PubNub/Network/PNURLBuilder.m @@ -12,13 +12,17 @@ /** * @brief API endpoints description basing on operation type. */ -static NSString * const PNOperationRequestTemplate[41] = { +static NSString * const PNOperationRequestTemplate[45] = { [PNSubscribeOperation] = @"/v2/subscribe/{sub-key}/{channels}/0", [PNUnsubscribeOperation] = @"/v2/presence/sub_key/{sub-key}/channel/{channels}/leave", [PNPublishOperation] = @"/publish/{pub-key}/{sub-key}/0/{channel}/0/{message}", [PNSignalOperation] = @"/signal/{pub-key}/{sub-key}/0/{channel}/0/{message}", + [PNAddMessageActionOperation] = @"/v1/message-actions/{sub-key}/channel/{channel}/message/{message-timetoken}", + [PNRemoveMessageActionOperation] = @"/v1/message-actions/{sub-key}/channel/{channel}/message/{message-timetoken}/action/{action-timetoken}", + [PNFetchMessagesActionsOperation] = @"/v1/message-actions/{sub-key}/channel/{channel}", [PNHistoryOperation] = @"/v2/history/sub-key/{sub-key}/channel/{channel}", [PNHistoryForChannelsOperation] = @"/v3/history/sub-key/{sub-key}/channel/{channels}", + [PNHistoryWithActionsOperation] = @"/v3/history-with-actions/sub-key/{sub-key}/channel/{channel}", [PNDeleteMessageOperation] = @"/v3/history/sub-key/{sub-key}/channel/{channel}", [PNMessageCountOperation] = @"/v3/history/sub-key/{sub-key}/message-counts/{channels}", [PNWhereNowOperation] = @"/v2/presence/sub-key/{sub-key}/uuid/{uuid}", @@ -40,19 +44,19 @@ [PNRemovePushNotificationsFromChannelsOperation] = @"/v1/push/sub-key/{sub-key}/devices/{token}", [PNRemoveAllPushNotificationsOperation] = @"/v1/push/sub-key/{sub-key}/devices/{token}/remove", [PNCreateUserOperation] = @"/v1/objects/{sub-key}/users", - [PNUpdateUserOperation] = @"/v1/objects/{sub-key}/users/{user_id}", - [PNDeleteUserOperation] = @"/v1/objects/{sub-key}/users/{user_id}", - [PNFetchUserOperation] = @"/v1/objects/{sub-key}/users/{user_id}", + [PNUpdateUserOperation] = @"/v1/objects/{sub-key}/users/{user-id}", + [PNDeleteUserOperation] = @"/v1/objects/{sub-key}/users/{user-id}", + [PNFetchUserOperation] = @"/v1/objects/{sub-key}/users/{user-id}", [PNFetchUsersOperation] = @"/v1/objects/{sub-key}/users", [PNCreateSpaceOperation] = @"/v1/objects/{sub-key}/spaces", - [PNUpdateSpaceOperation] = @"/v1/objects/{sub-key}/spaces/{space_id}", - [PNDeleteSpaceOperation] = @"/v1/objects/{sub-key}/spaces/{space_id}", - [PNFetchSpaceOperation] = @"/v1/objects/{sub-key}/spaces/{space_id}", + [PNUpdateSpaceOperation] = @"/v1/objects/{sub-key}/spaces/{space-id}", + [PNDeleteSpaceOperation] = @"/v1/objects/{sub-key}/spaces/{space-id}", + [PNFetchSpaceOperation] = @"/v1/objects/{sub-key}/spaces/{space-id}", [PNFetchSpacesOperation] = @"/v1/objects/{sub-key}/spaces", - [PNManageMembershipsOperation] = @"/v1/objects/{sub-key}/users/{user_id}/spaces", - [PNFetchMembershipsOperation] = @"/v1/objects/{sub-key}/users/{user_id}/spaces", - [PNManageMembersOperation] = @"/v1/objects/{sub-key}/spaces/{space_id}/users", - [PNFetchMembersOperation] = @"/v1/objects/{sub-key}/spaces/{space_id}/users", + [PNManageMembershipsOperation] = @"/v1/objects/{sub-key}/users/{user-id}/spaces", + [PNFetchMembershipsOperation] = @"/v1/objects/{sub-key}/users/{user-id}/spaces", + [PNManageMembersOperation] = @"/v1/objects/{sub-key}/spaces/{space-id}/users", + [PNFetchMembersOperation] = @"/v1/objects/{sub-key}/spaces/{space-id}/users", [PNTimeOperation] = @"/time/0" }; diff --git a/PubNub/Network/Parsers/Actions/Message/PNAddMessageActionParser.h b/PubNub/Network/Parsers/Actions/Message/PNAddMessageActionParser.h new file mode 100644 index 000000000..c43068467 --- /dev/null +++ b/PubNub/Network/Parsers/Actions/Message/PNAddMessageActionParser.h @@ -0,0 +1,24 @@ +#import "PNParser.h" + + +NS_ASSUME_NONNULL_BEGIN + +#pragma mark Interface declaration + +/** + * @brief \b PubNub service response parser for \c add \c message \c action requests. + * + * @author Serhii Mamontov + * @version 4.11.0 + * @since 4.11.0 + * @copyright © 2010-2019 PubNub, Inc. + */ +@interface PNAddMessageActionParser : NSObject + + +#pragma mark - + + +@end + +NS_ASSUME_NONNULL_END diff --git a/PubNub/Network/Parsers/Actions/Message/PNAddMessageActionParser.m b/PubNub/Network/Parsers/Actions/Message/PNAddMessageActionParser.m new file mode 100644 index 000000000..838b17faa --- /dev/null +++ b/PubNub/Network/Parsers/Actions/Message/PNAddMessageActionParser.m @@ -0,0 +1,53 @@ +/** + * @author Serhii Mamontov + * @version 4.11.0 + * @since 4.11.0 + * @copyright © 2010-2019 PubNub, Inc. + */ +#import "PNAddMessageActionParser.h" + + +#pragma mark Interface implementation + +@implementation PNAddMessageActionParser + + +#pragma mark - Identification + ++ (NSArray *)operations { + return @[@(PNAddMessageActionOperation)]; +} + ++ (BOOL)requireAdditionalData { + return NO; +} + + +#pragma mark - Parsing + ++ (NSDictionary *)parsedServiceResponse:(id)response { + NSDictionary *processedResponse = nil; + + if ([response isKindOfClass:[NSDictionary class]] && response[@"data"] && + ((NSNumber *)response[@"status"]).integerValue < 400) { + NSMutableDictionary *action = response[@"data"]; + NSMutableDictionary *actionInformation = [@{ @"action": action } mutableCopy]; + + action[@"messageTimetoken"] = @(((NSString *)action[@"messageTimetoken"]).longLongValue); + action[@"actionTimetoken"] = @(((NSString *)action[@"actionTimetoken"]).longLongValue); + + if ([response[@"error"] isKindOfClass:[NSDictionary class]]) { + actionInformation[@"information"] = response[@"error"][@"message"]; + actionInformation[@"isError"] = @YES; + } + + processedResponse = actionInformation; + } + + return processedResponse; +} + +#pragma mark - + + +@end diff --git a/PubNub/Network/Parsers/Actions/Message/PNFetchMessagesActionsParser.h b/PubNub/Network/Parsers/Actions/Message/PNFetchMessagesActionsParser.h new file mode 100644 index 000000000..84ed6e168 --- /dev/null +++ b/PubNub/Network/Parsers/Actions/Message/PNFetchMessagesActionsParser.h @@ -0,0 +1,24 @@ +#import "PNParser.h" + + +NS_ASSUME_NONNULL_BEGIN + +#pragma mark Interface declaration + +/** + * @brief \b PubNub service response parser for \c fetch \c messages \c actions requests. + * + * @author Serhii Mamontov + * @version 4.11.0 + * @since 4.11.0 + * @copyright © 2010-2019 PubNub, Inc. + */ +@interface PNFetchMessagesActionsParser : NSObject + + +#pragma mark - + + +@end + +NS_ASSUME_NONNULL_END diff --git a/PubNub/Network/Parsers/Actions/Message/PNFetchMessagesActionsParser.m b/PubNub/Network/Parsers/Actions/Message/PNFetchMessagesActionsParser.m new file mode 100644 index 000000000..cc3379920 --- /dev/null +++ b/PubNub/Network/Parsers/Actions/Message/PNFetchMessagesActionsParser.m @@ -0,0 +1,54 @@ +/** + * @author Serhii Mamontov + * @version 4.11.0 + * @since 4.11.0 + * @copyright © 2010-2019 PubNub, Inc. + */ +#import "PNFetchMessagesActionsParser.h" + + +#pragma mark Interface implementation + +@implementation PNFetchMessagesActionsParser + + +#pragma mark - Identification + ++ (NSArray *)operations { + return @[@(PNFetchMessagesActionsOperation)]; +} + ++ (BOOL)requireAdditionalData { + return NO; +} + + +#pragma mark - Parsing + ++ (NSDictionary *)parsedServiceResponse:(id)response { + NSDictionary *processedResponse = nil; + + if ([response isKindOfClass:[NSDictionary class]] && response[@"data"] && + ((NSNumber *)response[@"status"]).integerValue == 200) { + + NSMutableArray *actions = response[@"data"]; + + for (NSMutableDictionary *action in actions) { + action[@"messageTimetoken"] = @(((NSString *)action[@"messageTimetoken"]).longLongValue); + action[@"actionTimetoken"] = @(((NSString *)action[@"actionTimetoken"]).longLongValue); + } + + processedResponse = @{ + @"actions": actions, + @"start": actions.count ? actions.firstObject[@"actionTimetoken"] : @(0), + @"end": actions.count ? actions.lastObject[@"actionTimetoken"] : @(0) + }; + } + + return processedResponse; +} + +#pragma mark - + + +@end diff --git a/PubNub/Network/Parsers/Actions/Message/PNRemoveMessageActionParser.h b/PubNub/Network/Parsers/Actions/Message/PNRemoveMessageActionParser.h new file mode 100644 index 000000000..34761fa91 --- /dev/null +++ b/PubNub/Network/Parsers/Actions/Message/PNRemoveMessageActionParser.h @@ -0,0 +1,24 @@ +#import "PNParser.h" + + +NS_ASSUME_NONNULL_BEGIN + +#pragma mark Interface declaration + +/** + * @brief \b PubNub service response parser for \c remove \c message \c action requests. + * + * @author Serhii Mamontov + * @version 4.11.0 + * @since 4.11.0 + * @copyright © 2010-2019 PubNub, Inc. + */ +@interface PNRemoveMessageActionParser : NSObject + + +#pragma mark - + + +@end + +NS_ASSUME_NONNULL_END diff --git a/PubNub/Network/Parsers/Actions/Message/PNRemoveMessageActionParser.m b/PubNub/Network/Parsers/Actions/Message/PNRemoveMessageActionParser.m new file mode 100644 index 000000000..2a4079c9d --- /dev/null +++ b/PubNub/Network/Parsers/Actions/Message/PNRemoveMessageActionParser.m @@ -0,0 +1,43 @@ +/** + * @author Serhii Mamontov + * @version 4.11.0 + * @since 4.11.0 + * @copyright © 2010-2019 PubNub, Inc. + */ +#import "PNRemoveMessageActionParser.h" + + +#pragma mark Interface implementation + +@implementation PNRemoveMessageActionParser + + +#pragma mark - Identification + ++ (NSArray *)operations { + return @[@(PNRemoveMessageActionOperation)]; +} + ++ (BOOL)requireAdditionalData { + return NO; +} + + +#pragma mark - Parsing + ++ (NSDictionary *)parsedServiceResponse:(id)response { + NSDictionary *processedResponse = nil; + + if ([response isKindOfClass:[NSDictionary class]] && response[@"data"] && + ((NSNumber *)response[@"status"]).integerValue == 200) { + + processedResponse = @{ }; + } + + return processedResponse; +} + +#pragma mark - + + +@end diff --git a/PubNub/Network/Parsers/PNErrorParser.m b/PubNub/Network/Parsers/PNErrorParser.m index 966d01026..b1898bba1 100644 --- a/PubNub/Network/Parsers/PNErrorParser.m +++ b/PubNub/Network/Parsers/PNErrorParser.m @@ -54,11 +54,15 @@ + (BOOL)requireAdditionalData { NSMutableArray *detailStrings = [NSMutableArray new]; for (NSDictionary *details in errorDetails) { - NSString *detailString = [@"- " stringByAppendingString:details[@"message"]]; + NSString *detailString = @""; + + if (details[@"message"]) { + detailString = [@"- " stringByAppendingString:details[@"message"]]; + } if (details[@"location"]) { - detailString = [detailString stringByAppendingFormat:@" Location: %@", - details[@"location"]]; + detailString = [detailString stringByAppendingFormat:@"%@ Location: %@", + detailString.length ? @"" : @"-", details[@"location"]]; } [detailStrings addObject:detailString]; diff --git a/PubNub/Network/Parsers/PNHistoryParser.h b/PubNub/Network/Parsers/PNHistoryParser.h index 6eefdee2a..16e142042 100644 --- a/PubNub/Network/Parsers/PNHistoryParser.h +++ b/PubNub/Network/Parsers/PNHistoryParser.h @@ -12,7 +12,7 @@ NS_ASSUME_NONNULL_BEGIN * @discussion Handle and pre-process provided server data to fetch operation result from it. * * @author Serhii Mamontov - * @version 4.9.0 + * @version 4.11.0 * @since 4.0.0 * @copyright © 2010-2019 PubNub, Inc. */ diff --git a/PubNub/Network/Parsers/PNHistoryParser.m b/PubNub/Network/Parsers/PNHistoryParser.m index 6bf770ef1..08156dc78 100644 --- a/PubNub/Network/Parsers/PNHistoryParser.m +++ b/PubNub/Network/Parsers/PNHistoryParser.m @@ -35,6 +35,23 @@ @interface PNHistoryParser () + (NSMutableDictionary *)processedMessagesFrom:(nullable NSArray *)messages withData:(nullable NSDictionary *)additionalData; + +#pragma mark - Misc + +/** + * @brief Iterate through action senders and replace \a NSString \c actionTimetoken with \a NSNumber + * value. + * + * @param actionsForTypes List contains set of values for various \c action type (\c receip, + * \c reaction and \c custom). + * + * @since 4.11.0 + */ ++ (void)normalizeActionTimetokens:(NSArray *)actionsForTypes; + +#pragma mark - + + @end NS_ASSUME_NONNULL_END @@ -48,7 +65,11 @@ @implementation PNHistoryParser #pragma mark - Identification + (NSArray *)operations { - return @[ @(PNHistoryOperation), @(PNHistoryForChannelsOperation) ]; + return @[ + @(PNHistoryOperation), + @(PNHistoryForChannelsOperation), + @(PNHistoryWithActionsOperation) + ]; } + (BOOL)requireAdditionalData { @@ -113,16 +134,26 @@ + (NSMutableDictionary *)processedMessagesFrom:(NSArray *)messages [messages enumerateObjectsUsingBlock:^(id messageObject, __unused NSUInteger messageObjectIdx, __unused BOOL *messageObjectEnumeratorStop) { - NSNumber *timeToken = nil; + NSArray *actions = nil; + NSDictionary *metadata = nil; id message = messageObject; + NSNumber *timeToken = nil; - if ([messageObject isKindOfClass:[NSDictionary class]] && - messageObject[@"message"] && - messageObject[@"timetoken"]) { + if ([messageObject isKindOfClass:[NSDictionary class]] && messageObject[@"message"] && + (messageObject[@"timetoken"] || messageObject[@"meta"] || messageObject[@"actions"])) { timeToken = messageObject[@"timetoken"]; message = messageObject[@"message"]; + actions = messageObject[@"actions"]; + metadata = messageObject[@"meta"]; messageObject = message; + + if (![metadata isKindOfClass:[NSDictionary class]]) { + metadata = nil; + } + + timeToken = timeToken ? @(((NSString *)timeToken).longLongValue) : nil; + [self normalizeActionTimetokens:((NSDictionary *)actions).allValues]; } if (((NSString *)additionalData[@"cipherKey"]).length){ @@ -179,7 +210,24 @@ + (NSMutableDictionary *)processedMessagesFrom:(NSArray *)messages } if (message) { - message = timeToken ? @{ @"message": message, @"timetoken": timeToken } : message; + if (timeToken || metadata || actions) { + NSMutableDictionary *messageWithInfo = [@{ @"message": message } mutableCopy]; + + if (timeToken) { + messageWithInfo[@"timetoken"] = timeToken; + } + + if (metadata) { + messageWithInfo[@"metadata"] = metadata; + } + + if (actions) { + messageWithInfo[@"actions"] = actions; + } + + message = messageWithInfo; + } + [data[@"messages"] addObject:message]; } }]; @@ -187,6 +235,18 @@ + (NSMutableDictionary *)processedMessagesFrom:(NSArray *)messages return data; } +#pragma mark - Misc + ++ (void)normalizeActionTimetokens:(NSArray *)actionsForTypes { + for (NSMutableDictionary *actionValues in actionsForTypes) { + for (NSString *actionValue in actionValues) { + for (NSMutableDictionary *action in actionValues[actionValue]) { + action[@"actionTimetoken"] = @(((NSString *)action[@"actionTimetoken"]).longLongValue); + } + } + } +} + #pragma mark - diff --git a/PubNub/Network/Parsers/PNSubscribeParser.m b/PubNub/Network/Parsers/PNSubscribeParser.m index f94c0154a..545ee21e5 100644 --- a/PubNub/Network/Parsers/PNSubscribeParser.m +++ b/PubNub/Network/Parsers/PNSubscribeParser.m @@ -258,9 +258,24 @@ + (NSMutableDictionary *)messageFromData:(id)data * @param data Data which should be parsed to required 'object event' object format. * * @return Processed and parsed 'object event' object. + * + * @since 4.10.0 */ + (NSMutableDictionary *)objectFromData:(NSDictionary *)data; +/** + * @brief Parse provided data as \c action event. + * + * @param data Data which should be parsed to required 'action event' object format. + * @param envelope Object with additional information about parsed \c data. + * + * @return Processed and parsed 'action event' object. + * + * @since 4.11.0 + */ ++ (NSMutableDictionary *)actionFromData:(NSDictionary *)data + withEnvelope:(PNEnvelopeInformation *)envelope; + /** * @brief Parse provided data as presence event. * @@ -368,6 +383,11 @@ + (NSMutableDictionary *)eventFromData:(NSDictionary *)data event[@"channel"] = [PNChannel channelForPresence:event[@"channel"]]; } else if (messageType == PNObjectMessageType) { [event addEntriesFromDictionary:[self objectFromData:data[PNEventEnvelope.payload]]]; + } else if (messageType == PNMessageActionType) { + NSDictionary *action = [self actionFromData:data[PNEventEnvelope.payload] + withEnvelope:event[@"envelope"]]; + + [event addEntriesFromDictionary:action]; } else { NSDictionary *parserData = isEncryptionSupported ? additionalData : nil; @@ -467,6 +487,23 @@ + (NSMutableDictionary *)objectFromData:(NSDictionary *)data { return object; } ++ (NSMutableDictionary *)actionFromData:(NSDictionary *)data + withEnvelope:(PNEnvelopeInformation *)envelope { + + NSMutableDictionary *action = [@{ @"action": data[@"data"] } mutableCopy]; + NSString *messageTimetoken = action[@"action"][@"messageTimetoken"]; + NSString *actionTimetoken = action[@"action"][@"actionTimetoken"]; + + action[@"action"][@"messageTimetoken"] = @(messageTimetoken.longLongValue); + action[@"action"][@"actionTimetoken"] = @(actionTimetoken.longLongValue); + action[@"action"][@"uuid"] = envelope.senderIdentifier; + + NSArray *eventKeys = @[@"event", @"source", @"version"]; + [action addEntriesFromDictionary:[data dictionaryWithValuesForKeys:eventKeys]]; + + return action; +} + + (NSMutableDictionary *)presenceFromData:(NSDictionary *)data { NSMutableDictionary *presence = [NSMutableDictionary new]; diff --git a/PubNub/Network/Requests/Actions/Message/PNAddMessageActionRequest.h b/PubNub/Network/Requests/Actions/Message/PNAddMessageActionRequest.h new file mode 100644 index 000000000..2ba6c3d4e --- /dev/null +++ b/PubNub/Network/Requests/Actions/Message/PNAddMessageActionRequest.h @@ -0,0 +1,63 @@ +#import "PNBaseMessageActionRequest.h" +#import "PNStructures.h" + + +NS_ASSUME_NONNULL_BEGIN + +#pragma mark Interface declaration + +/** + * @brief \c Add \c message \c action request. + * + * @author Serhii Mamontov + * @version 4.11.0 + * @since 4.11.0 + * @copyright © 2010-2019 PubNub, Inc. + */ +@interface PNAddMessageActionRequest : PNBaseMessageActionRequest + + +#pragma mark - Information + +/** + * @brief What feature this \c message \c action represents. + * + * @note Maximum \b 15 characters. + */ +@property (nonatomic, copy) NSString *type; + +/** + * @brief Value which should be added with \c message \c action \b type. + */ +@property (nonatomic, copy) NSString *value; + + +#pragma mark - Initialization & Configuration + +/** + * @brief Create and configure \c add \c message \c action request. + * + * @param channel Name of channel which store \c message for which \c action should be added. + * @param messageTimetoken Timetoken (\b PubNub's high precision timestamp) of \c message to which + * \c action should be added. + * + * @return Configured and ready to use \c add \c message \c action request. + */ ++ (instancetype)requestWithChannel:(NSString *)channel messageTimetoken:(NSNumber *)messageTimetoken + NS_SWIFT_NAME(init(channel:messageTimetoken:)); + +/** + * @brief Forbids request initialization. + * + * @throws Interface not available exception and requirement to use provided constructor method. + * + * @return Initialized request. + */ +- (instancetype)init NS_UNAVAILABLE; + +#pragma mark - + + +@end + +NS_ASSUME_NONNULL_END diff --git a/PubNub/Network/Requests/Actions/Message/PNAddMessageActionRequest.m b/PubNub/Network/Requests/Actions/Message/PNAddMessageActionRequest.m new file mode 100644 index 000000000..ac5b3bf22 --- /dev/null +++ b/PubNub/Network/Requests/Actions/Message/PNAddMessageActionRequest.m @@ -0,0 +1,100 @@ +/** + * @author Serhii Mamontov + * @version 4.11.0 + * @since 4.11.0 + * @copyright © 2010-2019 PubNub, Inc. + */ +#import "PNBaseMessageActionRequest+Private.h" +#import "PNAddMessageActionRequest.h" +#import "PNRequest+Private.h" +#import "PNErrorCodes.h" + + +#pragma mark Interface implementation + +@implementation PNAddMessageActionRequest + + +#pragma mark - Information + +- (PNOperationType)operation { + return PNAddMessageActionOperation; +} + +- (NSString *)httpMethod { + return @"POST"; +} + + +#pragma mark - Information + +- (NSData *)bodyData { + if (!self.type || ([self.type isKindOfClass:[NSString class]] && !self.type.length)) { + self.parametersError = [self missingParameterError:@"type" + forObjectRequest:@"Message action"]; + } else if (self.type.length > 15) { + self.parametersError = [self valueTooLongErrorForParameter:@"type" + ofObjectRequest:@"Message action" + withLength:self.type.length + maximumLength:15]; + } else if (!self.value || ([self.value isKindOfClass:[NSString class]] && !self.value.length)) { + self.parametersError = [self missingParameterError:@"value" + forObjectRequest:@"Message action"]; + } + + if (self.parametersError) { + return nil; + } + + NSDictionary *actionData = @{ @"type": self.type, @"value": self.value }; + NSError *error = nil; + NSData *data = nil; + + if ([NSJSONSerialization isValidJSONObject:actionData]) { + data = [NSJSONSerialization dataWithJSONObject:actionData + options:(NSJSONWritingOptions)0 + error:&error]; + } else { + NSDictionary *errorInformation = @{ + NSLocalizedDescriptionKey: @"Unable to serialize to JSON string", + NSLocalizedFailureReasonErrorKey: @"Provided 'value' has unexpected data type." + }; + + error = [NSError errorWithDomain:NSCocoaErrorDomain + code:NSPropertyListWriteInvalidError + userInfo:errorInformation]; + } + + if (error) { + NSDictionary *errorInformation = @{ + NSLocalizedDescriptionKey: @"Message action data serialization did fail", + NSUnderlyingErrorKey: error + }; + + self.parametersError = [NSError errorWithDomain:kPNAPIErrorDomain + code:kPNAPIUnacceptableParameters + userInfo:errorInformation]; + } + + return data; +} + + +#pragma mark - Initialization & Configuration + ++ (instancetype)requestWithChannel:(NSString *)channel + messageTimetoken:(NSNumber *)messageTimetoken { + + return [[self alloc] initWithChannel:channel messageTimetoken:messageTimetoken]; +} + +- (instancetype)init { + [self throwUnavailableInitInterface]; + + return nil; +} + +#pragma mark - + + +@end diff --git a/PubNub/Network/Requests/Actions/Message/PNBaseMessageActionRequest+Private.h b/PubNub/Network/Requests/Actions/Message/PNBaseMessageActionRequest+Private.h new file mode 100644 index 000000000..073741637 --- /dev/null +++ b/PubNub/Network/Requests/Actions/Message/PNBaseMessageActionRequest+Private.h @@ -0,0 +1,50 @@ +#import "PNBaseMessageActionRequest.h" + + +NS_ASSUME_NONNULL_BEGIN + +#pragma mark Private interface declaration + +/** + * @brief Private \c base request extension to provide access to initializer. + * + * @author Serhii Mamontov + * @version 4.11.0 + * @since 4.11.0 + * @copyright © 2010-2019 PubNub, Inc. + */ +@interface PNBaseMessageActionRequest (Private) + + +#pragma mark - Information + +/** + * @brief Name of channel in which target \c message is stored. + */ +@property (nonatomic, readonly, copy) NSString *channel; + +/** + * @brief Timetoken (\b PubNub's high precision timestamp) of \c message for which \c action should + * be managed. + */ +@property (nonatomic, readonly, strong) NSNumber *messageTimetoken; + + +#pragma mark - Initialization & Configuration + +/** + * @brief Initialize \c base request. + * + * @param channel Name of channel in which target \c message is stored. + * @param messageTimetoken Timetoken of \c message for which action should be managed. + * + * @return Initialized and ready to use \c request. + */ +- (instancetype)initWithChannel:(NSString *)channel messageTimetoken:(NSNumber *)messageTimetoken; + +#pragma mark - + + +@end + +NS_ASSUME_NONNULL_END diff --git a/PubNub/Network/Requests/Actions/Message/PNBaseMessageActionRequest.h b/PubNub/Network/Requests/Actions/Message/PNBaseMessageActionRequest.h new file mode 100644 index 000000000..04f4f5ba3 --- /dev/null +++ b/PubNub/Network/Requests/Actions/Message/PNBaseMessageActionRequest.h @@ -0,0 +1,25 @@ +#import "PNStructures.h" +#import "PNRequest.h" + + +NS_ASSUME_NONNULL_BEGIN + +#pragma mark Interface declaration + +/** + * @brief Base class for all 'Message Action' API endpoints which has shared query options. + * + * @author Serhii Mamontov + * @version 4.11.0 + * @since 4.11.0 + * @copyright © 2010-2019 PubNub, Inc. + */ +@interface PNBaseMessageActionRequest : PNRequest + + +#pragma mark - + + +@end + +NS_ASSUME_NONNULL_END diff --git a/PubNub/Network/Requests/Actions/Message/PNBaseMessageActionRequest.m b/PubNub/Network/Requests/Actions/Message/PNBaseMessageActionRequest.m new file mode 100644 index 000000000..8e72fa659 --- /dev/null +++ b/PubNub/Network/Requests/Actions/Message/PNBaseMessageActionRequest.m @@ -0,0 +1,84 @@ +/** + * @author Serhii Mamontov + * @version 4.11.0 + * @since 4.11.0 + * @copyright © 2010-2019 PubNub, Inc. + */ +#import "PNBaseMessageActionRequest+Private.h" +#import "PNRequest+Private.h" +#import "PNErrorCodes.h" + + +NS_ASSUME_NONNULL_BEGIN + +#pragma mark Protected interface declaration + +@interface PNBaseMessageActionRequest () + + +#pragma mark - Information + +/** + * @brief Name of channel in which target \c message is stored. + */ +@property (nonatomic, copy) NSString *channel; + +/** + * @brief Timetoken (\b PubNub's high precision timestamp) of \c message for which \c action should + * be managed. + */ +@property (nonatomic, strong) NSNumber *messageTimetoken; + +#pragma mark - + + +@end + +NS_ASSUME_NONNULL_END + + +#pragma mark - Interface implementation + +@implementation PNBaseMessageActionRequest + + +#pragma mark - Information + +- (PNRequestParameters *)requestParameters { + PNRequestParameters *parameters = [super requestParameters]; + + if (self.parametersError) { + return parameters; + } + + [parameters addPathComponent:self.channel forPlaceholder:@"{channel}"]; + [parameters addPathComponent:self.messageTimetoken.stringValue + forPlaceholder:@"{message-timetoken}"]; + + return parameters; +} + + +#pragma mark - Initialization & Configuration + +- (instancetype)initWithChannel:(NSString *)channel messageTimetoken:(NSNumber *)messageTimetoken { + if ((self = [super init])) { + _messageTimetoken = messageTimetoken; + _channel = [channel copy]; + + if (!channel.length) { + self.parametersError = [self missingParameterError:@"channel" + forObjectRequest:@"Message action"]; + } else if (messageTimetoken.unsignedIntegerValue == 0) { + self.parametersError = [self missingParameterError:@"messageTimetoken" + forObjectRequest:@"Message action"]; + } + } + + return self; +} + +#pragma mark - + + +@end diff --git a/PubNub/Network/Requests/Actions/Message/PNFetchMessageActionsRequest.h b/PubNub/Network/Requests/Actions/Message/PNFetchMessageActionsRequest.h new file mode 100644 index 000000000..b73783d22 --- /dev/null +++ b/PubNub/Network/Requests/Actions/Message/PNFetchMessageActionsRequest.h @@ -0,0 +1,67 @@ +#import "PNStructures.h" +#import "PNRequest.h" + + +NS_ASSUME_NONNULL_BEGIN + +#pragma mark Interface declaration + +/** + * @brief \c Fetch \c message \c actions request. + * + * @author Serhii Mamontov + * @version 4.11.0 + * @since 4.11.0 + * @copyright © 2010-2019 PubNub, Inc. + */ +@interface PNFetchMessageActionsRequest : PNRequest + + +#pragma mark - Information + +/** + * @brief \c Message \c action timetoken denoting the start of the range requested. + * + * @note Return values will be less than start. + */ +@property (nonatomic, nullable, copy) NSNumber *start; + +/** + * @brief \c Message \c action timetoken denoting the end of the range requested. + * + * @note Return values will be greater than or equal to end. + */ +@property (nonatomic, nullable, copy) NSNumber *end; + +/** + * @brief Number of \c message \c actions to return in response. + */ +@property (nonatomic, assign) NSUInteger limit; + + +#pragma mark - Initialization & Configuration + +/** + * @brief Create and configure \c fetch \c message \c actions request. + * + * @param channel Name of channel from which list of \c message \c actions should be retrieved. + * + * @return Configured and ready to use \c fetch \c messages \c actions request. + */ ++ (instancetype)requestWithChannel:(NSString *)channel; + +/** + * @brief Forbids request initialization. + * + * @throws Interface not available exception and requirement to use provided constructor method. + * + * @return Initialized request. + */ +- (instancetype)init NS_UNAVAILABLE; + +#pragma mark - + + +@end + +NS_ASSUME_NONNULL_END diff --git a/PubNub/Network/Requests/Actions/Message/PNFetchMessageActionsRequest.m b/PubNub/Network/Requests/Actions/Message/PNFetchMessageActionsRequest.m new file mode 100644 index 000000000..331adf1f4 --- /dev/null +++ b/PubNub/Network/Requests/Actions/Message/PNFetchMessageActionsRequest.m @@ -0,0 +1,113 @@ +/** + * @author Serhii Mamontov + * @version 4.11.0 + * @since 4.11.0 + * @copyright © 2010-2019 PubNub, Inc. + */ +#import "PNFetchMessageActionsRequest.h" +#import "PNRequest+Private.h" + + +NS_ASSUME_NONNULL_BEGIN + +#pragma mark Protected interface declaration + +@interface PNFetchMessageActionsRequest () + + +#pragma mark - Information + +/** + * @brief Name of channel from which list of \c message \c actions should be retrieved. + */ +@property (nonatomic, copy) NSString *channel; + + +#pragma mark - Initialization & Configuration + +/** + * @brief Initialize \c fetch \c message \c actions request. + * + * @param channel Name of channel from which list of \c message \c actions should be retrieved. + * + * @return Initialized and ready to use \c fetch \c message \c actions request. + */ +- (instancetype)initWithChannel:(NSString *)channel; + +#pragma mark - + + +@end + +NS_ASSUME_NONNULL_END + + +#pragma mark - Interface implementation + +@implementation PNFetchMessageActionsRequest + + +#pragma mark - Information + +- (PNOperationType)operation { + return PNFetchMessagesActionsOperation; +} + +- (NSString *)httpMethod { + return @"GET"; +} + +- (PNRequestParameters *)requestParameters { + PNRequestParameters *parameters = [super requestParameters]; + + if (self.parametersError) { + return parameters; + } + + [parameters addPathComponent:self.channel forPlaceholder:@"{channel}"]; + + if (self.limit > 0) { + [parameters addQueryParameter:@(self.limit).stringValue forFieldName:@"limit"]; + } + + if (self.start) { + [parameters addQueryParameter:self.start.stringValue forFieldName:@"start"]; + } + + if (self.end) { + [parameters addQueryParameter:self.end.stringValue forFieldName:@"end"]; + } + + return parameters; +} + + +#pragma mark - Initialization & Configuration + ++ (instancetype)requestWithChannel:(NSString *)channel { + return [[self alloc] initWithChannel:channel]; +} + +- (instancetype)initWithChannel:(NSString *)channel { + if ((self = [super init])) { + _channel = [channel copy]; + + if (!channel.length) { + self.parametersError = [self missingParameterError:@"channel" + forObjectRequest:@"Message action"]; + } + } + + return self; +} + +- (instancetype)init { + [self throwUnavailableInitInterface]; + + return nil; +} + +#pragma mark - + + +@end diff --git a/PubNub/Network/Requests/Actions/Message/PNRemoveMessageActionRequest.h b/PubNub/Network/Requests/Actions/Message/PNRemoveMessageActionRequest.h new file mode 100644 index 000000000..0091aa14c --- /dev/null +++ b/PubNub/Network/Requests/Actions/Message/PNRemoveMessageActionRequest.h @@ -0,0 +1,56 @@ +#import "PNBaseMessageActionRequest.h" +#import "PNStructures.h" + + +NS_ASSUME_NONNULL_BEGIN + +#pragma mark Interface declaration + +/** + * @brief \c Remove \c message \c action request. + * + * @author Serhii Mamontov + * @version 4.11.0 + * @since 4.11.0 + * @copyright © 2010-2019 PubNub, Inc. + */ +@interface PNRemoveMessageActionRequest : PNBaseMessageActionRequest + + +#pragma mark - Information + +/** + * @brief \c Message \c action addition timetoken (\b PubNub's high precision timestamp). + */ +@property (nonatomic, strong) NSNumber *actionTimetoken; + + +#pragma mark - Initialization & Configuration + +/** + * @brief Create and configure \c remove \c message \c action request. + * + * @param channel Name of channel which store \c message for which \c action should be removed. + * @param messageTimetoken Timetoken (\b PubNub's high precision timestamp) of \c message from which + * \c action should be removed. + * + * @return Configured and ready to use \c remove \c message \c action request. + */ ++ (instancetype)requestWithChannel:(NSString *)channel messageTimetoken:(NSNumber *)messageTimetoken + NS_SWIFT_NAME(init(channel:messageTimetoken:)); + +/** + * @brief Forbids request initialization. + * + * @throws Interface not available exception and requirement to use provided constructor method. + * + * @return Initialized request. + */ +- (instancetype)init NS_UNAVAILABLE; + +#pragma mark - + + +@end + +NS_ASSUME_NONNULL_END diff --git a/PubNub/Network/Requests/Actions/Message/PNRemoveMessageActionRequest.m b/PubNub/Network/Requests/Actions/Message/PNRemoveMessageActionRequest.m new file mode 100644 index 000000000..a8cddf106 --- /dev/null +++ b/PubNub/Network/Requests/Actions/Message/PNRemoveMessageActionRequest.m @@ -0,0 +1,66 @@ +/** + * @author Serhii Mamontov + * @version 4.11.0 + * @since 4.11.0 + * @copyright © 2010-2019 PubNub, Inc. + */ +#import "PNBaseMessageActionRequest+Private.h" +#import "PNRemoveMessageActionRequest.h" +#import "PNRequest+Private.h" + + +#pragma nark Interface implementation + +@implementation PNRemoveMessageActionRequest + + +#pragma mark - Information + +- (PNOperationType)operation { + return PNRemoveMessageActionOperation; +} + +- (NSString *)httpMethod { + return @"DELETE"; +} + + +#pragma mark - Information + +- (PNRequestParameters *)requestParameters { + PNRequestParameters *parameters = [super requestParameters]; + + if (self.actionTimetoken.unsignedIntegerValue == 0) { + self.parametersError = [self missingParameterError:@"actionTimetoken" + forObjectRequest:@"Message action"]; + } + + if (self.parametersError) { + return parameters; + } + + [parameters addPathComponent:self.actionTimetoken.stringValue + forPlaceholder:@"{action-timetoken}"]; + + return parameters; +} + + +#pragma mark - Initialization & Configuration + ++ (instancetype)requestWithChannel:(NSString *)channel + messageTimetoken:(NSNumber *)messageTimetoken { + + return [[self alloc] initWithChannel:channel messageTimetoken:messageTimetoken]; +} + +- (instancetype)init { + [self throwUnavailableInitInterface]; + + return nil; +} + +#pragma mark - + + +@end diff --git a/PubNub/Network/Requests/Objects/PNBaseObjectsRequest.m b/PubNub/Network/Requests/Objects/PNBaseObjectsRequest.m index 28127aa2c..fb64c73df 100644 --- a/PubNub/Network/Requests/Objects/PNBaseObjectsRequest.m +++ b/PubNub/Network/Requests/Objects/PNBaseObjectsRequest.m @@ -71,7 +71,7 @@ - (PNRequestParameters *)requestParameters { } if (self.identifier) { - NSString *idKeyName = [@[self.objectType, @"id"] componentsJoinedByString:@"_"]; + NSString *idKeyName = [@[self.objectType, @"id"] componentsJoinedByString:@"-"]; NSString *placeholder = [@[@"{", idKeyName, @"}"] componentsJoinedByString:@""]; [parameters addPathComponent:self.identifier forPlaceholder:placeholder]; @@ -87,7 +87,7 @@ - (instancetype)initWithObject:(NSString *)objectType identifier:(NSString *)ide if ((self = [super init])) { _identifier = [identifier copy]; _objectType = [objectType.lowercaseString copy]; - NSString *idKey = [@[self.objectType, @"id"] componentsJoinedByString:@"_"]; + NSString *idKey = [@[self.objectType, @"id"] componentsJoinedByString:@"-"]; if (!_identifier.length) { self.parametersError = [self missingParameterError:idKey forObjectRequest:objectType]; diff --git a/PubNub/PubNub.h b/PubNub/PubNub.h index 9929ab07d..c2ad99c0e 100644 --- a/PubNub/PubNub.h +++ b/PubNub/PubNub.h @@ -35,6 +35,7 @@ // API #import "PubNub+Core.h" +#import "PubNub+MessageActions.h" #import "PubNub+ChannelGroup.h" #import "PubNub+Subscribe.h" #import "PNConfiguration.h" diff --git a/README.md b/README.md index 0d4e8c247..652bc1cf3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# PubNub 4.10.1 for iOS 9+ +# PubNub 4.11.0 for iOS 9+ [![Twitter](https://img.shields.io/badge/twitter-%40PubNub-blue.svg?style=flat)](https://twitter.com/PubNub) [![Twitter Releases](https://img.shields.io/badge/twitter-%40PubNubRelease-blue.svg?style=flat)](https://twitter.com/PubNubRelease) [![License](https://img.shields.io/github/license/pubnub/objective-c.svg?style=flat)](https://img.shields.io/github/license/pubnub/objective-c.svg) diff --git a/Tests/PNTestCase.h b/Tests/PNTestCase.h index 80f136540..d152d177d 100644 --- a/Tests/PNTestCase.h +++ b/Tests/PNTestCase.h @@ -24,6 +24,15 @@ typedef void (^PNTClientDidReceiveMessageHandler)(PubNub *client, PNMessageResul */ typedef void (^PNTClientDidReceiveSignalHandler)(PubNub *client, PNSignalResult *signal, BOOL *shouldRemove); +/** + * @brief Type used to describe block for \c action handling. + * + * @param client \b PubNub client which used delegate callback. + * @param action Object with information about received \c action. + * @param shouldRemove Whether handling block should be removed after call or not. + */ +typedef void (^PNTClientDidReceiveMessageActionHandler)(PubNub *client, PNMessageActionResult *action, BOOL *shouldRemove); + /** * @brief Type used to describe block for presence event handling. * @@ -203,6 +212,14 @@ typedef void (^PNTClientDidReceiveStatusHandler)(PubNub *client, PNSubscribeStat */ - (void)addMembershipHandlerForClient:(PubNub *)client withBlock:(PNTClientDidReceiveMembershipEventHandler)handler; +/** + * @brief Add block which will be called for each received \c action event. + * + * @param client \b PubNub client for which \c action events should be tracked. + * @param handler Block which should be called each \c action event. + */ +- (void)addActionHandlerForClient:(PubNub *)client withBlock:(PNTClientDidReceiveMessageActionHandler)handler; + /** * @brief Remove all handler for specified \c client. * diff --git a/Tests/PNTestCase.m b/Tests/PNTestCase.m index 653f049af..570b5d6a8 100644 --- a/Tests/PNTestCase.m +++ b/Tests/PNTestCase.m @@ -301,17 +301,14 @@ - (void)removeHandlers:(NSArray *)handlers ofType:(NSS } - (void)addStatusHandlerForClient:(PubNub *)client withBlock:(PNTClientDidReceiveStatusHandler)handler { - [self addHandlerOfType:@"status" forClient:client withBlock:handler]; } - (void)addMessageHandlerForClient:(PubNub *)client withBlock:(PNTClientDidReceiveMessageHandler)handler { - [self addHandlerOfType:@"message" forClient:client withBlock:handler]; } - (void)addSignalHandlerForClient:(PubNub *)client withBlock:(PNTClientDidReceiveSignalHandler)handler { - [self addHandlerOfType:@"signal" forClient:client withBlock:handler]; } @@ -332,6 +329,10 @@ - (void)addMembershipHandlerForClient:(PubNub *)client withBlock:(PNTClientDidRe [self addHandlerOfType:@"membership" forClient:client withBlock:handler]; } +- (void)addActionHandlerForClient:(PubNub *)client withBlock:(PNTClientDidReceiveMessageActionHandler)handler { + [self addHandlerOfType:@"actions" forClient:client withBlock:handler]; +} + - (void)removeAllHandlersForClient:(PubNub *)client { NSString *instanceID = client.instanceID; @@ -559,6 +560,23 @@ - (void)client:(PubNub *)client didReceiveSignal:(PNSignalResult *)signal { [self removeHandlers:handlersForRemoval ofType:@"signal" forClient:client]; } +- (void)client:(PubNub *)client didReceiveMessageAction:(PNMessageActionResult *)action { + NSMutableArray *handlersForRemoval = [NSMutableArray new]; + NSArray *handlers = [self handlersOfType:@"actions" forClient:client]; + + for (PNTClientCallbackHandler handler in handlers) { + BOOL shouldRemoved = NO; + + handler(client, action, &shouldRemoved); + + if (shouldRemoved) { + [handlersForRemoval addObject:handler]; + } + } + + [self removeHandlers:handlersForRemoval ofType:@"actions" forClient:client]; +} + - (void)client:(PubNub *)client didReceivePresenceEvent:(PNPresenceEventResult *)event { NSMutableArray *handlersForRemoval = [NSMutableArray new]; diff --git a/Tests/PubNub Tests.xcodeproj/project.pbxproj b/Tests/PubNub Tests.xcodeproj/project.pbxproj index d5473dc7e..207214b31 100644 --- a/Tests/PubNub Tests.xcodeproj/project.pbxproj +++ b/Tests/PubNub Tests.xcodeproj/project.pbxproj @@ -67,9 +67,6 @@ 7933B4DA22D666B70097458F /* PNSignalTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 7933B4D922D666B70097458F /* PNSignalTest.m */; }; 7933B4DB22D666B70097458F /* PNSignalTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 7933B4D922D666B70097458F /* PNSignalTest.m */; }; 7933B4DC22D666B70097458F /* PNSignalTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 7933B4D922D666B70097458F /* PNSignalTest.m */; }; - 7933B4DF22D6AC680097458F /* PNSignalIntegrationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 7933B4DE22D6AC680097458F /* PNSignalIntegrationTest.m */; }; - 7933B4E022D6AC680097458F /* PNSignalIntegrationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 7933B4DE22D6AC680097458F /* PNSignalIntegrationTest.m */; }; - 7933B4E122D6AC680097458F /* PNSignalIntegrationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 7933B4DE22D6AC680097458F /* PNSignalIntegrationTest.m */; }; 793C2D26228C5468000E7A35 /* PNPublishCompressedTests.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 793C2D25228C5467000E7A35 /* PNPublishCompressedTests.bundle */; }; 793C2D27228C5468000E7A35 /* PNPublishCompressedTests.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 793C2D25228C5467000E7A35 /* PNPublishCompressedTests.bundle */; }; 793C2D28228C5468000E7A35 /* PNPublishCompressedTests.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 793C2D25228C5467000E7A35 /* PNPublishCompressedTests.bundle */; }; @@ -94,7 +91,6 @@ 794B6B8A228D492400489D37 /* PNTimeTokenTests.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 794B6B89228D492400489D37 /* PNTimeTokenTests.bundle */; }; 794B6B8B228D492400489D37 /* PNTimeTokenTests.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 794B6B89228D492400489D37 /* PNTimeTokenTests.bundle */; }; 794B6B8C228D492400489D37 /* PNTimeTokenTests.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 794B6B89228D492400489D37 /* PNTimeTokenTests.bundle */; }; - 794C300A22B2C64000FE054B /* PNAPNSIntegrationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 794C300922B2C64000FE054B /* PNAPNSIntegrationTest.m */; }; 795CED0A1BCF002900D421BB /* PNHeartbeatTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 795CED091BCF002900D421BB /* PNHeartbeatTests.swift */; }; 796502F31C0E3709001F7782 /* PNDeviceIndependentMatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 51A6E5FF1BE421DC00C20B77 /* PNDeviceIndependentMatcher.m */; }; 797A333C22C32B3D0036D28C /* PNSubscribeIntegrationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 797A333B22C32B3D0036D28C /* PNSubscribeIntegrationTest.m */; }; @@ -207,9 +203,6 @@ 79A3E45F2215A42C00F2ADB9 /* PNMessageCountAPICallBuilderTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 79A3E45B2215A42C00F2ADB9 /* PNMessageCountAPICallBuilderTest.m */; }; 79A3E4602215A42C00F2ADB9 /* PNMessageCountAPICallBuilderTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 79A3E45B2215A42C00F2ADB9 /* PNMessageCountAPICallBuilderTest.m */; }; 79A3E4612215A42C00F2ADB9 /* PNMessageCountAPICallBuilderTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 79A3E45B2215A42C00F2ADB9 /* PNMessageCountAPICallBuilderTest.m */; }; - 79A3E4642215A47600F2ADB9 /* PNPubNubHistoryIntegrationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 79A3E4632215A47600F2ADB9 /* PNPubNubHistoryIntegrationTest.m */; }; - 79A3E4652215A47600F2ADB9 /* PNPubNubHistoryIntegrationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 79A3E4632215A47600F2ADB9 /* PNPubNubHistoryIntegrationTest.m */; }; - 79A3E4662215A47600F2ADB9 /* PNPubNubHistoryIntegrationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 79A3E4632215A47600F2ADB9 /* PNPubNubHistoryIntegrationTest.m */; }; 79C288D8228C038E004FD5C5 /* keysset.plist in Resources */ = {isa = PBXBuildFile; fileRef = 79C288D7228C038E004FD5C5 /* keysset.plist */; }; 79C288D9228C038E004FD5C5 /* keysset.plist in Resources */ = {isa = PBXBuildFile; fileRef = 79C288D7228C038E004FD5C5 /* keysset.plist */; }; 79E198C31CE3DCF600F36216 /* PNNumberTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 79E198C21CE3DCF600F36216 /* PNNumberTests.m */; }; @@ -281,16 +274,34 @@ A52DC1E123059898001F20B0 /* PNMembershipIntegrationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A52DC1DF23059898001F20B0 /* PNMembershipIntegrationTest.m */; }; A52DC1E223059898001F20B0 /* PNMembershipIntegrationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A52DC1DF23059898001F20B0 /* PNMembershipIntegrationTest.m */; }; A52DC1F4230811E8001F20B0 /* PNSpaceIntegrationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A525EA72230483EF00ABFDC2 /* PNSpaceIntegrationTest.m */; }; + A54F4173231FABC3007A368A /* PNMessageActionsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A54F4172231FABC3007A368A /* PNMessageActionsTest.m */; }; + A54F4174231FABC3007A368A /* PNMessageActionsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A54F4172231FABC3007A368A /* PNMessageActionsTest.m */; }; + A54F4175231FABC3007A368A /* PNMessageActionsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A54F4172231FABC3007A368A /* PNMessageActionsTest.m */; }; A55260532309555800CB4ACF /* PNUserIntegrationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A525EA642303EF7200ABFDC2 /* PNUserIntegrationTest.m */; }; A55260572309DF1900CB4ACF /* PNMemberIntegrationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A55260552309DF1900CB4ACF /* PNMemberIntegrationTest.m */; }; A55260582309DF1900CB4ACF /* PNMemberIntegrationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A55260552309DF1900CB4ACF /* PNMemberIntegrationTest.m */; }; A552605A2309EF7700CB4ACF /* PNMemberIntegrationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A55260552309DF1900CB4ACF /* PNMemberIntegrationTest.m */; }; + A55A64982328E36A0064E8E4 /* PNHistoryIntegrationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A55A64912328E36A0064E8E4 /* PNHistoryIntegrationTest.m */; }; + A55A64992328E36A0064E8E4 /* PNHistoryIntegrationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A55A64912328E36A0064E8E4 /* PNHistoryIntegrationTest.m */; }; + A55A649A2328E36A0064E8E4 /* PNHistoryIntegrationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A55A64912328E36A0064E8E4 /* PNHistoryIntegrationTest.m */; }; + A55A649B2328E36A0064E8E4 /* PNAPNSIntegrationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A55A64932328E36A0064E8E4 /* PNAPNSIntegrationTest.m */; }; + A55A649C2328E36A0064E8E4 /* PNAPNSIntegrationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A55A64932328E36A0064E8E4 /* PNAPNSIntegrationTest.m */; }; + A55A649D2328E36A0064E8E4 /* PNAPNSIntegrationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A55A64932328E36A0064E8E4 /* PNAPNSIntegrationTest.m */; }; + A55A649E2328E36A0064E8E4 /* PNSignalIntegrationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A55A64952328E36A0064E8E4 /* PNSignalIntegrationTest.m */; }; + A55A649F2328E36A0064E8E4 /* PNSignalIntegrationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A55A64952328E36A0064E8E4 /* PNSignalIntegrationTest.m */; }; + A55A64A02328E36A0064E8E4 /* PNSignalIntegrationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A55A64952328E36A0064E8E4 /* PNSignalIntegrationTest.m */; }; + A55A64A12328E36A0064E8E4 /* PNMessageActionsIntegrationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A55A64972328E36A0064E8E4 /* PNMessageActionsIntegrationTest.m */; }; + A55A64A22328E36A0064E8E4 /* PNMessageActionsIntegrationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A55A64972328E36A0064E8E4 /* PNMessageActionsIntegrationTest.m */; }; + A55A64A32328E36A0064E8E4 /* PNMessageActionsIntegrationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A55A64972328E36A0064E8E4 /* PNMessageActionsIntegrationTest.m */; }; A55A889422FD8398002D0A72 /* PNUserObjectsAPICallBuilderTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A55A889322FD8397002D0A72 /* PNUserObjectsAPICallBuilderTest.m */; }; A55A889522FD8398002D0A72 /* PNUserObjectsAPICallBuilderTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A55A889322FD8397002D0A72 /* PNUserObjectsAPICallBuilderTest.m */; }; A55A889622FD8398002D0A72 /* PNUserObjectsAPICallBuilderTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A55A889322FD8397002D0A72 /* PNUserObjectsAPICallBuilderTest.m */; }; A55A889922FD83B9002D0A72 /* PNUserObjectsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A55A889822FD83B9002D0A72 /* PNUserObjectsTest.m */; }; A55A889A22FD83B9002D0A72 /* PNUserObjectsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A55A889822FD83B9002D0A72 /* PNUserObjectsTest.m */; }; A55A889B22FD83B9002D0A72 /* PNUserObjectsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A55A889822FD83B9002D0A72 /* PNUserObjectsTest.m */; }; + A55BCD7D231E80220019DB68 /* PNMessageActionsAPICallBuilderTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A55BCD7C231E80220019DB68 /* PNMessageActionsAPICallBuilderTest.m */; }; + A55BCD7E231E80220019DB68 /* PNMessageActionsAPICallBuilderTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A55BCD7C231E80220019DB68 /* PNMessageActionsAPICallBuilderTest.m */; }; + A55BCD7F231E80220019DB68 /* PNMessageActionsAPICallBuilderTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A55BCD7C231E80220019DB68 /* PNMessageActionsAPICallBuilderTest.m */; }; A589756522FE711C0093BD9A /* PNSpaceObjectsAPICallBuilderTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A589756422FE711C0093BD9A /* PNSpaceObjectsAPICallBuilderTest.m */; }; A589756622FE711C0093BD9A /* PNSpaceObjectsAPICallBuilderTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A589756422FE711C0093BD9A /* PNSpaceObjectsAPICallBuilderTest.m */; }; A589756722FE711C0093BD9A /* PNSpaceObjectsAPICallBuilderTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A589756422FE711C0093BD9A /* PNSpaceObjectsAPICallBuilderTest.m */; }; @@ -324,7 +335,6 @@ 6690703E1216498F8F228D12 /* Pods_OSX_ObjC_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_OSX_ObjC_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 6B9C16A4C435306BAA05BD00 /* Pods_iOS_ObjC_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_iOS_ObjC_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7933B4D922D666B70097458F /* PNSignalTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PNSignalTest.m; sourceTree = ""; }; - 7933B4DE22D6AC680097458F /* PNSignalIntegrationTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PNSignalIntegrationTest.m; sourceTree = ""; }; 793C2D25228C5467000E7A35 /* PNPublishCompressedTests.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = PNPublishCompressedTests.bundle; path = Fixtures/PNPublishCompressedTests.bundle; sourceTree = ""; }; 793C2D2D228C7B5C000E7A35 /* PNPublishTests.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = PNPublishTests.bundle; path = Fixtures/PNPublishTests.bundle; sourceTree = ""; }; 793C2D31228C7B72000E7A35 /* PNPublishWithHistoryTests.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = PNPublishWithHistoryTests.bundle; path = Fixtures/PNPublishWithHistoryTests.bundle; sourceTree = ""; }; @@ -333,7 +343,6 @@ 794B5666228D4AA2007AB94F /* PNAPNSTests.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = PNAPNSTests.bundle; path = Fixtures/PNAPNSTests.bundle; sourceTree = ""; }; 794B566A228D4B19007AB94F /* PNPublishSizeOfMessage.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = PNPublishSizeOfMessage.bundle; path = Fixtures/PNPublishSizeOfMessage.bundle; sourceTree = ""; }; 794B6B89228D492400489D37 /* PNTimeTokenTests.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = PNTimeTokenTests.bundle; path = Fixtures/PNTimeTokenTests.bundle; sourceTree = ""; }; - 794C300922B2C64000FE054B /* PNAPNSIntegrationTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PNAPNSIntegrationTest.m; sourceTree = ""; }; 795CED091BCF002900D421BB /* PNHeartbeatTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PNHeartbeatTests.swift; sourceTree = ""; }; 797A333B22C32B3D0036D28C /* PNSubscribeIntegrationTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PNSubscribeIntegrationTest.m; sourceTree = ""; }; 797BDD021C1F5091006EF006 /* watchOS ObjC Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "watchOS ObjC Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -356,7 +365,6 @@ 79A3E4512215806D00F2ADB9 /* PNTestCase.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PNTestCase.m; sourceTree = ""; }; 79A3E4582215A42C00F2ADB9 /* PNMessageCountTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PNMessageCountTest.m; sourceTree = ""; }; 79A3E45B2215A42C00F2ADB9 /* PNMessageCountAPICallBuilderTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PNMessageCountAPICallBuilderTest.m; sourceTree = ""; }; - 79A3E4632215A47600F2ADB9 /* PNPubNubHistoryIntegrationTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PNPubNubHistoryIntegrationTest.m; sourceTree = ""; }; 79C288D7228C038E004FD5C5 /* keysset.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = keysset.plist; sourceTree = ""; }; 79E198C21CE3DCF600F36216 /* PNNumberTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PNNumberTests.m; path = Tests/PNNumberTests.m; sourceTree = ""; }; 79EF04911B4EAAB7007478CB /* PNAPNSTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PNAPNSTests.m; path = Tests/PNAPNSTests.m; sourceTree = ""; }; @@ -420,9 +428,15 @@ A525EA6923047CB300ABFDC2 /* PNObjectsTestCase.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PNObjectsTestCase.m; sourceTree = ""; }; A525EA72230483EF00ABFDC2 /* PNSpaceIntegrationTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PNSpaceIntegrationTest.m; sourceTree = ""; }; A52DC1DF23059898001F20B0 /* PNMembershipIntegrationTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PNMembershipIntegrationTest.m; sourceTree = ""; }; + A54F4172231FABC3007A368A /* PNMessageActionsTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PNMessageActionsTest.m; sourceTree = ""; }; A55260552309DF1900CB4ACF /* PNMemberIntegrationTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PNMemberIntegrationTest.m; sourceTree = ""; }; + A55A64912328E36A0064E8E4 /* PNHistoryIntegrationTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PNHistoryIntegrationTest.m; sourceTree = ""; }; + A55A64932328E36A0064E8E4 /* PNAPNSIntegrationTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PNAPNSIntegrationTest.m; sourceTree = ""; }; + A55A64952328E36A0064E8E4 /* PNSignalIntegrationTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PNSignalIntegrationTest.m; sourceTree = ""; }; + A55A64972328E36A0064E8E4 /* PNMessageActionsIntegrationTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PNMessageActionsIntegrationTest.m; sourceTree = ""; }; A55A889322FD8397002D0A72 /* PNUserObjectsAPICallBuilderTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PNUserObjectsAPICallBuilderTest.m; sourceTree = ""; }; A55A889822FD83B9002D0A72 /* PNUserObjectsTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PNUserObjectsTest.m; sourceTree = ""; }; + A55BCD7C231E80220019DB68 /* PNMessageActionsAPICallBuilderTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PNMessageActionsAPICallBuilderTest.m; sourceTree = ""; }; A589756422FE711C0093BD9A /* PNSpaceObjectsAPICallBuilderTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PNSpaceObjectsAPICallBuilderTest.m; sourceTree = ""; }; A5F62C3323017E88001CC2A5 /* PNMembershipsObjectsAPICallBuilderTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PNMembershipsObjectsAPICallBuilderTest.m; sourceTree = ""; }; A5F62C3723017E95001CC2A5 /* PNMembersObjectsAPICallBuilderTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PNMembersObjectsAPICallBuilderTest.m; sourceTree = ""; }; @@ -707,6 +721,7 @@ children = ( 79A3E4592215A42C00F2ADB9 /* Interfaces */, A55A889722FD83B9002D0A72 /* Objects */, + A55BCD78231DC1950019DB68 /* Actions */, 7933B4D822D6666D0097458F /* Publish */, 79A3E4572215A42C00F2ADB9 /* History */, ); @@ -725,6 +740,7 @@ isa = PBXGroup; children = ( A55A889222FD8397002D0A72 /* Objects */, + A55BCD7A231DC1A50019DB68 /* Actions */, 7933B4DD22D6AC370097458F /* Publish */, 79A3E45A2215A42C00F2ADB9 /* History */, ); @@ -742,11 +758,12 @@ 79A3E4622215A44000F2ADB9 /* Integration */ = { isa = PBXGroup; children = ( + A55A64922328E36A0064E8E4 /* Push Notifications */, A525EA5F2303EF2400ABFDC2 /* Objects */, - 79A3E4632215A47600F2ADB9 /* PNPubNubHistoryIntegrationTest.m */, + A55A64962328E36A0064E8E4 /* Actions */, + A55A64902328E36A0064E8E4 /* History */, + A55A64942328E36A0064E8E4 /* Publish */, 797A333B22C32B3D0036D28C /* PNSubscribeIntegrationTest.m */, - 7933B4DE22D6AC680097458F /* PNSignalIntegrationTest.m */, - 794C300922B2C64000FE054B /* PNAPNSIntegrationTest.m */, ); name = Integration; path = Tests/Integration; @@ -812,6 +829,38 @@ path = Objects; sourceTree = ""; }; + A55A64902328E36A0064E8E4 /* History */ = { + isa = PBXGroup; + children = ( + A55A64912328E36A0064E8E4 /* PNHistoryIntegrationTest.m */, + ); + path = History; + sourceTree = ""; + }; + A55A64922328E36A0064E8E4 /* Push Notifications */ = { + isa = PBXGroup; + children = ( + A55A64932328E36A0064E8E4 /* PNAPNSIntegrationTest.m */, + ); + path = "Push Notifications"; + sourceTree = ""; + }; + A55A64942328E36A0064E8E4 /* Publish */ = { + isa = PBXGroup; + children = ( + A55A64952328E36A0064E8E4 /* PNSignalIntegrationTest.m */, + ); + path = Publish; + sourceTree = ""; + }; + A55A64962328E36A0064E8E4 /* Actions */ = { + isa = PBXGroup; + children = ( + A56FAF1323322B6C0072ADD6 /* Messages */, + ); + path = Actions; + sourceTree = ""; + }; A55A889222FD8397002D0A72 /* Objects */ = { isa = PBXGroup; children = ( @@ -834,6 +883,46 @@ path = Objects; sourceTree = ""; }; + A55BCD78231DC1950019DB68 /* Actions */ = { + isa = PBXGroup; + children = ( + A56FAF12233226EA0072ADD6 /* Messages */, + ); + path = Actions; + sourceTree = ""; + }; + A55BCD7A231DC1A50019DB68 /* Actions */ = { + isa = PBXGroup; + children = ( + A56FAF11233225450072ADD6 /* Messages */, + ); + path = Actions; + sourceTree = ""; + }; + A56FAF11233225450072ADD6 /* Messages */ = { + isa = PBXGroup; + children = ( + A55BCD7C231E80220019DB68 /* PNMessageActionsAPICallBuilderTest.m */, + ); + path = Messages; + sourceTree = ""; + }; + A56FAF12233226EA0072ADD6 /* Messages */ = { + isa = PBXGroup; + children = ( + A54F4172231FABC3007A368A /* PNMessageActionsTest.m */, + ); + path = Messages; + sourceTree = ""; + }; + A56FAF1323322B6C0072ADD6 /* Messages */ = { + isa = PBXGroup; + children = ( + A55A64972328E36A0064E8E4 /* PNMessageActionsIntegrationTest.m */, + ); + path = Messages; + sourceTree = ""; + }; AC2AF694F0C5FDDADDE978EA /* Frameworks */ = { isa = PBXGroup; children = ( @@ -1337,22 +1426,26 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + A55A64A02328E36A0064E8E4 /* PNSignalIntegrationTest.m in Sources */, + A54F4175231FABC3007A368A /* PNMessageActionsTest.m in Sources */, 517A9EE11BE3185800FAA43A /* PNClientStateChannelTests.m in Sources */, 517A9EF41BE318B700FAA43A /* NSString+PNTest.m in Sources */, 517A9EF21BE3189900FAA43A /* PNBasicClientCryptTestCase.m in Sources */, A525EA672303EF7200ABFDC2 /* PNUserIntegrationTest.m in Sources */, A525EA562302A77A00ABFDC2 /* PNMembershipObjectsTest.m in Sources */, A525EA522301E69300ABFDC2 /* PNSpaceObjectsTest.m in Sources */, + A55A649A2328E36A0064E8E4 /* PNHistoryIntegrationTest.m in Sources */, 517A9EE71BE3185800FAA43A /* PNPublishSizeOfMessage.m in Sources */, + A55A64A32328E36A0064E8E4 /* PNMessageActionsIntegrationTest.m in Sources */, 517A9EEA1BE3185800FAA43A /* PNPublishWithMobilePayloadTests.m in Sources */, 7933B4DC22D666B70097458F /* PNSignalTest.m in Sources */, 517A9EEE1BE3185800FAA43A /* PNUnsubscribeTests.m in Sources */, 517A9EF01BE3189300FAA43A /* PNBasicClientTestCase.m in Sources */, 517A9EE01BE3185800FAA43A /* PNClientStateChannelGroupTests.m in Sources */, - 79A3E4662215A47600F2ADB9 /* PNPubNubHistoryIntegrationTest.m in Sources */, 517A9EF51BE318B900FAA43A /* NSArray+PNTest.m in Sources */, A52DC1E223059898001F20B0 /* PNMembershipIntegrationTest.m in Sources */, 517A9EEC1BE3185800FAA43A /* PNSubscribeToPresenceChannelsTests.m in Sources */, + A55BCD7F231E80220019DB68 /* PNMessageActionsAPICallBuilderTest.m in Sources */, 79A3E4542215806D00F2ADB9 /* PNTestCase.m in Sources */, 517A9EF31BE318B400FAA43A /* NSDictionary+PNTest.m in Sources */, 517A9EDC1BE3185800FAA43A /* PNChannelGroupSubscribeTests.m in Sources */, @@ -1375,7 +1468,6 @@ A525EA6C23047CB300ABFDC2 /* PNObjectsTestCase.m in Sources */, 517A9EDE1BE3185800FAA43A /* PNChannelGroupUnsubscribeTests.m in Sources */, 517A9EE61BE3185800FAA43A /* PNPublishCompressedTests.m in Sources */, - 7933B4E122D6AC680097458F /* PNSignalIntegrationTest.m in Sources */, 517A9EDD1BE3185800FAA43A /* PNChannelGroupTests.m in Sources */, 517A9EE91BE3185800FAA43A /* PNPublishWithHistoryTests.m in Sources */, A525EA5A2302A79000ABFDC2 /* PNMemberObjectsTest.m in Sources */, @@ -1387,6 +1479,7 @@ 517A9EE41BE3185800FAA43A /* PNPresenceTests.m in Sources */, 517A9EED1BE3185800FAA43A /* PNTimeTokenTests.m in Sources */, 517A9EDF1BE3185800FAA43A /* PNClientConfigurationTests.m in Sources */, + A55A649D2328E36A0064E8E4 /* PNAPNSIntegrationTest.m in Sources */, 79E20D2E1C8B1C64001BC9CC /* PNBasicPresenceTestCase.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1395,11 +1488,11 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 794C300A22B2C64000FE054B /* PNAPNSIntegrationTest.m in Sources */, 79EF04B21B4EAAB7007478CB /* PNPublishWithMobilePayloadTests.m in Sources */, 9652F3E51BA31F7D001E940A /* PNConfigurationChiperKeyTests.m in Sources */, 79EF04B01B4EAAB7007478CB /* PNPublishTests.m in Sources */, A525EA6A23047CB300ABFDC2 /* PNObjectsTestCase.m in Sources */, + A54F4173231FABC3007A368A /* PNMessageActionsTest.m in Sources */, 79EF04B51B4EAAB7007478CB /* PNTimeTokenTests.m in Sources */, 79EF04B61B4EAAB7007478CB /* PNUnsubscribeTests.m in Sources */, 79EF04AE1B4EAAB7007478CB /* PNPublishCompressedTests.m in Sources */, @@ -1414,6 +1507,7 @@ 79EF04B31B4EAAB7007478CB /* PNSubscribeTests.m in Sources */, 79EF04BC1B4EAAE4007478CB /* PNBasicSubscribeTestCase.m in Sources */, 79E198C31CE3DCF600F36216 /* PNNumberTests.m in Sources */, + A55A649E2328E36A0064E8E4 /* PNSignalIntegrationTest.m in Sources */, 79EF04A51B4EAAB7007478CB /* PNChannelGroupSubscribeTests.m in Sources */, 799CE2F91C45B9FD00AAEBDC /* PNFilteringSubscribeTests.m in Sources */, 9652F3E21BA31D9A001E940A /* PNBasicClientCryptTestCase.m in Sources */, @@ -1434,13 +1528,12 @@ 79A3E45C2215A42C00F2ADB9 /* PNMessageCountTest.m in Sources */, 79EF04C21B4EAB01007478CB /* NSString+PNTest.m in Sources */, 79EF04B11B4EAAB7007478CB /* PNPublishWithHistoryTests.m in Sources */, - 79A3E4642215A47600F2ADB9 /* PNPubNubHistoryIntegrationTest.m in Sources */, 79EF04AD1B4EAAB7007478CB /* PNPresenceTests.m in Sources */, A525EA542302A77A00ABFDC2 /* PNMembershipObjectsTest.m in Sources */, 79EF04A61B4EAAB7007478CB /* PNChannelGroupTests.m in Sources */, 96F0239E1B580D0000C4A581 /* NSArray+PNTest.m in Sources */, A589756522FE711C0093BD9A /* PNSpaceObjectsAPICallBuilderTest.m in Sources */, - 7933B4DF22D6AC680097458F /* PNSignalIntegrationTest.m in Sources */, + A55A649B2328E36A0064E8E4 /* PNAPNSIntegrationTest.m in Sources */, A525EA502301E69300ABFDC2 /* PNSpaceObjectsTest.m in Sources */, 79EF04AF1B4EAAB7007478CB /* PNPublishSizeOfMessage.m in Sources */, 79A3E4522215806D00F2ADB9 /* PNTestCase.m in Sources */, @@ -1448,7 +1541,10 @@ 79EF04A81B4EAAB7007478CB /* PNClientConfigurationTests.m in Sources */, A55A889422FD8398002D0A72 /* PNUserObjectsAPICallBuilderTest.m in Sources */, A5F62C3423017E88001CC2A5 /* PNMembershipsObjectsAPICallBuilderTest.m in Sources */, + A55A64A12328E36A0064E8E4 /* PNMessageActionsIntegrationTest.m in Sources */, + A55A64982328E36A0064E8E4 /* PNHistoryIntegrationTest.m in Sources */, 7933B4DA22D666B70097458F /* PNSignalTest.m in Sources */, + A55BCD7D231E80220019DB68 /* PNMessageActionsAPICallBuilderTest.m in Sources */, 799CE2F71C45B95400AAEBDC /* PNChannelGroupFilteringSubscribeTests.m in Sources */, A55260532309555800CB4ACF /* PNUserIntegrationTest.m in Sources */, 79A3E45F2215A42C00F2ADB9 /* PNMessageCountAPICallBuilderTest.m in Sources */, @@ -1512,22 +1608,26 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + A55A649F2328E36A0064E8E4 /* PNSignalIntegrationTest.m in Sources */, + A54F4174231FABC3007A368A /* PNMessageActionsTest.m in Sources */, 797BDD071C1F5176006EF006 /* PNPublishWithMobilePayloadTests.m in Sources */, 797BDD081C1F5176006EF006 /* PNConfigurationChiperKeyTests.m in Sources */, 797BDD091C1F5176006EF006 /* PNPublishTests.m in Sources */, A525EA662303EF7200ABFDC2 /* PNUserIntegrationTest.m in Sources */, A525EA552302A77A00ABFDC2 /* PNMembershipObjectsTest.m in Sources */, A525EA512301E69300ABFDC2 /* PNSpaceObjectsTest.m in Sources */, + A55A64992328E36A0064E8E4 /* PNHistoryIntegrationTest.m in Sources */, 797BDD0A1C1F5176006EF006 /* PNTimeTokenTests.m in Sources */, + A55A64A22328E36A0064E8E4 /* PNMessageActionsIntegrationTest.m in Sources */, 797BDD0B1C1F5176006EF006 /* PNUnsubscribeTests.m in Sources */, 7933B4DB22D666B70097458F /* PNSignalTest.m in Sources */, 797BDD0C1C1F5176006EF006 /* PNPublishCompressedTests.m in Sources */, 797BDD0D1C1F5176006EF006 /* PNSubscribeToPresenceChannelsTests.m in Sources */, 797BDD0E1C1F5176006EF006 /* NSDictionary+PNTest.m in Sources */, - 79A3E4652215A47600F2ADB9 /* PNPubNubHistoryIntegrationTest.m in Sources */, 797BDD0F1C1F5176006EF006 /* PNAPNSTests.m in Sources */, A52DC1E123059898001F20B0 /* PNMembershipIntegrationTest.m in Sources */, 797BDD101C1F5176006EF006 /* PNHistoryTests.m in Sources */, + A55BCD7E231E80220019DB68 /* PNMessageActionsAPICallBuilderTest.m in Sources */, 79A3E4532215806D00F2ADB9 /* PNTestCase.m in Sources */, 797BDD111C1F5176006EF006 /* PNSubscribeTests.m in Sources */, 797BDD121C1F5176006EF006 /* PNBasicSubscribeTestCase.m in Sources */, @@ -1550,7 +1650,6 @@ A525EA6B23047CB300ABFDC2 /* PNObjectsTestCase.m in Sources */, 797BDD1A1C1F5176006EF006 /* PNBasicClientTestCase.m in Sources */, 797BDD1B1C1F5176006EF006 /* NSString+PNTest.m in Sources */, - 7933B4E022D6AC680097458F /* PNSignalIntegrationTest.m in Sources */, 797BDD1C1C1F5176006EF006 /* PNPublishWithHistoryTests.m in Sources */, 797BDD1D1C1F5176006EF006 /* PNPresenceTests.m in Sources */, A525EA592302A79000ABFDC2 /* PNMemberObjectsTest.m in Sources */, @@ -1562,6 +1661,7 @@ 797BDD201C1F5176006EF006 /* PNPublishSizeOfMessage.m in Sources */, 797BDD211C1F5176006EF006 /* PNHeartbeatTests.m in Sources */, 797BDD221C1F5176006EF006 /* PNClientConfigurationTests.m in Sources */, + A55A649C2328E36A0064E8E4 /* PNAPNSIntegrationTest.m in Sources */, 79E20D2D1C8B1C64001BC9CC /* PNBasicPresenceTestCase.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Tests/PubNub Tests.xcodeproj/xcshareddata/xcschemes/iOS Tests (ObjC).xcscheme b/Tests/PubNub Tests.xcodeproj/xcshareddata/xcschemes/iOS Tests (ObjC).xcscheme index 92888ed98..c54b629b5 100644 --- a/Tests/PubNub Tests.xcodeproj/xcshareddata/xcschemes/iOS Tests (ObjC).xcscheme +++ b/Tests/PubNub Tests.xcodeproj/xcshareddata/xcschemes/iOS Tests (ObjC).xcscheme @@ -23,7 +23,7 @@ *)publishMessages:(NSUInteger)messagesCount toChannel:(NSString *)channel; + +/** + * @brief Publish test messages to specified \c channel. + * + * @param actionsCount How many actions should be added for each message. + * @param messages List of publish timetokens for messages to which \c actions will be added. + * @param channel Name of channel which contains references messages. + * + * @return List of message action timetokens. + */ +- (NSArray *)addActions:(NSUInteger)actionsCount + toMessages:(NSArray *)messages + inChannel:(NSString *)channel; + +#pragma mark - + + +@end + +NS_ASSUME_NONNULL_END + + +#pragma mark - Interface implementation + +@implementation PNMessageActionsIntegrationTest + + +#pragma mark - Setup / Tear down + +- (void)setUp { + [super setUp]; + + + dispatch_queue_t callbackQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + PNConfiguration *configuration = [PNConfiguration configurationWithPublishKey:self.publishKey + subscribeKey:self.subscribeKey]; + + self.client1 = [PubNub clientWithConfiguration:configuration callbackQueue:callbackQueue]; + self.client2 = [PubNub clientWithConfiguration:configuration callbackQueue:callbackQueue]; +} + +- (void)tearDown { + [self removeAllHandlersForClient:self.client1]; + + if (self.client2) { + [self removeAllHandlersForClient:self.client2]; + [self.client2 removeListener:self]; + } + + [self.client1 removeListener:self]; + + + [super tearDown]; +} + + +#pragma mark - Tests :: Add Action + +- (void)testAddAction_ShouldAddAction_WhenCalled { + NSString *expectedChannel = [NSUUID UUID].UUIDString; + NSString *expectedValue = [NSUUID UUID].UUIDString; + NSString *expectedType = @"custom"; + + + NSArray *messageTimetokens = [self publishMessages:1 toChannel:expectedChannel]; + + [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { + self.client1.addMessageAction() + .channel(expectedChannel) + .messageTimetoken(messageTimetokens[0]) + .type(expectedType) + .value(expectedValue) + .performWithCompletion(^(PNAddMessageActionStatus *status) { + PNMessageAction *action = status.data.action; + XCTAssertFalse(status.isError); + XCTAssertNotNil(action); + XCTAssertEqualObjects(action.type, expectedType); + XCTAssertEqualObjects(action.value, expectedValue); + XCTAssertEqualObjects(action.uuid, self.client1.uuid); + XCTAssertEqualObjects(action.messageTimetoken, messageTimetokens[0]); + XCTAssertNotNil(action.actionTimetoken); + + handler(); + }); + }]; +} + +- (void)testAddAction_ShouldTriggerAddedEvent_WhenAddingAction { + NSString *expectedChannel = [NSUUID UUID].UUIDString; + NSString *expectedValue = [NSUUID UUID].UUIDString; + NSString *expectedType = @"custom"; + [self.client2 addListener:self]; + + + NSArray *messageTimetokens = [self publishMessages:1 toChannel:expectedChannel]; + + [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { + [self addStatusHandlerForClient:self.client2 + withBlock:^(PubNub *client, PNSubscribeStatus *status, BOOL *remove) { + + if (status.category == PNConnectedCategory) { + *remove = YES; + + handler(); + } + }]; + + self.client2.subscribe().channels(@[expectedChannel]).perform(); + }]; + + [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { + [self addActionHandlerForClient:self.client2 + withBlock:^(PubNub *client, PNMessageActionResult *event, BOOL *remove) { + *remove = YES; +#pragma GCC diagnostic push +#pragma clang diagnostic ignored "-Warc-retain-cycles" + PNMessageAction *action = event.data.action; + XCTAssertNotNil(action); + XCTAssertEqualObjects(action.type, expectedType); + XCTAssertEqualObjects(action.value, expectedValue); + XCTAssertEqualObjects(action.uuid, self.client1.uuid); + XCTAssertEqualObjects(action.messageTimetoken, messageTimetokens[0]); + XCTAssertNotNil(action.actionTimetoken); + XCTAssertEqualObjects(event.data.event, @"added"); +#pragma GCC diagnostic pop + + handler(); + }]; + + self.client1.addMessageAction() + .channel(expectedChannel) + .messageTimetoken(messageTimetokens[0]) + .type(expectedType) + .value(expectedValue) + .performWithCompletion(^(PNAddMessageActionStatus *status) { + XCTAssertFalse(status.isError); + }); + }]; +} + + +#pragma mark - Tests :: Remove Action + +- (void)testRemoveAction_ShouldRemoveAction_WhenCalled { + NSString *expectedChannel = [NSUUID UUID].UUIDString; + + + NSArray *messageTimetokens = [self publishMessages:1 toChannel:expectedChannel]; + NSArray *actionTimetokens = [self addActions:1 + toMessages:messageTimetokens + inChannel:expectedChannel]; + + [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { + self.client1.fetchMessageActions() + .channel(expectedChannel) + .performWithCompletion(^(PNFetchMessageActionsResult *result, PNErrorStatus *status) { + XCTAssertFalse(status.isError); + XCTAssertEqual(result.data.actions.count, actionTimetokens.count); + + handler(); + }); + }]; + + [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { + self.client1.removeMessageAction() + .channel(expectedChannel) + .messageTimetoken(messageTimetokens[0]) + .actionTimetoken(actionTimetokens[0]) + .performWithCompletion(^(PNAcknowledgmentStatus *status) { + XCTAssertFalse(status.isError); + + handler(); + }); + }]; + + [self waitTask:@"actionDeletePropagation" completionFor:2.f]; + + [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { + self.client1.fetchMessageActions() + .channel(expectedChannel) + .performWithCompletion(^(PNFetchMessageActionsResult *result, PNErrorStatus *status) { + XCTAssertFalse(status.isError); + XCTAssertEqual(result.data.actions.count, 0); + + handler(); + }); + }]; +} + +- (void)testRemoveAction_ShouldTriggerRemoveEvent_WhenRemovingAction { + NSString *expectedChannel = [NSUUID UUID].UUIDString; + [self.client2 addListener:self]; + + + NSArray *messageTimetokens = [self publishMessages:1 toChannel:expectedChannel]; + NSArray *actionTimetokens = [self addActions:1 + toMessages:messageTimetokens + inChannel:expectedChannel]; + + [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { + [self addStatusHandlerForClient:self.client2 + withBlock:^(PubNub *client, PNSubscribeStatus *status, BOOL *remove) { + + if (status.category == PNConnectedCategory) { + *remove = YES; + + handler(); + } + }]; + + self.client2.subscribe().channels(@[expectedChannel]).perform(); + }]; + + [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { + [self addActionHandlerForClient:self.client2 + withBlock:^(PubNub *client, PNMessageActionResult *event, BOOL *remove) { + *remove = YES; +#pragma GCC diagnostic push +#pragma clang diagnostic ignored "-Warc-retain-cycles" + PNMessageAction *action = event.data.action; + XCTAssertNotNil(action); + XCTAssertEqualObjects(action.uuid, self.client1.uuid); + XCTAssertEqualObjects(action.messageTimetoken, messageTimetokens[0]); + XCTAssertNotNil(action.actionTimetoken); + XCTAssertEqualObjects(event.data.event, @"removed"); +#pragma GCC diagnostic pop + + handler(); + }]; + + self.client1.removeMessageAction() + .channel(expectedChannel) + .messageTimetoken(messageTimetokens[0]) + .actionTimetoken(actionTimetokens[0]) + .performWithCompletion(^(PNAcknowledgmentStatus *status) { }); + }]; +} + + +#pragma mark - Tests :: Fetch Action + +- (void)testFetchActions_ShouldFetchActions_WhenCalled { + NSString *expectedChannel = [NSUUID UUID].UUIDString; + + + NSArray *messageTimetokens = [self publishMessages:2 toChannel:expectedChannel]; + NSArray *actionTimetokens = [self addActions:3 + toMessages:messageTimetokens + inChannel:expectedChannel]; + NSNumber *firstPublishedActionTimetoken = actionTimetokens[0]; + NSNumber *lastPublishedActionTimetoken = actionTimetokens[actionTimetokens.count - 1]; + + [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { + self.client1.fetchMessageActions() + .channel(expectedChannel) + .performWithCompletion(^(PNFetchMessageActionsResult *result, PNErrorStatus *status) { + NSArray *fetchedActions = result.data.actions; + XCTAssertFalse(status.isError); + NSNumber *firstFetchedActionTimetoken = fetchedActions[0].actionTimetoken; + NSNumber *lastFetchedActionTimetoken = fetchedActions[fetchedActions.count - 1].actionTimetoken; + + XCTAssertEqual([firstFetchedActionTimetoken compare:firstPublishedActionTimetoken], + NSOrderedSame); + XCTAssertEqual([lastFetchedActionTimetoken compare:lastPublishedActionTimetoken], + NSOrderedSame); + XCTAssertEqual(fetchedActions.count, actionTimetokens.count); + XCTAssertEqual([result.data.start compare:firstPublishedActionTimetoken], + NSOrderedSame); + XCTAssertEqual([result.data.end compare:lastPublishedActionTimetoken], + NSOrderedSame); + + + handler(); + }); + }]; +} + +- (void)testFetchActions_ShouldFetchNextActionsPage_WhenCalledWithLimitAndStart { + NSString *expectedChannel = [NSUUID UUID].UUIDString; + + + NSArray *messageTimetokens = [self publishMessages:2 toChannel:expectedChannel]; + NSArray *actionTimetokens = [self addActions:5 + toMessages:messageTimetokens + inChannel:expectedChannel]; + NSNumber *firstPublishedActionTimetoken = actionTimetokens[0]; + NSNumber *lastPublishedActionTimetoken = actionTimetokens[actionTimetokens.count - 1]; + NSUInteger halfSize = (NSUInteger)(actionTimetokens.count * 0.5f); + NSNumber *middlePublishedActionTimetoken = actionTimetokens[halfSize]; + NSNumber *middleMinusOnePublishedActionTimetoken = actionTimetokens[halfSize - 1]; + + [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { + self.client1.fetchMessageActions() + .channel(expectedChannel) + .limit(halfSize) + .performWithCompletion(^(PNFetchMessageActionsResult *result, PNErrorStatus *status) { + NSArray *fetchedActions = result.data.actions; + XCTAssertFalse(status.isError); + NSNumber *firstFetchedActionTimetoken = fetchedActions[0].actionTimetoken; + NSNumber *lastFetchedActionTimetoken = fetchedActions[fetchedActions.count - 1].actionTimetoken; + + XCTAssertEqual([firstFetchedActionTimetoken compare:middlePublishedActionTimetoken], + NSOrderedSame); + XCTAssertEqual([lastFetchedActionTimetoken compare:lastPublishedActionTimetoken], + NSOrderedSame); + XCTAssertEqual(fetchedActions.count, halfSize); + XCTAssertEqual([result.data.start compare:middlePublishedActionTimetoken], + NSOrderedSame); + XCTAssertEqual([result.data.end compare:lastPublishedActionTimetoken], + NSOrderedSame); + + handler(); + }); + }]; + + [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { + self.client1.fetchMessageActions() + .channel(expectedChannel) + .start(middlePublishedActionTimetoken) + .limit(halfSize) + .performWithCompletion(^(PNFetchMessageActionsResult *result, PNErrorStatus *status) { + NSArray *fetchedActions = result.data.actions; + XCTAssertFalse(status.isError); + NSNumber *firstFetchedActionTimetoken = fetchedActions[0].actionTimetoken; + NSNumber *lastFetchedActionTimetoken = fetchedActions[fetchedActions.count - 1].actionTimetoken; + + XCTAssertEqual([firstFetchedActionTimetoken compare:firstPublishedActionTimetoken], + NSOrderedSame); + XCTAssertEqual([lastFetchedActionTimetoken compare:middleMinusOnePublishedActionTimetoken], + NSOrderedSame); + XCTAssertEqual(fetchedActions.count, halfSize); + XCTAssertEqual([result.data.start compare:firstPublishedActionTimetoken], + NSOrderedSame); + XCTAssertEqual([result.data.end compare:middleMinusOnePublishedActionTimetoken], + NSOrderedSame); + + handler(); + }); + }]; +} + + +#pragma mark - Misc + +- (NSArray *)publishMessages:(NSUInteger)messagesCount toChannel:(NSString *)channel { + NSMutableArray *timetokens = [NSMutableArray new]; + + for (NSUInteger messageIdx = 0; messageIdx < messagesCount; messageIdx++) { + [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { + self.client1.publish() + .message(@{ + @"messageIdx": @(messageIdx), + @"time": @([NSDate date].timeIntervalSince1970) + }) + .channel(channel) + .performWithCompletion(^(PNPublishStatus *status) { + if (!status.isError) { + [timetokens addObject:status.data.timetoken]; + } else { + NSLog(@"Publish did fail: %@", status.errorData.information); + } + + handler(); + }); + }]; + } + + return timetokens; +} + +- (NSArray *)addActions:(NSUInteger)actionsCount + toMessages:(NSArray *)messages + inChannel:(NSString *)channel { + NSArray *types = @[@"reaction", @"receipt", @"custom"]; + NSArray *values = @[ + [NSUUID UUID].UUIDString, [NSUUID UUID].UUIDString, + [NSUUID UUID].UUIDString, [NSUUID UUID].UUIDString, + [NSUUID UUID].UUIDString, [NSUUID UUID].UUIDString, + [NSUUID UUID].UUIDString, [NSUUID UUID].UUIDString, + [NSUUID UUID].UUIDString, [NSUUID UUID].UUIDString + ]; + NSMutableArray *timetokens = [NSMutableArray new]; + + for (NSUInteger messageIdx = 0; messageIdx < messages.count; messageIdx++) { + NSNumber *messageTimetoken = messages[messageIdx]; + + for (NSUInteger messageActionIdx = 0; messageActionIdx < actionsCount; messageActionIdx++) { + [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { + self.client1.addMessageAction() + .channel(channel) + .messageTimetoken(messageTimetoken) + .type(types[(messageActionIdx + 1)%3]) + .value(values[(messageActionIdx + 1)%10]) + .performWithCompletion(^(PNAddMessageActionStatus *status) { + if (!status.isError) { + [timetokens addObject:status.data.action.actionTimetoken]; + } + handler(); + }); + }]; + } + } + + return timetokens; +} + +#pragma mark - + + +@end diff --git a/Tests/iOS Tests/Tests/Integration/History/PNHistoryIntegrationTest.m b/Tests/iOS Tests/Tests/Integration/History/PNHistoryIntegrationTest.m new file mode 100644 index 000000000..cd1cf61bd --- /dev/null +++ b/Tests/iOS Tests/Tests/Integration/History/PNHistoryIntegrationTest.m @@ -0,0 +1,578 @@ +#import "PNTestCase.h" + + +NS_ASSUME_NONNULL_BEGIN + + +#pragma mark Test interface declaration + +@interface PNHistoryIntegrationTest : PNTestCase + + +#pragma mark - Information + +@property (nonatomic, strong) PubNub *client; + + +#pragma mark - Misc + +/** + * @brief Publish test messages to specified \c channel. + * + * @param messagesCount How many messages should be published to specified \c channel. + * @param channel Name of channel which will be used in test with pre-published messages. + * + * @return List of published message and timetokens. + */ +- (NSArray *)publishMessages:(NSUInteger)messagesCount + toChannel:(NSString *)channel; + +/** + * @brief Publish test messages to set of specified \c channels. + * + * @param messagesCount How many messages should be published to specified \c channel. + * @param channels List of channel names which will be used in test with pre-published messages. + * + * @return List of published message and timetokens mapped to channel names. + */ +- (NSDictionary *> *)publishMessages:(NSUInteger)messagesCount + toChannels:(NSArray *)channels; + +/** + * @brief Publish test messages to specified \c channel. + * + * @param actionsCount How many actions should be added for each message. + * @param messages List of publish timetokens for messages to which \c actions will be added. + * @param channel Name of channel which contains references messages. + * + * @return List of message actions. + */ +- (NSArray *)addActions:(NSUInteger)actionsCount + toMessages:(NSArray *)messages + inChannel:(NSString *)channel; + +/** + * @brief Publish test messages to set of specified \c channels. + * + * @param actionsCount How many actions should be added for each message. + * @param messages List of publish timetokens for messages to which \c actions will be added. + * @param channel List of channel names which contains references messages. + * + * @return List of message actions mapped to channel names. + */ +- (NSDictionary *> *)addActions:(NSUInteger)actionsCount + toMessages:(NSArray *)messages + inChannels:(NSArray *)channels; + +#pragma mark - + + +@end + +NS_ASSUME_NONNULL_END + + +#pragma mark - Interface implementation + +@implementation PNHistoryIntegrationTest + + +#pragma mark - Setup / Tear down + +- (void)setUp { + [super setUp]; + + + dispatch_queue_t callbackQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + PNConfiguration *configuration = [PNConfiguration configurationWithPublishKey:self.publishKey + subscribeKey:self.subscribeKey]; + + self.client = [PubNub clientWithConfiguration:configuration callbackQueue:callbackQueue]; +} + +- (void)tearDown { + [self removeAllHandlersForClient:self.client]; + [self.client removeListener:self]; + + + [super tearDown]; +} + + +#pragma mark - Tests :: History for channel + +- (void)testHistoryForChannel_ShouldFetchHistory_WhenCalled { + NSString *expectedChannel = [NSUUID UUID].UUIDString; + NSUInteger expectedCount = 4; + NSUInteger verifiedMessageIdx = (NSUInteger)(expectedCount * 0.5f); + + + NSArray *messages = [self publishMessages:expectedCount toChannel:expectedChannel]; + NSNumber *firstMessageTimetoken = messages[0][@"timetoken"]; + NSNumber *lastMessageTimetoken = messages[expectedCount - 1][@"timetoken"]; + + [self waitTask:@"propagateToStorage" completionFor:2.f]; + + [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { + self.client.history() + .channel(expectedChannel) + .performWithCompletion(^(PNHistoryResult *result, PNErrorStatus *status) { + XCTAssertFalse(status.isError); + XCTAssertNotNil(result.data.messages); + XCTAssertEqualObjects(result.data.channels, @{}); + XCTAssertEqual(result.data.messages.count, messages.count); + XCTAssertEqualObjects(result.data.messages[verifiedMessageIdx], + messages[verifiedMessageIdx][@"message"]); + XCTAssertEqual([result.data.start compare:firstMessageTimetoken], NSOrderedSame); + XCTAssertEqual([result.data.end compare:lastMessageTimetoken], NSOrderedSame); + + handler(); + }); + }]; +} + +- (void)testHistoryForChannel_ShouldFetchHistoryWithMessageMetadata_WhenCalled { + NSString *expectedChannel = [NSUUID UUID].UUIDString; + NSUInteger expectedCount = 4; + NSUInteger verifiedMessageIdx = (NSUInteger)(expectedCount * 0.5f); + + + NSArray *messages = [self publishMessages:expectedCount toChannel:expectedChannel]; + NSNumber *firstMessageTimetoken = messages[0][@"timetoken"]; + NSNumber *lastMessageTimetoken = messages[expectedCount - 1][@"timetoken"]; + NSDictionary *verifiedMessage = messages[verifiedMessageIdx]; + + [self waitTask:@"propagateToStorage" completionFor:2.f]; + + [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { + self.client.history() + .channel(expectedChannel) + .includeMetadata(YES) + .performWithCompletion(^(PNHistoryResult *result, PNErrorStatus *status) { + XCTAssertFalse(status.isError); + XCTAssertNotNil(result.data.messages); + XCTAssertEqualObjects(result.data.channels, @{}); + XCTAssertEqual(result.data.messages.count, messages.count); + XCTAssertEqualObjects(result.data.messages[verifiedMessageIdx][@"message"], + verifiedMessage[@"message"]); + XCTAssertEqualObjects(result.data.messages[verifiedMessageIdx][@"metadata"], + @{ @"time": verifiedMessage[@"message"][@"time"]}); + XCTAssertEqual([result.data.start compare:firstMessageTimetoken], NSOrderedSame); + XCTAssertEqual([result.data.end compare:lastMessageTimetoken], NSOrderedSame); + + handler(); + }); + }]; +} + +- (void)testHistoryForChannel_ShouldFetchNextMessagesPage_WhenCalledWithLimitAndStart { + NSString *expectedChannel = [NSUUID UUID].UUIDString; + NSUInteger expectedCount = 10; + NSUInteger halfSize = (NSUInteger)(expectedCount * 0.5f); + + + NSArray *messages = [self publishMessages:expectedCount toChannel:expectedChannel]; + [self waitTask:@"propagateToStorage" completionFor:2.f]; + NSNumber *firstMessageTimetoken = messages[0][@"timetoken"]; + NSNumber *lastMessageTimetoken = messages[expectedCount - 1][@"timetoken"]; + NSNumber *middlePublishedMessageTimetoken = messages[halfSize][@"timetoken"]; + NSNumber *middleMinusOnePublishedPublishedTimetoken = messages[halfSize - 1][@"timetoken"]; + + [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { + self.client.history() + .channel(expectedChannel) + .includeTimeToken(YES) + .limit(halfSize) + .performWithCompletion(^(PNHistoryResult *result, PNErrorStatus *status) { + XCTAssertFalse(status.isError); + NSArray *fetchedMessages = result.data.messages; + NSNumber *firstFetchedMessageTimetoken = fetchedMessages[0][@"timetoken"]; + NSNumber *lastFetchedMessageTimetoken = fetchedMessages.lastObject[@"timetoken"]; + + XCTAssertEqual([firstFetchedMessageTimetoken compare:middlePublishedMessageTimetoken], + NSOrderedSame); + XCTAssertEqual([lastFetchedMessageTimetoken compare:lastMessageTimetoken], + NSOrderedSame); + XCTAssertEqual(fetchedMessages.count, halfSize); + XCTAssertEqual([result.data.start compare:middlePublishedMessageTimetoken], + NSOrderedSame); + XCTAssertEqual([result.data.end compare:lastMessageTimetoken], + NSOrderedSame); + + handler(); + }); + }]; + + [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { + self.client.history() + .channel(expectedChannel) + .includeTimeToken(YES) + .start(middlePublishedMessageTimetoken) + .limit(halfSize) + .performWithCompletion(^(PNHistoryResult *result, PNErrorStatus *status) { + XCTAssertFalse(status.isError); + NSArray *fetchedMessages = result.data.messages; + NSNumber *firstFetchedMessageTimetoken = fetchedMessages[0][@"timetoken"]; + NSNumber *lastFetchedMessageTimetoken = fetchedMessages.lastObject[@"timetoken"]; + + XCTAssertEqual([firstFetchedMessageTimetoken compare:firstMessageTimetoken], + NSOrderedSame); + XCTAssertEqual([lastFetchedMessageTimetoken compare:middleMinusOnePublishedPublishedTimetoken], + NSOrderedSame); + XCTAssertEqual(fetchedMessages.count, halfSize); + XCTAssertEqual([result.data.start compare:firstMessageTimetoken], + NSOrderedSame); + XCTAssertEqual([result.data.end compare:middleMinusOnePublishedPublishedTimetoken], + NSOrderedSame); + + handler(); + }); + }]; +} + + +#pragma mark - Tests :: History for channel with actions + +- (void)testHistoryWithActions_ShouldFetchWithActions_WhenCalled { + NSString *expectedChannel = [NSUUID UUID].UUIDString; + NSUInteger expectedMessagesCount = 2; + NSUInteger expectedActionsCount = 4; + + + NSArray *messages = [self publishMessages:expectedMessagesCount + toChannel:expectedChannel]; + NSArray *messageTimetokens = [messages valueForKey:@"timetoken"]; + NSArray *actions = [self addActions:expectedActionsCount + toMessages:messageTimetokens + inChannel:expectedChannel]; + + [self waitTask:@"propagateToStorage" completionFor:2.f]; + + [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { + self.client.history() + .channel(expectedChannel) + .includeMessageActions(YES) + .includeMetadata(YES) + .performWithCompletion(^(PNHistoryResult *result, PNErrorStatus *status) { + XCTAssertFalse(status.isError); + NSArray *messages = result.data.channels[expectedChannel]; + NSDictionary *actionsByType = [messages.firstObject valueForKey:@"actions"]; + NSUInteger historyActionsCount = 0; + + for (NSString *actionType in actionsByType) { + for (NSString *actionValue in actionsByType[actionType]) { + BOOL actionFound = NO; + historyActionsCount++; + + for (PNMessageAction *action in actions) { + if (![action.value isEqualToString:actionValue]) { + continue; + } + + actionFound = YES; + } + + XCTAssertTrue(actionFound); + } + }; + + XCTAssertEqual(historyActionsCount, expectedActionsCount); + XCTAssertEqualObjects(messages.firstObject[@"timetoken"], messageTimetokens.firstObject); + XCTAssertEqualObjects(messages.lastObject[@"timetoken"], messageTimetokens.lastObject); + + handler(); + }); + }]; +} + + +#pragma mark - Tests :: History for channels + +- (void)testHistoryForChannels_ShouldFetchOneMessageForEachChannel_WhenCalledWithOutLimit { + NSArray *expectedChannels = @[[NSUUID UUID].UUIDString, [NSUUID UUID].UUIDString]; + NSUInteger expectedCount = 4; + + + NSDictionary *messages = [self publishMessages:expectedCount + toChannels:expectedChannels]; + NSArray *messages1 = messages[expectedChannels.firstObject]; + NSArray *messages2 = messages[expectedChannels.lastObject]; + + [self waitTask:@"propagateToStorage" completionFor:2.f]; + + [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { + self.client.history() + .channels(expectedChannels) + .performWithCompletion(^(PNHistoryResult *result, PNErrorStatus *status) { + XCTAssertFalse(status.isError); + XCTAssertNotEqualObjects(result.data.channels, @{}); + XCTAssertEqualObjects(result.data.messages, @[]); + NSDictionary *channels = result.data.channels; + NSArray *channel1Messages = channels[expectedChannels.firstObject]; + NSArray *channel2Messages = channels[expectedChannels.lastObject]; + XCTAssertEqual(channels.count, expectedChannels.count); + XCTAssertEqual(channel1Messages.count, 1); + XCTAssertEqual(channel2Messages.count, 1); + XCTAssertEqualObjects(channel1Messages.firstObject[@"message"], + messages1.lastObject[@"message"]); + XCTAssertEqualObjects(channel2Messages.firstObject[@"message"], + messages2.lastObject[@"message"]); + XCTAssertEqual([channel1Messages.firstObject[@"timetoken"] + compare:messages1.lastObject[@"timetoken"]], NSOrderedSame); + XCTAssertEqual([channel2Messages.firstObject[@"timetoken"] + compare:messages2.lastObject[@"timetoken"]], NSOrderedSame); + XCTAssertEqual([result.data.start compare:@(0)], NSOrderedSame); + XCTAssertEqual([result.data.end compare:@(0)], NSOrderedSame); + + handler(); + }); + }]; +} + +- (void)testHistoryForChannels_ShouldFetchMessagesForEachChannel_WhenCalledWithLimit { + NSArray *expectedChannels = @[[NSUUID UUID].UUIDString, [NSUUID UUID].UUIDString]; + NSUInteger expectedCount = 4; + NSUInteger verifiedMessageIdx = (NSUInteger)(expectedCount * 0.5f); + + NSDictionary *messages = [self publishMessages:expectedCount + toChannels:expectedChannels]; + NSArray *messages1 = messages[expectedChannels.firstObject]; + NSArray *messages2 = messages[expectedChannels.lastObject]; + NSNumber *firstChannl1MessageTimetoken = messages1.firstObject[@"timetoken"]; + NSNumber *lastChannl1MessageTimetoken = messages1.lastObject[@"timetoken"]; + NSNumber *firstChannl2MessageTimetoken = messages2.firstObject[@"timetoken"]; + NSNumber *lastChannl2MessageTimetoken = messages2.lastObject[@"timetoken"]; + + [self waitTask:@"propagateToStorage" completionFor:2.f]; + + [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { + self.client.history() + .channels(expectedChannels) + .limit(25) + .includeMetadata(YES) + .performWithCompletion(^(PNHistoryResult *result, PNErrorStatus *status) { + XCTAssertFalse(status.isError); + XCTAssertNotEqualObjects(result.data.channels, @{}); + XCTAssertEqualObjects(result.data.messages, @[]); + NSDictionary *channels = result.data.channels; + NSArray *channel1Messages = channels[expectedChannels.firstObject]; + NSArray *channel2Messages = channels[expectedChannels.lastObject]; + XCTAssertEqual(channels.count, expectedChannels.count); + XCTAssertEqual(channel1Messages.count, messages1.count); + XCTAssertEqual(channel2Messages.count, messages2.count); + XCTAssertEqualObjects(channel1Messages[verifiedMessageIdx][@"message"], + messages1[verifiedMessageIdx][@"message"]); + XCTAssertEqualObjects(channel2Messages[verifiedMessageIdx][@"message"], + messages2[verifiedMessageIdx][@"message"]); + XCTAssertEqualObjects(channel1Messages[verifiedMessageIdx][@"metadata"], + @{ @"time": messages1[verifiedMessageIdx][@"message"][@"time"]}); + XCTAssertEqualObjects(channel2Messages[verifiedMessageIdx][@"metadata"], + @{ @"time": messages2[verifiedMessageIdx][@"message"][@"time"]}); + XCTAssertEqual([channel1Messages.firstObject[@"timetoken"] + compare:firstChannl1MessageTimetoken], NSOrderedSame); + XCTAssertEqual([channel1Messages.lastObject[@"timetoken"] + compare:lastChannl1MessageTimetoken], NSOrderedSame); + XCTAssertEqual([channel2Messages.firstObject[@"timetoken"] + compare:firstChannl2MessageTimetoken], NSOrderedSame); + XCTAssertEqual([channel2Messages.lastObject[@"timetoken"] + compare:lastChannl2MessageTimetoken], NSOrderedSame); + + handler(); + }); + }]; +} + + +#pragma mark - Tests :: Messages Count + +- (void)testMessageCounts_ShouldFetchCount_WhenSingleChannelAndTimetokenPassed { + NSArray *expectedChannels = @[[NSUUID UUID].UUIDString]; + NSUInteger expectedCount = 3; + + + NSDictionary *messages = [self publishMessages:expectedCount + toChannels:expectedChannels]; + NSArray *channelTimetokens = [messages[expectedChannels.firstObject] valueForKey:@"timetoken"]; + NSNumber *timetoken = channelTimetokens[channelTimetokens.count - 2]; + NSDictionary *expected = @{ expectedChannels.firstObject: @(1) }; + + [self waitTask:@"propagateToStorage" completionFor:2.f]; + + [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { + self.client.messageCounts().channels(expectedChannels).timetokens(@[timetoken]) + .performWithCompletion(^(PNMessageCountResult *result, PNErrorStatus *status) { + XCTAssertNil(status); + XCTAssertEqualObjects(result.data.channels, expected); + handler(); + }); + }]; +} + +- (void)testMessageCounts_ShouldFetchCount_WhenSingleTimetokenAndMultipleChannelsPassed { + NSArray *expectedChannels = @[[NSUUID UUID].UUIDString, [NSUUID UUID].UUIDString]; + NSUInteger expectedCount = 3; + + + NSDictionary *messages = [self publishMessages:expectedCount + toChannels:expectedChannels]; + NSArray *channelTimetokens = [messages[expectedChannels.firstObject] valueForKey:@"timetoken"]; + NSNumber *timetoken = channelTimetokens[channelTimetokens.count - 2]; + NSDictionary *expected = @{ expectedChannels.firstObject: @(1), expectedChannels.lastObject: @(3) }; + + [self waitTask:@"propagateToStorage" completionFor:2.f]; + + [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { + self.client.messageCounts().channels(expectedChannels).timetokens(@[timetoken]) + .performWithCompletion(^(PNMessageCountResult *result, PNErrorStatus *status) { + XCTAssertNil(status); + XCTAssertEqualObjects(result.data.channels, expected); + handler(); + }); + }]; +} + +- (void)testMessageCounts_ShouldFetchCount_WhenPerChannelTimetokenPassed { + NSArray *expectedChannels = @[[NSUUID UUID].UUIDString, [NSUUID UUID].UUIDString]; + NSUInteger expectedCount = 3; + + + NSDictionary *messages = [self publishMessages:expectedCount + toChannels:expectedChannels]; + NSArray *channelTimetokens1 = [messages[expectedChannels.firstObject] valueForKey:@"timetoken"]; + NSArray *channelTimetokens2 = [messages[expectedChannels.lastObject] valueForKey:@"timetoken"]; + NSNumber *timetoken1 = channelTimetokens1[channelTimetokens1.count - 2]; + NSNumber *timetoken2 = channelTimetokens2[channelTimetokens2.count - 2]; + NSArray *timetokens = @[timetoken1, timetoken2]; + NSDictionary *expected = @{ expectedChannels.firstObject: @(1), expectedChannels.lastObject: @(1) }; + + [self waitTask:@"propagateToStorage" completionFor:2.f]; + + [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { + self.client.messageCounts().channels(expectedChannels).timetokens(timetokens) + .performWithCompletion(^(PNMessageCountResult *result, PNErrorStatus *status) { + XCTAssertNil(status); + XCTAssertEqualObjects(result.data.channels, expected); + handler(); + }); + }]; +} + +- (void)testMessageCounts_ShouldFail_WhenTimetokenNotPassed { + NSArray *channels = @[[NSUUID UUID].UUIDString, [NSUUID UUID].UUIDString]; + + [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { + self.client.messageCounts().channels(channels) + .performWithCompletion(^(PNMessageCountResult *result, PNErrorStatus *status) { + XCTAssertNil(result); + XCTAssertTrue(status.isError); + handler(); + }); + }]; +} + + +#pragma mark - Misc + +- (NSArray *)publishMessages:(NSUInteger)messagesCount + toChannel:(NSString *)channel { + + NSMutableArray *messages = [NSMutableArray new]; + + for (NSUInteger messageIdx = 0; messageIdx < messagesCount; messageIdx++) { + [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { + NSDictionary *message = @{ + @"messageIdx": [@[channel, @(messageIdx)] componentsJoinedByString:@": "], + @"time": @([NSDate date].timeIntervalSince1970) + }; + + PNPublishAPICallBuilder *builder = self.client.publish().message(message).channel(channel); + if (messageIdx % 2 == 0) { + builder = builder.metadata(@{ @"time": message[@"time"] }); + } + + builder.performWithCompletion(^(PNPublishStatus *status) { + if (!status.isError) { + [messages addObject:@{ @"message": message, @"timetoken": status.data.timetoken }]; + } + + handler(); + }); + }]; + } + + return messages; +} + +- (NSDictionary *> *)publishMessages:(NSUInteger)messagesCount + toChannels:(NSArray *)channels { + + NSMutableDictionary *channelMessages = [NSMutableDictionary new]; + + for (NSString *channel in channels) { + channelMessages[channel] = [self publishMessages:messagesCount toChannel:channel]; + } + + return channelMessages; +} + +- (NSArray *)addActions:(NSUInteger)actionsCount + toMessages:(NSArray *)messages + inChannel:(NSString *)channel { + + NSArray *types = @[@"reaction", @"receipt", @"custom"]; + + static NSArray *_sharedHistoryActionValues; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _sharedHistoryActionValues = @[ + [NSUUID UUID].UUIDString, [NSUUID UUID].UUIDString, + [NSUUID UUID].UUIDString, [NSUUID UUID].UUIDString, + [NSUUID UUID].UUIDString, [NSUUID UUID].UUIDString, + [NSUUID UUID].UUIDString, [NSUUID UUID].UUIDString, + [NSUUID UUID].UUIDString, [NSUUID UUID].UUIDString + ]; + }); + + NSMutableArray *actions = [NSMutableArray new]; + + for (NSUInteger messageIdx = 0; messageIdx < messages.count; messageIdx++) { + NSNumber *messageTimetoken = messages[messageIdx]; + + for (NSUInteger actionIdx = 0; actionIdx < actionsCount; actionIdx++) { + [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { + self.client.addMessageAction() + .channel(channel) + .messageTimetoken(messageTimetoken) + .type(types[(actionIdx + 1)%3]) + .value(_sharedHistoryActionValues[(actionIdx + 1)%10]) + .performWithCompletion(^(PNAddMessageActionStatus *status) { + if (!status.isError) { + [actions addObject:status.data.action]; + } + handler(); + }); + }]; + } + } + + return actions; +} + +- (NSDictionary *> *)addActions:(NSUInteger)actionsCount + toMessages:(NSArray *)messages + inChannels:(NSArray *)channels { + + NSMutableDictionary *channelActions = [NSMutableDictionary new]; + + for (NSString *channel in channels) { + channelActions[channel] = [self addActions:actionsCount + toMessages:messages + inChannel:channel]; + } + + return channelActions; +} + +#pragma mark - + +@end diff --git a/Tests/iOS Tests/Tests/Integration/Objects/PNObjectsTestCase.h b/Tests/iOS Tests/Tests/Integration/Objects/PNObjectsTestCase.h index 095ac6084..4d7e3a52f 100644 --- a/Tests/iOS Tests/Tests/Integration/Objects/PNObjectsTestCase.h +++ b/Tests/iOS Tests/Tests/Integration/Objects/PNObjectsTestCase.h @@ -1,5 +1,6 @@ #import "PNTestCase.h" + NS_ASSUME_NONNULL_BEGIN /** diff --git a/Tests/iOS Tests/Tests/Integration/Objects/PNObjectsTestCase.m b/Tests/iOS Tests/Tests/Integration/Objects/PNObjectsTestCase.m index da0067280..d6ff4b6a6 100644 --- a/Tests/iOS Tests/Tests/Integration/Objects/PNObjectsTestCase.m +++ b/Tests/iOS Tests/Tests/Integration/Objects/PNObjectsTestCase.m @@ -12,6 +12,7 @@ @interface PNObjectsTestCase () + #pragma mark - Information /** @@ -36,6 +37,9 @@ @interface PNObjectsTestCase () @implementation PNObjectsTestCase + +#pragma mark - Setup / Tear down + - (void)setUp { [super setUp]; diff --git a/Tests/iOS Tests/Tests/Integration/Objects/PNUserIntegrationTest.m b/Tests/iOS Tests/Tests/Integration/Objects/PNUserIntegrationTest.m index 014afd319..f021b1de4 100644 --- a/Tests/iOS Tests/Tests/Integration/Objects/PNUserIntegrationTest.m +++ b/Tests/iOS Tests/Tests/Integration/Objects/PNUserIntegrationTest.m @@ -217,7 +217,6 @@ - (void)testUpdate_ShouldTriggerUpdateEvent_WhenUpdatingExistingUser { self.client1.updateUser().userId(userId).name(expectedName).custom(expectedCustom) .performWithCompletion(^(PNUpdateUserStatus *status) { }); }]; - } - (void)testUpdate_ShouldFail_WhenUserNotExist { diff --git a/Tests/iOS Tests/Tests/Integration/PNPubNubHistoryIntegrationTest.m b/Tests/iOS Tests/Tests/Integration/PNPubNubHistoryIntegrationTest.m deleted file mode 100644 index af4724dc6..000000000 --- a/Tests/iOS Tests/Tests/Integration/PNPubNubHistoryIntegrationTest.m +++ /dev/null @@ -1,170 +0,0 @@ -/** - * @author Serhii Mamontov - * @copyright © 2010-2019 PubNub, Inc. - */ -#import -#import "PNTestCase.h" - - -#pragma mark Test interface declaration - -@interface PNPubNubHistoryIntegrationTest : PNTestCase - - -#pragma mark - Information - -@property (nonatomic, strong) PubNub *client; - - -#pragma mark - Misc - -/** - * @brief Publish messages with random time tokens to specified channels. - * - * @param messagesCount Number of messages which should be published. - * @param channels List of channel names to which messages should be published. - * - * @return Dictinoary where each key represent name of channel and values are timetokens of - * published messages. - */ -- (NSDictionary *> *)publishMessages:(NSUInteger)messagesCount - toChannels:(NSArray *)channels; - -#pragma mark - - - -@end - - -#pragma mark - Tests - -@implementation PNPubNubHistoryIntegrationTest - - -#pragma mark - Setup / Tear down - -- (void)setUp { - - [super setUp]; - - - dispatch_queue_t callbackQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); - PNConfiguration *configuration = [PNConfiguration configurationWithPublishKey:self.publishKey - subscribeKey:self.subscribeKey]; - - self.client = [PubNub clientWithConfiguration:configuration callbackQueue:callbackQueue]; -} - - -#pragma mark - Tests :: messageCounts - -- (void)testMessageCounts_ShouldFetchCount_WhenSingleChannelAndTimetokenPassed { - - NSArray *channels = @[[NSUUID UUID].UUIDString]; - NSDictionary *> *timetokensData = nil; - timetokensData = [self publishMessages:3 toChannels:channels]; - NSArray *channelTimetokens = timetokensData[channels.firstObject]; - NSNumber *timetoken = channelTimetokens[channelTimetokens.count - 2]; - NSDictionary *expected = @{ channels.firstObject: @(1) }; - - [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { - self.client.messageCounts().channels(channels).timetokens(@[timetoken]) - .performWithCompletion(^(PNMessageCountResult *result, PNErrorStatus *status) { - XCTAssertNil(status); - XCTAssertEqualObjects(result.data.channels, expected); - handler(); - }); - }]; -} - -- (void)testMessageCounts_ShouldFetchCount_WhenSingleTimetokenAndMultipleChannelsPassed { - - NSArray *channels = @[[NSUUID UUID].UUIDString, [NSUUID UUID].UUIDString]; - NSDictionary *> *timetokensData = nil; - timetokensData = [self publishMessages:3 toChannels:channels]; - NSArray *channelTimetokens = timetokensData[channels.firstObject]; - NSNumber *timetoken = channelTimetokens[channelTimetokens.count - 2]; - NSDictionary *expected = @{ channels.firstObject: @(1), channels.lastObject: @(3) }; - - [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { - self.client.messageCounts().channels(channels).timetokens(@[timetoken]) - .performWithCompletion(^(PNMessageCountResult *result, PNErrorStatus *status) { - XCTAssertNil(status); - XCTAssertEqualObjects(result.data.channels, expected); - handler(); - }); - }]; -} - -- (void)testMessageCounts_ShouldFetchCount_WhenPerChannelTimetokenPassed { - - NSArray *channels = @[[NSUUID UUID].UUIDString, [NSUUID UUID].UUIDString]; - NSDictionary *> *timetokensData = nil; - timetokensData = [self publishMessages:3 toChannels:channels]; - NSArray *channelTimetokens1 = timetokensData[channels.firstObject]; - NSArray *channelTimetokens2 = timetokensData[channels.lastObject]; - NSNumber *timetoken1 = channelTimetokens1[channelTimetokens1.count - 2]; - NSNumber *timetoken2 = channelTimetokens2[channelTimetokens2.count - 2]; - NSArray *timetokens = @[timetoken1, timetoken2]; - NSDictionary *expected = @{ channels.firstObject: @(1), channels.lastObject: @(1) }; - - [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { - self.client.messageCounts().channels(channels).timetokens(timetokens) - .performWithCompletion(^(PNMessageCountResult *result, PNErrorStatus *status) { - XCTAssertNil(status); - XCTAssertEqualObjects(result.data.channels, expected); - handler(); - }); - }]; -} - -- (void)testMessageCounts_ShouldFail_WhenTimetokenNotPassed { - - NSArray *channels = @[[NSUUID UUID].UUIDString, [NSUUID UUID].UUIDString]; - - [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { - self.client.messageCounts().channels(channels) - .performWithCompletion(^(PNMessageCountResult *result, PNErrorStatus *status) { - XCTAssertNil(result); - XCTAssertTrue(status.isError); - handler(); - }); - }]; -} - - -#pragma mark - Misc - -- (NSDictionary *> *)publishMessages:(NSUInteger)messagesCount - toChannels:(NSArray *)channels { - - NSMutableDictionary *channelsMessageTimetokens = [NSMutableDictionary new]; - - for (NSString *channel in channels) { - NSMutableArray *messageTimetokens = [NSMutableArray new]; - - for (NSUInteger msgIdx = 0; msgIdx < messagesCount; msgIdx++) { - [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { - self.client.publish().channel(channel) - .message(@{ @"msg": @([NSDate date].timeIntervalSince1970) }) - .performWithCompletion(^(PNPublishStatus * status) { - if (!status.isError) { - [messageTimetokens addObject:status.data.timetoken]; - } - handler(); - }); - }]; - } - - channelsMessageTimetokens[channel] = messageTimetokens; - } - - [self waitTask:@"waitHistoryABit" completionFor:self.delayedCheck]; - - return channelsMessageTimetokens; -} - -#pragma mark - - - -@end diff --git a/Tests/iOS Tests/Tests/Integration/PNSignalIntegrationTest.m b/Tests/iOS Tests/Tests/Integration/Publish/PNSignalIntegrationTest.m similarity index 100% rename from Tests/iOS Tests/Tests/Integration/PNSignalIntegrationTest.m rename to Tests/iOS Tests/Tests/Integration/Publish/PNSignalIntegrationTest.m diff --git a/Tests/iOS Tests/Tests/Integration/PNAPNSIntegrationTest.m b/Tests/iOS Tests/Tests/Integration/Push Notifications/PNAPNSIntegrationTest.m similarity index 100% rename from Tests/iOS Tests/Tests/Integration/PNAPNSIntegrationTest.m rename to Tests/iOS Tests/Tests/Integration/Push Notifications/PNAPNSIntegrationTest.m diff --git a/Tests/iOS Tests/Tests/Unit/Core/Actions/Messages/PNMessageActionsTest.m b/Tests/iOS Tests/Tests/Unit/Core/Actions/Messages/PNMessageActionsTest.m new file mode 100644 index 000000000..ad749a686 --- /dev/null +++ b/Tests/iOS Tests/Tests/Unit/Core/Actions/Messages/PNMessageActionsTest.m @@ -0,0 +1,379 @@ +/** + * @author Serhii Mamontov + * @copyright © 2010-2019 PubNub, Inc. + */ +#import +#import +#import +#import +#import "PNTestCase.h" + + +#pragma mark Test interface declaration + +@interface PNMessageActionsTest : PNTestCase + + +#pragma mark - Information + +@property (nonatomic, strong) PubNub *client; + +#pragma mark - + + +@end + + +#pragma mark - Tests + +@implementation PNMessageActionsTest + + +#pragma mark - Setup / Tear down + +- (void)setUp { + [super setUp]; + + + dispatch_queue_t callbackQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + PNConfiguration *configuration = [PNConfiguration configurationWithPublishKey:self.publishKey + subscribeKey:self.subscribeKey]; + + self.client = [PubNub clientWithConfiguration:configuration callbackQueue:callbackQueue]; +} + + +#pragma mark - Tests :: Add Message Action + +- (void)testAddMessageAction_ShouldReturnBuilder { + XCTAssertTrue([self.client.addMessageAction() isKindOfClass:[PNAddMessageActionAPICallBuilder class]]); +} + + +#pragma mark - Tests :: Add Message Action :: Call + +- (void)testAddMessageAction_ShouldProcessOperation_WhenCalled { + NSNumber *expectedMessageTimetoken = @([NSDate date].timeIntervalSince1970 * 1000); + NSString *expectedChannel = [NSUUID UUID].UUIDString; + NSString *expectedValue = [NSUUID UUID].UUIDString; + NSDictionary *expectedBody = @{ @"type": @"custom", @"value": expectedValue }; + NSData *expectedPayload = [NSJSONSerialization dataWithJSONObject:expectedBody + options:(NSJSONWritingOptions)0 + error:nil]; + + + id clientMock = [self mockForObject:self.client]; + id recorded = OCMExpect([clientMock processOperation:PNAddMessageActionOperation + withParameters:[OCMArg any] + data:[OCMArg any] + completionBlock:[OCMArg any]]) + .andDo(^(NSInvocation *invocation) { + PNRequestParameters *parameters = [self objectForInvocation:invocation argumentAtIndex:2]; + NSData *sentData = [self objectForInvocation:invocation argumentAtIndex:3]; + + XCTAssertEqualObjects(parameters.pathComponents[@"{channel}"], expectedChannel); + XCTAssertEqualObjects(parameters.pathComponents[@"{message-timetoken}"], + expectedMessageTimetoken.stringValue); + XCTAssertEqualObjects(sentData, expectedPayload); + }); + + [self waitForObject:clientMock recordedInvocationCall:recorded afterBlock:^{ + self.client.addMessageAction() + .channel(expectedChannel) + .messageTimetoken(expectedMessageTimetoken) + .type(@"custom") + .value(expectedValue) + .performWithCompletion(^(PNAddMessageActionStatus *status) {}); + }]; +} + +- (void)testAddMessageAction_ShouldReturnError_WhenChannelIsMissing { + NSNumber *expectedMessageTimetoken = @([NSDate date].timeIntervalSince1970 * 1000); + NSString *expectedValue = [NSUUID UUID].UUIDString; + + + [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { + self.client.addMessageAction() + .messageTimetoken(expectedMessageTimetoken) + .type(@"custom") + .value(expectedValue) + .performWithCompletion(^(PNAddMessageActionStatus *status) { + XCTAssertTrue(status.isError); + XCTAssertNotEqual([status.errorData.information rangeOfString:@"'channel'"].location, + NSNotFound); + + handler(); + }); + }]; +} + +- (void)testAddMessageAction_ShouldReturnError_WhenMessageTimetokenIsMissing { + NSString *expectedChannel = [NSUUID UUID].UUIDString; + NSString *expectedValue = [NSUUID UUID].UUIDString; + + + [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { + self.client.addMessageAction() + .channel(expectedChannel) + .type(@"custom") + .value(expectedValue) + .performWithCompletion(^(PNAddMessageActionStatus *status) { + XCTAssertTrue(status.isError); + XCTAssertNotEqual([status.errorData.information rangeOfString:@"'messageTimetoken'"].location, + NSNotFound); + + handler(); + }); + }]; +} + +- (void)testAddMessageAction_ShouldReturnError_WhenValueIsMissing { + NSNumber *expectedMessageTimetoken = @([NSDate date].timeIntervalSince1970 * 1000); + NSString *expectedChannel = [NSUUID UUID].UUIDString; + + + [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { + self.client.addMessageAction() + .channel(expectedChannel) + .messageTimetoken(expectedMessageTimetoken) + .type(@"custom") + .performWithCompletion(^(PNAddMessageActionStatus *status) { + XCTAssertTrue(status.isError); + XCTAssertNotEqual([status.errorData.information rangeOfString:@"'value'"].location, + NSNotFound); + + handler(); + }); + }]; +} + +- (void)testAddMessageAction_ShouldReturnError_WhenActionTypeNotSet { + NSNumber *expectedMessageTimetoken = @([NSDate date].timeIntervalSince1970 * 1000); + NSString *expectedChannel = [NSUUID UUID].UUIDString; + NSString *expectedValue = [NSUUID UUID].UUIDString; + + + [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { + self.client.addMessageAction() + .channel(expectedChannel) + .messageTimetoken(expectedMessageTimetoken) + .value(expectedValue) + .performWithCompletion(^(PNAddMessageActionStatus *status) { + XCTAssertTrue(status.isError); + XCTAssertNotEqual([status.errorData.information rangeOfString:@"'type'"].location, + NSNotFound); + + handler(); + }); + }]; +} + +- (void)testAddMessageAction_ShouldReturnError_WhenActionTypeTooLong { + NSNumber *expectedMessageTimetoken = @([NSDate date].timeIntervalSince1970 * 1000); + NSString *expectedChannel = [NSUUID UUID].UUIDString; + NSString *expectedValue = [NSUUID UUID].UUIDString; + + + [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { + self.client.addMessageAction() + .channel(expectedChannel) + .messageTimetoken(expectedMessageTimetoken) + .type([NSUUID UUID].UUIDString) + .value(expectedValue) + .performWithCompletion(^(PNAddMessageActionStatus *status) { + XCTAssertTrue(status.isError); + XCTAssertNotEqual([status.errorData.information rangeOfString:@"too long"].location, + NSNotFound); + + handler(); + }); + }]; +} + +- (void)testAddMessageAction_ShouldReturnError_WhenUnableToSerializeValue { + NSNumber *expectedMessageTimetoken = @([NSDate date].timeIntervalSince1970 * 1000); + NSString *expectedChannel = [NSUUID UUID].UUIDString; + NSString *expectedValue = (id)[NSDate date]; + + + [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { + PNAddMessageActionRequest *request = [PNAddMessageActionRequest requestWithChannel:expectedChannel + messageTimetoken:expectedMessageTimetoken]; + request.type = @"custom"; + request.value = expectedValue; + + [self.client addMessageActionWithRequest:request completion:^(PNAddMessageActionStatus *status) { +#pragma GCC diagnostic push +#pragma clang diagnostic ignored "-Warc-retain-cycles" + XCTAssertTrue(status.isError); +#pragma GCC diagnostic pop + XCTAssertNotEqual([status.errorData.information rangeOfString:@"Message action"].location, + NSNotFound); + + handler(); + }]; + }]; +} + + +#pragma mark - Tests :: Remove Message Action + +- (void)testRemoveMessageAction_ShouldReturnBuilder { + XCTAssertTrue([self.client.removeMessageAction() isKindOfClass:[PNRemoveMessageActionAPICallBuilder class]]); +} + + +#pragma mark - Tests :: Remove Message Action :: Call + +- (void)testRemoveMessageAction_ShouldProcessOperation_WhenCalled { + NSNumber *expectedActionTimetoken = @([NSDate date].timeIntervalSince1970 * 1000 + 1); + NSNumber *expectedMessageTimetoken = @([NSDate date].timeIntervalSince1970 * 1000); + NSString *expectedChannel = [NSUUID UUID].UUIDString; + + + id clientMock = [self mockForObject:self.client]; + id recorded = OCMExpect([clientMock processOperation:PNRemoveMessageActionOperation + withParameters:[OCMArg any] + data:[OCMArg any] + completionBlock:[OCMArg any]]) + .andDo(^(NSInvocation *invocation) { + PNRequestParameters *parameters = [self objectForInvocation:invocation argumentAtIndex:2]; + + XCTAssertEqualObjects(parameters.pathComponents[@"{channel}"], expectedChannel); + XCTAssertEqualObjects(parameters.pathComponents[@"{message-timetoken}"], + expectedMessageTimetoken.stringValue); + XCTAssertEqualObjects(parameters.pathComponents[@"{action-timetoken}"], + expectedActionTimetoken.stringValue); + }); + + [self waitForObject:clientMock recordedInvocationCall:recorded afterBlock:^{ + self.client.removeMessageAction() + .channel(expectedChannel) + .messageTimetoken(expectedMessageTimetoken) + .actionTimetoken(expectedActionTimetoken) + .performWithCompletion(^(PNAcknowledgmentStatus *status) {}); + }]; +} + +- (void)testRemoveMessageAction_ShouldReturnError_WhenChannelIsMissing { + NSNumber *expectedActionTimetoken = @([NSDate date].timeIntervalSince1970 * 1000 + 1); + NSNumber *expectedMessageTimetoken = @([NSDate date].timeIntervalSince1970 * 1000); + + + [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { + self.client.removeMessageAction() + .messageTimetoken(expectedMessageTimetoken) + .actionTimetoken(expectedActionTimetoken) + .performWithCompletion(^(PNAcknowledgmentStatus *status) { + XCTAssertTrue(status.isError); + XCTAssertNotEqual([status.errorData.information rangeOfString:@"'channel'"].location, + NSNotFound); + + handler(); + }); + }]; +} + +- (void)testRemoveMessageAction_ShouldReturnError_WhenMessageTimetokenIsMissing { + NSNumber *expectedActionTimetoken = @([NSDate date].timeIntervalSince1970 * 1000 + 1); + NSString *expectedChannel = [NSUUID UUID].UUIDString; + + + [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { + self.client.removeMessageAction() + .channel(expectedChannel) + .actionTimetoken(expectedActionTimetoken) + .performWithCompletion(^(PNAcknowledgmentStatus *status) { + XCTAssertTrue(status.isError); + XCTAssertNotEqual([status.errorData.information rangeOfString:@"'messageTimetoken'"].location, + NSNotFound); + + handler(); + }); + }]; +} + +- (void)testRemoveMessageAction_ShouldReturnError_WhenActionTimetokenIsMissing { + NSNumber *expectedMessageTimetoken = @([NSDate date].timeIntervalSince1970 * 1000); + NSString *expectedChannel = [NSUUID UUID].UUIDString; + + + [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { + self.client.removeMessageAction() + .channel(expectedChannel) + .messageTimetoken(expectedMessageTimetoken) + .performWithCompletion(^(PNAcknowledgmentStatus *status) { + XCTAssertTrue(status.isError); + XCTAssertNotEqual([status.errorData.information rangeOfString:@"'actionTimetoken'"].location, + NSNotFound); + + handler(); + }); + }]; +} + + +#pragma mark - Tests :: Fetch Messages Actions + +- (void)testFetchMessagesActions_ShouldReturnBuilder { + XCTAssertTrue([self.client.fetchMessageActions() isKindOfClass:[PNFetchMessagesActionsAPICallBuilder class]]); +} + + +#pragma mark - Tests :: Fetch Messages Actions + +- (void)testFetchMessagesActions_ShouldProcessOperation_WhenCalled { + NSNumber *expectedStart = @([NSDate date].timeIntervalSince1970 * 1000 + 1); + NSNumber *expectedEnd = @([NSDate date].timeIntervalSince1970 * 1000); + NSString *expectedChannel = [NSUUID UUID].UUIDString; + NSNumber *expectedLimit = @(56); + + + id clientMock = [self mockForObject:self.client]; + id recorded = OCMExpect([clientMock processOperation:PNFetchMessagesActionsOperation + withParameters:[OCMArg any] + data:[OCMArg any] + completionBlock:[OCMArg any]]) + .andDo(^(NSInvocation *invocation) { + PNRequestParameters *parameters = [self objectForInvocation:invocation argumentAtIndex:2]; + + XCTAssertEqualObjects(parameters.query[@"start"], expectedStart.stringValue); + XCTAssertEqualObjects(parameters.query[@"end"], expectedEnd.stringValue); + XCTAssertEqualObjects(parameters.query[@"limit"], expectedLimit.stringValue); + }); + + [self waitForObject:clientMock recordedInvocationCall:recorded afterBlock:^{ + self.client.fetchMessageActions() + .channel(expectedChannel) + .start(expectedStart) + .end(expectedEnd) + .limit(expectedLimit.unsignedIntegerValue) + .performWithCompletion(^(PNFetchMessageActionsResult *result, PNErrorStatus *status) {}); + }]; +} + +- (void)testFetchMessagesActions_ShouldReturnError_WhenChannelIsMissing { + NSNumber *expectedStart = @([NSDate date].timeIntervalSince1970 * 1000 + 1); + NSNumber *expectedEnd = @([NSDate date].timeIntervalSince1970 * 1000); + NSNumber *expectedLimit = @(56); + + + [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { + self.client.fetchMessageActions() + .start(expectedStart) + .end(expectedEnd) + .limit(expectedLimit.unsignedIntegerValue) + .performWithCompletion(^(PNFetchMessageActionsResult *result, PNErrorStatus *status) { + XCTAssertTrue(status.isError); + XCTAssertNotEqual([status.errorData.information rangeOfString:@"'channel'"].location, + NSNotFound); + + handler(); + }); + }]; +} + +#pragma mark - + + +@end diff --git a/Tests/iOS Tests/Tests/Unit/Core/Interfaces/Actions/Messages/PNMessageActionsAPICallBuilderTest.m b/Tests/iOS Tests/Tests/Unit/Core/Interfaces/Actions/Messages/PNMessageActionsAPICallBuilderTest.m new file mode 100644 index 000000000..9301894cb --- /dev/null +++ b/Tests/iOS Tests/Tests/Unit/Core/Interfaces/Actions/Messages/PNMessageActionsAPICallBuilderTest.m @@ -0,0 +1,520 @@ +/** + * @author Serhii Mamontov + * @copyright © 2010-2019 PubNub, Inc. + */ +#import +#import +#import +#import + + +#pragma mark Test interface declaration + +@interface PNMessageActionsAPICallBuilderTest : XCTestCase + + +#pragma mark - Misc + +- (PNAddMessageActionAPICallBuilder *)addMessageActionBuilder; +- (PNRemoveMessageActionAPICallBuilder *)removeMessageActionBuilder; +- (PNFetchMessagesActionsAPICallBuilder *)fetchMessagesActionsBuilder; + +- (void)expect:(BOOL)shouldCall mock:(id)mockedObject toSetValue:(id)value toParameter:(NSString *)parameter; +- (NSString *)mockedParameterFrom:(NSString *)parameter; + +#pragma mark - + + +@end + + +#pragma mark - Tests + +@implementation PNMessageActionsAPICallBuilderTest + + +#pragma mark - Tests :: add :: messageTimetoken + +- (void)testAddMessageTimetoken_ShouldReturnAddBuilder_WhenCalled { + id builder = [self addMessageActionBuilder]; + + id addBuilder = ((PNAddMessageActionAPICallBuilder *)builder).messageTimetoken(@(2010)); + XCTAssertEqual(addBuilder, builder); +} + +- (void)testAdd_ShouldSetMessageTimetoken_WhenNSNumberPassed { + PNAddMessageActionAPICallBuilder *builder = [self addMessageActionBuilder]; + NSNumber *expected = @(2010); + + + id builderMock = OCMPartialMock(builder); + [self expect:YES mock:builderMock toSetValue:expected toParameter:@"messageTimetoken"]; + + builder.messageTimetoken(expected); + + OCMVerify(builderMock); +} + +- (void)testAdd_ShouldNotSetMessageTimetoken_WhenNonNSNumberPassed { + PNAddMessageActionAPICallBuilder *builder = [self addMessageActionBuilder]; + NSNumber *expected = (id)@"PubNub"; + + + id builderMock = OCMPartialMock(builder); + [self expect:NO mock:builderMock toSetValue:expected toParameter:@"messageTimetoken"]; + + builder.messageTimetoken(expected); + + OCMVerify(builderMock); +} + + +#pragma mark - Tests :: add :: type + +- (void)testAddType_ShouldReturnAddBuilder_WhenCalled { + id builder = [self addMessageActionBuilder]; + + id addBuilder = ((PNAddMessageActionAPICallBuilder *)builder).type(@"receipt"); + XCTAssertEqual(addBuilder, builder); +} + +- (void)testAdd_ShouldSetType_WhenNSStringPassed { + PNAddMessageActionAPICallBuilder *builder = [self addMessageActionBuilder]; + NSString *expected = @"receipt"; + + + id builderMock = OCMPartialMock(builder); + [self expect:YES mock:builderMock toSetValue:expected toParameter:@"type"]; + + builder.type(expected); + + OCMVerify(builderMock); +} + +- (void)testAdd_ShouldNotSetType_WhenEmptyNSStringPassed { + PNAddMessageActionAPICallBuilder *builder = [self addMessageActionBuilder]; + NSString *expected = @""; + + + id builderMock = OCMPartialMock(builder); + [self expect:NO mock:builderMock toSetValue:expected toParameter:@"type"]; + + builder.type(expected); + + OCMVerify(builderMock); +} + +- (void)testAdd_ShouldNotSetType_WhenNonNSStringPassed { + PNAddMessageActionAPICallBuilder *builder = [self addMessageActionBuilder]; + NSString *expected = (id)@2010; + + + id builderMock = OCMPartialMock(builder); + [self expect:NO mock:builderMock toSetValue:expected toParameter:@"type"]; + + builder.type(expected); + + OCMVerify(builderMock); +} + + +#pragma mark - Tests :: add :: channel + +- (void)testAddChannel_ShouldReturnAddBuilder_WhenCalled { + id builder = [self addMessageActionBuilder]; + + id addBuilder = ((PNAddMessageActionAPICallBuilder *)builder).channel(@"secret"); + XCTAssertEqual(addBuilder, builder); +} + +- (void)testAdd_ShouldSetChannel_WhenNSStringPassed { + PNAddMessageActionAPICallBuilder *builder = [self addMessageActionBuilder]; + NSString *expected = @"secret"; + + + id builderMock = OCMPartialMock(builder); + [self expect:YES mock:builderMock toSetValue:expected toParameter:@"channel"]; + + builder.channel(expected); + + OCMVerify(builderMock); +} + +- (void)testAdd_ShouldNotSetChannel_WhenEmptyNSStringPassed { + PNAddMessageActionAPICallBuilder *builder = [self addMessageActionBuilder]; + NSString *expected = @""; + + + id builderMock = OCMPartialMock(builder); + [self expect:NO mock:builderMock toSetValue:expected toParameter:@"channel"]; + + builder.channel(expected); + + OCMVerify(builderMock); +} + +- (void)testAdd_ShouldNotSetChannel_WhenNonNSStringPassed { + PNAddMessageActionAPICallBuilder *builder = [self addMessageActionBuilder]; + NSString *expected = (id)@2010; + + + id builderMock = OCMPartialMock(builder); + [self expect:NO mock:builderMock toSetValue:expected toParameter:@"channel"]; + + builder.channel(expected); + + OCMVerify(builderMock); +} + + +#pragma mark - Tests :: add :: value + +- (void)testAddValue_ShouldReturnAddBuilder_WhenCalled { + id builder = [self addMessageActionBuilder]; + + id addBuilder = ((PNAddMessageActionAPICallBuilder *)builder).value(@"smile"); + XCTAssertEqual(addBuilder, builder); +} + +- (void)testAdd_ShouldSetValue_WhenNSStringPassed { + PNAddMessageActionAPICallBuilder *builder = [self addMessageActionBuilder]; + NSString *expected = @"smile"; + + + id builderMock = OCMPartialMock(builder); + [self expect:YES mock:builderMock toSetValue:expected toParameter:@"value"]; + + builder.value(expected); + + OCMVerify(builderMock); +} + +- (void)testAdd_ShouldNotSetValue_WhenEmptyNSStringPassed { + PNAddMessageActionAPICallBuilder *builder = [self addMessageActionBuilder]; + NSString *expected = @""; + + + id builderMock = OCMPartialMock(builder); + [self expect:NO mock:builderMock toSetValue:expected toParameter:@"value"]; + + builder.value(expected); + + OCMVerify(builderMock); +} + +- (void)testAdd_ShouldNotSetValue_WhenNonNSStringPassed { + PNAddMessageActionAPICallBuilder *builder = [self addMessageActionBuilder]; + NSString *expected = (id)@2010; + + + id builderMock = OCMPartialMock(builder); + [self expect:NO mock:builderMock toSetValue:expected toParameter:@"value"]; + + builder.value(expected); + + OCMVerify(builderMock); +} + + +#pragma mark - Tests :: remove :: messageTimetoken + +- (void)testRemoveMessageTimetoken_ShouldReturnAddBuilder_WhenCalled { + id builder = [self removeMessageActionBuilder]; + + id removeBuilder = ((PNRemoveMessageActionAPICallBuilder *)builder).messageTimetoken(@(2010)); + XCTAssertEqual(removeBuilder, builder); +} + +- (void)testRemove_ShouldSetMessageTimetoken_WhenNSNumberPassed { + PNRemoveMessageActionAPICallBuilder *builder = [self removeMessageActionBuilder]; + NSNumber *expected = @(2010); + + + id builderMock = OCMPartialMock(builder); + [self expect:YES mock:builderMock toSetValue:expected toParameter:@"messageTimetoken"]; + + builder.messageTimetoken(expected); + + OCMVerify(builderMock); +} + +- (void)testRemove_ShouldNotSetMessageTimetoken_WhenNonNSNumberPassed { + PNRemoveMessageActionAPICallBuilder *builder = [self removeMessageActionBuilder]; + NSNumber *expected = (id)@"PubNub"; + + + id builderMock = OCMPartialMock(builder); + [self expect:NO mock:builderMock toSetValue:expected toParameter:@"messageTimetoken"]; + + builder.messageTimetoken(expected); + + OCMVerify(builderMock); +} + + +#pragma mark - Tests :: remove :: actionTimetoken + +- (void)testRemoveActionTimetoken_ShouldReturnAddBuilder_WhenCalled { + id builder = [self removeMessageActionBuilder]; + + id removeBuilder = ((PNRemoveMessageActionAPICallBuilder *)builder).actionTimetoken(@(2010)); + XCTAssertEqual(removeBuilder, builder); +} + +- (void)testRemove_ShouldSetActionTimetoken_WhenNSNumberPassed { + PNRemoveMessageActionAPICallBuilder *builder = [self removeMessageActionBuilder]; + NSNumber *expected = @(2010); + + + id builderMock = OCMPartialMock(builder); + [self expect:YES mock:builderMock toSetValue:expected toParameter:@"actionTimetoken"]; + + builder.actionTimetoken(expected); + + OCMVerify(builderMock); +} + +- (void)testRemove_ShouldNotSetActionTimetoken_WhenNonNSNumberPassed { + PNRemoveMessageActionAPICallBuilder *builder = [self removeMessageActionBuilder]; + NSNumber *expected = (id)@"PubNub"; + + + id builderMock = OCMPartialMock(builder); + [self expect:NO mock:builderMock toSetValue:expected toParameter:@"actionTimetoken"]; + + builder.actionTimetoken(expected); + + OCMVerify(builderMock); +} + + +#pragma mark - Tests :: remove :: channel + +- (void)testRemoveChannel_ShouldReturnAddBuilder_WhenCalled { + id builder = [self removeMessageActionBuilder]; + + id removeBuilder = ((PNRemoveMessageActionAPICallBuilder *)builder).channel(@"secret"); + XCTAssertEqual(removeBuilder, builder); +} + +- (void)testRemove_ShouldSetChannel_WhenNSStringPassed { + PNRemoveMessageActionAPICallBuilder *builder = [self removeMessageActionBuilder]; + NSString *expected = @"secret"; + + + id builderMock = OCMPartialMock(builder); + [self expect:YES mock:builderMock toSetValue:expected toParameter:@"channel"]; + + builder.channel(expected); + + OCMVerify(builderMock); +} + +- (void)testRemove_ShouldNotSetChannel_WhenEmptyNSStringPassed { + PNRemoveMessageActionAPICallBuilder *builder = [self removeMessageActionBuilder]; + NSString *expected = @""; + + + id builderMock = OCMPartialMock(builder); + [self expect:NO mock:builderMock toSetValue:expected toParameter:@"channel"]; + + builder.channel(expected); + + OCMVerify(builderMock); +} + +- (void)testRemove_ShouldNotSetChannel_WhenNonNSStringPassed { + PNRemoveMessageActionAPICallBuilder *builder = [self removeMessageActionBuilder]; + NSString *expected = (id)@2010; + + + id builderMock = OCMPartialMock(builder); + [self expect:NO mock:builderMock toSetValue:expected toParameter:@"channel"]; + + builder.channel(expected); + + OCMVerify(builderMock); +} + + +#pragma mark - Tests :: fetch :: channel + +- (void)testFetchChannel_ShouldReturnFetchBuilder_WhenCalled { + id builder = [self fetchMessagesActionsBuilder]; + + id fetchBuilder = ((PNFetchMessagesActionsAPICallBuilder *)builder).channel(@"secret"); + XCTAssertEqual(fetchBuilder, builder); +} + +- (void)testFetch_ShouldSetChannel_WhenNSStringPassed { + PNFetchMessagesActionsAPICallBuilder *builder = [self fetchMessagesActionsBuilder]; + NSString *expected = @"secret"; + + + id builderMock = OCMPartialMock(builder); + [self expect:YES mock:builderMock toSetValue:expected toParameter:@"channel"]; + + builder.channel(expected); + + OCMVerify(builderMock); +} + +- (void)testFetch_ShouldNotSetChannel_WhenEmptyNSStringPassed { + PNFetchMessagesActionsAPICallBuilder *builder = [self fetchMessagesActionsBuilder]; + NSString *expected = @""; + + + id builderMock = OCMPartialMock(builder); + [self expect:NO mock:builderMock toSetValue:expected toParameter:@"channel"]; + + builder.channel(expected); + + OCMVerify(builderMock); +} + +- (void)testFetch_ShouldNotSetChannel_WhenNonNSStringPassed { + PNFetchMessagesActionsAPICallBuilder *builder = [self fetchMessagesActionsBuilder]; + NSString *expected = (id)@2010; + + + id builderMock = OCMPartialMock(builder); + [self expect:NO mock:builderMock toSetValue:expected toParameter:@"channel"]; + + builder.channel(expected); + + OCMVerify(builderMock); +} + +#pragma mark - Tests :: fetch :: start + +- (void)testFetchStart_ShouldReturnFetchBuilder_WhenCalled { + id builder = [self fetchMessagesActionsBuilder]; + + id fetchBuilder = ((PNFetchMessagesActionsAPICallBuilder *)builder).start(@(2010)); + XCTAssertEqual(fetchBuilder, builder); +} + +- (void)testFetch_ShouldSetStart_WhenNSNumberPassed { + PNFetchMessagesActionsAPICallBuilder *builder = [self fetchMessagesActionsBuilder]; + NSNumber *expected = @(2010); + + + id builderMock = OCMPartialMock(builder); + [self expect:YES mock:builderMock toSetValue:expected toParameter:@"start"]; + + builder.start(expected); + + OCMVerify(builderMock); +} + +- (void)testFetch_ShouldNotSetStart_WhenNonNSNumberPassed { + PNFetchMessagesActionsAPICallBuilder *builder = [self fetchMessagesActionsBuilder]; + NSNumber *expected = (id)@"PubNub"; + + + id builderMock = OCMPartialMock(builder); + [self expect:NO mock:builderMock toSetValue:expected toParameter:@"start"]; + + builder.start(expected); + + OCMVerify(builderMock); +} + + +#pragma mark - Tests :: fetch :: end + +- (void)testFetchEnd_ShouldReturnFetchAllBuilder_WhenCalled { + id builder = [self fetchMessagesActionsBuilder]; + + id fetchBuilder = ((PNFetchMessagesActionsAPICallBuilder *)builder).end(@(2010)); + XCTAssertEqual(fetchBuilder, builder); +} + +- (void)testFetch_ShouldSetEnd_WhenNSStringPassed { + PNFetchMessagesActionsAPICallBuilder *builder = [self fetchMessagesActionsBuilder]; + NSNumber *expected = @(2010); + + + id builderMock = OCMPartialMock(builder); + [self expect:YES mock:builderMock toSetValue:expected toParameter:@"end"]; + + builder.end(expected); + + OCMVerify(builderMock); +} + +- (void)testFetch_ShouldNotSetEnd_WhenNonNSStringPassed { + PNFetchMessagesActionsAPICallBuilder *builder = [self fetchMessagesActionsBuilder]; + NSNumber *expected = (id)@"PubNub"; + + + id builderMock = OCMPartialMock(builder); + [self expect:NO mock:builderMock toSetValue:expected toParameter:@"end"]; + + builder.end(expected); + + OCMVerify(builderMock); +} + + +#pragma mark - Tests :: fetch :: limit + +- (void)testFetchLimit_ShouldReturnFetchAllBuilder_WhenCalled { + id builder = [self fetchMessagesActionsBuilder]; + + id fetchBuilder = ((PNFetchMessagesActionsAPICallBuilder *)builder).limit(20); + XCTAssertEqual(fetchBuilder, builder); +} + +- (void)testFetchAll_ShouldSetLimit_WhenCalled { + PNFetchMessagesActionsAPICallBuilder *builder = [self fetchMessagesActionsBuilder]; + NSNumber *expected = @YES; + + + id builderMock = OCMPartialMock(builder); + [self expect:YES mock:builderMock toSetValue:expected toParameter:@"limit"]; + + builder.limit(35); + + OCMVerify(builderMock); +} + + +#pragma mark - Misc + +- (PNAddMessageActionAPICallBuilder *)addMessageActionBuilder { + return [PNAddMessageActionAPICallBuilder builderWithExecutionBlock:^(NSArray *flags, + NSDictionary *arguments) { + }]; +} + +- (PNRemoveMessageActionAPICallBuilder *)removeMessageActionBuilder { + return [PNRemoveMessageActionAPICallBuilder builderWithExecutionBlock:^(NSArray *flags, + NSDictionary *arguments) { + }]; +} + +- (PNFetchMessagesActionsAPICallBuilder *)fetchMessagesActionsBuilder { + return [PNFetchMessagesActionsAPICallBuilder builderWithExecutionBlock:^(NSArray *flags, + NSDictionary *arguments) { + }]; +} + +- (void)expect:(BOOL)shouldCall mock:(id)mockedObject toSetValue:(id)value toParameter:(NSString *)parameter { + parameter = [self mockedParameterFrom:parameter]; + + if (shouldCall) { + OCMExpect([mockedObject setValue:value forParameter:parameter]); + } else { + OCMExpect([[mockedObject reject] setValue:value forParameter:parameter]); + } +} + +- (NSString *)mockedParameterFrom:(NSString *)parameter { + return [@[@"ocmock_replaced", parameter] componentsJoinedByString:@"_"]; +} + +#pragma mark - + + +@end diff --git a/Tests/iOS Tests/Tests/Unit/Core/Interfaces/Objects/PNUserObjectsAPICallBuilderTest.m b/Tests/iOS Tests/Tests/Unit/Core/Interfaces/Objects/PNUserObjectsAPICallBuilderTest.m index d661e3bee..5c06be2b0 100644 --- a/Tests/iOS Tests/Tests/Unit/Core/Interfaces/Objects/PNUserObjectsAPICallBuilderTest.m +++ b/Tests/iOS Tests/Tests/Unit/Core/Interfaces/Objects/PNUserObjectsAPICallBuilderTest.m @@ -839,7 +839,7 @@ - (void)testFetchAll_ShouldNotSetStart_WhenNonNSStringPassed { } -#pragma mark - Tests :: fetch all :: start +#pragma mark - Tests :: fetch all :: end - (void)testFetchAllEnd_ShouldReturnFetchAllBuilder_WhenCalled { id builder = [self fetchAllBuilder]; diff --git a/Tests/iOS Tests/Tests/Unit/Core/Objects/PNMemberObjectsTest.m b/Tests/iOS Tests/Tests/Unit/Core/Objects/PNMemberObjectsTest.m index bcb940a18..048323851 100644 --- a/Tests/iOS Tests/Tests/Unit/Core/Objects/PNMemberObjectsTest.m +++ b/Tests/iOS Tests/Tests/Unit/Core/Objects/PNMemberObjectsTest.m @@ -80,7 +80,7 @@ - (void)testManageMembers_ShouldProcessOperation_WhenCalled { PNRequestParameters *parameters = [self objectForInvocation:invocation argumentAtIndex:2]; NSData *sentData = [self objectForInvocation:invocation argumentAtIndex:3]; - XCTAssertEqualObjects(parameters.pathComponents[@"{space_id}"], expectedId); + XCTAssertEqualObjects(parameters.pathComponents[@"{space-id}"], expectedId); XCTAssertEqualObjects(parameters.query[@"include"], @"custom,user.custom"); XCTAssertEqualObjects(sentData, expectedPayload); }); @@ -99,7 +99,7 @@ - (void)testManageMembers_ShouldReturnError_WhenSpaceIdIsMissing { self.client.manageMembers().includeFields(PNMemberCustomField) .performWithCompletion(^(PNManageMembersStatus *status) { XCTAssertTrue(status.isError); - XCTAssertNotEqual([status.errorData.information rangeOfString:@"'space_id'"].location, + XCTAssertNotEqual([status.errorData.information rangeOfString:@"'space-id'"].location, NSNotFound); handler(); @@ -155,7 +155,7 @@ - (void)testFetchMembers_ShouldProcessOperation_WhenCalled { .andDo(^(NSInvocation *invocation) { PNRequestParameters *parameters = [self objectForInvocation:invocation argumentAtIndex:2]; - XCTAssertEqualObjects(parameters.pathComponents[@"{space_id}"], expectedId); + XCTAssertEqualObjects(parameters.pathComponents[@"{space-id}"], expectedId); XCTAssertEqualObjects(parameters.query[@"include"], @"custom"); XCTAssertEqualObjects(parameters.query[@"start"], expectedStart); XCTAssertEqualObjects(parameters.query[@"end"], expectedEnd); @@ -179,7 +179,7 @@ - (void)testFetchMembers_ShouldReturnError_WhenSpaceIdIsMissing { self.client.fetchMembers() .performWithCompletion(^(PNFetchMembersResult *result, PNErrorStatus *status) { XCTAssertTrue(status.isError); - XCTAssertNotEqual([status.errorData.information rangeOfString:@"'space_id'"].location, + XCTAssertNotEqual([status.errorData.information rangeOfString:@"'space-id'"].location, NSNotFound); handler(); diff --git a/Tests/iOS Tests/Tests/Unit/Core/Objects/PNMembershipObjectsTest.m b/Tests/iOS Tests/Tests/Unit/Core/Objects/PNMembershipObjectsTest.m index 3efb05291..4a33c7b2a 100644 --- a/Tests/iOS Tests/Tests/Unit/Core/Objects/PNMembershipObjectsTest.m +++ b/Tests/iOS Tests/Tests/Unit/Core/Objects/PNMembershipObjectsTest.m @@ -80,7 +80,7 @@ - (void)testManageMemberships_ShouldProcessOperation_WhenCalled { PNRequestParameters *parameters = [self objectForInvocation:invocation argumentAtIndex:2]; NSData *sentData = [self objectForInvocation:invocation argumentAtIndex:3]; - XCTAssertEqualObjects(parameters.pathComponents[@"{user_id}"], expectedId); + XCTAssertEqualObjects(parameters.pathComponents[@"{user-id}"], expectedId); XCTAssertEqualObjects(parameters.query[@"include"], @"custom,space.custom"); XCTAssertEqualObjects(sentData, expectedPayload); }); @@ -99,7 +99,7 @@ - (void)testManageMemberships_ShouldReturnError_WhenUserIdIsMissing { self.client.manageMemberships().includeFields(PNMembershipCustomField) .performWithCompletion(^(PNManageMembershipsStatus *status) { XCTAssertTrue(status.isError); - XCTAssertNotEqual([status.errorData.information rangeOfString:@"'user_id'"].location, + XCTAssertNotEqual([status.errorData.information rangeOfString:@"'user-id'"].location, NSNotFound); handler(); @@ -155,7 +155,7 @@ - (void)testFetchMemberships_ShouldProcessOperation_WhenCalled { .andDo(^(NSInvocation *invocation) { PNRequestParameters *parameters = [self objectForInvocation:invocation argumentAtIndex:2]; - XCTAssertEqualObjects(parameters.pathComponents[@"{user_id}"], expectedId); + XCTAssertEqualObjects(parameters.pathComponents[@"{user-id}"], expectedId); XCTAssertEqualObjects(parameters.query[@"include"], @"custom"); XCTAssertEqualObjects(parameters.query[@"start"], expectedStart); XCTAssertEqualObjects(parameters.query[@"end"], expectedEnd); @@ -179,7 +179,7 @@ - (void)testFetchMemberships_ShouldReturnError_WhenUserIdIsMissing { self.client.fetchMemberships() .performWithCompletion(^(PNFetchMembershipsResult *result, PNErrorStatus *status) { XCTAssertTrue(status.isError); - XCTAssertNotEqual([status.errorData.information rangeOfString:@"'user_id'"].location, + XCTAssertNotEqual([status.errorData.information rangeOfString:@"'user-id'"].location, NSNotFound); handler(); diff --git a/Tests/iOS Tests/Tests/Unit/Core/Objects/PNSpaceObjectsTest.m b/Tests/iOS Tests/Tests/Unit/Core/Objects/PNSpaceObjectsTest.m index 146038bf3..2ab7ec377 100644 --- a/Tests/iOS Tests/Tests/Unit/Core/Objects/PNSpaceObjectsTest.m +++ b/Tests/iOS Tests/Tests/Unit/Core/Objects/PNSpaceObjectsTest.m @@ -85,7 +85,7 @@ - (void)testCreateSpace_ShouldProcessOperation_WhenCalled { PNRequestParameters *parameters = [self objectForInvocation:invocation argumentAtIndex:2]; NSData *sentData = [self objectForInvocation:invocation argumentAtIndex:3]; - XCTAssertEqualObjects(parameters.pathComponents[@"{space_id}"], expectedId); + XCTAssertEqualObjects(parameters.pathComponents[@"{space-id}"], expectedId); XCTAssertEqualObjects(parameters.query[@"include"], @"custom"); XCTAssertEqualObjects(sentData, expectedPayload); }); @@ -124,7 +124,7 @@ - (void)testCreateSpace_ShouldReturnError_WhenSpaceIdIsMissing { self.client.createSpace().name(expectedName).includeFields(PNSpaceCustomField) .performWithCompletion(^(PNCreateSpaceStatus *status) { XCTAssertTrue(status.isError); - XCTAssertNotEqual([status.errorData.information rangeOfString:@"'space_id'"].location, + XCTAssertNotEqual([status.errorData.information rangeOfString:@"'space-id'"].location, NSNotFound); handler(); @@ -229,7 +229,7 @@ - (void)testUpdateSpace_ShouldProcessOperation_WhenCalled { PNRequestParameters *parameters = [self objectForInvocation:invocation argumentAtIndex:2]; NSData *sentData = [self objectForInvocation:invocation argumentAtIndex:3]; - XCTAssertEqualObjects(parameters.pathComponents[@"{space_id}"], expectedId); + XCTAssertEqualObjects(parameters.pathComponents[@"{space-id}"], expectedId); XCTAssertEqualObjects(parameters.query[@"include"], @"custom"); XCTAssertEqualObjects(sentData, expectedPayload); }); @@ -267,7 +267,7 @@ - (void)testUpdateSpace_ShouldReturnError_WhenSpaceIdIsMissing { self.client.updateSpace().name(expectedName).includeFields(PNSpaceCustomField) .performWithCompletion(^(PNUpdateSpaceStatus *status) { XCTAssertTrue(status.isError); - XCTAssertNotEqual([status.errorData.information rangeOfString:@"'space_id'"].location, + XCTAssertNotEqual([status.errorData.information rangeOfString:@"'space-id'"].location, NSNotFound); handler(); @@ -328,7 +328,7 @@ - (void)testDeleteSpace_ShouldProcessOperation_WhenCalled { .andDo(^(NSInvocation *invocation) { PNRequestParameters *parameters = [self objectForInvocation:invocation argumentAtIndex:2]; - XCTAssertEqualObjects(parameters.pathComponents[@"{space_id}"], expectedId); + XCTAssertEqualObjects(parameters.pathComponents[@"{space-id}"], expectedId); }); [self waitForObject:clientMock recordedInvocationCall:recorded afterBlock:^{ @@ -341,7 +341,7 @@ - (void)testDeleteSpace_ShouldReturnError_WhenSpaceIdIsMissing { [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { self.client.deleteSpace().performWithCompletion(^(PNAcknowledgmentStatus *status) { XCTAssertTrue(status.isError); - XCTAssertNotEqual([status.errorData.information rangeOfString:@"'space_id'"].location, + XCTAssertNotEqual([status.errorData.information rangeOfString:@"'space-id'"].location, NSNotFound); handler(); @@ -378,7 +378,7 @@ - (void)testFetchSpace_ShouldProcessOperation_WhenCalled { .andDo(^(NSInvocation *invocation) { PNRequestParameters *parameters = [self objectForInvocation:invocation argumentAtIndex:2]; - XCTAssertEqualObjects(parameters.pathComponents[@"{space_id}"], expectedId); + XCTAssertEqualObjects(parameters.pathComponents[@"{space-id}"], expectedId); XCTAssertEqualObjects(parameters.query[@"include"], @"custom"); }); @@ -411,7 +411,7 @@ - (void)testFetchSpace_ShouldReturnError_WhenSpaceIdIsMissing { self.client.fetchSpace() .performWithCompletion(^(PNFetchSpaceResult *result, PNErrorStatus *status) { XCTAssertTrue(status.isError); - XCTAssertNotEqual([status.errorData.information rangeOfString:@"'space_id'"].location, + XCTAssertNotEqual([status.errorData.information rangeOfString:@"'space-id'"].location, NSNotFound); handler(); diff --git a/Tests/iOS Tests/Tests/Unit/Core/Objects/PNUserObjectsTest.m b/Tests/iOS Tests/Tests/Unit/Core/Objects/PNUserObjectsTest.m index 409a81dc5..368995397 100644 --- a/Tests/iOS Tests/Tests/Unit/Core/Objects/PNUserObjectsTest.m +++ b/Tests/iOS Tests/Tests/Unit/Core/Objects/PNUserObjectsTest.m @@ -83,7 +83,7 @@ - (void)testCreateUser_ShouldProcessOperation_WhenCalled { PNRequestParameters *parameters = [self objectForInvocation:invocation argumentAtIndex:2]; NSData *sentData = [self objectForInvocation:invocation argumentAtIndex:3]; - XCTAssertEqualObjects(parameters.pathComponents[@"{user_id}"], expectedId); + XCTAssertEqualObjects(parameters.pathComponents[@"{user-id}"], expectedId); XCTAssertEqualObjects(parameters.query[@"include"], @"custom"); XCTAssertEqualObjects(sentData, expectedPayload); }); @@ -121,7 +121,7 @@ - (void)testCreateUser_ShouldReturnError_WhenUserIdIsMissing { self.client.createUser().name(expectedName).includeFields(PNUserCustomField) .performWithCompletion(^(PNCreateUserStatus *status) { XCTAssertTrue(status.isError); - XCTAssertNotEqual([status.errorData.information rangeOfString:@"'user_id'"].location, + XCTAssertNotEqual([status.errorData.information rangeOfString:@"'user-id'"].location, NSNotFound); handler(); @@ -224,7 +224,7 @@ - (void)testUpdateUser_ShouldProcessOperation_WhenCalled { PNRequestParameters *parameters = [self objectForInvocation:invocation argumentAtIndex:2]; NSData *sentData = [self objectForInvocation:invocation argumentAtIndex:3]; - XCTAssertEqualObjects(parameters.pathComponents[@"{user_id}"], expectedId); + XCTAssertEqualObjects(parameters.pathComponents[@"{user-id}"], expectedId); XCTAssertEqualObjects(parameters.query[@"include"], @"custom"); XCTAssertEqualObjects(sentData, expectedPayload); }); @@ -262,7 +262,7 @@ - (void)testUpdateUser_ShouldReturnError_WhenUserIdIsMissing { self.client.updateUser().name(expectedName).includeFields(PNUserCustomField) .performWithCompletion(^(PNUpdateUserStatus *status) { XCTAssertTrue(status.isError); - XCTAssertNotEqual([status.errorData.information rangeOfString:@"'user_id'"].location, + XCTAssertNotEqual([status.errorData.information rangeOfString:@"'user-id'"].location, NSNotFound); handler(); @@ -324,7 +324,7 @@ - (void)testDeleteUser_ShouldProcessOperation_WhenCalled { .andDo(^(NSInvocation *invocation) { PNRequestParameters *parameters = [self objectForInvocation:invocation argumentAtIndex:2]; - XCTAssertEqualObjects(parameters.pathComponents[@"{user_id}"], expectedId); + XCTAssertEqualObjects(parameters.pathComponents[@"{user-id}"], expectedId); }); [self waitForObject:clientMock recordedInvocationCall:recorded afterBlock:^{ @@ -337,7 +337,7 @@ - (void)testDeleteUser_ShouldReturnError_WhenUserIdIsMissing { [self waitToCompleteIn:self.testCompletionDelay codeBlock:^(dispatch_block_t handler) { self.client.updateUser().performWithCompletion(^(PNAcknowledgmentStatus *status) { XCTAssertTrue(status.isError); - XCTAssertNotEqual([status.errorData.information rangeOfString:@"'user_id'"].location, + XCTAssertNotEqual([status.errorData.information rangeOfString:@"'user-id'"].location, NSNotFound); handler(); @@ -374,7 +374,7 @@ - (void)testFetchUser_ShouldProcessOperation_WhenCalled { .andDo(^(NSInvocation *invocation) { PNRequestParameters *parameters = [self objectForInvocation:invocation argumentAtIndex:2]; - XCTAssertEqualObjects(parameters.pathComponents[@"{user_id}"], expectedId); + XCTAssertEqualObjects(parameters.pathComponents[@"{user-id}"], expectedId); XCTAssertEqualObjects(parameters.query[@"include"], @"custom"); }); @@ -407,7 +407,7 @@ - (void)testFetchUser_ShouldReturnError_WhenUserIdIsMissing { self.client.fetchUser() .performWithCompletion(^(PNFetchUserResult *result, PNErrorStatus *status) { XCTAssertTrue(status.isError); - XCTAssertNotEqual([status.errorData.information rangeOfString:@"'user_id'"].location, + XCTAssertNotEqual([status.errorData.information rangeOfString:@"'user-id'"].location, NSNotFound); handler(); diff --git a/VERSION b/VERSION index ad96464c4..91f3b4384 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.10.1 +4.11.0 \ No newline at end of file