Search code examples
iosswiftrealmnsdata

Save arrays of data or custom classes with Realm


I have a custom class holding a lot of information. It is a location tracking app, so i need the locations later on (basically only x,y,z, but i use CLLocations for convenience). Right now i have a custom class holding all of the information regarding each tracking, like a record/save file). I use Realm to save this, and Realm does it fine with my Doubles and Strings, but i have trouble with the arrays of data.

The most convenient solution i could find is to convert the data into NSData. With help from Google, i have found two methods to encode and decode. But i have no experience in this, so i am not sure if it works. And i don't think it works.

I can load the Doubles and Strings fine, but my NSData seems to be empty. I have tried to just encode my class to NSData and save it with Realm, but that doesn't seem to work, hence my theory that the encode/decode functions doesn't work properly.

TL;DR I have my custom class (a record of data) i would like to save with Realm. How do i come around this best?

My class:

class DataRecord {
    var startLocation : CLLocation = CLLocation.init()
    var endLocation : CLLocation = CLLocation.init()
    var duration : Double = 0.0
    var distance: Double = 0.0

   var avgSpeed : Double = 0.0
   var topSpeed : Double = 0.0

   var locations : [CLLocation] = []
   var altitudes : [Double] = []
   var angles : [Double] = []
   var speeds : [Double] = []
}

My NSData encode/decoder

func encode<T>(value: T) -> NSData {
    var val = value
    return withUnsafePointer(&val) { p in
        NSData(bytes: p, length: sizeofValue(value))
    }
}

func decode<T>(data: NSData) -> T {
    let pointer = UnsafeMutablePointer<T>.alloc(sizeof(T.Type))
    data.getBytes(pointer, length: sizeof(T))

    return pointer.move()
}

The Realm class have almost the same as my record, hence the reason why it would be easier to just encode the record and save it to Realm. But here it is:

class Record: Object {
    dynamic var name = ""
    dynamic var locations = NSData()
    dynamic var altitudes = NSData()
    dynamic var angles = NSData()
    dynamic var speeds = NSData()
    dynamic var distance = 0.0
    dynamic var duration = 0.0
    dynamic var topSpeed = 0.0
    dynamic var avgSpeed = 0.0
    dynamic var topAngle = 0.0
    dynamic var avgAngle = 0.0
}

Solution

  • Realm only supports the basic data types so you're correct in needing to 'translate' the data from CLLocation to something that Realm can store.

    In this case, instead of trying to serialize a CLLocation to NSData, it would be a lot easier to just make another Realm Object subclass that holds the same data as CLLocation, and can create objects of that type on the fly.

    Additionally, while it's somewhat limiting, Realm can only store other Realm Objects in its List properties. So in this case, it would be necessary to wrap the other values (e.g. altitudes etc) in their own Realm Object subclasses too.

    class Location: Object {
        dynamic var latitude = 0.0
        dynamic var longitude = 0.0
        var clLocation: CLLocation {
            return CLLocation(latitude: self.latitude, longitude: self.longitude)
        }
    
        init(clLocation: CLLocation) {
            self.latitude = clLocation.latitude
            self.longitude = clLocation.longitude
        }
    }
    
    class Altitude: Object {
        dynamic var altitudeValue = 0.0
    }
    
    class Angle: Object {
        dynamic var angleValue = 0.0
    }
    
    class Speed: Object {
        dynamic var speedValue = 0.0
    }
    
    class Record: Object {
        dynamic var name = ""
    
        dynamic var startLocation: Location?
        dynamic var endLocation: Location?
    
        dynamic var distance = 0.0
        dynamic var duration = 0.0
        dynamic var topSpeed = 0.0
        dynamic var avgSpeed = 0.0
        dynamic var topAngle = 0.0
        dynamic var avgAngle = 0.0
    
        let locations = List<Location>()
        let altitudes = List<Altitude>()
        let angles = List<Angle>()
        let speed = List<Speed>()
    }