CloudKitで画像を連携する

今回はCloudKitで画像ファイルを連携してみます。

環境：swift 4.2、Xcode 10.1

#RecrodTypeの設定
まずCloudKit DashboardでRecordTypeに画像を保存するフィールドを追加します。
画像はBytes型で保存できます。
あと画像の向き(詳細は後述)を保存する為にInt型のフィールドも用意しておきます。
<img width="590" alt="スクリーンショット 2019-02-22 21.41.12.png" src="https://qiita-image-store.s3.amazonaws.com/0/362719/9d165824-a605-4114-f155-70807f644675.png">

#画像をCloudKitに保存
画像をCloudKitに保存する時にはUIImage型からNSData型に変換する必要があります。

```swift
//UIImageをNSDataに変換
let imageData = image.jpegData(compressionQuality: 1.0)
```

CloudKitの1レコードあたりのサイズは最大1MB以内という制限があります。
その為、1MBに収まる様にcompressionQuality(圧縮率)を調整する必要があります。

また画像の向き(UIImage.Orientation)をチェックしておきます。
画像の向きが下向きになっている場合はNSData型からUIImage型に戻す時に画像を下向きにするように指定しないと画像の上下が反転してしまう為です。

```swift
//UIImageの方向を確認
var imageOrientation:Int = 0
if (image.imageOrientation == UIImage.Orientation.down){
    imageOrientation = 2
}else{
    imageOrientation = 1
}
```

こうして取得した画像のデータ(NSData)と画像の向きをCloudKitに保存します。

```swift:ViewController.swift
import CloudKit
---------- (中略) -----------
    /**
     Cloud KitにデータをINSERTする
     @param INSERTするデータ code:コード name:名称 costRate:原価率 salesPrice:売単価 image:画像
     */
    private func insertData(code:String,name:String,costRate:Double,salesPrice:Int,image:UIImage!){
        let ckDatabase = CKContainer.default().privateCloudDatabase
        
        //INSERTするデータを設定
        let ckRecord = CKRecord(recordType: "GoodsMaster")
        ckRecord["code"] = code
        ckRecord["name"] = name
        ckRecord["costRate"] = costRate
        ckRecord["salesPrice"] = salesPrice
        
        if (image != nil){
            //UIImageをNSDataに変換
            let imageData = image.jpegData(compressionQuality: 1.0)
        
            //UIImageの方向を確認
            var imageOrientation:Int = 0
            if (image.imageOrientation == UIImage.Orientation.down){
                imageOrientation = 2
            }else{
                imageOrientation = 1
            }
            
            ckRecord["image"] = imageData
            ckRecord["imageOrientation"] = imageOrientation
        }
        
        //データのINSERTを実行
        ckDatabase.save(ckRecord, completionHandler: { (ckRecords, error) in
            if error != nil {
                //INSERTがエラーになった場合
                print("\(String(describing: error?.localizedDescription))")
            }
        })
    }
```

#画像をCloudKitから検索
では次にCloudKitに保存した画像を検索してみます。

```swift:ViewController.swift
import CloudKit
---------- (中略) -----------
    private let tableView:UITableView = UITableView()
    private var goodsMasters:[GoodsMaster] = [GoodsMaster]()

    struct GoodsMaster {
        var code:String
        var name:String
        var costPrice:Double
        var salesPrice:Int
        var image:UIImage!
    }
---------- (中略) -----------
    /**
     Cloud Kitからデータを検索する
     @param 検索条件 minSalesPrice:検索条件のsalesPriceの最小値 maxSalesPrice:検索条件のsalesPriceの最大値
     */
    private func searchData(minSalesPrice:Int,maxSalesPrice:Int){
        let ckDatabase = CKContainer.default().privateCloudDatabase
        //検索条件指定
        let ckQuery = CKQuery(recordType: "GoodsMaster", predicate: NSPredicate(format: "salesPrice >= %d and salesPrice <= %d", argumentArray: [minSalesPrice,maxSalesPrice]))
        
        //ソート条件指定
        ckQuery.sortDescriptors = [NSSortDescriptor(key: "salesPrice", ascending: false),NSSortDescriptor(key: "costRate", ascending: true)]
        
        //検索実行
        ckDatabase.perform(ckQuery, inZoneWith: nil, completionHandler: { (ckRecords, error) in
            if error != nil {
                //検索エラー
                print("\(String(describing: error?.localizedDescription))")
            }else{
                //検索成功
                self.goodsMasters.removeAll()
                for ckRecord in ckRecords!{
                    var image:UIImage! = nil
                    if (ckRecord["image"] != nil){
                        image = UIImage(data: ckRecord["image"]!)
                        let imageOrientation:Int = ckRecord["imageOrientation"]!
                        if (imageOrientation == 2) {
                            image = UIImage(cgImage: image!.cgImage!, scale: image!.scale, orientation: UIImage.Orientation.down)
                        }
                    }
                    let goodsMaster = GoodsMaster(code: ckRecord["code"]!, name: ckRecord["name"]!, costPrice: ckRecord["costRate"]!, salesPrice: ckRecord["salesPrice"]!,image:image)
                    self.goodsMasters.append(goodsMaster)
                }
                DispatchQueue.main.async {
                    self.tableView.reloadData()
                }
            }
        })
    }
```


検索して取得したNSData型をUIImage型に変換します。

```swift
if (ckRecord["image"] != nil){
    image = UIImage(data: imageData!)
    ---------- (中略) ----------
}
```


画像の向きが下向きだった時は以下の様に画像を下向きに変換します。
前述の様にこれを行わないと画像の上下が反転してしまいます。

```swift
let imageOrientation:Int = ckRecord["imageOrientation"]!
if (imageOrientation == 2) {
     image = UIImage(cgImage: image!.cgImage!, scale: image!.scale, orientation: UIImage.Orientation.down)
}
```

いかがでしたか。
圧縮してNSDataに変換して保存しているので、iCloud Documentsに画像ファイルのまま保存するより効率がいいと思います。

#GitHub
今回紹介したサンプルコードはGitHubで公開しています。

https://github.com/naosekig/CloudKitSample

#参考文献
[Creating a Database Schema by Saving Records - Apple Developer](https://developer.apple.com/library/archive/documentation/DataManagement/Conceptual/CloudKitQuickStart/CreatingaSchemabySavingRecords/CreatingaSchemabySavingRecords.html)
[CloudKitを使ってみた Swift4版 - Qiita](https://qiita.com/NaoSekig/items/25b6640fcdd7450581fa)

#関連記事
[CloudKitを使ってみた Swift4版](https://qiita.com/NaoSekig/items/25b6640fcdd7450581fa)
[CloudKitで画像を連携する - CKAsset編](https://qiita.com/NaoSekig/items/b3b25794fc075225a80d)