LoginSignup
4
3

More than 1 year has passed since last update.

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

Last updated at Posted at 2022-01-20

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

4
3
0

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
  3. You can use dark theme
What you can do with signing up
4
3