Help us understand the problem. What is going on with this article?

SwiftでCoreData「NSManagedObjectのプロパティが上手く取れない」ときの対処法

More than 5 years have passed since last update.

やることはとても単純で。
ベースはこちら

SwiftでCoreDataを使ってみる(Stepごとに解説)

ここでは、ViewController.swiftに、CoreDataへの書き込みと読み込みを記述しています。

そうすると特に問題なく、できるのですが。

しかし、上の事例を、

  • ViewController
  • SecondViewController

と2つのViewにまたがってやろうとすると、突然!

NSManagedeObjectのプロパティにアクセスできなくなっちゃうのです。

具体的には、

ViewController.swift
result.memo
result.date

といった感じで、プロパティアクセスできていたのが、エラーになるのです。
(memoとdateはAtrributeです。resultは何かしらのNSManagedObject。)

いきなり解決策

で、この問題で困ったー。てなってて、なんとか、値が取りたくて、できたにはできたので、先に解決方法を書きます。とても簡単です。

SecondViewController.swift
result.valueForKey("memo") as String
result.valueForKey("date") as NSDate

こんな感じでvalueForKeyを使って無理矢理とります。なんだか、キレイではありません。。。しかし、今はこの方法しか見つからないのです。

※コメントを頂き、まっとうな対処方法が見つかりましたので、下に追記しております!

以下にコード部分の全文を記載するので、問題が分かる方いたら、ご教示頂けますと幸いです。。。。

SwiftでCoreDataを使ってみる(Stepごとに解説)
をもとに、Viewを2つに分割する。

ViewController.swift
import UIKit
import CoreData

class ViewController: UIViewController {

    var memoField: UITextField!
    var memos: [NSString] = []
    var dates: [NSDate] = []

    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = UIColor.whiteColor()

        // text field
        memoField = UITextField(frame: CGRectMake(100, 100, 200, 30))
        memoField.borderStyle = UITextBorderStyle.RoundedRect
        self.view.addSubview(memoField)

        // write button
        let writeBtn: UIButton = UIButton(frame: CGRectMake(100, 150, 200, 30))
        writeBtn.backgroundColor = UIColor.magentaColor()
        writeBtn.setTitle("write", forState: UIControlState.Normal)
        writeBtn.addTarget(self, action: "writeData", forControlEvents: UIControlEvents.TouchUpInside)
        self.view.addSubview(writeBtn)

        // next button
        let nextBtn: UIButton = UIButton(frame: CGRectMake(100, 250, 200, 30))
        nextBtn.backgroundColor = UIColor.greenColor()
        nextBtn.setTitle("next", forState: UIControlState.Normal)
        nextBtn.addTarget(self, action: "goNext", forControlEvents: UIControlEvents.TouchUpInside)
        self.view.addSubview(nextBtn)

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    // 書き込み処理(writeBtnのアクション)
    func writeData() {
        let appDel: AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
        let memoContext: NSManagedObjectContext = appDel.managedObjectContext!
        let memoEntity: NSEntityDescription! = NSEntityDescription.entityForName("MemoStore", inManagedObjectContext: memoContext)
        var newData = MemoStore(entity: memoEntity, insertIntoManagedObjectContext: memoContext)
        newData.memo = memoField.text
        newData.date = NSDate()
    }

    // 次ページへ移動処理
    func goNext() {
        let second = SecondViewController()
        self.presentViewController(second, animated: true, completion: nil)
    }
}
SecondViewController.swift
import UIKit
import CoreData

class SecondViewController: UIViewController {
    var memos: [NSString] = []
    var dates: [NSDate] = []

    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = UIColor.cyanColor()

        // read button
        let readBtn: UIButton = UIButton(frame: CGRectMake(100, 250, 200, 30))
        readBtn.setTitle("read", forState: UIControlState.Normal)
        readBtn.backgroundColor = UIColor.redColor()
        readBtn.addTarget(self, action: "readData", forControlEvents: UIControlEvents.TouchUpInside)
        self.view.addSubview(readBtn)

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    // 読み込み処理(readBtnのアクション)
    func readData() {

        let appDel: AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
        let memoContext: NSManagedObjectContext = appDel.managedObjectContext!
        let memoRequest: NSFetchRequest = NSFetchRequest(entityName: "MemoStore")
        memoRequest.returnsObjectsAsFaults = false
        var results: NSArray! = memoContext.executeFetchRequest(memoRequest, error: nil)

        memos = []
        dates = []

        for result in results {

            // これで、なんとか出来るようになりました。       
            memos.append(result.valueForKey("memo") as String)
            dates.append(result.valueForKey("date") as NSDate)            
        }

        // コンソールに表示
        println(memos)
        println(dates)
    }
}

Xcodeのバグの噂も聞きますが。。。わかりません。。。

分かる方いらっしゃいましたら、ぜひ宜しくお願い致します。

追記:まっとうな対処法

結論からいきます。やることは3つです。

  • [1] Entity「MemoStore」にちゃんとClass名をつける
  • [2] MemeStore.swiftの修正
  • [3] NSArraryの部分を型変換して、as [MemoStore]!と書いておく

[1] EntityのClassをちゃんと書く。

スクリーンショット 2014-11-23 13.53.32.png

こんな感じで、モジュール名をprefixにしてClass名を書いておく。

[2] MemoStore.swiftの修正

MemoStore.swift
import UIKit
import CoreData

// @objc(MemoStore) <- この行を削除
class MemoStore: NSManagedObject {

    @NSManaged var memo:String
    @NSManaged var date:NSDate
}

[3] 型変換をして、NS

SecondViewController.swift
// 修正前
var results: NSArray! = memoContext.executeFetchRequest(memoRequest, error: nil)

// 修正後
var results = memoContext.executeFetchRequest(memoRequest, error: nil) as [MemoStore]!

こうすることで、

SecondViewController.swift
result.memo
result.date

で呼び出せるようになりました。

ちなみに、[1]と[2]を行わずに[3]を行った場合は、
fatal error: NSArray element failed to match the Swift Array Element type
というエラーが出ちゃいまいした。

兎に角、これで解決はしたのですが、
@u39ueda さんの仰る通り、なぜViewController側は、これらの設定をしなくても正常にうごくのか。。。それは謎です。。

コメントにて、ご指摘を頂きまして本当にありがとうございました!!
まだ、モジュール名とか型変換とかオプショナルとか基礎的なことが、ちゃんと理解できていないので、勉強します。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away