Skip to content

Instantly share code, notes, and snippets.

@shaps80
Last active September 25, 2020 08:29
Show Gist options
  • Save shaps80/eb0016303dcfceb3152285d4ea0c6691 to your computer and use it in GitHub Desktop.
Save shaps80/eb0016303dcfceb3152285d4ea0c6691 to your computer and use it in GitHub Desktop.
Adds a Swift extension to make UserDefaults more consistent to work with.
//
// UserDefaults.swift
//
// Created by Shaps Benkau on 24/05/2018.
// Copyright © 2018 152percent Ltd. All rights reserved.
//
import Foundation
#if os(iOS)
import UIKit
public typealias SystemColor = UIColor
#else
import Cocoa
public typealias SystemColor = NSColor
#endif
public extension UserDefaults {
func set<T>(_ value: T?, forKey key: Key) {
set(value, forKey: key.rawValue)
}
func value<T>(forKey key: Key) -> T? {
return value(forKey: key.rawValue) as? T
}
func register(defaults: [Key: Any]) {
let mapped = Dictionary(uniqueKeysWithValues: defaults.map { (key, value) -> (String, Any) in
if let color = value as? SystemColor {
return (key.rawValue, NSKeyedArchiver.archivedData(withRootObject: color))
} else if let url = value as? URL {
return (key.rawValue, url.absoluteString)
} else {
return (key.rawValue, value)
}
})
register(defaults: mapped)
}
}
public extension UserDefaults {
subscript<T>(key: Key) -> T? {
get { return value(forKey: key) }
set { set(newValue, forKey: key) }
}
subscript(key: Key) -> SystemColor? {
get { return color(forKey: key) }
set { set(newValue, forKey: key) }
}
subscript(key: Key) -> URL? {
get { return url(forKey: key) }
set { set(newValue, forKey: key) }
}
subscript(key: Key) -> Bool {
get { return bool(forKey: key) }
set { set(newValue, forKey: key) }
}
subscript(key: Key) -> Int {
get { return integer(forKey: key) }
set { set(newValue, forKey: key) }
}
subscript(key: Key) -> Double {
get { return double(forKey: key) }
set { set(newValue, forKey: key) }
}
subscript(key: Key) -> Float {
get { return float(forKey: key) }
set { set(newValue, forKey: key) }
}
subscript(key: Key) -> CGFloat {
get { return CGFloat(float(forKey: key) as Float) }
set { set(newValue, forKey: key) }
}
}
public extension UserDefaults {
func bool(forKey key: Key) -> Bool {
return bool(forKey: key.rawValue)
}
func integer(forKey key: Key) -> Int {
return integer(forKey: key.rawValue)
}
func float(forKey key: Key) -> Float {
return float(forKey: key.rawValue)
}
func float(forKey key: Key) -> CGFloat {
return CGFloat(float(forKey: key) as Float)
}
func double(forKey key: Key) -> Double {
return double(forKey: key.rawValue)
}
func url(forKey key: Key) -> URL? {
return string(forKey: key).flatMap { URL(string: $0) }
}
func date(forKey key: Key) -> Date? {
return object(forKey: key.rawValue) as? Date
}
func string(forKey key: Key) -> String? {
return string(forKey: key.rawValue)
}
func set(_ url: URL?, forKey key: Key) {
set(url?.absoluteString, forKey: key.rawValue)
}
func set(_ color: SystemColor?, forKey key: Key) {
guard let color = color else {
set(nil, forKey: key.rawValue)
return
}
let data = NSKeyedArchiver.archivedData(withRootObject: color)
set(data, forKey: key.rawValue)
}
func color(forKey key: Key) -> SystemColor? {
return data(forKey: key.rawValue)
.flatMap { NSKeyedUnarchiver.unarchiveObject(with: $0) as? SystemColor }
}
}
public extension UserDefaults {
struct Key: Hashable, RawRepresentable, ExpressibleByStringLiteral {
public var rawValue: String
public init(rawValue: String) {
self.rawValue = rawValue
}
public init(stringLiteral value: String) {
self.rawValue = value
}
}
}
let defaults = UserDefaults.standard
defaults.register(defaults: [.defaultFontName: "Menlo"])
var fontName = defaults[.defaultFontName]
print(fontName) // "Menlo"
defaults[.defaultFontName] = "Arial"
fontName = defaults[.defaultFontName]
print(fontName) // "Arial"
@shaps80
Copy link
Author

shaps80 commented May 24, 2018

Added subscripting as a suggestion from @NSExceptional on Twitter. Thanks!

@KhayalSuleymani
Copy link

Could you tell me please how and where can I declare .defaultFontName or another property? Please with code.

@shaps80
Copy link
Author

shaps80 commented Jun 3, 2019

@KhayalSuleymani – you can add it wherever you want but generally you'd add an extension to UserDefaults.Key. This is exactly how many other Apple frameworks provide this type of API, e.g. Notification.Name.

public extension UserDefaults.Key {
    // note: `defaults.fontName` does not have to match the property name `defaultFontName`
    static let defaultFontName: UserDefaults.Key = "defaults.fontName"
}

@daniloc
Copy link

daniloc commented Sep 24, 2020

This is really nice, thank you. Would you be willing to explicitly license it as MIT or similar?

@shaps80
Copy link
Author

shaps80 commented Sep 25, 2020

I don't waste time with licensing on Gists, if its public and has no explicit license, its fully available to all.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment