27364


1対多の親子関係にあるRealmのデータの子クラス側に新たなデータを追加したいです。  
新しい親クラスと新しい子クラスの両方を一度に追加する方法はわかりますが、既に存在する親クラスに、新たな子クラスのデータを追加する方法がわかりません。

具体的には、親クラス`ThreadData`はチャットのスレッド概要（タイトル名、登録者名等）、子クラス`ThreaadTalkData`にはそのスレッドに対する返信ごとに、その返信内容（返信文章、返信者名等）のデータを追加していきます。ひとつのスレッド概要に、複数の返信データがあるイメージです。  
親クラスは、`FirstVC`内に情報を入力、ボタンを押すことでRealmにデータが追加され、スレッド概要が`FirestV`C内にある`tableView`に表示されます。`tableView`のセルをクリックすると、下記`ThreadVC`に移動して親クラスのスレッド概要と子クラスの返信データを`ThreadVC`内にある`tableView`に表示します。  
子クラスは、Lineの返信のように`textField`に返信内容を記入し、投稿ボタン`func
Toko()`を押すと子クラス`ThreaadTalkData`を親クラス`ThreadData`と紐付いている新たなデータとして追加、保存したいのですが、その方法がわかりません。  
試行錯誤し、下記コードのTreadVC内、`func Toko`のように書きましたが、エラーが出ます。（詳細は｀func Toko｀部分のコメントを参照）

以下、コードの詳細です。

`FirstVC`のセル選択によるVC移動部分

[code]

    func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
        //delegateを通じて、何を選択したか通知
        appDelegate.selectedThreadIndexPath = indexPath.row
        let threadViewController = ThreadViewController()
        threadViewController.modalTransitionStyle = UIModalTransitionStyle.CoverVertical
        self.presentViewController(threadViewController, animated: true, completion: nil)
    }
    
[/code]

`ThreadVC`（一部省略）

[code]

    import UIKit
    import RealmSwift
    class ThreadViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UITextFieldDelegate {
        var appDelegate: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
        var selectedIndexPath: Int = Int()
    
        var keyboardToolBar = UIToolbar()
        var toolbarTextfield: UITextField = UITextField()
        var threadTableView: UITableView = UITableView()
    
        let realm = try! Realm()
        var threadTalkData: NSObject = NSObject()
        var selectedThread = ThreadData()
        var selectedThreadTalkData = List<ThreaadTalkData>()
        var tableData:[ThreadData4Test] = []
        var threadTitle: String = String()
        var threadDate: String = String()
        var threadName: String = String()//これ、スレッド主の名前
        var threadNushiPlayerID: Int = Int()
        var threadNushiImageName = "personal_icon@2x.png"//!!playerIDから画像をロードする。
        var threadNushiComment = String()
    
        ...
    
        var isObserving = false
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            selectedIndexPath = appDelegate.selectedThreadIndexPath
            //選択したスレッドの取得
            selectedThread = threadCollectionDataSource![selectedIndexPath]
            //トークデータ全て取得
            threadTalkData = realm.objects(ThreaadTalkData)
            selectedThreadTalkData = selectedThread.threadTalkDataList
            threadTitle = selectedThread.threadTitle
            threadDate = selectedThread.threadDate
            threadName = selectedThread.threadSpeaker
            threadNushiComment = selectedThread.threadMessage
            threadNushiPlayerID = selectedThread.threadSpeakerID
    
            var postThread : ThreadData = ThreadData()
            print("\(postThread.threadTalkDataList.count)")
            ...
        }
    
        func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return selectedThreadTalkData.count
        }
    
        func toko(sender: UIButton) {
            //時間の取得
            let now = NSDate()
    
            let formatter = NSDateFormatter()
            formatter.dateFormat = "yyyy/MM/dd HH:mm:ss"
    
            let stringNow = formatter.stringFromDate(now)
    
            let realm = try! Realm()
            //var postThread : ThreadData = ThreadData()
            var postData: ThreaadTalkData = ThreaadTalkData()
            postData.postedthreadID = selectedThread.threadID
            postData.postNo = selectedThreadTalkData.count //これで、+1される。
            //返信したプレイヤーを識別するための番号。PersonalDataのplayerIDと同一
            var postPerson = realm.objects(PersonalData).first
            postData.postPlayerID = (postPerson?.playerID)!
            postData.postDate = stringNow
            postData.postMessage = commentTextField.text!
            postData.postPlayerName = (postPerson?.playerName)!
            postData.postPlayerImage = (postPerson?.playerIconImage)!
            //postData.ownerThread = selectedThread
    
            if commentTextField.text == "" {
                let alertController = UIAlertController(title: "コメントが未入力です。", message: "コメントを入力しましょう。", preferredStyle: .Alert)
                let defaultAction = UIAlertAction(title: "OK", style: .Default, handler: nil)
                alertController.addAction(defaultAction)
                presentViewController(alertController, animated: true, completion: nil)
            } else {
                selectedThread.threadTalkDataList.append(postData)
                do {
                    let realm = try! Realm()
                    let thisThreadData = realm.objects(ThreadData).last!
                    try realm.write{
                        thisThreadData.threadTalkDataList = selectedThread.threadTalkDataList
                        //realm.add(selectedThread)
                    }
                } catch {
                    print("失敗")
                }
                //print("\(postThread.threadTalkDataList.count)")
                toolbarView.hidden = true
                commentTextField.resignFirstResponder()
                threadTableView.reloadData()
            }
        }
        //この内容で実行すると、
        // *** Terminating app due to uncaught exception 'RLMException', reason: 'Can only add, remove, or create objects in a Realm in a write transaction - call beginWriteTransaction on an RLMRealm instance first.'
        // *** First throw call stack:
        // となります。
    
[/code]

`ThreadData`（親データ）

[code]

    import Foundation
    import RealmSwift
    
    class ThreadData: Object {
        //threadのID。取得していない新規のスレッドがあったら、取得するための番号
        dynamic var threadID = 0
        dynamic var threadTitle = ""
        dynamic var threadMessage = ""
        dynamic var threadSpeaker = ""
        //しゃべった人のID名前が変わった際に取得できるように。PersonalDataのplayerIDと同一
        dynamic var threadSpeakerID = 0
        dynamic var threadDate = ""
    
        let threadTalkDataList = List<ThreaadTalkData>()
    
        //以下は使わない。
        //会話の順序付けされるための
        //collectionviewの順序付けんのために使うかもしれないので、作成
        dynamic var countNumber = 0
        dynamic var threadMessageNumber = 0
    }
    
[/code]

`ThreaadTalkData`（子データ）

[code]

    class ThreaadTalkData: Object {
        //アルファフラッグ全体でスレッドを識別するためのID
        dynamic var postedthreadID = 0
        //返信を投稿した順番付けのためのNo.　0から始まる
        dynamic var postNo = 0
        //返信したプレイヤーを識別するための番号。PersonalDataのplayerIDと同一
        dynamic var postPlayerID = 0
    
        //以下、表示情報
        dynamic var postDate = ""
        dynamic var postMessage = ""
        dynamic var postPlayerName = ""
        dynamic var postPlayerImage : NSData? = UIImagePNGRepresentation(UIImage(named: "personal_icon@2x.png")!)!
        dynamic var ownerThread: ThreadData?
    }
    
[/code]


CC BY-SA 3.0
2016-07-05T12:42:09.293
27362
2016-07-05T16:50:13.060
2016-07-05T16:38:55.380
5519
15277
question
swift swift2 realm
1対多の関係にあるデータの子クラス側にデータを追加保存したい


まず

[code]

    Terminating app due to uncaught exception 'RLMException', reason: 'Can only add, remove, or create objects in a Realm in a write transaction - call beginWriteTransaction on an RLMRealm instance first.'
    
[/code]

というエラーは、書いてある通り、Realmに保存されたオブジェクトを変更するのはトランザクションの中で行う必要がありますが、トランザクションが作られてないことによります。

[code]

    ...
    } else {
        selectedThread.threadTalkDataList.append(postData)
        do {
                let realm = try! Realm()
                ...
    
[/code]

上記のコードの`selectedThread.threadTalkDataList.append(postData)`は、Realmを検索して取得した`selectedThread`に対して変更を加えているので、トランザクションの中（`realm.write
{}`）で行う必要があります。

コードがややこしいので推測ですが、おそらくやりたいことは下記のように単に`selectedThread`の`threadTalkDataList`に`todo()`内で新しく作成した`postData`を追加するだけでいいと思います。

[code]

    } else {
        do {
            let realm = try! Realm()
            try realm.write{
                selectedThread.threadTalkDataList.append(postData)
            }
        } catch {
            print("失敗")
        }
    
[/code]


CC BY-SA 3.0
2016-07-05T16:50:13.060
27364
2016-07-05T16:50:13.060
5519
27362
answer