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
Related
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")
}
}
I am currently working on test for my application and I have faced a problem when mocking user defaults. Let me first show you my setup :
this is how I mock user Defaults :
class MockUserDefaults: UserDefaults {
typealias FakeData = Dictionary<String, Any?>
var data: FakeData
convenience init() {
self.init(suiteName: "mocking")!
}
override init?(suiteName suitename: String?) {
data = FakeDefaults()
UserDefaults().removePersistentDomain(forName: suitename!)
super.init(suiteName: suitename)
}
override func object(forKey defaultName: String) -> Any? {
if let data = data[defaultName] {
return data
}
return nil
}
override func set(_ value: Any?, forKey defaultName: String) {
if defaultName == "favs"{
data[defaultName] = value
}
}
}
I have a variable in my view controller called : userDefaults, and I set it like this :
var userDefaults : UserDefaults {
if (NSClassFromString("XCTest") != nil) {
return MockUserDefaults()
}
return UserDefaults.standard
}
this variable is actually an extension to a protocol which a made uiviewcontroller conform to it to make sure all my view controllers have this variable.
I also have a variable in myViewcontroller called favoriteMovie which I set like this :
private var favoriteMovie: Favorite? {
if let favoriteString = userDefaults.value(forKey: "favs") as? String {
return favorites.first(where: {$0.name == favoriteString})
}
return nil
}
now here's where the problem is, when I go and try to test this view controller , I need to set userDefault with an object for example :
myviewController.userDefaults.set("avengers", forKey: "favs")
before the test runs, but the problem is that favoriteMovie variable always return nil and I need it to return an object before the test runs . Any help. Thanks in advance.
UPDATE :
this is the protocol :
protocol Mockable: class {
var userDefaults: UserDefaults { get }
}
this is the extension :
extension UIViewController: Mockable {}
extension Mockable {
var userDefaults : UserDefaults {
if (NSClassFromString("XCTest") != nil) {
return MockUserDefaults()
}
return UserDefaults.standard
}
}
Here are two ways to fix it.
1) By doing some DI. In you viewController declare userDefaults as non-computed property as below
var userDefaults : UserDefaults?
In your test case, create MockUserDefaults object, set values and assign it to viewController when you are initiating it as below,
let mockUD = MockUserDefaults()
mockUD.set("avengers", forKey: "favs")
myviewController.userDefaults = mockUD
Now you will get the avengers object.
2) As the question is updated, here is the fix to hold the mockDefaults object,
struct AssociatedMock {
static var key: UInt8 = 0
}
extension Mockable {
private (set) var _mockDefaults: MockUserDefaults? {
get {
guard let value = objc_getAssociatedObject(self, &AssociatedMock.key) as? MockUserDefaults else {
return nil
}
return value
}
set(newValue) {
objc_setAssociatedObject(self, &AssociatedMock.key, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
var userDefaults : UserDefaults {
if (NSClassFromString("XCTest") != nil) {
if self._mockDefaults == nil {
self._mockDefaults = MockUserDefaults()
}
return self._mockDefaults!
}
return UserDefaults.standard
}
}
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)
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) }
I have implemented a Parse subclass called Filler.swiftthat contains five variables that are held in the Parse backend.
Whilst trying to save data to the Parse backend using this subclass I get the error: fatal error: unexpectedly found nil while unwrapping an Optional value.
This is the code that brings about the error (on the self.object.username = username line):
if let username = PFUser.currentUser()?.username {
// set username equal to current user
self.object.username = username
}else{
println("PFUser.currentUser()?.username contained a nil value.")
}
I've figured out that it's something to do with how I'm handling optional variables in my subclass but the Parse documentation isn't clear on exactly how to do this. Here's my code for the subclass:
class Fillup : PFObject, PFSubclassing {
var amount : String? {
get {
return self["amount"] as? String
}
set{
self["amount"] = newValue
}
}
var cost : String? {
get {
return self["cost"] as? String
}
set{
self["cost"] = newValue
}
}
var date : NSDate {
get {
return self["date"] as! NSDate
}
set{
self["date"] = newValue
}
}
var username: String? {
get{
return self["username"] as? String
}
set {
self["username"] = newValue
}
}
var id : Int?{
get {
return self["id"] as? Int
}
}
override class func initialize() {
var onceToken : dispatch_once_t = 0;
dispatch_once(&onceToken) {
self.registerSubclass()
}
}
class func parseClassName() -> String {
return "Fillup"
}
}
Any help would be really appreciated.
Did you try adding a ? like so:
self.object?.username = username