Skip to content

Instantly share code, notes, and snippets.

@KentarouKanno
Last active May 31, 2018 04:20
Show Gist options
  • Save KentarouKanno/986dd60630a1ad0cf637 to your computer and use it in GitHub Desktop.
Save KentarouKanno/986dd60630a1ad0cf637 to your computer and use it in GitHub Desktop.
UserDefaults

UserDefaults

★ UserDefaultsのオブジェクトを取得する

let ud = UserDefaults.standard

★ NSUserDefaultsに初期値を登録する

var dictionary = ["key":"value"]

// 既に同じキーが存在する場合は初期値をセットせず、キーが存在しない場合だけ値をセットしてくれる
ud.registerDefaults(dictionary)

 registerDefaults: メソッドを使って設定した初期値はアプリを終了すると消えてしまいますので使う時は注意!

★ キーを設定してオブジェクトを保存する

ud.setObject("string", forKey: "stringKey")

★ 保存する情報を即時に反映させる

ud.synchronize()

★ キーを設定してString型を取得する

var string = ud.stringForKey("stringKey")

var string:String = ud.stringForKey("stringKey")!

★ キーを設定してデータを削除する

ud.removeObjectForKey("stringKey")

★ Bool値を保存、取得する

var boolValue = true
ud.setBool(boolValue, forKey: "boolKey")

var boolValue = ud.boolForKey("boolKey")

★ Int型を保存、取得する

var intValue = 10
ud.setInteger(intValue, forKey: "intKey")

var intValue = ud.integerForKey("intKey")

★ float型を保存、取得する

var floatValue:Float = 123.4
ud.setFloat(floatValue, forKey: "floatKey")

var floatValue = ud.floatForKey("floatKey")

★ Double型を保存、取得する

var doubleValue: Double = 123.4
ud.setDouble(doubleValue, forKey: "doubleKey")

var doubleValue = ud.doubleForKey("doubleKey")

★ NSURL型を保存、取得する

var url:NSURL = NSURL(string: "https://www.apple.com")!
ud.setURL(url, forKey: "URLKey")

var urlValue = ud.URLForKey("URLKey")

★ 複雑な構造のデータ

typealias DataType = [[Int: [String]]]

// --- 保存 ---
let dataArray: DataType = [[1:["value1","value2"]],
                           [2:["value3","value4"]],
                           [3:["value5","value6"]],
                           [4:["value7","value8"]]]

let defaults = NSUserDefaults.standardUserDefaults()

var saveDataArray = [NSData]()
for data in dataArray {
    let nsdata = NSKeyedArchiver.archivedDataWithRootObject(data)
    saveDataArray.append(nsdata)
}

defaults.setObject(saveDataArray, forKey: "data")
defaults.synchronize()


// --- 読み込み ---
if let nsdataArray = defaults.objectForKey("data") as? [NSData] {
    
    var dataArray: DataType = []
    for data in nsdataArray {
        if let data = NSKeyedUnarchiver.unarchiveObjectWithData(data) as? [Int: [String]] {
            dataArray.append(data)
        }
    }
    
    print(dataArray[0][1]![1])
    //=> value2
    
    print(dataArray)
    //=> [[1: ["value1", "value2"]], [2: ["value3", "value4"]], [3: ["value5", "value6"]], [4: ["value7", "value8"]]]
}

Customクラスを保存する

★ NSCodingとNSKeyedArchiverを使用したパターン

// DataModel
class Person: NSObject, NSCoding {
    var name: String = ""
    var age : Int = 0
    
    init(name: String, age: Int) {
        self.name = name
        self.age  = age
    }
    
    @objc required init(coder aDecoder: NSCoder) {
        let name = aDecoder.decodeObjectForKey("name") as? String
        let age  = aDecoder.decodeObjectForKey("age") as? Int
        if let name = name,age = age {
            self.name = name
            self.age  = age
        }
    }
    
    @objc func encodeWithCoder(aCoder: NSCoder) {
        aCoder.encodeObject(self.name, forKey: "name")
        aCoder.encodeObject(self.age , forKey: "age")
    }
}

extension NSUserDefaults {
    var personArray: [Person] {
        get{
            let rowData: NSData = self.objectForKey("PersonData") as? NSData ?? NSData()
            let datas = NSKeyedUnarchiver.unarchiveObjectWithData(rowData) as? [Person] ?? []
            return datas
        }
        set(newDatas) {
            let archive = NSKeyedArchiver.archivedDataWithRootObject(newDatas)
            self.setObject(archive, forKey: "PersonData")
        }
    }
}

// Usage (以下の全てのパターンも使い方は同じ)

let data1 = Person(name: "MyName1", age: 20)
let data2 = Person(name: "MyName2", age: 21)
let data3 = Person(name: "MyName3", age: 22)

let defaults = NSUserDefaults.standardUserDefaults()

defaults.personArray = [data1, data2, data3]
defaults.synchronize()


let data = defaults.personArray
print(data[1].name, data[1].age)
//=> MyName2 21

★ Swift3

class Person: NSObject, NSCoding {
    var name: String = ""
    var age : Int64 = 0
    
    init(name: String, age: Int) {
        self.name = name
        self.age  = Int64(age)
    }
    
    required init?(coder aDecoder: NSCoder) {
        let name = aDecoder.decodeObject(forKey: "name") as? String
        let age  = aDecoder.decodeInt64(forKey: "age")
        if let name = name {
            self.name = name
            self.age  = age
        }
    }
    
    func encode(with aCoder: NSCoder) {
        aCoder.encode(self.name, forKey: "name")
        aCoder.encode(self.age , forKey: "age")
    }
}

extension UserDefaults {
    var personArray: [Person] {
        get{
            let rowData: NSData = self.object(forKey: "PersonData") as? NSData ?? NSData()
            let datas = NSKeyedUnarchiver.unarchiveObject(with: rowData as Data) as? [Person] ?? []
            return datas
        }
        set(newDatas) {
            let archive = NSKeyedArchiver.archivedData(withRootObject: newDatas)
            self.set(archive, forKey: "PersonData")
        }
    }
}

★ NSCodingとNSKeyedArchiverを使用しないパターン(Struct,ClassもOK)

// DataModel
struct Person {
    
    let name: String
    let age : Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age  = age
    }
}

// StructをNSObjec型(NSDictionary)に変換することでUserDefaultsに保存する
extension NSUserDefaults {
    
    var personArray: [Person] {
        get {
            let dictArray = self.objectForKey("PersonData") as? [NSDictionary] ?? []
            
            let array = dictArray.reduce([]){ (ary, dict: NSDictionary) -> [Person] in
                
                if let name = dict["name"] as? String, age = dict["age"] as? Int {
                    return ary + [Person(name: name, age: age)]
                } else {
                    return ary
                }
            }
            return array
        }
        
        set(personDatas) {
    
            let newDatas: [NSDictionary] = personDatas.map {
                  ["name": $0.name,
                    "age": $0.age] as NSDictionary
            }
            
            self.setObject(newDatas,forKey:"PersonData")
        }
    }
}

★ 相互変換するinitを定義する

// DataModel
struct Person {
    
    let name: String
    let age : Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age  = age
    }
}


extension NSDictionary {
    convenience init(_ person: Person) {
        let dict = ["name": person.name, "age": person.age]
        self.init(dictionary: dict)
    }
}

extension Person {
    init(_ dict: NSDictionary) {
        let name = dict["name"] as? String ?? ""
        let age  = dict["age"]  as? Int    ?? 0
        self.init(name: name, age: age)
    }
}


extension NSUserDefaults {
    var personArray: [Person] {
        get {
            let datas = self.objectForKey("PersonData") as? [NSDictionary] ?? []
            return datas.map { Person($0) }
        }
        set(personDatas) {
            let saveDatas = personDatas.map { NSDictionary($0) }
            self.setObject(saveDatas,forKey: "PersonData")
        }
    }
}

★ クラスを拡張して保存できるようにする

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let defaults = NSUserDefaults.standardUserDefaults()
        
        // 取得
        let data = defaults.objectForKey("SaveKey")
        print(data.dynamicType) // Optional<AnyObject>
        
        var myClass: MyClass
        if let data = data {
            // UnArchive
            myClass = NSKeyedUnarchiver.unarchiveObjectWithData(data as! NSData) as! MyClass
            
        } else {
            //保存データがない場合は新規にオブジェクト作成
            myClass = MyClass()
            myClass.intValue = 1
            myClass.stringValue = "a"
        }
        
        print(myClass.intValue)
        print(myClass.stringValue)
        
        
        // 保存
        let nsdata = NSKeyedArchiver.archivedDataWithRootObject(myClass)
        defaults.setObject(nsdata, forKey:"SaveKey")
        defaults.synchronize()
    }
}


// NSUserDefaultに保存できるクラス
class MyClass: NSObject, NSCoding {
    
    var intValue: Int = 0
    var stringValue: String = ""
    
    override init() {
        super.init()
    }
    
    // Encode
    func encodeWithCoder(aCoder: NSCoder) {
        aCoder.encodeObject(intValue, forKey: "IntValueKey")
        aCoder.encodeObject(stringValue, forKey: "StringValueKey")
    }
    
    // Decode
    required init(coder: NSCoder) {
        self.intValue    = coder.decodeObjectForKey("IntValueKey") as! Int
        self.stringValue = coder.decodeObjectForKey("StringValueKey") as! String
    }
}

★ UIImageの配列をNSUserDefaultsに保存する

import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var imageV1: UIImageView!
    @IBOutlet weak var imageV2: UIImageView!
    @IBOutlet weak var imageV3: UIImageView!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let defaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()
        
        let photos = [UIImage(named: "image1")!, UIImage(named: "image2")!, UIImage(named: "image3")!]
        
        // [UIImage] → [NSData]
        let dataImages: [NSData] = photos.map { (image) -> NSData in
            UIImagePNGRepresentation(image)!
        }
        
        defaults.setObject(dataImages, forKey: "photos")
        defaults.synchronize()
        
        let images = defaults.objectForKey("photos") as! [NSData]
        
        imageV1.image = UIImage(data: images[0])
        imageV2.image = UIImage(data: images[1])
        imageV3.image = UIImage(data: images[2])
    }
}

★ enum(関連型)をUserDefaultsに保存する
参考プロジェクト: ReviewAlert

enum ReviewStatus {
    // Reviewの状態
    case none(count: Int)
    case after(count: Int)
    case never
    
    // Review Statusを保存
    func saveReviewStatus() {
        var value: [String: AnyObject]!
        switch self {
        case .none(let count) : value = ["none" : count]
        case .after(let count): value = ["after": count]
        case .never           : value = ["never": ""]
        }
        
        NSUserDefaults.standardUserDefaults().setObject(value, forKey: "ReviewStatusKey")
        NSUserDefaults.standardUserDefaults().synchronize()
    }
    
    // Review Statusを呼び出し
    static func readReviewStatus() -> ReviewStatus {
        if let status = NSUserDefaults.standardUserDefaults().objectForKey("ReviewStatusKey") as? [String: AnyObject] {
            let key = status.keys.first!
            let value = status[key]
            switch key {
            case "none" : return .none(count: value as! Int)
            case "after": return .after(count: value as! Int)
            default     : return .never
            }
            
        } else {
            // 初期値
            return .none(count: 0)
        }
    }
}

★ NSUserDefaults の内容を全削除する

let appDomain = NSBundle.mainBundle().bundleIdentifier!
NSUserDefaults.standardUserDefaults().removePersistentDomainForName(appDomain)

★ Class配列を保存する(Swift4.1)

