NCMBのSwift SDKとDeepL APIを使って翻訳アプリを作る（その2：翻訳処理とデータの保存）

NCMBのSwift SDKを使ってデモアプリを作ってみます。今回はDeepL APIを使った翻訳アプリです。翻訳結果をNCMBのデータストアに保存し、後で確認できるようにします。データストアにデータを保存したり、逆に取り出すのはよくあるケースなので、様々な場面で応用できるはずです。

[前回は画面の説明とSDKの導入までを解説しました](https://qiita.com/goofmint/items/3a4c13bc2425e65b3a93)ので、今回は翻訳処理とデータ保存について解説します。

## コードについて

今回のコードは[NCMBMania/Swift_DeepL_App: Swift NCMB SDKとDeepL APIを組み合わせた翻訳アプリのデモです](https://github.com/NCMBMania/Swift_DeepL_App)にアップロードしてあります。実装時の参考にしてください。

## 翻訳処理について

翻訳処理はDeepL APIを用いて行います。まず、[DeepL API](https://www.deepl.com/ja/docs-api/translating-text/response/)のレスポンスに合わせた構造体を定義します。

```swift
// DeepL APIのレスポンス用構造体
struct DeepLResult: Codable {
    let translations: [Translation]
    struct Translation: Codable {
        var detected_source_language: String
        var text: String
    }
}
```

## Alamofireの追加

ネットワークアクセスはAlamofireを利用します。Swift Package Mangerにて `https://github.com/Alamofire/Alamofire.git` を指定してインストールします。

インストールしたら、インポートします。

```swift
import Alamofire
```

## 翻訳画面について

前の記事でも紹介しました、翻訳画面 `TranslateView` は次のようになっています。

```swift
// 翻訳用ビュー
struct TranslateView: View {
    // 日本語のテキスト
    @State private var originalText = ""
    // 翻訳結果のテキスト
    @State private var translatedText = ""
    // 保存完了した際のアラート制御用
    @State private var uploaded = false
    // JSONデコード用
    let decoder: JSONDecoder = JSONDecoder()
    
    // 翻訳実行
    func translation() {
    }
    
    // 翻訳結果をNCMBに保存する処理
    func saveResult() {
    }
    
    // 画面表示
    var body: some View {
        NavigationView{
            VStack(spacing:0){
                ZStack{
                    VStack(spacing:20){
                        // 日本語入力欄
                        TextEditor(text: $originalText)
                            .textFieldStyle(RoundedBorderTextFieldStyle())
                            .frame(maxWidth: 280, maxHeight: 100)
                        // 翻訳結果があれば表示
                        if translatedText != "" {
                            Text(translatedText).padding()
                        }
                        // 翻訳実行するボタン
                        Button(action: {
                            translation()
                        }, label: {
                            Text("翻訳する")
                        })
                    }
                }
            }
            .navigationBarTitle("翻訳", displayMode: .inline)
            // 保存完了時のアラート用
            .alert(isPresented: $uploaded, content: {
                Alert(
                    title: Text("保存完了"),
                    message: Text("翻訳結果を記録しました"),
                    dismissButton: .default(Text("閉じる"))
                )
            })
                
        }
    }
}
```

### 翻訳処理の実装

翻訳処理は `translation` 関数に実装します。DeepL用のライブラリはないので、Alamofireで直接実行しています。結果が返ってきたら、正常終了しているか判定した上でJSONデータを解析しています。その処理がうまくいったら画面に翻訳結果を反映（translatedTextに文字列を適用）し、 `saveResult` 関数を実行しています。

```swift
// 翻訳実行
func translation() {
		// APIKey.plistに保存したDeepLの認証キーを取得
		let authKey = KeyManager().getValue(key: "DeepLAuthKey") as! String
		// APIリクエストするパラメータを作成
		let parameters: [String: String] = [
				"text": originalText,
				"target_lang": "EN-US",
				"auth_key": authKey
		]
		// ヘッダーを作成
		let headers: HTTPHeaders = [
				"Content-Type": "application/x-www-form-urlencoded"
		]
		// DeepL APIを実行
		AF.request("https://api-free.deepl.com/v2/translate", method: .post, parameters: parameters, encoder: URLEncodedFormParameterEncoder.default, headers: headers).responseDecodable(of: DeepLResult.self) { response in
				// リクエスト成功か判定
				if case .success = response.result {
						do {
								// 結果をデコード
								let result = try decoder.decode(DeepLResult.self, from: response.data!)
								// 結果のテキストを取得&画面に反映
								translatedText =  result.translations[0].text
								// 結果をNCMBに保存する処理を呼び出し
								saveResult()
						} catch {
								debugPrint("デコード失敗")
						}
				} else {
						debugPrint("APIリクエストエラー")
				}
		}
}
```

### データをNCMBに保存

NCMBのSwift SDKをインポートします。

```swift
import NCMB
```

翻訳前の日本語と、翻訳結果をNCMBのデータストアに保存します。データストアのクラス名（DBで言うところのテーブル名相当）はTranslateとしています。

保存がうまくいったら、 `updated` フラグを有効にし、アラートダイアログを表示します。

```swift
// 翻訳結果をNCMBに保存する処理
func saveResult() {
		let translate = NCMBObject(className: "Translate")
		translate["original"] = originalText
		translate["translate"] = translatedText
		translate.saveInBackground(callback: { result in
                // 処理が成功しているか判定
				if case let .success(_) = result {
                    uploaded = true
                }
		})
}
```

![translate-app-4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/e58642ab-8c98-f4a3-0c6a-0e7cb3754dfc.png)


ここまででデータの保存が完了です。実際にデータストアを見ると、Translateクラスができあがり、データが追加されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/e32349a0-d0d1-db04-cd4e-278b0bd7389c.png)


## まとめ

NCMBを使うとクラウドデータベースへの保存がわずか数行で書けます。今回は使っていませんが、ACL（アクセス権限）も指定できます。ぜひ試してください。

[次回は保存したデータの取得と表示を行います](https://qiita.com/goofmint/items/cee045c69f9a1aa204f2)。