search
LoginSignup
3

More than 1 year has passed since last update.

posted at

updated at

【SwiftUI】CoreDataでカスタムクラスのデータを保存する!

CoreDataにカスタム(独自)クラスのデータを保存できたら嬉しいですよね!
SwiftUIでの記事が見つからなかったので書いてみます。

型をTransformableにしたり、NSCodingじゃなくてCodingを使うやり方もないかと試行していたのですが(その方が軽いと言っている方も)、とりあえず動いたので共有します。

環境

Swift 5.5
Xcode 13.0

1 CoreDataの使用

新規プロジェクト作成時にUse CoreDataにチェックを入れます。

Booking というエンティティを作り、以下のようなAttributeを設定しました。

スクリーンショット 2022-01-20 12.26.12.png

このdetailListに、カスタムクラスの配列 [Detail] を保存したいと思います。
TypeはBinary Dataにしてください。

2 カスタムクラスの設定

Detailというカスタムのclassを作っていきます。

class Detail: NSObject, NSCoding{
      var ID    = UUID()
      var day: Int = 0
      var free: Bool = false

    init(day: Int?, free: Bool?) {
          self.day = day!
          self.free = free!
       }


    func encode(with aCoder: NSCoder) {
        aCoder.encode(self.day, forKey: "day")
        aCoder.encode(self.free, forKey: "free")
    }

    required init?(coder aDecoder: NSCoder) {
        self.day = aDecoder.decodeInteger(forKey: "day") as! Int
        self.free = aDecoder.decodeBool(forKey: "free") as! Bool
    }
}

ここで注意ですが

        self.day = coder.decodeObject(forKey: "day") 
        self.free = coder.decodeObject(forKey: "free")

↑のように、decodeObjectにしているとエラーが出ます!こんなかんじ

-[NSKeyedUnarchiver decodeObjectForKey:] : value for key ( day ) is not an object. This will become an error in the future.

型に合わせてdecodeInteger, decodeBool等を使用してください!
※文字列をデコードするときは、decodeObject(forKey :)で。

3 データの保存

struct AddingNewBookingView: View {
    @Environment(\.managedObjectContext) private var viewContext
    ...
    var body: some View {
    ...
    }
    func addBooking(){
        let bookings = myBooking // View内で作成した配列([Bool])

        for i in 0..<bookings.count {
            let detail = Detail(day: i, free:  bookings[i])
            detailList.append(detail)
        }

        let newBooking = Booking(context: viewContext)

        do{
            // ここが大切!
            let detailListData: Data = try NSKeyedArchiver.archivedData(withRootObject: detailList, requiringSecureCoding: false) as Data

            newBooking.id = UUID()
            newBooking.name = name
            newBooking.studentNum = Int16(studentNum)!
            newBooking.detailList = detailListData

        } catch let error {
            print(error.localizedDescription)
        }
        do {
            try viewContext.save()
        } catch {
            let nsError = error as NSError
            fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
        }
    }
}

4 データの読み出し

struct BookingEditView: View {
    @Environment(\.managedObjectContext) private var viewContext
    @ObservedObject var booking: Booking //親ビューでフェッチしたものを受け取っています
    ...
    var body: some View {
       VStack{
         ...
       }.onAppear(perform: {
                if  let detailListData = booking.detailList {
                do {
                    name = booking.name!
                    studentNum = String(booking.studentNum)
                    let data: Data = detailListData as Data

                    // ここが大切!
                    detailList =  try! NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as! [Detail]
                } catch let error{
                    print(error.localizedDescription)
                }
            })
        }
    }
}

参考:
https://qiita.com/tky823/items/7f3899dc07191d4b8635
https://stackoverflow-com.translate.goog/questions/39784513/swift-nscoding-decodeobject-with-nil-all-the-time?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja&_x_tr_pto=sc

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
3