Swift UserDefaults optional Int - ios

I want to set or retrieve value from UserDefaults that is an optional Int, meaning it is either nil or set to a value. I am confused in the setter however. In the code below, I set it to either nil or an integer. I am not sure if this is correct. Is there a better or cleaner way (assuming if at all what I am doing is correct)?
public var selectedOption:myOption? {
get {
if let opt = UserDefaults.standard.object(forKey: "myKey") as? Int {
if let val = myOption(rawValue: opt) {
return val
}
}
return nil
}
set {
if let newVal = newValue {
UserDefaults.standard.set(newVal.rawValue, forKey:"myKey")
} else {
UserDefaults.standard.set(nil, forKey:"myKey")
}
}
}

You can reduce the setter
set {
UserDefaults.standard.set(newValue?.rawValue, forKey:"myKey")
}
Due to optional chaining nil is written out if newValue is nil.

public var selectedOption:myOption? {
get {
if let opt = UserDefaults.standard.object(forKey: "myKey") as? Int {
return myOption(rawValue: opt)
}
return nil
}
set {
UserDefaults.standard.set(newValue?.rawValue, forKey: "myKey")
}
}
Or
Confirm to codable protocol and store the enum instance in UserDefaults
enum myOption: Int, Codable {
case a
case b
}
public var selectedOption1:myOption? {
get {
if let storedObject: Data = UserDefaults.standard.object(forKey: "myKey") as? Data {
return try? PropertyListDecoder().decode(myOption.self, from: storedObject)
}
return nil
}
set {
UserDefaults.standard.set(try? PropertyListEncoder().encode(newValue), forKey: "myKey")
}
}

Related

Swift Cannot assign through subscript: 'peerDeviceSettings' is a get-only property"

I have this declaration in my class:
var peerDevice: [String: Any]?
private var peerDeviceSettings:[String:Any]? {
get {
if let settings = peerDevice?["settings"] as? [String:Any] {
return settings
}
return nil
}
}
Now the problem is if I wish to change peerDeviceSettings, such as this:
fileprivate func set(_ value: Any?, forKey key: Key) {
if peerDeviceSettings != nil {
peerDeviceSettings?[key.rawValue] = value
}
}
I get an error
Cannot assign through subscript: 'peerDeviceSettings' is a get-only property
The point is how do I rearchitect this thing without creating a new mutable variable peerDeviceSettings?
Just add set (setter) to the property and handle it there.
private var peerDeviceSettings:[String:Any]? {
get {
if let settings = peerDevice?["settings"] as? [String:Any] {
return settings
}
return nil
}
set {
}
}
your code seems to be fine, since you're creating links to the nested settings dictionary (sounds reasonably)
just add setter:
private var peerDeviceSettings: [String: Any]? {
get {
if let settings = peerDevice?["settings"] as? [String:Any] {
return settings
}
return nil
}
set {
if peerDevice == nil {
peerDevice = [:] // if it's desired behavior
}
peerDevice?["settings"] = newValue
}
}
I'd also suggest getting rid of optionality in peerDevice, just initialize it with empty value (if it feets your logic)
You need a setter
private var peerDeviceSettings:[String:Any]? {
get {
return peerDevice?["settings"] as? [String:Any]
}
set {
peerDevice?["settings"] = newValue
}
}
You see this error because you declared only getter for your property, you have also add setter
private var peerDeviceSettings:[String:Any]? {
get {
if let settings = peerDevice?["settings"] as? [String:Any] {
return settings
}
return nil
}
set {
peerDevice?["settings"] = newValue
}
}
UPDATE: to show how it must be in this certain case

JsonEncoder always return empty array

Im trying to save struct array into UserDefaults and I cant figure out why JsonEncoder return empty data. I have setup model that conforms Codable protocol
struct MenuItem : Codable{
let name : String?
let icon : String?
init(name : String?, icon : String?){
self.name = name
self.icon = icon
}
}
and also created Defaults manager for saving it into user defaults.
class DefaultsManager {
static let shared = DefaultsManager()
init(){}
var items: [MenuItem]{
get{
if let json = UserDefaults.standard.data(forKey: "key"){
return decodeFromJson(jsonData: json)
} else {
return []
}
}
set{
let json = codeToJson(data: items)
UserDefaults.standard.set(json, forKey: "key")
}
}
fileprivate func codeToJson<T:Codable>(data: Array<T>) -> Data?{
do {
return try JSONEncoder().encode(data)
} catch {
print(error)
return nil
}
}
fileprivate func decodeFromJson<T:Codable>(jsonData: Data) -> [T]{
do {
return try JSONDecoder().decode(Array<T>.self, from: jsonData)
} catch {
print(error)
return []
}
}
}
but whatever I do JsonEncoder returns empty data.. I tried to google but without success.
That's a very common mistake.
In a setter of a computed property the new value is represented by the implicit newValue variable, it's not the property itself.
set {
let json = codeToJson(data: newValue)
UserDefaults.standard.set(json, forKey: "key")
}
Change Array<T>.self to [MenuItem].self
return try JSONDecoder().decode([MenuItem].self, from: jsonData)

String initializer for optional primitive types: Int? Double? Float? etc

Is it possible to have an extension for all of the above types without specifying each type individually?
For example, here is such extension for Double:
extension String {
init?(_ value: Double?) {
if let nonOpt = value {
self.init(nonOpt)
} else {
return nil
}
}
}
let nonOpt: Double = 1
let opt: Double? = 1
let string = String(opt)
print(string)
I'd like to allow string initialization with optional type if it is possible to initialize string with the original type.
Do you mean something like this
extension String {
init?<T : CustomStringConvertible>(_ value : T?) {
guard let value = value else { return nil }
self.init(describing: value)
}
}
or
extension String {
init?<T : LosslessStringConvertible>(_ value : T?) {
guard let value = value else { return nil }
self.init(value)
}
}
Rather than declaring an custom initializer, just use map to map the value to a String as such:
let optDouble: Double? = nil
let optionalDoubleString = opt.map { String($0) }
let optInt: Int? = nil
let optionalIntString = opt.map { String($0) }

NSUserDefaults not saving in setter of class variable

When I try to save to NSUserDefaults by adding a setter to my class variable (in this case the id and authToken variables, the values don't seem to be saved. When I run with a breakpoint on, the getter of id and authToken always return nil even after setting them with a value.
class CurrentUser {
static let defaultInstance = CurrentUser()
func updateUser(id id: String, authToken: String) {
self.id = id
self.authToken = authToken
}
var authToken: String? {
get {
if let authToken = NSUserDefaults.standardUserDefaults().objectForKey("userAuthToken") {
return (authToken as! String)
} else {
return nil
}
}
set {
NSUserDefaults.standardUserDefaults().setObject(authToken, forKey: "userAuthToken")
NSUserDefaults.standardUserDefaults().synchronize()
}
}
var id: String? {
get {
if let id = NSUserDefaults.standardUserDefaults().objectForKey("userId") {
return (id as! String)
} else {
return nil
}
}
set {
NSUserDefaults.standardUserDefaults().setObject(id, forKey: "userId")
NSUserDefaults.standardUserDefaults().synchronize()
}
}
}
However, when I pull the lines out to the updateUser function (one level higher), all works as expected.
class CurrentUser {
static let defaultInstance = CurrentUser()
func updateUser(id id: String, authToken: String) {
NSUserDefaults.standardUserDefaults().setObject(id, forKey: "userId")
NSUserDefaults.standardUserDefaults().synchronize()
NSUserDefaults.standardUserDefaults().setObject(authToken, forKey: "userAuthToken")
NSUserDefaults.standardUserDefaults().synchronize()
}
var authToken: String? {
get {
if let authToken = NSUserDefaults.standardUserDefaults().objectForKey("userAuthToken") {
return (authToken as! String)
} else {
return nil
}
}
}
var id: String? {
get {
if let id = NSUserDefaults.standardUserDefaults().objectForKey("userId") {
return (id as! String)
} else {
return nil
}
}
}
}
Why would this be? What am I missing? Does the { set } run on a different thread / mode where NSUserDefaults isn't accessible?
You must use newValue inside set method, value is still nil, or use didSet and then you can use variable.
As Alex said, you must use newValue in set method. Moreover, you can refer to this link for more detail:
Store [String] in NSUserDefaults (Swift)

Swift 2.0 Cannot invoke 'map' with argumet list of type ([AnyObject],(_) -> _)

I've been trying to fix all my projects after the swift 2.0 update. After some work I narrowed it down to this single error:
Cannot invoke 'map' with argumet list of type ([AnyObject],(_) -> _)
This is the whole code:
extension JSON {
//Optional [JSON]
public var array: [JSON]? {
get {
if self.type == .Array {
return map(self.object as! [AnyObject]){ JSON($0) }
} else {
return nil
}
}
}
//Non-optional [JSON]
public var arrayValue: [JSON] {
get {
return self.array ?? []
}
}
//Optional [AnyObject]
public var arrayObject: [AnyObject]? {
get {
switch self.type {
case .Array:
return self.object as? [AnyObject]
default:
return nil
}
}
set {
if newValue != nil {
self.object = NSMutableArray(array: newValue!, copyItems: true)
} else {
self.object = NSNull()
}
}
}
}
The error appears here:
return map(self.object as! [AnyObject]){ JSON($0) }
Any ideas? My swift knowledge doesnt go this far...
map is now a member function on the CollectionType protocol.
Try return (self.object as! [AnyObject]).map { JSON($0) }

Resources