MarketingCloudSDK iOSでシンプルなpush通知を送信する

#環境
・xcode Version 11.3.1 (11C504)
・swift Version 5.1.3
・MarketingCloudSDK iOS (v7.2.1)
・Mac OS 10.14.6(Mojave)
#準備
・APNs用証明書(.p12)
・認証キー（.p8）
※2020/08/04段階では両方取得する必要がある。（多分そのうち.p8認証キーだけでいけるっぽい）
参考リンク
.p12
https://qiita.com/natsumo/items/d5cc1d0be427ca3af1cb
.p8
https://docs.repro.io/ja/dev/sdk/push-notification/setup-ios.html#app-id
#SMCのMobilePushでアプリを作成する
1. mobilePushを選択する

 ![スクリーンショット 2020-08-04 13.26.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246356/cbcfda00-c113-dc47-6068-d7643502e7f2.png)
2.管理を選択する

![スクリーンショット 2020-08-04 13.26.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246356/f1ff3370-3435-b5c6-5542-59f41a242717.png)

3.新しいアプリの作成を選択する

![スクリーンショット 2020-08-04 13.27.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246356/4fc57864-6e32-2125-ca9b-6bdecd69e789.png)

4.わかりやすい名前と説明を記載する

![スクリーンショット 2020-08-04 13.27.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246356/08a32fba-79e9-19f2-0311-eb2be549976f.png)

5.作成した認証キーファイル、APNS証明書を選択する
キーID：認証キー（.P8)をダウンロードする際に表示されるKey ID
チームID：Apple Developer ProgramのチームID
バンドルID:Apple Developer Programでアプリを登録する際に記述したBundle ID


![スクリーンショット 2020-08-04 13.28.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246356/8f989e19-7310-c951-fae6-d29159f43fb9.png)

6.保存後に有効になっているか確認

![スクリーンショット 2020-08-04 14.27.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246356/699ad25f-ee8d-e050-9a8b-3da9ef5357d4.png)

#SDKを追加
**CocoaPodsを利用してSDKを追加する**
1.プロジェクト名.xcodeproj」と同じディレクトリにターミナル上で移動する
`$cd [指定ディレクトリ]`
2.CocoaPodsをインストールする
`sudo gem install cocoapods`
※すでにインストールしている場合
`$ sudo gem update --system`
3.セットアップする
`pod setup`
4.バージョンを確認する
`$ pod --version`
バージョンが表示されればインストールされている
5.Podfile（どのライブラリをインストールするか指定するファイル）を作成する
`$ pod init`
6.作成したpodfileに以下の内容を記述する

```Podfile:Podfile
target 'MyApp' do
  pod 'MarketingCloudSDK', '~> 7.2'
end
```

7.workspaceを作成する
`$ pod install --no-repo-update`
作成された「プロジェクト名.xcworkspace」をダブルクリックしてXcodeを開く
※**.xcodeprojではなく.xcworkspaceを開く**

# SDKの初期設定を記述

```AppDelegate.swift
// 導入したSDKをインポート
import MarketingCloudSDK

class AppDelegate: UIResponder, UIApplicationDelegate {
    
    var window: UIWindow?
    
    // MobilePush SDK: REQUIRED IMPLEMENTATION
    
    // The appID, accessToken and appEndpoint are required values for MobilePush SDK configuration and are obtained from your MobilePush app.
    // See https://salesforce-marketingcloud.github.io/MarketingCloudSDK-iOS/get-started/get-started-setupapps.html for more information.
    
    // Use the builder method to configure the SDK for usage. This gives you the maximum flexibility in SDK configuration.
    // The builder lets you configure the SDK parameters at runtime.
    #if DEBUG
    // 下記キャプチャ①参考
    let appID = "[DEV-APNS App ID value from MobilePush app admin]"
    // 下記キャプチャ②参考
    let accessToken = "[DEV-APNS Access Token value from MobilePush app admin]"
    // 下記キャプチャ③参考
    let appEndpoint = "[DEV-APNS App Endpoint value from MobilePush app admin]"
    // 下記キャプチャ④参考
    let mid = "[DEV-APNS account MID value from MobilePush app admin]"
    #else
    // 以下上と同じ
    let appId = "[PROD-APNS appId value from MobilePush app admin]"
    let accessToken = "[PROD-APNS accessToken value from MobilePush app admin]"
    let appEndpoint = "[PROD-APNS app endpoint value from MobilePush app admin]"
    let mid = "[PROD-APNS account MID value from MobilePush app admin]"
    #endif
    
    
    // Define features of MobilePush your app will use.
    // 必要に応じてtrueに変更する
    let inbox = false // 受信トレイ
    let location = false // 位置情報
    let analytics = true // SMCの分析機能
    
    // MobilePush SDK: REQUIRED IMPLEMENTATION
    @discardableResult
    func configureMarketingCloudSDK() -> Bool {
        // Use the builder method to configure the SDK for usage. This gives you the maximum flexibility in SDK configuration.
        // The builder lets you configure the SDK parameters at runtime.
        // パラメータをセット（コンタクトキーが設定されるまで登録を遅らせることが可能）
        let builder = MarketingCloudSDKConfigBuilder()
            .sfmc_setApplicationId(appID)
            .sfmc_setAccessToken(accessToken)
            .sfmc_setMarketingCloudServerUrl(appEndpoint)
            .sfmc_setMid(mid)
            .sfmc_setInboxEnabled(inbox as NSNumber)
            .sfmc_setLocationEnabled(location as NSNumber)
            .sfmc_setAnalyticsEnabled(analytics as NSNumber)
            .sfmc_build()!
        
        var success = false
        
        // Once you've created the builder, pass it to the sfmc_configure method.
        do {
            try MarketingCloudSDK.sharedInstance().sfmc_configure(with:builder)
            // ユーザ情報をSMCに登録できる
            MarketingCloudSDK.sharedInstance().sfmc_setContactKey("test12345")
            // ユーザの属性をSMCに登録できる
            MarketingCloudSDK.sharedInstance().sfmc_setAttributeNamed("test_id", value: "12345678")
            success = true
        } catch let error as NSError {
            // Errors returned from configuration will be in the NSError parameter and can be used to determine
            // if you've implemented the SDK correctly.
            
            let configErrorString = String(format: "MarketingCloudSDK sfmc_configure failed with error = %@", error)
            print(configErrorString)
        }
        
        if success == true {
            // The SDK has been fully configured and is ready for use!
            
            // Enable logging for debugging. Not recommended for production apps, as significant data
            // about MobilePush will be logged to the console.
            #if DEBUG
            // trueにしておくとxcodeのコンソール上でデバッグできる
            MarketingCloudSDK.sharedInstance().sfmc_setDebugLoggingEnabled(true)
            #endif

            // Set the MarketingCloudSDKURLHandlingDelegate to a class adhering to the protocol.
            // In this example, the AppDelegate class adheres to the protocol
            // and handles URLs passed back from the SDK.
            // For more information, see https://salesforce-marketingcloud.github.io/MarketingCloudSDK-iOS/sdk-implementation/implementation-urlhandling.html
            // opendirect機能に必要（Extensionでプロトコルを追加する必要あり）
            MarketingCloudSDK.sharedInstance().sfmc_setURLHandlingDelegate(self)

            // Make sure to dispatch this to the main thread, as UNUserNotificationCenter will present UI.
            DispatchQueue.main.async {
              if #available(iOS 10.0, *) {
                // Set the UNUserNotificationCenterDelegate to a class adhering to thie protocol.
                // In this exmple, the AppDelegate class adheres to the protocol (see below)
                // and handles Notification Center delegate methods from iOS.
                UNUserNotificationCenter.current().delegate = self
                
                // Request authorization from the user for push notification alerts.
                UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge], completionHandler: {(_ granted: Bool, _ error: Error?) -> Void in
                    if error == nil {
                        if granted == true {
                            // Your application may want to do something specific if the user has granted authorization
                            // for the notification types specified; it would be done here.
                            print(MarketingCloudSDK.sharedInstance().sfmc_deviceToken() ?? "error: no token - was UIApplication.shared.registerForRemoteNotifications() called?")
                        }
                    }
                })
              }
            
              // In any case, your application should register for remote notifications *each time* your application
              // launches to ensure that the push token used by MobilePush (for silent push) is updated if necessary.
            
              // Registering in this manner does *not* mean that a user will see a notification - it only means
              // that the application will receive a unique push token from iOS
              // ここでデバイストークンをSMC側に渡すメソッドを呼ぶ
              UIApplication.shared.registerForRemoteNotifications()
            }
        }
        
        return success
    }
    
    // MobilePush SDK: REQUIRED IMPLEMENTATION
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        return self.configureMarketingCloudSDK()
    }
    
    // MobilePush SDK: OPTIONAL IMPLEMENTATION (if using Data Protection)
    func applicationProtectedDataDidBecomeAvailable(_ application: UIApplication) {
        if(MarketingCloudSDK.sharedInstance().sfmc_isReady() == false)
        {
            self.configureMarketingCloudSDK()
        }
    }
}
```

mobilePush > 管理
![MobilePush_-_Marketing_Cloud.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246356/be8f4f34-88bf-be9e-0963-f83eb0a23f0b.png)
<img width="361" alt="スクリーンショット_2020-08-04_18_26_53_png.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246356/a4dca74a-ffd1-3914-2363-e26826670f9b.png">

#Push通知実装
1. xcodeのcapabilityからPush Notificationsを有効にする

![スクリーンショット 2020-08-04 18.45.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246356/94ca9ead-efaf-87e9-3690-5fdb3265ef40.png)

2.push通知のために必要な`UIApplicationDelegate`メソッドをAppDelegateクラスに追加(必要に応じて拡張する）

```AppDelegate.swift
// MobilePush SDK: REQUIRED IMPLEMENTATION
// アプリを最初にタップして、起動した時に呼ばれるメソッド
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    return self.configureMarketingCloudSDK()
}
// MobilePush SDK: REQUIRED IMPLEMENTATION
// registerForRemoteNotifications()が呼ばれると下記のメソッドが呼ばれ、引数としてデバイストークンをSDKに渡す。
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    MarketingCloudSDK.sharedInstance().sfmc_setDeviceToken(deviceToken)
}

// MobilePush SDK: REQUIRED IMPLEMENTATION
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
    print(error)
}

// MobilePush SDK: REQUIRED IMPLEMENTATION
if this method is implemented. **/
// アプリ未起動時、バックグラウンド時に通知をタップすると呼び出される。フォアグラウンド時は通知表示はないが呼び出される。
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {   
    MarketingCloudSDK.sharedInstance().sfmc_setNotificationUserInfo(userInfo)  
    completionHandler(.newData)
}
```
3.push通知のために必要な`UNUserNotificationCenterDelegate `メソッドをAppDelegateクラスに追加

```AppDelegate.swift
// MobilePush SDK: REQUIRED IMPLEMENTATION
// The method will be called on the delegate when the user responded to the notification by opening the application, dismissing the notification or choosing a UNNotificationAction. The delegate must be set before the application returns from applicationDidFinishLaunching:.
// このメソッドでSMCから受け取ったカスタムキーを取得したりする
extension AppDelegate: UNUserNotificationCenterDelegate {
  @available(iOS 10.0, *)
  func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, 
  withCompletionHandler completionHandler: @escaping () -> Void) {
      // Required: tell the MarketingCloudSDK about the notification. This will collect MobilePush analytics
      // and process the notification on behalf of your application.
      MarketingCloudSDK.sharedInstance().sfmc_setNotificationRequest(response.notification.request)
      completionHandler()
  }

  // MobilePush SDK: REQUIRED IMPLEMENTATION
  // The method will be called on the delegate only if the application is in the foreground. If the method is not implemented or the handler is not called in a timely manner then the notification will not be presented. The application can choose to have the notification presented as a sound, badge, alert and/or in the notification list. This decision should be based on whether the information in the notification is otherwise visible to the user.
  @available(iOS 10.0, *)
  func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, 
  withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
      completionHandler(.alert)
  }
}
```
#SMCからpush通知を送信する
アプリ起動時にpush通知を許可にし、無事とどけばOK
#参考リンク（公式）
https://salesforce-marketingcloud.github.io/MarketingCloudSDK-iOS/