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