evermeer / AlamofireJsonToObjects

An Alamofire extension which converts JSON response data into swift objects using EVReflection
Other
161 stars 28 forks source link

Saving json on NSUserDefaults #19

Closed sagits closed 8 years ago

sagits commented 8 years ago

Hi evermeer, i just want to expose the approach im using to save json for local usage. My app basically loads data from the api on the first time and on the next it check if theres local data instead of downloading it again, the user can use a pull to reflesh to force the redownload.

Im using NSUserDefaults for saving a json array and using EVReflection to parse it when retrieving. What do you think about this approach? Heres the code:

// this class basically provides an id so i can use getById and compare objects 
class MyModel : EVObject{
    var id : Int = 0
}
import UIKit
import EVReflection
import AlamofireJsonToObjects

class AbstractLocalDB<T where T:MyModel>: NSObject {

    // add single object to list
    func add(object : T, name : String) {

        var oldArray = NSUserDefaults.standardUserDefaults().objectForKey(name)
        var array : [NSString]
        if (oldArray != nil) {
             array = oldArray as! [NSString]
        }
        else {
            array = [NSString]()
        }

        var toJson = EVReflection.toJsonString(object, performKeyCleanup: false)

        array.append(toJson as! NSString!)
        NSUserDefaults.standardUserDefaults().setObject(array, forKey: name)

    }

    func clearAll() {
        let appDomain = NSBundle.mainBundle().bundleIdentifier!

        NSUserDefaults.standardUserDefaults().removePersistentDomainForName(appDomain)
    }

    func add(object : T) {
        self.add(object, name: String(T().dynamicType))
    }

    // add or replace saved list
    func add(objects : [T], name : String) {

        var array : [NSString] = [NSString]()

        for object in objects {
            var toJson = EVReflection.toJsonString(object, performKeyCleanup: false)
            array.append(toJson as! NSString!)

        }

        NSUserDefaults.standardUserDefaults().setObject(array, forKey: name)
        print(array)

    }

    func add(objects : [T]) {

     self.add(objects, name: String(T().dynamicType))
    }

    func getAll(object : T, name : String) -> [T]? {
        var oldArray = NSUserDefaults.standardUserDefaults().objectForKey(name)
        var array : [NSString]
        var objArray:[T]
        if (oldArray != nil) {
            var str : String = formatStringToJson(oldArray as! [NSString])

            print(str)
            objArray = EVReflection.arrayFromJson(T().dynamicType.init(), json: str)

            return objArray;

        }
        return nil;

    }

    func clear(object : T, name : String) {
        var list : [T]? = getAll(object, name: name)

        if (list != nil) {
            self.remove(list!, name: name)
        }
    }

    func remove(object : T) {
        self.remove(object, name: String(T().dynamicType))
    }

    func remove(list : [T], name : String) {
        for object in list {
            self.remove(object, name: name)
        }
    }

    func remove(object : T, name : String) {
        var exist : Bool = false
        let objArray = self.getAll(object, name : name)
        var myObject : T
        var index : Int = 0;
        let id : Int = (object as MyModel!).id

        if (objArray != nil) {
            var objectArray : [T] = objArray!
            for (var x = 0 ; x < objectArray.count ; x++) {
                let myModel = objectArray[x] as MyModel!
                if (myModel.id == id) {
                    exist = true
                    index = x
                    break
                }
            }
        }

        if (exist) {

            var stringArray : [NSString] = NSUserDefaults.standardUserDefaults().objectForKey(name) as! [NSString]

            stringArray.removeAtIndex(index)
            NSUserDefaults.standardUserDefaults().setObject(stringArray, forKey: name)

        }
    }

    func edit(object : T) {
        self.edit(object, name: String(T().dynamicType))
    }

    func edit(object : T, name : String) {

        var exist : Bool = false
        var objArray = self.getAll(object, name : name)
        var myObject : T
        var index : Int = 0;

        if (objArray != nil) {
            var objectArray : [T] = objArray!
            for (var x = 0 ; x < objectArray.count ; x++) {
                var myModel = objectArray[x] as MyModel!
                if (myModel.id == object.id) {
                    exist = true
                    index = x
                    break
                }
            }
        }
        else {
            self.add(object, name: name)
            return
        }

        if (exist) {

            var stringArray : [NSString] = NSUserDefaults.standardUserDefaults().objectForKey(name) as! [NSString]

            var toJson = EVReflection.toJsonString(object, performKeyCleanup: false)
            stringArray[index] = toJson
            NSUserDefaults.standardUserDefaults().setObject(stringArray, forKey: name)

        }
        else {
            self.add(object, name: name)
        }

    }

    func getById(object : T, name : String, id: Int) -> T? {
        let objArray = self.getAll(object, name : name)
        var myObject : T

        if (objArray != nil) {
            let objectArray : [T] = objArray!
            for model in objectArray {
                let myModel = model as MyModel!
                if (myModel.id == id) {
                    return model
                }
            }
        }
        return nil

    }

    private func formatStringToJson(array : [NSString]) -> String {
        var str = "["
        for (var x = 0 ; x < array.count ; x++) {
            str += array[x] as String
            if (x < array.count - 1) {
                str += ", "
            }
        }

        str += "]"
      //  println(str)

        return str

    }

  }
evermeer commented 8 years ago

EVObject supports NSCoding. You could directly save an object to the NSUserDefaults. you do not have to convert it back and forward to json.

If your data is more than a couple of K, I think it's better to save it to disk instead. You could use NSCoding for that or just the saveToTemp or saveToDocuments function on the EVObject itself. If you have an array of object, then you only have to call: NSKeyedArchiver.archiveRootObject(data, toFile: filePath)

sagits commented 8 years ago

Thank you for answering. Can i save a list o EVObjects directly too? I'll try this approach (saving in files) and do some benchmarking.

evermeer commented 8 years ago

Yes you can. Arrays also support NSCoding. So you can write an array with something like: NSKeyedArchiver.archiveRootObject(data, toFile: filePath) I have used that in EVCloudKitDao. See this code as a sample: https://github.com/evermeer/EVCloudKitDao/blob/0ee2bb09e407d5cf72fd6b1339029ec6b018c686/AppMessage/AppMessage/CloudKit/EVCloudKitDao.swift#L504-L504