🔥
Core DataのNSManagedObjectをDecodableへ
例えば、Core Dataで「User」というEntityを作り出した時点では
Xcodeのどこかで「User」というクラスも作り出されています
その「User」クラスをそのままDecodable化にして、JSONファイルを直接にデコードできればばと思い、その実践の流れです
まずは画面から始める
デコード用のStruct(仮)を定義
Struct.swift
struct User: Codable {
let name: String
let age: Int
}
画面を定義
ContentView.swift
struct ContentView: View {
@State private var users = [User]()
let json = """
[
{
"name": "Carry",
"age": 21
},
{
"name": "Ben",
"age": 12
}
]
"""
var body: some View {
List {
Section {
Button("Decode Data", action: loadData)
}
ForEach(users, id: \.name) { user in
Section(user.name) {
HStack {
Text("Age")
Spacer()
Text(user.age.formatted())
}
}
}
}
}
func loadData() {
let data = Data(json.utf8)
do {
let decoder = JSONDecoder()
users = try decoder.decode([User].self, from: data)
} catch {
print(error.localizedDescription)
}
}
}
起動して、問題なく作動できるのを確認
Core DataのData Modelを作成
Struct.swiftを一旦削除します
下記のようにData Modelを作ってみます
そして、NSManagedObject Subclassを作成
以下のファイルが作り出されることを確認
User+CoreDataClass.swift
import Foundation
import CoreData
@objc(User)
public class User: NSManagedObject {
}
NSManagedObjectをDecodableに
まずはDecodable
というプロトコルを追加
User+CoreDataClass.swift
+public class User: NSManagedObject, Decodable {
}
CodingKeys
を追加
User+CoreDataClass.swift
public class User: NSManagedObject, Decodable {
+ enum CodingKeys: CodingKey {
+ case name, age
+ }
}
マニュアルデコードを追加
User+CoreDataClass.swift
public class User: NSManagedObject, Decodable {
enum CodingKeys: CodingKey {
case name, age
}
+ required public convenience init(from decoder: Decoder) throws {
+
+ guard let context = decoder.userInfo[CodingUserInfoKey(rawValue: "managedObjectContext")!] as? NSManagedObjectContext else { fatalError() }
+
+ self.init(context: context)
+
+ let container = try decoder.container(keyedBy: CodingKeys.self)
+ self.name = try container.decode(String.self, forKey: .name)
+ self.age = try container.decode(Int16.self, forKey: .age)
}
}
Data Controllerを追加
DataController.swiftを新しく作成
DataController.swift
import CoreData
import Foundation
class DataController: ObservableObject {
let container = NSPersistentContainer(name: "TestProject")
init() {
container.loadPersistentStores { description, error in
if let error = error {
print("Core Data failed to loaded: \(error.localizedDescription)")
return
}
}
}
}
"TestProject"
の部分は、Data Modelのファイル名と同じようにします
TestProjectApp.swift(エントリーファイル)に以下のコードを追加
TestProjectApp.swift
import SwiftUI
@main
struct TestProjectApp: App {
+ @StateObject private var dataController = DataController()
var body: some Scene {
WindowGroup {
ContentView()
+ .environment(\.managedObjectContext, dataController.container.viewContext)
}
}
}
ロード関数を添削
ContentView.swiftに戻り、managedObjectContextの環境変数を追加
そして、decoderにuserInfoを追加
ContentView.swift
struct ContentView: View {
+ @Environment(\.managedObjectContext) var moc
var body: Some View {
// 中間略
}
func loadData() {
let data = Data(json.utf8)
do {
let decoder = JSONDecoder()
+ decoder.userInfo[CodingUserInfoKey(rawValue: "managedObjectContext")!] = self.moc
users = try decoder.decode([User].self, from: data)
} catch {
print(error.localizedDescription)
}
}
}
そしたら完成でした!
Discussion