import UIKit

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let item1 = Item(time: "01.55", date: "2018/05/05", compareTime: 01.55)
        let item2 = Item(time: "01.23", date: "2018/05/04", compareTime: 1.23)
        let item3 = Item(time: "00.23", date: "2018/05/06", compareTime: 0.23)
        let saveItems = [item1, item2, item3]
        
        // 保存
        let userDefaults = UserDefaults.standard
        let archivedObject = NSKeyedArchiver.archivedData(withRootObject: saveItems)
        userDefaults.set(archivedObject, forKey: "ItemSaveKey")
        userDefaults.synchronize()
        
        // 取得
        var resultItems: [Item] = []
        if let itemData = userDefaults.object(forKey: "ItemSaveKey") as? Data,
           let getItems = NSKeyedUnarchiver.unarchiveObject(with: itemData) as? [Item] {
            
            // ソート
            resultItems = getItems.sorted{ $0.time < $1.time }
        }
        
        // resultItems => [Item(time: "00.23", date: "2018/05/06", compareTime: 0.23),
        //                 Item(time: "01.23", date: "2018/05/04", compareTime: 1.23),
        //                 Item(time: "01.55", date: "2018/05/05", compareTime: 01.55)]
    }
}


class Item: NSObject, NSCoding {
    
    enum ItemKey: String {
        case itemTimeKey
        case itemDateKey
        case itemCompareTimeKey
    }
    
    /// タイム表示用
    var time: String
    /// 日付
    var date: String
    /// タイム比較用
    var compareTime: Double
    
    init(time: String, date: String, compareTime: Double) {
        self.time = time
        self.date = date
        self.compareTime = compareTime
    }
    
    required init?(coder aDecoder: NSCoder) {
        guard let time = aDecoder.decodeObject(forKey: Item.ItemKey.itemTimeKey.rawValue) as? String,
            let date = aDecoder.decodeObject(forKey: Item.ItemKey.itemDateKey.rawValue) as? String else {
                return nil
        }
        self.time = time
        self.date = date
        self.compareTime = aDecoder.decodeDouble(forKey: Item.ItemKey.itemCompareTimeKey.rawValue)
    }
    
    func encode(with aCoder: NSCoder) {
        aCoder.encode(time, forKey: Item.ItemKey.itemTimeKey.rawValue)
        aCoder.encode(date, forKey: Item.ItemKey.itemDateKey.rawValue)
        aCoder.encode(compareTime, forKey: Item.ItemKey.itemCompareTimeKey.rawValue)
    }
}
  • Swift4
import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var result: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 保存
        let controller = UserDefalutsController(sourceName: "Name", value: 1.0)
        UserDefaults.standard.userDefalutsController = controller
    
        // 読み込み
        if let controller = UserDefaults.standard.userDefalutsController {
            controller.setAddNum(1.0)
            print(controller.getNum())
            //=> 2.0
        }
    }
}



class UserDefalutsController: NSObject, NSCoding {
    let sourceName: String
    var value: Double
    
    init(sourceName: String, value: Double) {
        self.sourceName = sourceName
        self.value = value
    }
    
    required init?(coder aDecoder: NSCoder) {
        self.sourceName = aDecoder.decodeObject(forKey: "sourceName") as? String ?? ""
        self.value = aDecoder.decodeDouble(forKey: "value")
    }
    
    func encode(with aCoder: NSCoder) {
        aCoder.encode(sourceName, forKey: "sourceName")
        aCoder.encode(value, forKey: "value")
    }
    
    func setAddNum(_ numner:Double){
        value += numner
    }
    func getNum() -> Double{
        return value
    }
}

extension UserDefaults {
    
    var userDefalutsController: UserDefalutsController? {
        get{
            guard let data = self.object(forKey: "volume") as? NSData,
                let controller = NSKeyedUnarchiver.unarchiveObject(with: data as Data) as? UserDefalutsController else { return nil }
            return controller
        }
        set(controller) {
            guard let controller = controller else { return }
            let archive = NSKeyedArchiver.archivedData(withRootObject: controller)
            self.set(archive, forKey: "volume")
        }
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment