Revision control

1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
* License, v. 2.0. If a copy of the MPL was not distributed with this
3
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5
import Foundation
6
import Shared
7
8
open class NSUserDefaultsPrefs: Prefs {
9
10
fileprivate let prefixWithDot: String
11
fileprivate let userDefaults: UserDefaults
12
13
open func getBranchPrefix() -> String {
14
return self.prefixWithDot
15
}
16
17
init(prefix: String, userDefaults: UserDefaults) {
18
self.prefixWithDot = prefix + (prefix.hasSuffix(".") ? "" : ".")
19
self.userDefaults = userDefaults
20
}
21
22
init(prefix: String) {
23
self.prefixWithDot = prefix + (prefix.hasSuffix(".") ? "" : ".")
24
self.userDefaults = UserDefaults(suiteName: AppInfo.sharedContainerIdentifier)!
25
}
26
27
open func branch(_ branch: String) -> Prefs {
28
let prefix = self.prefixWithDot + branch + "."
29
return NSUserDefaultsPrefs(prefix: prefix, userDefaults: self.userDefaults)
30
}
31
32
// Preferences are qualified by the profile's local name.
33
// Connecting a profile to a Firefox Account, or changing to another, won't alter this.
34
fileprivate func qualifyKey(_ key: String) -> String {
35
return self.prefixWithDot + key
36
}
37
38
open func setInt(_ value: Int32, forKey defaultName: String) {
39
// Why aren't you using userDefaults.setInteger?
40
// Because userDefaults.getInteger returns a non-optional; it's impossible
41
// to tell whether there's a value set, and you thus can't distinguish
42
// between "not present" and zero.
43
// Yeah, NSUserDefaults is meant to be used for storing "defaults", not data.
44
setObject(NSNumber(value: value), forKey: defaultName)
45
}
46
47
open func setTimestamp(_ value: Timestamp, forKey defaultName: String) {
48
setLong(value, forKey: defaultName)
49
}
50
51
open func setLong(_ value: UInt64, forKey defaultName: String) {
52
setObject(NSNumber(value: value), forKey: defaultName)
53
}
54
55
open func setLong(_ value: Int64, forKey defaultName: String) {
56
setObject(NSNumber(value: value), forKey: defaultName)
57
}
58
59
open func setString(_ value: String, forKey defaultName: String) {
60
setObject(value as AnyObject?, forKey: defaultName)
61
}
62
63
open func setObject(_ value: Any?, forKey defaultName: String) {
64
userDefaults.set(value, forKey: qualifyKey(defaultName))
65
}
66
67
open func stringForKey(_ defaultName: String) -> String? {
68
// stringForKey converts numbers to strings, which is almost always a bug.
69
return userDefaults.object(forKey: qualifyKey(defaultName)) as? String
70
}
71
72
open func setBool(_ value: Bool, forKey defaultName: String) {
73
setObject(NSNumber(value: value as Bool), forKey: defaultName)
74
}
75
76
open func boolForKey(_ defaultName: String) -> Bool? {
77
// boolForKey just returns false if the key doesn't exist. We need to
78
// distinguish between false and non-existent keys, so use objectForKey
79
// and cast the result instead.
80
let number = userDefaults.object(forKey: qualifyKey(defaultName)) as? NSNumber
81
return number?.boolValue
82
}
83
84
fileprivate func nsNumberForKey(_ defaultName: String) -> NSNumber? {
85
return userDefaults.object(forKey: qualifyKey(defaultName)) as? NSNumber
86
}
87
88
open func unsignedLongForKey(_ defaultName: String) -> UInt64? {
89
return nsNumberForKey(defaultName)?.uint64Value
90
}
91
92
open func timestampForKey(_ defaultName: String) -> Timestamp? {
93
return unsignedLongForKey(defaultName)
94
}
95
96
open func longForKey(_ defaultName: String) -> Int64? {
97
return nsNumberForKey(defaultName)?.int64Value
98
}
99
100
open func objectForKey<T: Any>(_ defaultName: String) -> T? {
101
return userDefaults.object(forKey: qualifyKey(defaultName)) as? T
102
}
103
104
open func intForKey(_ defaultName: String) -> Int32? {
105
return nsNumberForKey(defaultName)?.int32Value
106
}
107
108
open func stringArrayForKey(_ defaultName: String) -> [String]? {
109
let objects = userDefaults.stringArray(forKey: qualifyKey(defaultName))
110
if let strings = objects {
111
return strings
112
}
113
return nil
114
}
115
116
open func arrayForKey(_ defaultName: String) -> [Any]? {
117
return userDefaults.array(forKey: qualifyKey(defaultName)) as [Any]?
118
}
119
120
open func dictionaryForKey(_ defaultName: String) -> [String: Any]? {
121
return userDefaults.dictionary(forKey: qualifyKey(defaultName)) as [String: Any]?
122
}
123
124
open func removeObjectForKey(_ defaultName: String) {
125
userDefaults.removeObject(forKey: qualifyKey(defaultName))
126
}
127
128
open func clearAll() {
129
// TODO: userDefaults.removePersistentDomainForName() has no effect for app group suites.
130
// iOS Bug? Iterate and remove each manually for now.
131
for key in userDefaults.dictionaryRepresentation().keys {
132
if key.hasPrefix(prefixWithDot) {
133
userDefaults.removeObject(forKey: key)
134
}
135
}
136
}
137
}