Further work on wrapping NSUserDefaults
This commit is contained in:
parent
ccaf61f56f
commit
e4ddfb975a
30
examples/defaults.rs
Normal file
30
examples/defaults.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
//! This tests the `defaults` module to ensure things behave as they should.
|
||||
|
||||
use cacao::macos::app::{App, AppDelegate};
|
||||
use cacao::defaults::UserDefaults;
|
||||
|
||||
#[derive(Default)]
|
||||
struct DefaultsTest;
|
||||
|
||||
impl AppDelegate for DefaultsTest {
|
||||
fn did_finish_launching(&self) {
|
||||
let mut defaults = UserDefaults::standard();
|
||||
|
||||
match defaults.get_string("LOL") {
|
||||
Some(s) => {
|
||||
println!("Retrieved {}", s);
|
||||
},
|
||||
|
||||
None => {
|
||||
defaults.set_string("LOL", "laugh");
|
||||
println!("Run this again to get a laugh");
|
||||
}
|
||||
}
|
||||
|
||||
App::terminate();
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
App::new("com.cacao.defaults-test", DefaultsTest::default()).run();
|
||||
}
|
123
src/defaults.rs
Normal file
123
src/defaults.rs
Normal file
|
@ -0,0 +1,123 @@
|
|||
//! Wraps `NSUserDefaults`, providing an interface to store and query small amounts of data.
|
||||
//!
|
||||
//! It may seem a bit verbose at points, but it aims to implement everything on the Objective-C
|
||||
//! side as closely as possible.
|
||||
|
||||
use std::unreachable;
|
||||
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc::runtime::Object;
|
||||
use objc_id::Id;
|
||||
|
||||
use crate::foundation::{id, nil, YES, NO, BOOL, NSString};
|
||||
|
||||
/// Wraps and provides methods for interacting with `NSUserDefaults`, which can be used for storing
|
||||
/// pieces of information (preferences, or _defaults_) to persist across application restores.
|
||||
///
|
||||
/// This should not be used for sensitive data - use the Keychain for that.
|
||||
#[derive(Debug)]
|
||||
pub struct UserDefaults(pub Id<Object>);
|
||||
|
||||
impl Default for UserDefaults {
|
||||
/// Equivalent to calling `UserDefaults::standard()`.
|
||||
fn default() -> Self {
|
||||
UserDefaults::standard()
|
||||
}
|
||||
}
|
||||
|
||||
impl UserDefaults {
|
||||
/// Returns the `standardUserDefaults`, which is... exactly what it sounds like.
|
||||
pub fn standard() -> Self {
|
||||
UserDefaults(unsafe {
|
||||
Id::from_ptr(msg_send![class!(NSUserDefaults), standardUserDefaults])
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns a new user defaults to work with. You probably don't want this, and either want
|
||||
/// `suite()` or `standard()`.
|
||||
pub fn new() -> Self {
|
||||
UserDefaults(unsafe {
|
||||
let alloc: id = msg_send![class!(NSUserDefaults), alloc];
|
||||
Id::from_ptr(msg_send![alloc, init])
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns a user defaults instance for the given suite name. You typically use this to share
|
||||
/// preferences across apps and extensions.
|
||||
pub fn suite(named: &str) -> Self {
|
||||
let name = NSString::new(named);
|
||||
|
||||
UserDefaults(unsafe {
|
||||
let alloc: id = msg_send![class!(NSUserDefaults), alloc];
|
||||
Id::from_ptr(msg_send![alloc, initWithSuiteName:name.into_inner()])
|
||||
})
|
||||
}
|
||||
|
||||
/// Remove the default associated with the key. If the key doesn't exist, this is a noop.
|
||||
pub fn remove(&mut self, key: &str) {
|
||||
let key = NSString::new(key);
|
||||
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.0, removeObjectForKey:key.into_inner()];
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a bool for the given key. If the key doesn't exist, it returns `false`.
|
||||
///
|
||||
/// Note that behind the scenes, this will coerce certain "truthy" and "falsy" values - this is
|
||||
/// done on the system side, and is not something that can be changed.
|
||||
///
|
||||
/// e.g:
|
||||
/// `"true"`, `"YES"`, `"1"`, `1`, `1.0` will become `true`
|
||||
/// `"false"`, `"NO"`, `"0"`, `0`, `0.0` will become `false`
|
||||
pub fn get_bool(&self, key: &str) -> bool {
|
||||
let key = NSString::new(key);
|
||||
|
||||
let result: BOOL = unsafe {
|
||||
msg_send![&*self.0, boolForKey:key.into_inner()]
|
||||
};
|
||||
|
||||
match result {
|
||||
YES => true,
|
||||
NO => false,
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the bool for the given key to the specified value.
|
||||
pub fn set_bool(&mut self, key: &str, value: bool) {
|
||||
let key = NSString::new(key);
|
||||
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.0, setBool:match value {
|
||||
true => YES,
|
||||
false => NO
|
||||
} forKey:key];
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the given String if it exists, mapping Objective-C's `nil` to `None`.
|
||||
pub fn get_string(&self, key: &str) -> Option<String> {
|
||||
let key = NSString::new(key);
|
||||
|
||||
let result: id = unsafe {
|
||||
msg_send![&*self.0, stringForKey:key.into_inner()]
|
||||
};
|
||||
|
||||
if result == nil {
|
||||
None
|
||||
} else {
|
||||
Some(NSString::wrap(result).to_str().to_string())
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the string for the given key to the specified value.
|
||||
pub fn set_string(&mut self, key: &str, value: &str) {
|
||||
let key = NSString::new(key);
|
||||
let value = NSString::new(value);
|
||||
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.0, setObject:value.into_inner() forKey:key.into_inner()];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -85,6 +85,7 @@ pub mod color;
|
|||
pub mod dragdrop;
|
||||
pub mod error;
|
||||
pub mod events;
|
||||
pub mod defaults;
|
||||
pub mod filesystem;
|
||||
pub mod foundation;
|
||||
pub mod geometry;
|
||||
|
@ -98,7 +99,6 @@ pub mod user_notifications;
|
|||
|
||||
pub mod user_activity;
|
||||
pub(crate) mod utils;
|
||||
pub mod user_defaults;
|
||||
|
||||
pub mod view;
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ use objc_id::Id;
|
|||
use objc::runtime::Object;
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
|
||||
use crate::foundation::{id, YES, NO, NSUInteger, AutoReleasePool};
|
||||
use crate::foundation::{id, nil, YES, NO, NSUInteger, AutoReleasePool};
|
||||
use crate::macos::menu::Menu;
|
||||
use crate::notification_center::Dispatcher;
|
||||
|
||||
|
@ -236,4 +236,13 @@ impl App {
|
|||
let _: () = msg_send![app, setMainMenu:main_menu];
|
||||
});
|
||||
}
|
||||
|
||||
/// Terminates the application, firing the requisite cleanup delegate methods in the process.
|
||||
///
|
||||
/// This is typically called when the user chooses to quit via the App menu.
|
||||
pub fn terminate() {
|
||||
shared_application(|app| unsafe {
|
||||
let _: () = msg_send![app, terminate:nil];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
//! Wraps `NSUserDefaults`, providing an interface to store and query small amounts of data.
|
||||
//!
|
||||
//! It mirrors much of the API of the standard Rust `HashMap`, but uses `NSUserDefaults` as a
|
||||
//! backing store.
|
||||
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc::runtime::Object;
|
||||
use objc_id::Id;
|
||||
|
||||
use crate::foundation::{id, NSString};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UserDefaults(pub Id<Object>);
|
||||
|
||||
impl Default for UserDefaults {
|
||||
fn default() -> Self {
|
||||
UserDefaults::standard()
|
||||
}
|
||||
}
|
||||
|
||||
impl UserDefaults {
|
||||
pub fn standard() -> Self {
|
||||
UserDefaults(unsafe {
|
||||
Id::from_ptr(msg_send![class!(NSUserDefaults), standardUserDefaults])
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
UserDefaults(unsafe {
|
||||
let alloc: id = msg_send![class!(NSUserDefaults), alloc];
|
||||
Id::from_ptr(msg_send![alloc, init])
|
||||
})
|
||||
}
|
||||
|
||||
pub fn suite(named: &str) -> Self {
|
||||
let name = NSString::new(named);
|
||||
|
||||
UserDefaults(unsafe {
|
||||
let alloc: id = msg_send![class!(NSUserDefaults), alloc];
|
||||
Id::from_ptr(msg_send![alloc, initWithSuiteName:name.into_inner()])
|
||||
})
|
||||
}
|
||||
|
||||
/// Remove the default associated with the key. If the key doesn't exist, this is a noop.
|
||||
pub fn remove(&self, key: &str) {
|
||||
let key = NSString::new(key);
|
||||
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.0, removeObjectForKey:key.into_inner()];
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue