「だいじょぶだぁ」と言うとSAP CP Workflow Serviceで購買依頼登録が承認されるiOSアプリを実装する(Appendix)

[「だいじょぶだぁ」と言うとSAP CP Workflow Serviceで購買依頼登録が承認されるiOSアプリを実装する(2/2)](https://qiita.com/monamona/private/60bfc512509258b83842)で紹介したいソースコードが長くなったのでこちらにまとめました。

※ SAP API Business Hubで"Code Snippet"ボタンを押すと各種言語によるAPIの呼び方が出てくるので、基本的にはそこからのコピペベースで書けるのですが、Swiftの場合はSnippetのソースのバージョンが古く、現在の最新バージョンであるSwift5ではエラーになりました。下記はSwift5.0.1でのHTTPリクエストの呼び方です。

##　だっふんだ！と言ってPOSTメソッド（Start a new instance）を使用しWorkflow Instanceを開始する

```swift:ViewController.swift
//だっふんだの認識に成功していた場合のメソッド
func dafundaRecognized() {
    
    //認証情報の暗号化
    let originalString = "POOO123456:xxxxxxxxxx" //ユーザ名とパスワードを「：」でつなぐ
    let originalData = originalString.data(using: .utf8)
    let encodedString = originalData?.base64EncodedString()
    print(encodedString!)
    
    //Tokenを取るためのリクエストURLをセット
    let httpUrl = "https://bpmworkflowruntimewfs-p2000123456trial.hanatrial.ondemand.com/workflow-service/rest/v1/xsrf-token"
    guard let url = URL(string: httpUrl) else {
        return
    }
    
    //リクエストHeaderを設定
    var fetchTokenRequest = URLRequest(url: url)
    fetchTokenRequest.setValue("Fetch", forHTTPHeaderField: "X-CSRF-Token")
    fetchTokenRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
    fetchTokenRequest.setValue("application/json", forHTTPHeaderField: "Accept")
    fetchTokenRequest.setValue("Basic " + encodedString!, forHTTPHeaderField: "Authorization")
    fetchTokenRequest.httpMethod = "GET"
    
    //HTTPリクエスト
    let fetchToken = URLSession.shared.dataTask(with: fetchTokenRequest) { (data, response, error) in
        if let data = data, let response = response as? HTTPURLResponse {
            print(response)
            print(data)
            do {
                //Tokenの取得に成功した場合、POSTメソッドへ進む
                let token =  response.allHeaderFields["X-CSRF-Token"]
                print (token as Any)

                
                //POSTメソッドのURLをセット
                let httpUrl = "https://bpmworkflowruntimewfs-p2000123456trial.hanatrial.ondemand.com/workflow-service/rest/v1/workflow-instances"
                guard let url = URL(string: httpUrl) else {
                    return
                }
                
                //リクエストHeaderを設定
                var updateUserTaskRequest = URLRequest(url: url)
                updateUserTaskRequest.setValue(token as? String, forHTTPHeaderField: "X-CSRF-Token") //取得したTokenをセット
                updateUserTaskRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
                updateUserTaskRequest.setValue("application/json", forHTTPHeaderField: "Accept")
                updateUserTaskRequest.setValue("Basic " + encodedString!, forHTTPHeaderField: "Authorization") //暗号化した認証情報をセット
                updateUserTaskRequest.httpMethod = "POST"
                
                //リクエストbodyを準備
                let parameter : [String: Any] = [
                    "definitionId": "mydafundaworkflow", //Workflow定義のID
                    "context": [                         //Workflow Instanceに反映されるWorkflow Context
                        "odata":[
                            "request":[                  //Service Task実行時に渡されるパラメータ。使用するODataの仕様に合わせて設定
                                "Matnr": "300280",       //「ドリフ大爆笑DVD」のS/4HANA上の品目コード
                                "Menge": 1,              //「ドリフ大爆笑DVD」の個数
                                "Meins": "EA"            //「ドリフ大爆笑DVD」の単位
                            ],
                            "response":[]
                        ]
                    ]
                ]
                let jsonData = try JSONSerialization.data(withJSONObject: parameter, options: [])
                let jsonString = String(data: jsonData, encoding: String.Encoding.ascii)!
                print (jsonString)
                
                //リクエストbodyを設定
                updateUserTaskRequest.httpBody =  jsonData
                
                //POSTリクエスト
                let updateUserTask = URLSession.shared.dataTask(with: updateUserTaskRequest) { (data, response, error) in
                    if let data = data, let response = response as? HTTPURLResponse {
                        print(data)
                        print(response)
                        print(response.statusCode)
                        
                        //成功していた場合、成功ダイアログを表示（別途実装が必要）
                        self.displayDialog()
                        
                    } else {
                        print(error ?? "Error")
                    }
                }
                updateUserTask.resume()
            } catch {
                print("Error")
            }
        } else {
            print(error ?? "Error")
        }
    }
    fetchToken.resume()
}

```

##　GETメソッド（Retrieve task by ID）を使用しWorkflow Instanceを取得する

```Swift:TableViewController
    @objc private func refreshData(_ sender: UIRefreshControl?) -> Void{
        
        //処理中くるくるマーク表示開始(別途設定が必要)
        DispatchQueue.main.async {
            self.activityIndicatorView.startAnimating()
        }

        //認証情報の暗号化
        let originalString = "POOO123456:xxxxxxxxxx" //ユーザ名とパスワードを「：」でつなぐ
        let originalData = originalString.data(using: .utf8)
        let encodedString = originalData?.base64EncodedString()
        print(encodedString!)
        
        //フィルタをかけて特定のTask Instanceの情報のみ取得
        let httpUrl = "https://bpmworkflowruntimewfs-p2000123456trial.hanatrial.ondemand.com/workflow-service/rest/v1/task-instances?%24skip=0&%24top=100&%24inlinecount=none&%24orderby=createdAt%20asc&workflowDefinitionId=mydafundaworkflow&status=READY" 
        //分かりにくいですが、上記のURLのなかでWorkflow定義IDとTask Instance Statusをフィルタ用パラメータとして指定しました。
        guard let url = URL(string: httpUrl) else {
            return
        }
        
        //リクエストHeaderをセット
        var request = URLRequest(url: url)
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.setValue("application/json", forHTTPHeaderField: "Accept")
        request.setValue("Basic " + encodedString!, forHTTPHeaderField: "Authorization")
        
        let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
            if let data = data, let response = response {
                print(data)
                print(response)
                do {
                    let json = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments)
                    print(json)
                    
                    //配列を初期化（あらかじめ配列の宣言が必要）
                    self.localMaster = []

                    //取得した情報の一部を配列に格納
                    let jsonArray = json as? NSArray
                    for item in jsonArray!{
                        if let item = item as? NSDictionary{
                            var workflowTaskParameter = WorkflowTask()
                            workflowTaskParameter.taskID = (item["id"] as? String)! //Task Instance ID
                            workflowTaskParameter.applicantName = (item["createdBy"] as? String)! 
                            self.localMaster.append(workflowTaskParameter)
                        }
                    }
                    DispatchQueue.main.async {
                        //テーブルをリロードする
                        self.tableView.reloadData()
                        // senderがある場合（テーブルを下に引っ張ってリフレッシュした場合）は、リフレッシュ完了させる
                        if sender != nil {
                            sender?.endRefreshing()
                            print("refresh")
                        }
                        //処理中くるくるマーク表示終了
                        self.activityIndicatorView.stopAnimating()
                    }
                } catch {
                    print("Serialize Error")
                }
            } else {
                print(error ?? "Error")
            }
        }
        task.resume()
    }
```

話は逸れますが、データ取得処理中に表示するくるくるマークはかわいくて便利です。実装方法は例えば[こちらの記事](https://qiita.com/wai21/items/aa105f167bc94a94fe51)を参照ください。


##　だいじょぶだぁ！と言ってPATCHメソッド（Update task by ID）を使用しUser TaskのStatusをCOMPLETEDにする

```Swift:TableViewController
    private func completeUserTask (taskID: String, approved: String){

       //認証情報の暗号化
        activityIndicatorView.startAnimating()
        let originalString = "P2000123456:xxxxxx"
        let originalData = originalString.data(using: .utf8)
        let encodedString = originalData?.base64EncodedString()
        print(encodedString!)
        
        //Tokenを取得するためのURLをセット
        let httpUrl = "https://bpmworkflowruntimewfs-p2000123456trial.hanatrial.ondemand.com/workflow-service/rest/v1/xsrf-token"
        guard let url = URL(string: httpUrl) else {
            return
        }

        //リクエストHeaderをセット        
        var fetchTokenRequest = URLRequest(url: url)
        fetchTokenRequest.setValue("Fetch", forHTTPHeaderField: "X-CSRF-Token")
        fetchTokenRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
        fetchTokenRequest.setValue("application/json", forHTTPHeaderField: "Accept")
        fetchTokenRequest.setValue("Basic " + encodedString!, forHTTPHeaderField: "Authorization")
        fetchTokenRequest.httpMethod = "GET"
        
        //Tokenを取得する
        let fetchToken = URLSession.shared.dataTask(with: fetchTokenRequest) { (data, response, error) in
            if let data = data, let response = response as? HTTPURLResponse {
                print(response)
                print(data)
                do {
                    let token =  response.allHeaderFields["X-CSRF-Token"]
                    print (token as Any)
                    print ("selected task id is " + taskID)

                    //Tokenの取得に成功した場合、PATCHリクエストへ進む
                    //URLにWorkflow Instance IDを含める
                    let httpUrl = "https://bpmworkflowruntimewfs-p2000123456trial.hanatrial.ondemand.com/workflow-service/rest/v1/task-instances/" + taskID
                    guard let url = URL(string: httpUrl) else {
                        return
                    }

                    //リクエストbodyを準備
                    let parameter : [String: Any] = [
                        //引数のapprovedはBool変数。承認or否認に応じてtrue or falseをWorkflow Contextに渡す
                        "context": ["approved" : approved], 
                        "status": "COMPLETED",              //Task InstanceのStatusをCOMPLETEDに。他のパラメータは使わない。
                        // "subject": "string",
                        // "description": "string",
                        // "recipientUsers": "string",
                        // "recipientGroups": "string",
                        // "processor": "string",
                        // "dueDate": "2019-06-12T14:06:52.892Z",
                        // "priority": "VERY_HIGH"
                    ]
                    let jsonData = try JSONSerialization.data(withJSONObject: parameter, options: [])
                    let jsonString = String(data: jsonData, encoding: String.Encoding.ascii)!
                    print (jsonString)
                    
                    //リクエストHeaderをセット
                    var updateUserTaskRequest = URLRequest(url: url)
                    updateUserTaskRequest.setValue(token as? String, forHTTPHeaderField: "X-CSRF-Token")
                    updateUserTaskRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
                    updateUserTaskRequest.setValue("application/json", forHTTPHeaderField: "Accept")
                    updateUserTaskRequest.setValue("Basic " + encodedString!, forHTTPHeaderField: "Authorization")
                    updateUserTaskRequest.httpMethod = "PATCH"
                    updateUserTaskRequest.httpBody =  jsonData
                    
                    //PATCHリクエスト
                    let updateUserTask = URLSession.shared.dataTask(with: updateUserTaskRequest) { (data, response, error) in
                        if let data = data, let response = response as? HTTPURLResponse {
                            print(data)
                            print(response)
                            print(response.statusCode)
                            self.refreshData(nil)
                        } else {
                            print(error ?? "Error")
                        }
                    }
                    updateUserTask.resume()
                    
                } catch {
                    print("Error")
                }
            } else {
                print(error ?? "Error")
            }
        }
        fetchToken.resume()
    }
```