Skip to content

Commit

Permalink
Merge pull request #70 in MML/infobip-mobile-messaging-ios from okoro…
Browse files Browse the repository at this point in the history
…leva-MM-2047ReplyAction to master

Squashed commit of the following:

commit b27a4657a8c9a82e5d19ca7a09a139b597657fcc
Author: okoroleva <[email protected]>
Date:   Tue Aug 29 12:25:25 2017 +0300

    Tests

commit f6d99c6c3b3f5c2ac18cef38dbb99e5f4c88e40a
Author: olga.koroleva <[email protected]>
Date:   Mon Aug 28 18:24:45 2017 +0300

    Example and handling

commit 073e0dee8057e9dd6e9684728dca5b17ce241d75
Author: olga.koroleva <[email protected]>
Date:   Mon Aug 28 16:29:03 2017 +0300

    handlers

commit 4c7a17f1d50b3aed9a1b3d49ad0a068022fd3ab0
Author: olga.koroleva <[email protected]>
Date:   Mon Aug 28 16:05:44 2017 +0300

    TextInputNotificationAction
  • Loading branch information
riskpp committed Aug 29, 2017
1 parent e739202 commit e31e05c
Show file tree
Hide file tree
Showing 8 changed files with 227 additions and 62 deletions.
35 changes: 31 additions & 4 deletions Example/MobileMessagingExample.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -618,9 +618,14 @@
files = (
);
inputPaths = (
"${SRCROOT}/Pods/Target Support Files/Pods-MobileMessagingExample/Pods-MobileMessagingExample-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/CocoaLumberjack/CocoaLumberjack.framework",
"${BUILT_PRODUCTS_DIR}/MobileMessaging/MobileMessaging.framework",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CocoaLumberjack.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MobileMessaging.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
Expand Down Expand Up @@ -661,9 +666,14 @@
files = (
);
inputPaths = (
"${SRCROOT}/Pods/Target Support Files/Pods-MobileMessagingExample_Tests_Device/Pods-MobileMessagingExample_Tests_Device-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/CocoaLumberjack/CocoaLumberjack.framework",
"${BUILT_PRODUCTS_DIR}/MobileMessaging/MobileMessaging.framework",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CocoaLumberjack.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MobileMessaging.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
Expand Down Expand Up @@ -719,9 +729,14 @@
files = (
);
inputPaths = (
"${SRCROOT}/Pods/Target Support Files/Pods-MobileMessagingExample_Tests/Pods-MobileMessagingExample_Tests-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/CocoaLumberjack/CocoaLumberjack.framework",
"${BUILT_PRODUCTS_DIR}/MobileMessaging/MobileMessaging.framework",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CocoaLumberjack.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MobileMessaging.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
Expand Down Expand Up @@ -764,13 +779,16 @@
files = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-MobileMessagingExample_Tests_Device-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n";
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
E29CA7FF694F6C884B7A4A81 /* [CP] Copy Pods Resources */ = {
Expand All @@ -794,13 +812,16 @@
files = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-MobileMessagingExample_Tests-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n";
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
FDAE422B74F7B1991521A7F6 /* [CP] Check Pods Manifest.lock */ = {
Expand All @@ -809,13 +830,16 @@
files = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-MobileMessagingExample-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n";
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
FF4C78924BA3EAE65513FDC6 /* [CP] Check Pods Manifest.lock */ = {
Expand All @@ -824,13 +848,16 @@
files = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-NotificationServiceExtension-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n";
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
Expand Down
88 changes: 58 additions & 30 deletions Example/MobileMessagingExample/InteractiveNotifications.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,43 +10,71 @@ import MobileMessaging

extension AppDelegate {
var customCategories: Set<NotificationCategory>? {
//Action with title "Cancel", which will be marked as destructive and will require device to be unlocked before proceed
let cancelAction = NotificationAction(identifier: "cancel",
title: "Cancel",
options: [.destructive, .authenticationRequired])
//Action with title "Share", which will require device to be unlocked before proceed and will bring application to the foreground
let shareAction = NotificationAction(identifier: "share",
title: "Share",
options: [.foreground, .authenticationRequired])


guard let _cancelAction = cancelAction,
let _shareAction = shareAction else {
return nil
}
let category: NotificationCategory?
if #available(iOS 10.0, *) {
category = NotificationCategory(identifier: "category_share_cancel",
actions: [_shareAction, _cancelAction],
options: [.customDismissAction],
intentIdentifiers: nil)
} else {
category = NotificationCategory(identifier: "category_share_cancel",
actions: [_shareAction, _cancelAction],
options: nil,
intentIdentifiers: nil)
var categories = Set<NotificationCategory>()
if let _categoryShareCancel = categoryShareCancel {
categories.insert(_categoryShareCancel)
}

guard let _category = category else {
return nil
}
return [_category]
if let _replyCategory = replyCategory {
categories.insert(_replyCategory)
}
return categories
}

var categoryShareCancel: NotificationCategory? {
//Action with title "Cancel", which will be marked as destructive and will require device to be unlocked before proceed
let cancelAction = NotificationAction(identifier: "cancel",
title: "Cancel",
options: [.destructive, .authenticationRequired])
//Action with title "Share", which will require device to be unlocked before proceed and will bring application to the foreground
let shareAction = NotificationAction(identifier: "share",
title: "Share",
options: [.foreground, .authenticationRequired])


guard let _cancelAction = cancelAction,
let _shareAction = shareAction else {
return nil
}
let category: NotificationCategory?
if #available(iOS 10.0, *) {
category = NotificationCategory(identifier: "category_share_cancel",
actions: [_shareAction, _cancelAction],
options: [.customDismissAction],
intentIdentifiers: nil)
} else {
category = NotificationCategory(identifier: "category_share_cancel",
actions: [_shareAction, _cancelAction],
options: nil,
intentIdentifiers: nil)
}
return category
}

var replyCategory: NotificationCategory? {
if #available(iOS 9.0, *) {
let replyAction = TextInputNotificationAction(identifier: "reply", title: "Reply", options: [], textInputActionButtonTitle: "Reply", textInputPlaceholder: "print reply here")
guard let _replyAction = replyAction else {
return nil
}
return NotificationCategory(identifier: "category_reply",
actions: [_replyAction],
options: nil,
intentIdentifiers: nil)
} else {
return nil
}
}
}

class CustomActionHandler: NotificationActionHandling {
func handle(action: NotificationAction, forMessage message: MTMessage, withCompletionHandler completionHandler: @escaping () -> Void) {
print(action.identifier)
if #available(iOS 9.0, *) {
if let textInputAction = action as? TextInputNotificationAction,
let typedText = textInputAction.typedText {
print(typedText)
}
}
completionHandler()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,35 @@ class InteractiveNotificationsTests: MMTestCase {

func testActionHandlerCalledAndMOSent() {
weak var testCompleted = expectation(description: "testCompleted")

let action = NotificationAction(identifier: actionId, title: "Action", options: [.moRequired])!
checkActionHandlerCalledAndMoSent(withAction: action, responseInfo: nil) { _action, completionHandler in
if _action == action {
testCompleted?.fulfill()
}
completionHandler()
}
waitForExpectations(timeout: 10, handler: nil)
}

func testTextInputActionHandlerCalledAndMOSent() {
guard #available(iOS 9.0, *) else {
return
}
let typedText = "Hello world!"
weak var testCompleted = expectation(description: "testCompleted")
let textInputAction = TextInputNotificationAction(identifier: "textInputActionId", title: "Reply", options: [.moRequired], textInputActionButtonTitle: "Reply", textInputPlaceholder: "print text here")!
checkActionHandlerCalledAndMoSent(withAction: textInputAction, responseInfo: [UIUserNotificationActionResponseTypedTextKey: typedText]) { _action, completionHandler in
if let _textInputAction = _action as? TextInputNotificationAction,
_textInputAction == textInputAction {
XCTAssertEqual(typedText, _textInputAction.typedText)
testCompleted?.fulfill()
}
completionHandler()
}
waitForExpectations(timeout: 10, handler: nil)
}

func checkActionHandlerCalledAndMoSent(withAction action: NotificationAction, responseInfo: [AnyHashable: Any]?, completion: @escaping (NotificationAction, () -> Void) -> Void) {
let category = NotificationCategory(identifier: categoryId, actions: [action], options: nil, intentIdentifiers: nil)!
var set = Set<NotificationCategory>()
set.insert(category)
Expand All @@ -52,23 +79,18 @@ class InteractiveNotificationsTests: MMTestCase {
weak var seenCalled = expectation(description: "seenCalled")
weak var sendMessageCalled = expectation(description: "sendMessageCalled")
msgHandlerMock.sendMessageWasCalled = { messages in
XCTAssertEqual(messages.first!.text, "\(self.categoryId) \(self.actionId)")
XCTAssertEqual(messages.first!.text, "\(self.categoryId) \(action.identifier)")
sendMessageCalled?.fulfill()
}
msgHandlerMock.setSeenWasCalled = { seenCalled?.fulfill() }
mm.messageHandler = msgHandlerMock


MobileMessaging.notificationActionHandler = NotificationActionHandlerMock(handlingBlock: { (_action, message, completionHandler) in
if _action == action {
testCompleted?.fulfill()
}
completionHandler()
completion(_action, completionHandler)
})

MobileMessaging.handleActionWithIdentifier(identifier: action.identifier, forRemoteNotification: ["messageId": UUID.init().uuidString, "aps": ["alert": ["body": "text"], "category": category.identifier]], responseInfo: nil) {}

waitForExpectations(timeout: 10, handler: nil)
MobileMessaging.handleActionWithIdentifier(identifier: action.identifier, forRemoteNotification: ["messageId": UUID.init().uuidString, "aps": ["alert": ["body": "text"], "category": category.identifier]], responseInfo: responseInfo) {}
}

func testActionOptions() {
Expand Down
24 changes: 19 additions & 5 deletions Pod/Classes/Core/MobileMessagingAppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,27 +96,41 @@ open class MobileMessagingAppDelegate: UIResponder, UIApplicationDelegate {
if !isTestingProcessRunning {
MobileMessaging.handleActionWithIdentifier(identifier: identifier, localNotification: notification, responseInfo: nil, completionHandler: completionHandler)
}
mm_application(application, handleActionWithIdentifier: identifier, for: notification, completionHandler: completionHandler)
mm_application(application, handleActionWithIdentifier: identifier, for: notification, withResponseInfo: nil, completionHandler: completionHandler)
}

public func application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, forRemoteNotification userInfo: [AnyHashable : Any], completionHandler: @escaping () -> Void) {
if !isTestingProcessRunning {
MobileMessaging.handleActionWithIdentifier(identifier: identifier, forRemoteNotification: userInfo, responseInfo: nil, completionHandler: completionHandler)
}
mm_application(application, handleActionWithIdentifier: identifier, forRemoteNotification: userInfo, completionHandler: completionHandler)
}
mm_application(application, handleActionWithIdentifier: identifier, forRemoteNotification: userInfo, withResponseInfo: nil, completionHandler: completionHandler)
}

public func application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, for notification: UILocalNotification, withResponseInfo responseInfo: [AnyHashable : Any], completionHandler: @escaping () -> Void) {
if !isTestingProcessRunning {
MobileMessaging.handleActionWithIdentifier(identifier: identifier, localNotification: notification, responseInfo: responseInfo, completionHandler: completionHandler)
}
mm_application(application, handleActionWithIdentifier: identifier, for: notification, withResponseInfo: responseInfo, completionHandler: completionHandler)
}

public func application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, forRemoteNotification userInfo: [AnyHashable : Any], withResponseInfo responseInfo: [AnyHashable : Any], completionHandler: @escaping () -> Void) {
if !isTestingProcessRunning {
MobileMessaging.handleActionWithIdentifier(identifier: identifier, forRemoteNotification: userInfo, responseInfo: responseInfo, completionHandler: completionHandler)
}
mm_application(application, handleActionWithIdentifier: identifier, forRemoteNotification: userInfo, withResponseInfo: responseInfo, completionHandler: completionHandler)
}

/// This is substitution for standart `application(:handleActionWithIdentifier:for:completionHandler)`
///
///You can override this method in your own application delegate in case you have choosen th Application Delegate inheritance way to integrate with Mobile Messaging SDK.
@nonobjc public func mm_application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, for notification: UILocalNotification, completionHandler: @escaping () -> Void) {
@nonobjc public func mm_application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, for notification: UILocalNotification, withResponseInfo responseInfo: [AnyHashable : Any]?,completionHandler: @escaping () -> Void) {
// override this callback in your AppDelegate if needed
}

/// This is substitution for standart `application(:handleActionWithIdentifier:handleActionWithIdentifier:completionHandler)`
///
///You can override this method in your own application delegate in case you have choosen th Application Delegate inheritance way to integrate with Mobile Messaging SDK.
@nonobjc public func mm_application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, forRemoteNotification userInfo: [AnyHashable : Any], completionHandler: @escaping () -> Void) {
@nonobjc public func mm_application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, forRemoteNotification userInfo: [AnyHashable : Any], withResponseInfo responseInfo: [AnyHashable : Any]?, completionHandler: @escaping () -> Void) {
// override this callback in your AppDelegate if needed
}

Expand Down
Loading

0 comments on commit e31e05c

Please sign in to comment.