はじめに
Xcode6 beta4 で、SwiftでCoreDataを利用したいときに、
NSManagedObjectのSubclassを使うといろいろ罠があるので共有します。
何かしらのバグっぽいので、いずれ不要になることを願います。
断片的に色々情報がありますが、最終的には以下の記事が参考になりました。
http://www.sep.com/sep-blog/2014/06/23/a-c-developer-learns-swift-part-1-core-data/
@objc(HogeEntity) の おまじない
【更新】2014/8/2
これは付けないほうが良さそうです。
SwiftでNSManagedObjectのSubclassを使う場合は、対応Class名にモジュール名のPrefixを付ける
まず、 NSManagedObject の Subclassは @objc(EntityClass) を付けておきます。
@objc(ChildEntity)
class ChildEntity: NSManagedObject {
@NSManaged var identity: String
@NSManaged var name: String?
@NSManaged var birthday: NSDate?
@NSManaged var sex: Int
@NSManaged var imageFile: String?
}
以下のように書いてもOKです。 ←嘘でした。
@obj class ChildEntity: NSManagedObject {
...
2014/8/2 追記
@objc(MyClass)
class MyClass ...
と書くのと、
@objc class MyClass
と書くのではどうやら意味が少し変わるようです。
少なくとも前者は名前空間が (root).MyClass
になるようで、
従来のObjectiveCの interface MyClass
と衝突します。
が、後者では衝突しないようです。
以下の内容も 前者の書き方 だと両方のコードで動きました。
後者の書き方だと一見動くけど何かハマるだけのようです。
そして最終的には、下記のやり方の方がオススメで、この方法でも下記の両方のコードが動きました。
SwiftでNSManagedObjectのSubclassを使う場合は、対応Class名にモジュール名のPrefixを付ける
家と会社のPCで動作が違うような気がしないでもない、、、とにかくハマりやすいので気をつけましょう。
Entityを作る時
何故かうまくいかない書き方
以下のように書くとCastに失敗するようでエラーになりました。
NSManagedObject の Instanceは取得できているのですが、そこからのCastが失敗します。
let obj = NSEntityDescription.insertNewObjectForEntityForName("ChildEntity", inManagedObjectContext: mainContext)
let child = obj as ChildEntity
上手くいく書き方
こうすると上手く行きました。明示的に Class と CoreData Entity を結びつけてしまうわけですね。
本来こんな必要はなさそうですが、とりあえず今はしょうがないのかな。。
let entity = NSEntityDescription.entityForName("ChildEntity", inManagedObjectContext: mainContext)
let child = ChildEntity(entity: entity, insertIntoManagedObjectContext: mainContext)
Fetchする時
何故か上手くいかない書き方
こう書くとやはり Castするところでnilになってしまいます。 as?
を as
にすると当然エラーになります。
func loadAll() -> [ChildEntity]? {
let request = NSFetchRequest(entityName: "ChildEntity")
var array = mainContext.executeFetchRequest(request, error: nil)
return array as? [ChildEntity]
}
何故か上手くいく書き方
そしてこれが何故か上手くいく書き方です。 array:NSArray
というのがおまじないです。
何がなんだかもうわかりません。
func loadAll() -> [ChildEntity]? {
let request = NSFetchRequest(entityName: "ChildEntity")
let array: NSArray = mainContext.executeFetchRequest(request, error: nil)
return Array.bridgeFromObjectiveC(array)
}
(2014/8/6追記)beta5だと上手くいく書き方
beta5だと Fetchの結果をNSArrayにしてから、array as [T]
で良いようです。
あと、 Array.bridgeFromObjectiveC
は無くなったようですね。
func loadAll() -> [ChildEntity]? {
let request = NSFetchRequest(entityName: "ChildEntity")
let array: NSArray = mainContext.executeFetchRequest(request, error: nil)
return array as? [ChildEntity]
}