Skip to content

Instantly share code, notes, and snippets.

@yaslab
Created November 10, 2017 11:17
Show Gist options
  • Save yaslab/cadc51754997621909fddad5635408d1 to your computer and use it in GitHub Desktop.
Save yaslab/cadc51754997621909fddad5635408d1 to your computer and use it in GitHub Desktop.
import Foundation
// -----------------------------------------------------------------------------
// Protocol
protocol UserDefaultsVariableProtocol {
associatedtype Value
var key: String { get }
var defaults: UserDefaults { get }
var value: Value { get }
var valueIfPresent: Value? { get }
func set(_ value: Value)
}
extension UserDefaultsVariableProtocol {
var value: Value {
fatalError()
}
var valueIfPresent: Value? {
fatalError()
}
func set(_ value: Value) {
fatalError()
}
}
extension UserDefaultsVariableProtocol where Value == String {
var value: Value {
return defaults.string(forKey: key)!
}
var valueIfPresent: Value? {
return defaults.string(forKey: key)
}
func set(_ value: Value) {
defaults.set(value, forKey: key)
}
}
extension UserDefaultsVariableProtocol where Value == Int {
var value: Value {
return defaults.integer(forKey: key)
}
var valueIfPresent: Value? {
return defaults.integer(forKey: key)
}
func set(_ value: Value) {
defaults.set(value, forKey: key)
}
}
extension UserDefaultsVariableProtocol where Value: Codable {
var value: Value {
let json = defaults.data(forKey: key)!
let decoder = JSONDecoder()
return try! decoder.decode(Value.self, from: json)
}
var valueIfPresent: Value? {
guard let json = defaults.data(forKey: key) else {
return nil
}
do {
let decoder = JSONDecoder()
return try decoder.decode(Value.self, from: json)
} catch {
return nil
}
}
func set(_ value: Value) {
let encoder = JSONEncoder()
let json = try! encoder.encode(value) // FIXME: force unwrap
defaults.set(json, forKey: key)
}
}
// -----------------------------------------------------------------------------
// Impl
struct UserDefaultsVariable<V>: UserDefaultsVariableProtocol {
typealias Value = V
let key: String
let defaults: UserDefaults
init(_ key: String, defaults: UserDefaults = .standard) {
self.key = key
self.defaults = defaults
}
}
// -----------------------------------------------------------------------------
// Operator
infix operator <~
func <~ (lhs: UserDefaultsVariable<String>, rhs: String) {
lhs.set(rhs)
}
func <~ (lhs: UserDefaultsVariable<Int>, rhs: Int) {
lhs.set(rhs)
}
func <~ <T: Codable>(lhs: UserDefaultsVariable<T>, rhs: T) {
lhs.set(rhs)
}
// -----------------------------------------------------------------------------
// Test
struct Friend: Codable, CustomStringConvertible {
let name: String
let age: Int
var description: String {
return "{ name: \(name), age: \(age) }"
}
}
enum Settings {
enum UserInfo {}
enum FriendInfo {}
}
extension Settings.UserInfo {
static let name = UserDefaultsVariable<String>("UserInfo.name")
static let age = UserDefaultsVariable<Int>("UserInfo.age")
}
extension Settings.FriendInfo {
static let friend = UserDefaultsVariable<Friend>("FriendInfo.friend")
}
Settings.UserInfo.name.valueIfPresent
Settings.UserInfo.name.set("Taro")
Settings.UserInfo.name.value
Settings.UserInfo.name <~ "Jiro"
Settings.UserInfo.name.value
Settings.UserInfo.age <~ 10
Settings.UserInfo.age.value
Settings.FriendInfo.friend <~ Friend(name: "Saburo", age: 12)
Settings.FriendInfo.friend.value
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment