简体   繁体   中英

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
}

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 Object s in its List properties. So in this case, it would be necessary to wrap the other values (eg 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>()
}

If your model class confirms NSCoding protocol then this how you can put it in the Realm.

Example: Route is my model structure.

struct Route {
    fileprivate (set) var locations: [CLLocation]

    init() {
        locations = []
    }

    init(withLocations locations: [CLLocation]) {
        self.locations = locations
    }
}

RouteRealm is wrapping for Route makes possible to save object to Realm

class RouteRealm: Object {
    dynamic var locations: Data? = nil

    convenience init(route: Route) {
        self.init()
        self.locations = NSKeyedArchiver.archivedData(withRootObject: route.locations)
    }

    func route() -> Route {
        if let locations = locations,
            let clLocations = NSKeyedUnarchiver.unarchiveObject(with: locations) as? [CLLocation] {
            return Route(withLocations: clLocations)
        }
        return Route()
    }
}

Storage:

struct RealmStore: DataStore {
    let realm = try! Realm()

    func store(route: Route) {
        let routeRealm = RouteRealm(route: route)
        try! realm.write {
            realm.add(routeRealm)
        }
    }

    func routes() -> [Route] {
        let routesRealm = realm.objects(RouteRealm.self)
        let routes = routesRealm.map() { $0.route() }
        return Array(routes)
    }
}

protocol DataStore {
    func store(route: Route)
    func routes() -> [Route]
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM