We inherited 'MarketingCloudSDKURLHandlingDelegate' for the class that runs MobilePush for Marketing Cloud and overrode the methods. We wanted the following method to be called as handler when we tried opening the push notification: "public func sfmc_handle(URL(string: userinfo["_od"] as! String), type: String)" However, method "self.sfmc_handle(_ url: URL, type: SFMCURLTypeOpenDirect) " is called explicitly instead.
With the explicitly called method mentioned right above, when we open the push notification and the app runs in the background, opening another push notification causes the following error: sfmc_setNotificationRequest called before the SDK is ready. Deferring until isReady == true.
isReady' in 'MarketingCloudSDK+Base.h' is initialized by SDK, so when we try to use, its value becomes 'true'. What is the situation in which its value becomes 'false'?
Also, 'sfmc_handle(URL(string: userinfo["_od"] as! String), type: String)' , is it supposed to be explicitly called?
Implementation code
import Flutter
import UIKit
import MarketingCloudSDK
import AdSupport
public class SwiftFlutterSmcSdkPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, UNUserNotificationCenterDelegate,
MarketingCloudSDKURLHandlingDelegate{
private var eventSink: FlutterEventSink?
let EVENT_NAME:String = "eventName"
let EVENT_RESULT:String = "eventResult"
let ERROR_EVENT_NAME:String = "errorEventName"
var articleUrl: String?
enum EVENT_NAMES {
static let onBehaviorOfAdvertisingIdCondition = "onBehaviorOfAdvertisingIdCondition"
static let onBehaviorOfNotificationMessageDisplayed = "onBehaviorOfNotificationMessageDisplayed"
static let onRequestAdvertisingId = "onRequestAdvertisingId"
}
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "flutter_smc_sdk_plugin_method_channel", binaryMessenger: registrar.messenger())
let eventchannel = FlutterEventChannel.init(name: "flutter_smc_sdk_plugin_event_channel", binaryMessenger: registrar.messenger())
let instance = SwiftFlutterSmcSdkPlugin()
MarketingCloudSDK.sharedInstance().sfmc_setURLHandlingDelegate(instance as MarketingCloudSDKURLHandlingDelegate)
registrar.addMethodCallDelegate(instance, channel: channel)
registrar.addApplicationDelegate(instance)
eventchannel.setStreamHandler(instance)
if #available(iOS 10.0, *) {
let center = UNUserNotificationCenter.current()
center.delegate = instance
print("initialize iOS UNNUserNotification Delegate")
} else {
}
print("Mens App: [App Call Method] :: Flutter Plugin Registretion")
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
//debugPrint(call.method, call.arguments)
switch call.method {
case "getPlatformVersion": getPlatformVersion(result)
case "registerMemberId": registerMemberId(call: call)
case "getContactKey": self.getContactKey(result: result)
case "isSmcSdkStartupSuccess": isSmcSdkStartupSuccess(result: result)
case "requestPermissionForPushNotification": requestNotificationPermission()
case "getAdvertisingId": getAdvertisingId(result: result)
default: result(nil)
}
}
public func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [AnyHashable : Any] = [:]) -> Bool {
NSLog("LaunchOptionsKey : \(launchOptions.description as? [AnyHashable : Any])")
if launchOptions[UIApplicationLaunchOptionsKey.remoteNotification] != nil {
NSLog("PushNotification Tap Lunch")
let userInfo = launchOptions[UIApplicationLaunchOptionsKey.remoteNotification] as? [AnyHashable : Any]
NSLog("UserInfo : \(String(describing: userInfo?.values))")
articleUrl = userInfo?["_od"] as! String?
if articleUrl == nil{
return true
}else{
DispatchQueue.main.asyncAfter( deadline: DispatchTime.now() + 5){
self.sfmc_handle(URL(string: userInfo!["_od"] as! String), type: SFMCURLTypeOpenDirect)
}
}
}
return true
}
public func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) -> Bool {
switch application.applicationState {
case .inactive:
NSLog("I tap the Push notification from the state where the app is closed")
NSLog("Application Active")
if articleUrl != nil && articleUrl != ""{
NSLog("running OpenUrl")
DispatchQueue.main.async {
self.sfmc_handle(URL(string: self.articleUrl ?? ""), type: SFMCURLTypeOpenDirect)
}
}
case .active:
break
case .background:
break
}
completionHandler(UIBackgroundFetchResult.noData)
return true
}
public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
print("called :: onListen")
self.eventSink = events
return nil
}
public func onCancel(withArguments arguments: Any?) -> FlutterError? {
print("called :: onCancel")
eventSink = nil
return nil
}
public func requestNotificationPermission() -> Bool {
let sdk : MarketingCloudSDK = MarketingCloudSDK.sharedInstance()
let advertising_id = ASIdentifierManager.shared().advertisingIdentifier.uuidString
var error : NSError? = nil
sdk.sfmc_tearDown()
let isSuccess : Bool = sdk.sfmc_configure(&error)
let userDeaults = UserDefaults.standard
userDeaults.set(isSuccess, forKey: "isSmcSdkStartupSuccess")
print("Error ::: \(String(describing: error?.domain.description))")
if isSuccess == true {
// The SDK has been fully configured and is ready for use!
// turn on logging for debugging. Not recommended for production apps.
sdk.sfmc_setDebugLoggingEnabled(true)
//DispatchQueue.global(qos: .default).async {}
DispatchQueue.main.async (execute:{
// configure the Marketing Cloud SDK
if #available(iOS 10.0, *) {
// iOS 10
UNUserNotificationCenter.current().delegate = self
UNUserNotificationCenter.current().requestAuthorization(options: [.badge, .sound, .alert], completionHandler: { (granted, error) in
if error != nil {
return
}
if granted {
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
print("set app_device_id")
sdk.sfmc_setAttributeNamed("app_device_id", value: advertising_id)
} else {
}
})
} else {
// iOS 9
let settings = UIUserNotificationSettings(types: [.badge, .sound, .alert], categories: nil)
UIApplication.shared.registerUserNotificationSettings(settings)
}
})
}else {
print("MarketingCloudSDK sfmc_configure failed with error = \(String(describing: error?.localizedDescription))", error!)
}
return isSuccess;
//GeneratedPluginRegistrant.register(with: self)
//print("isRegisteredForRemoteNotifications ::: \(application.isRegisteredForRemoteNotifications)")
}
private func getPlatformVersion(_ result: @escaping FlutterResult) {
result("iOS " + UIDevice.current.systemVersion)
}
private func registerMemberId(call: FlutterMethodCall) {
guard
let args = call.arguments as? [String: Any]
else {
return
}
let memberId :String? = args["memberId"] as? String
let contactNumber :String? = args["contactNumber"] as? String
let hogeCardRank :String? = args["hogeCardRank"] as? String
let sdk : MarketingCloudSDK = MarketingCloudSDK.sharedInstance()
let advertising_id = ASIdentifierManager.shared().advertisingIdentifier.uuidString
if(memberId != nil){
debugPrint("set sfmc_setContactKey")
sdk.sfmc_setContactKey(memberId!)
if(contactNumber != nil){
debugPrint("set sfmc_setAttributeNamed :: otiaws_no :: ")
sdk.sfmc_setAttributeNamed("otiaws_no", value: contactNumber!)
}
if(hogeCardRank != nil){
debugPrint("set sfmc_setAttributeNamed :: gcp_stage ::")
sdk.sfmc_setAttributeNamed("gcp_stage", value: hogeCardRank!)
}
}
debugPrint("set sfmc_setAttributeNamed :: app_device_id :: \(hogeCardRank)")
sdk.sfmc_setAttributeNamed("app_device_id", value: advertising_id)
}
func identifierForAdvertising() -> String? {
// Check whether advertising tracking is enabled
print("AdvertisingFlag ::: \(ASIdentifierManager.shared().isAdvertisingTrackingEnabled)")
// Check whether advertising tracking is enabled
if ASIdentifierManager.shared().isAdvertisingTrackingEnabled {
var dic = Dictionary<NSObject, NSObject>()
dic[EVENT_NAME as NSObject] = EVENT_NAMES.onBehaviorOfAdvertisingIdCondition as NSObject
dic[EVENT_RESULT as NSObject] = ASIdentifierManager.shared().isAdvertisingTrackingEnabled as NSObject
eventSink!(dic)
}else{
var dic = Dictionary<NSObject, NSObject>()
dic[EVENT_NAME as NSObject] = EVENT_NAMES.onBehaviorOfAdvertisingIdCondition as NSObject
dic[EVENT_RESULT as NSObject] = ASIdentifierManager.shared().isAdvertisingTrackingEnabled as NSObject
eventSink!(dic)
}
// Get and return IDFA
return ASIdentifierManager.shared().advertisingIdentifier.uuidString
}
private func isSmcSdkStartupSuccess (result: @escaping FlutterResult) -> Bool {
let userDeaults = UserDefaults.standard
print("call method isSmcSdkStartupSuccess")
print("sdk status ::: \(userDeaults.bool(forKey: "isSmcSdkStartupSuccess"))")
result(userDeaults.bool(forKey: "isSmcSdkStartupSuccess"))
return userDeaults.bool(forKey: "isSmcSdkStartupSuccess")
}
private func getContactKey(result: @escaping FlutterResult){
result(MarketingCloudSDK.sharedInstance().sfmc_contactKey()!)
}
private func getAdvertisingId(result: @escaping FlutterResult){
result(ASIdentifierManager.shared().advertisingIdentifier.uuidString)
}
@available(iOS 10.0, *)
public func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
print(response.notification.request.content.userInfo)
NSLog("Tap Push Notification Center")
let userinfo = response.notification.request.content.userInfo
let sdk : MarketingCloudSDK = MarketingCloudSDK.sharedInstance()
sdk.sfmc_setNotificationRequest(response.notification.request)
if userinfo["_od"] != nil {
self.sfmc_handle(URL(string: userinfo["_od"] as! String), type: SFMCURLTypeOpenDirect)
}
completionHandler()
}
@available(iOS 10.0, *)
public func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: (UNNotificationPresentationOptions) -> Void) {
print(notification.request.content.userInfo)
let userinfo = notification.request.content.userInfo
let sdk : MarketingCloudSDK = MarketingCloudSDK.sharedInstance()
sdk.sfmc_setNotificationRequest(notification.request)
if userinfo["_od"] != nil {
sfmc_handle(URL(string: userinfo["_od"] as! String), type: SFMCURLTypeOpenDirect)
}*/
completionHandler([.alert, .sound])
//...
}
public func application(_ application: UIApplication, open url: URL, sourceApplication: String, annotation: Any) -> Bool {
print("url : \(url.absoluteString)")
print("scheme : \(url.scheme!)")
print("host : \(url.host!)")
print("port : \(url.port!)")
print("query : \(url.query!)")
return true
}
func notifiPush(userInfo:[NSObject : AnyObject]){
var dic = Dictionary<NSObject, NSObject>()
dic[EVENT_NAME as NSObject] = EVENT_NAMES.onBehaviorOfNotificationMessageDisplayed as NSObject
dic[EVENT_RESULT as NSObject] = userInfo as NSObject
eventSink!(dic)
}
public func sfmc_handle(_ url: URL!, type: String) {
if try! NSRegularExpression(pattern: "https?://([-\\w\\.]+)+(:\\d+)?(/([\\w/_\\.]*(\\?\\S+)?)?)?", options: NSRegularExpression.Options(rawValue: 0)).isEqual(url){
return
}
if let anUrl = url {
if UIApplication.shared.canOpenURL(anUrl) == true {
if #available(iOS 10.0, *) {
print("url ::: \(url)")
let customURL: URL = (URL(string: "jp.hoge.huga://open-direct/" + url.absoluteString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)!)
UIApplication.shared.open(customURL, options: [:], completionHandler: { success in
if success {
NSLog("UIApplication.shared.open url \(customURL) opened successfully")
print("url \(customURL) opened successfully")
} else {
NSLog("UIApplication.shared.open url \(customURL) could not be opened")
print("UIApplication.shared.open url \(customURL) could not be opened")
}
})
} else {
if UIApplication.shared.openURL(anUrl) == true {
if url != nil {
NSLog("UIApplication.shared.openURL url \(anUrl) opened successfully")
print("UIApplication.shared.openURL url \(anUrl) opened successfully")
}
} else {
NSLog("UIApplication.shared.openURL url \(anUrl) could not be opened")
print("UIApplication.shared.openURL url \(anUrl) could not be opened")
}
}
}
}
}
}