【Swift】Realmのモックを作成する

# はじめに
昨日、Realmを使ってみたという記事を書きました。
それに関連してRealmのテストを書く際にMockを読み込む場面があったので記録しておきます。

https://qiita.com/SNQ-2001/items/c7dfa9e9e65ebc4334be

# 必要ツール
https://docs.realm.io/sync/realm-studio/

# 事前準備
```swift:RealmEntity
public class RealmEntity: Object {
    @Persisted(primaryKey: true) public var id: Int = 0
}
```

```swift:RealmRepositoryProtocol
public protocol RealmRepositoryProtocol {
    func get() -> Results<RealmEntity>
    func add(_ bookmark: RealmEntity) throws
    func delete(_ bookmark: RealmEntity) throws
}

public final class RealmRepository: RealmRepositoryProtocol {
    let realm: Realm
    
    public init(realm: Realm = try! Realm()) {
        self.realm = realm
    }
    
    public func get() -> Results<RealmEntity> {
        realm.objects(RealmEntity.self)
    }
    
    public func add(_ bookmark: RealmEntity) throws {
        try realm.write {
            realm.add(bookmark)
        }
    }
    
    public func delete(_ bookmark: RealmEntity) throws {
        guard let bookmark = realm.object(ofType: RealmEntity.self, forPrimaryKey: bookmark.id) else {
            throw NSError(domain: "primary key not found", code: 0)
        }
        try realm.write {
            realm.delete(bookmark)
        }
    }
}
```

# やりかた
### ① Realmオブジェクトを保存する
```swift:ContentView
import SwiftUI

struct ContentView: View {
    private let repository = RealmRepository()
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundColor(.accentColor)
            Text("Hello, world!")
        }
        .padding()
        .onAppear {
            let realmEntity = RealmEntity()
            realmEntity.id = 100
            try? repository.add(realmEntity)
        }
    }
}
```


### ② 保存されてるファイルを取得する
```swift:ContentView
import SwiftUI
import RealmSwift

struct ContentView: View {
    private let repository = RealmRepository()
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundColor(.accentColor)
            Text("Hello, world!")
        }
        .padding()
        .onAppear {
//            let realmEntity = RealmEntity()
//            realmEntity.id = 100
//            try? repository.add(realmEntity)
            
            print(Realm.Configuration.defaultConfiguration.fileURL!)
            // ↓ これが出力された ↓
            // file:///Users/miyamototaishin/Library/Developer/CoreSimulator/Devices/737336AC-3C03-4CD2-BC16-EAC4B1559152/data/Containers/Data/Application/0A92DBCD-5B9A-4F56-98E8-5E14390F3E18/Documents/default.realm
        }
    }
}
```

### ③ 保存されてるファイルを保存する
```:ターミナル
open file:///Users/miyamototaishin/Library/Developer/CoreSimulator/Devices/737336AC-3C03-4CD2-BC16-EAC4B1559152/data/Containers/Data/Application/AE835C93-B793-4B0F-9C9D-7179C2BB5A04/Documents/default.realm
```

以下のように先ほど保存した`100`があるのがわかる
![スクリーンショット 2023-04-16 22.25.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/01c5ec85-b1b6-5cf6-1bc1-4fe0811a4a7f.png)

「Local Realm」を選択して書き出します。
![スクリーンショット 2023-04-16 22.26.16 1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/58095472-6ff0-d316-1a9e-960a72a607be.png)

### ④ 保存したファイルをXcodeに取り込む

![スクリーンショット 2023-04-16 22.28.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/475a755c-8ff7-d70a-5d88-6617623ca36c.png)

![スクリーンショット 2023-04-16 22.29.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/ef5ef9f2-a6f6-42c9-fd66-402671d80e2f.png)

### ④ RepositoryMockを作成する
```swift:RealmRepositoryMock
public final class RealmRepositoryMock: RealmRepositoryProtocol {
    var realm: Realm {
        let url = URL(fileURLWithPath: Bundle.main.path(forResource: "default", ofType: "realm")!)
        return try! Realm(configuration: Realm.Configuration(fileURL: url, schemaVersion: 4))
    }
    
    public func get() -> Results<RealmEntity> {
        realm.objects(RealmEntity.self)
    }
    
    public func add(_ bookmark: RealmEntity) throws {}
    
    public func delete(_ bookmark: RealmEntity) throws {}
}
```

### ⑤ ビルドとプレビューでわける
```swift:ContentView
import SwiftUI
import RealmSwift

struct ContentView: View {
    @State var items: [RealmEntity] = []
    
    let repository: RealmRepositoryProtocol
    
    var body: some View {
        List(items, id: \.id) { item in
            Text(item.id.description)
        }
        .onAppear {
            // 起動時にランダムで数値を保存
            let realmEntity = RealmEntity()
            realmEntity.id = Int.random(in: 0...100)
            try? repository.add(realmEntity)
            // 保存されたものを取得
            items = Array(repository.get())
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView(repository: RealmRepositoryMock())
    }
}
```

# おわり
シュミレーターの方は起動するたびに数値が増えていきます。
プレビューの方は何回起動しても増えません。
|シュミレーター|プレビュー|
|-|-|
|![Simulator Screen Shot - iPhone 14 Pro - 2023-04-16 at 22.44.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/86250115-19db-3460-ebdf-6abd12577b09.png)|![スクリーンショット 2023-04-16 22.44.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/41135a49-22d1-2b0b-f2ca-ad3d78bc8448.png)|

# 参考記事
https://yamatooo.blog/entry/2020/12/02/083000

https://yamatooo.blog/entry/2021/01/07/083000