Skip to content

Commit 6d29b24

Browse files
committed
feat: enable domain-specific activity
1 parent 0553f81 commit 6d29b24

File tree

4 files changed

+134
-46
lines changed

4 files changed

+134
-46
lines changed

README.md

+11-5
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ This native Node.js module provides an interface to the user’s defaults databa
1111

1212
## API
1313

14-
### `defaults.getAllDefaults()`
14+
### `defaults.getAllDefaults([domain])`
15+
16+
* `domain` String (optional) - The domain identifier for an `NSUserDefaults` suite.
1517

1618
Returns `Record<string, any>` - An object containing all currently set defaults and their values for the current user.
1719

@@ -55,11 +57,12 @@ console.log(allDefaults)
5557
*/
5658
```
5759

58-
### `defaults.getUserDefault(type, key)`
60+
### `defaults.getUserDefault(type, key[, domain])`
5961

6062
* `key` String - The `NSUserDefault` to fetch, e.g `AppleInterfaceStyle`.
6163
* `type` String - Can be one of `string`, `boolean`, `integer`, `float`, `double`,
6264
`url`, `array` or `dictionary`.
65+
* `domain` String (optional) - The domain identifier for an `NSUserDefaults` suite.
6366

6467
Returns `any` - The value of `key` in `NSUserDefaults` of type `type`.
6568

@@ -72,11 +75,12 @@ const interfaceStyle = getUserDefault('AppleInterfaceStyle', 'string')
7275
console.log(interfaceStyle) // 'Dark'
7376
```
7477

75-
### `defaults.setUserDefault(type, key, value)`
78+
### `defaults.setUserDefault(type, key, value[, domain])`
7679

7780
* `type` String - Can be `string`, `boolean`, `integer`, `float`, `double`, `url`, `array` or `dictionary`.
7881
* `key` String - The `NSUserDefault` to update, e.g `AppleInterfaceStyle`.
7982
* `value` any - The new value to set for `key`; must match type of `type`.
83+
* `domain` String (optional) - The domain identifier for an `NSUserDefaults` suite.
8084

8185
Sets the value of `key` in `NSUserDefaults`.
8286

@@ -87,9 +91,10 @@ const { setUserDefault } = require('node-mac-userdefaults')
8791
setUserDefault('boolean', 'ApplePressAndHoldEnabled', true)
8892
```
8993

90-
### `defaults.removeUserDefault(key)`
94+
### `defaults.removeUserDefault(key[, domain])`
9195

9296
* `key` String - The `NSUserDefault` to remove, e.g `AppleInterfaceStyle`.
97+
* `domain` String (optional) - The domain identifier for an `NSUserDefaults` suite.
9398

9499
Removes the `key` in `NSUserDefaults`.
95100

@@ -102,9 +107,10 @@ const { removeUserDefault } = require('node-mac-userdefaults')
102107
removeUserDefault('ApplePressAndHoldEnabled')
103108
```
104109

105-
### `defaults.isKeyManaged(key)`
110+
### `defaults.isKeyManaged(key[, domain])`
106111

107112
* `key` String - The `NSUserDefault` to check, e.g `AppleInterfaceStyle`.
113+
* `domain` String (optional) - The domain identifier for an `NSUserDefaults` suite.
108114

109115
Returns a Boolean value indicating whether the specified key is managed by an administrator.
110116

index.js

+44-18
Original file line numberDiff line numberDiff line change
@@ -11,49 +11,75 @@ const VALID_TYPES = [
1111
'dictionary',
1212
]
1313

14-
function getUserDefault(type, key) {
15-
if (arguments.length !== 2) {
16-
throw new Error('Arguments must be (type, key)')
17-
} else if (!VALID_TYPES.includes(type)) {
14+
function getAllDefaults(domain) {
15+
if (domain && typeof domain !== 'string') {
16+
throw new TypeError('domain must be a valid string')
17+
}
18+
19+
return defaults.getAllDefaults.call(this, domain)
20+
}
21+
22+
function getUserDefault(type, key, domain) {
23+
if (!VALID_TYPES.includes(type)) {
1824
throw new TypeError(`${type} must be one of ${VALID_TYPES.join(', ')}`)
25+
} else if (domain && typeof domain !== 'string') {
26+
throw new TypeError('domain must be a valid string')
1927
}
2028

21-
return defaults.getUserDefault.call(this, type, key)
29+
return defaults.getUserDefault.call(this, type, key, domain)
2230
}
2331

24-
function setUserDefault(type, key, value) {
32+
function setUserDefault(type, key, value, domain) {
2533
if (!VALID_TYPES.includes(type)) {
2634
throw new TypeError(`${type} must be one of ${VALID_TYPES.join(', ')}`)
35+
} else if (domain && typeof domain !== 'string') {
36+
throw new TypeError('domain must be a valid string')
2737
}
2838

2939
const isFloatOrDouble = (n) => !isNaN(parseFloat(n))
3040
const isObject = (o) => Object.prototype.toString.call(o) === '[object Object]'
3141

3242
if (type === 'string' && typeof value !== 'string') {
33-
throw new Error(`${value} must be a valid string`)
43+
throw new TypeError('value must be a valid string')
3444
} else if (type === 'double' && !isFloatOrDouble(value)) {
35-
throw new Error(`${value} must be a valid double`)
45+
throw new TypeError('value must be a valid double')
3646
} else if (type === 'float' && !isFloatOrDouble(value)) {
37-
throw new Error(`${value} must be a valid float`)
47+
throw new TypeError('value must be a valid float')
3848
} else if (type === 'boolean' && typeof value !== 'boolean') {
39-
throw new Error(`${value} must be a valid boolean`)
49+
throw new TypeError('value must be a valid boolean')
4050
} else if (type === 'integer' && !Number.isInteger(value)) {
41-
throw new Error(`${value} must be a valid integer`)
51+
throw new TypeError('value must be a valid integer')
4252
} else if (type === 'array' && !Array.isArray(value)) {
43-
throw new Error(`${value} must be a valid array`)
53+
throw new TypeError('value must be a valid array')
4454
} else if (type == 'dictionary' && !isObject(value)) {
45-
throw new Error(`${value} must be a valid dictionary`)
55+
throw new TypeError('value must be a valid dictionary')
4656
} else if (type === 'url' && typeof value !== 'string') {
47-
throw new Error(`${value} must be a valid url`)
57+
throw new TypeError('value must be a valid url')
58+
}
59+
60+
return defaults.setUserDefault.call(this, type, key, value, domain)
61+
}
62+
63+
function isKeyManaged(key, domain) {
64+
if (domain && typeof domain !== 'string') {
65+
throw new TypeError('domain must be a valid string')
66+
}
67+
68+
return defaults.isKeyManaged.call(this, key, domain)
69+
}
70+
71+
function removeUserDefault(key, domain) {
72+
if (domain && typeof domain !== 'string') {
73+
throw new TypeError('domain must be a valid string')
4874
}
4975

50-
return defaults.setUserDefault.call(this, type, key, value)
76+
return defaults.removeUserDefault.call(this, key, domain)
5177
}
5278

5379
module.exports = {
54-
getAllDefaults: defaults.getAllDefaults,
80+
getAllDefaults,
5581
getUserDefault,
56-
isKeyManaged: defaults.isKeyManaged,
82+
isKeyManaged,
5783
setUserDefault,
58-
removeUserDefault: defaults.removeUserDefault,
84+
removeUserDefault
5985
}

src/defaults.mm

+47-18
Original file line numberDiff line numberDiff line change
@@ -139,21 +139,33 @@
139139

140140
// Returns all NSUserDefaults for the current user.
141141
Napi::Object GetAllDefaults(const Napi::CallbackInfo &info) {
142-
Napi::Env env = info.Env();
142+
NSUserDefaults *defaults = [&]() {
143+
if (!info[0].IsEmpty()) {
144+
const std::string domain = (std::string)info[0].ToString();
145+
return [[NSUserDefaults alloc] initWithSuiteName:ToNSString(domain)];
146+
}
147+
return [NSUserDefaults standardUserDefaults];
148+
}();
143149

144-
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
145150
NSDictionary *all_defaults = [defaults dictionaryRepresentation];
146-
return NSDictionaryToNapiObject(env, all_defaults);
151+
return NSDictionaryToNapiObject(info.Env(), all_defaults);
147152
}
148153

149154
// Returns the value of 'key' in NSUserDefaults for a specified type.
150155
Napi::Value GetUserDefault(const Napi::CallbackInfo &info) {
151156
Napi::Env env = info.Env();
152157

153-
const std::string type = info[0].As<Napi::String>().Utf8Value();
154-
const std::string key = info[1].As<Napi::String>().Utf8Value();
158+
const std::string type = (std::string)info[0].ToString();
159+
const std::string key = (std::string)info[1].ToString();
160+
161+
NSUserDefaults *defaults = [&]() {
162+
if (!info[2].IsEmpty()) {
163+
const std::string domain = (std::string)info[2].ToString();
164+
return [[NSUserDefaults alloc] initWithSuiteName:ToNSString(domain)];
165+
}
166+
return [NSUserDefaults standardUserDefaults];
167+
}();
155168

156-
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
157169
NSString *default_key = [NSString stringWithUTF8String:key.c_str()];
158170

159171
if (type == "string") {
@@ -187,12 +199,18 @@
187199

188200
// Sets the value of the NSUserDefault for 'key' in NSUserDefaults.
189201
void SetUserDefault(const Napi::CallbackInfo &info) {
190-
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
191-
const std::string type = info[0].As<Napi::String>().Utf8Value();
192-
const std::string key = info[1].As<Napi::String>().Utf8Value();
193-
202+
const std::string type = (std::string)info[0].ToString();
203+
const std::string key = (std::string)info[1].ToString();
194204
NSString *default_key = ToNSString(key);
195205

206+
NSUserDefaults *defaults = [&]() {
207+
if (!info[3].IsEmpty()) {
208+
const std::string domain = (std::string)info[3].ToString();
209+
return [[NSUserDefaults alloc] initWithSuiteName:ToNSString(domain)];
210+
}
211+
return [NSUserDefaults standardUserDefaults];
212+
}();
213+
196214
if (type == "string") {
197215
const std::string value = (std::string)info[2].ToString();
198216
[defaults setObject:ToNSString(value) forKey:default_key];
@@ -219,32 +237,43 @@ void SetUserDefault(const Napi::CallbackInfo &info) {
219237
}
220238
} else if (type == "dictionary") {
221239
Napi::Value value = info[2].As<Napi::Value>();
222-
if (NSDictionary* dict = NapiObjectToNSDictionary(value)) {
240+
if (NSDictionary *dict = NapiObjectToNSDictionary(value)) {
223241
[defaults setObject:dict forKey:default_key];
224242
}
225243
}
226244
}
227245

228246
// Removes the default for 'key' in NSUserDefaults.
229247
void RemoveUserDefault(const Napi::CallbackInfo &info) {
230-
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
231-
232-
const std::string key = info[1].As<Napi::String>().Utf8Value();
248+
const std::string key = (std::string)info[0].ToString();
233249
NSString *default_key = ToNSString(key);
234250

251+
NSUserDefaults *defaults = [&]() {
252+
if (!info[1].IsEmpty()) {
253+
const std::string domain = (std::string)info[1].ToString();
254+
return [[NSUserDefaults alloc] initWithSuiteName:ToNSString(domain)];
255+
}
256+
return [NSUserDefaults standardUserDefaults];
257+
}();
258+
235259
[defaults removeObjectForKey:default_key];
236260
}
237261

238262
// Returns whether or not an NSUserDefault is managed by an admin.
239263
Napi::Boolean IsKeyManaged(const Napi::CallbackInfo &info) {
240-
Napi::Env env = info.Env();
264+
const std::string key = (std::string)info[0].ToString();
241265

242-
const std::string key = info[0].As<Napi::String>().Utf8Value();
266+
NSUserDefaults *defaults = [&]() {
267+
if (!info[1].IsEmpty()) {
268+
const std::string domain = (std::string)info[1].ToString();
269+
return [[NSUserDefaults alloc] initWithSuiteName:ToNSString(domain)];
270+
}
271+
return [NSUserDefaults standardUserDefaults];
272+
}();
243273

244-
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
245274
bool managed = [defaults objectIsForcedForKey:ToNSString(key)];
246275

247-
return Napi::Boolean::New(env, managed);
276+
return Napi::Boolean::New(info.Env(), managed);
248277
}
249278

250279
// Initializes all functions exposed to JS.

test/module.spec.js

+32-5
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ const {
33
getAllDefaults,
44
getUserDefault,
55
isKeyManaged,
6-
setUserDefault
6+
setUserDefault,
7+
removeUserDefault
78
} = require('../index')
89

910
const VALID_TYPES = [
@@ -19,6 +20,12 @@ const VALID_TYPES = [
1920

2021
describe('node-mac-userdefaults', () => {
2122
describe('getAllDefaults()', () => {
23+
it('should throw on a bad domain', () => {
24+
expect(() => {
25+
getAllDefaults(1)
26+
}).to.throw(/domain must be a valid string/)
27+
})
28+
2229
it('should return an object containing all current user defaults', () => {
2330
const allDefaults = getAllDefaults()
2431
expect(allDefaults).to.be.an.an('object')
@@ -27,10 +34,10 @@ describe('node-mac-userdefaults', () => {
2734
})
2835

2936
describe('getUserDefault()', () => {
30-
it('should throw on extra arguments', () => {
37+
it('should throw on a bad domain', () => {
3138
expect(() => {
32-
getUserDefault('bad-type', 'hello-world', 'blah')
33-
}).to.throw('Arguments must be (type, key)')
39+
getUserDefault('string', 'hello-world', 1)
40+
}).to.throw(/domain must be a valid string/)
3441
})
3542

3643
it('should throw on invalid types', () => {
@@ -60,11 +67,17 @@ describe('node-mac-userdefaults', () => {
6067
}).to.throw(`bad-type must be one of ${VALID_TYPES.join(', ')}`)
6168
})
6269

70+
it('should throw on a bad domain', () => {
71+
expect(() => {
72+
setUserDefault('string', 'hello', 'world', 1)
73+
}).to.throw(/domain must be a valid string/)
74+
})
75+
6376
it('should throw on mismatched values for types', () => {
6477
for (const type of VALID_TYPES) {
6578
expect(() => {
6679
setUserDefault(type, 'some-key', undefined)
67-
}).to.throw(`undefined must be a valid ${type}`)
80+
}).to.throw(`value must be a valid ${type}`)
6881
}
6982
})
7083

@@ -121,9 +134,23 @@ describe('node-mac-userdefaults', () => {
121134
})
122135

123136
describe('isKeyManaged()', () => {
137+
it('should throw on a bad domain', () => {
138+
expect(() => {
139+
isKeyManaged('AppleInterfaceStyle', 1)
140+
}).to.throw(/domain must be a valid string/)
141+
})
142+
124143
it('returns a boolean', () => {
125144
const managed = isKeyManaged('AppleInterfaceStyle')
126145
expect(managed).to.be.a('boolean')
127146
})
128147
})
148+
149+
describe('removeUserDefault()', () => {
150+
it('should throw on a bad domain', () => {
151+
expect(() => {
152+
removeUserDefault('i-dont-exist', 1)
153+
}).to.throw(/domain must be a valid string/)
154+
})
155+
})
129156
})

0 commit comments

Comments
 (0)