Peter Pik Peter Pik - 7 months ago 33
Swift Question

Realm append data to a type List<t>

I'm trying to go through data and save it in my model, however whatever i do i keep getting the following error:

Can't mutate a persisted array outside of a write transaction.
. What am i doing wrong? i'm appending each match to the league however it does not seem to work?

realm model

class League: Object {
dynamic var id: Int = 0
dynamic var name: String? = ""
var matches = List<Match>()

override class func primaryKey() -> String {
return "id"
}
}

class Match: Object {
dynamic var matchId: Int = 0
dynamic var date: NSDate?
dynamic var homeName: String? = ""
dynamic var awayName: String? = ""
dynamic var awayScore: Int = 0
dynamic var homeScore: Int = 0
dynamic var leagueName: String? = ""
dynamic var homeLogo: NSData?
dynamic var awayLogo: NSData?
}


Code

for (_, item) in result {
if let leagueId = item["league"].int,
let leagueName = item["name"].string,
let allMatches = item["matches"].array {
let league = League()
league.name = leagueName
league.id = leagueId

for match in allMatches {
if let matchId = match["matchId"].int,
let tournament = match["tournament"].string,
let homeTeam = match["homeName"].string,
let awayTeam = match["awayName"].string,
let homeScore = match["homeScore"].int,
let awayScore = match["awayScore"].int,
let homeLogo = match["homeLogo"].string,
let awayLogo = match["awayLogo"].string,
let date = match["date"].string {
if let awayLogoUrl = NSURL(string: awayLogo),
let homeLogoUrl = NSURL(string: homeLogo) {
if let awayLogoData = NSData(contentsOfURL: awayLogoUrl),
let homeLogoData = NSData(contentsOfURL: homeLogoUrl) {
let matchObject = Match()
matchObject.matchId = matchId
matchObject.leagueName = tournament
matchObject.homeName = homeTeam
matchObject.awayName = awayTeam
matchObject.homeScore = homeScore
matchObject.awayScore = awayScore
matchObject.homeLogo = homeLogoData
matchObject.awayLogo = awayLogoData

let formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
formatter.timeZone = NSTimeZone(abbreviation: "CET")
matchObject.date = formatter.dateFromString(date)!

league.matches.append(matchObject)
}
}
}

print(league)

try! realm.write {
realm.add(league, update: true)
}
}
}
}
}

Answer

Simplifying your code to show only the general structure helps to reveal the issue:

let league = League()
league.name = leagueName
league.id = leagueId

for match in allMatches {
    if … {
        let matchObject = Match()
        …

        league.matches.append(matchObject)
    }

    print(league)

    try! realm.write {
        realm.add(league, update: true)
    }
}

This is the sequence of events: You start by creating a standalone, unpersisted League instance, league. For each match, you create a standalone, unpersisted Match instance and append it to league.matches. You then create a write transaction, and save league to the Realm. From this point, league is no longer standalone, and may only be modified within a write transaction. On the next iteration of the loop you create another Match instance and attempt to append it to league.matches. This throws since league is persisted and we're not in a write transaction.

One solution here would be to restructure the code so you only save league to the Realm once, like so:

let league = League()
league.name = leagueName
league.id = leagueId

for match in allMatches {
    if … {
        let matchObject = Match()
        …

        league.matches.append(matchObject)
    }
}

print(league)

try! realm.write {
    realm.add(league, update: true)
}
Comments