Set new value with computed properties - swift

i have a model object and i want to set new value to the object.
bellow i have mentioned the object variable and initialization
private var _data: NSMutableDictionary
// MARK:- Init
init(data: [String: AnyObject])
{
_data = NSMutableDictionary(dictionary: data)
}
How can i set a new value with setter. bellow i have mentioned the computed variable
var name: String? {
get {
if let nameObject = _data.objectForKey("network")?.objectForKey("name") {
return (nameObject as! String)
} else {
return nil
}
}
set {
}
}
need to complete the setter
Edit
I could found the answer for this question.
I need to change the name of the _data object. So i have to update name without change other variables in side the _data. so i keep a mutable copy of the _data object and changed the new value there. then updated the _data object.

Something like
set {
_data.objectForKey("network")?.setObject(newValue, forKey: "name")
}

How about this:
var name: String? {
get {
return _data["network"]?["name"] as? String
}
set {
if let network = _data["network"] as? Dictionary {
network["name"] = newValue
} else {
let network: [String:AnyObject] = ["name": newValue]
data["network"] = network
}
}
}
The setter also takes care of the situation the network key doesn't exist. If also _data can be nil then an extra if-let can be added to address this situation too.
BTW, I also added a shorter version of the getter.

Not really sure what you are trying to do here but can't you just write this:
set {
_data.objectForKey("network")?.objectForKey("name") = newValue
}
Edit: Wrong function, should have been:
set {
_data.objectForKey("network")?.setObject(newValue, forKey: "name")
}

Related

Is it possible to exclude certain property (Like class type) from being copied during struct copy?

I have the following struct, which contains class.
import Foundation
func generateRichText(body: String?) -> NSMutableAttributedString? {
if body == nil {
return nil
}
// TODO: Some complex logic to decorate body string will be added soon...
let myAttrString = NSMutableAttributedString(string: body!)
return myAttrString
}
struct Note {
var body: String?
// Technique described in https://stackoverflow.com/a/25073176/72437
var bodyAsRichText: NSMutableAttributedString? {
mutating get {
if (cachedBodyAsRichText == nil) {
cachedBodyAsRichText = generateRichText(body: body)
}
return cachedBodyAsRichText
}
}
// TODO: This is a class. I don't want it to be copied over during struct copy.
// If it is copied during struct copy, both struct will be sharing the same
// class instance.
private var cachedBodyAsRichText: NSMutableAttributedString?
}
var note = Note()
note.body = "hello"
print("note.bodyAsRichText = \(Unmanaged.passUnretained(note.bodyAsRichText!).toOpaque())")
var note_copy = note
print("note_copy.bodyAsRichText = \(Unmanaged.passUnretained(note_copy.bodyAsRichText!).toOpaque())")
For the above code, the output will be
note.bodyAsRichText = 0x000055c035cfce70
note_copy.bodyAsRichText = 0x000055c035cfce70
What my desired output is, different struct instance, should be having their very own class instance (cachedBodyAsRichText)
Hence, is there a way, to exclude cachedBodyAsRichText from being copied over, during struct copy?
Your solution is incomplete. Here is a complete and correct solution.
struct Note {
var body: String = "" {
didSet {
cachedBodyAsRichText = nil
}
}
var bodyAsRichText: NSAttributedString {
mutating get {
if (cachedBodyAsRichText == nil) {
cachedBodyAsRichText = generateRichText(body: body)
}
return cachedBodyAsRichText!.copy() as! NSAttributedString
}
}
private var cachedBodyAsRichText: NSAttributedString? = nil
}
You need to clear out the cache every time the body is modified. Once you do that, it won't matter if the object is shared among structs.

How do I verify incoming data for properties?

In my model, I have a singleton class which will contain some global properties and methods. I think I've set up the class correctly but I need a way to verify incoming data for the properties. I'm trying to use get and set but these seem to need to return void. I can't use init because it's a singleton.
Am I missing something?
final class Globals {
private init(){}
static let sharedInstance = Globals()
//MARK: Properties
private var _peopleCount: Int!
var peopleCount: Int! {
get {
return _peopleCount
}
set(newPeopleCount) {
guard newPeopleCount > 0 else {
return nil // can't return nil here
}
}
}
}
You shouldn't define your variables as implicitly unwrapped optionals unless you have a very good reason to do so.
Your immediate error is that you cannot return a value in a setter, you need to assign the value to the variable there. If you want to mark an invalid value by peopleCount being nil, define peopleCount as Int? and assign to it nil when the check fails.
final class Globals {
private init(){}
static let sharedInstance = Globals()
//MARK: Properties
private var _peopleCount: Int?
var peopleCount: Int? {
get {
return _peopleCount
}
set(newPeopleCount) {
if let newValue = newPeopleCount, newValue > 0 {
_peopleCount = newValue
}
}
}
}
For most use cases, there is no need for the private backing variable, you can just use didSet to check the value before assigning it. Thanks for #LeoDabus for the idea in comments.
var peopleCount: Int? {
didSet {
if let newValue = peopleCount, newValue > 0 {
peopleCount = newValue
} else {
peopleCount = oldValue
}
}
}

How to present a dictionary in a set method swift?

I'm trying to do a set and get method to this Data manager class, from a dictionary and I don't know how to insert the values of the dictionary' in the set and get method (i have changed it from an array to Dic) thanks
class DataManager {
private static var sharedInstance: DataManager?
private var recordsArray: [[String:String]] = []
private let defaults = UserDefaults.standard
let userRecord: String = "userRecord";
private init(){}
public static func getInstance()-> DataManager{
if DataManager.sharedInstance == nil {
DataManager.sharedInstance = DataManager()
}
return DataManager.sharedInstance!
}
//here is my problem - set and get methods
//I don't know how to move the parameters
public func setRecordsArray([_:String,path:String]) {
self.recordsArray.append(?);
defaults.set(self.recordsArray, forKey: self.userRecord)
}
// again the same problem
public func getRecordsArray() -> [String] {
let a = self.defaults.array(forKey: self.userRecord);
return a as! [String];
}
}
The key to answering your question is to know the type of variable you want to set and get.
In this case, the recordsArray variable is an array of dictionaries of string values and keys: [[String:String]]
So, one way to pass this parameter is to create a variable of the same type as it should be set:
public func setRecordsArray(array:[[String:String]]) {
self.recordsArray = array
defaults.set(self.recordsArray, forKey: self.userRecord)
}
It simply updates the value of the self.recordsArray variable and sets the value in user defaults.
The get method works similar, however it returns a variable with the same type that should be returned.
A ? was added in the return because if there is no saved user defalts, the method returns nil:
public func getRecordsArray() -> [[String:String]]?{
if let array = defaults.object(forKey: self.userRecord) as? [[String:String]]{
self.recordsArray = array
return array
}
return nil
}
Also, you can make a set method for insert elements inside the array.
in this case, parameter type must be like [String:String], the element type of this array:
public func setRecord(record:[String:String]) {
if let array = defaults.object(forKey: self.userRecord) as? [[String:String]]{
self.recordsArray = array
self.recordsArray.append(record)
defaults.set(self.recordsArray, forKey: self.userRecord)
} else{
self.recordsArray.append(record)
defaults.set(self.recordsArray, forKey: self.userRecord)
}
}

Swift Dictionary - Update value in setter function

My code looks a little like:
Protocol
protocol MyProtocol: class {
var featureConfigs: [String: Any]? { get set }
}
Base Class
class MyBaseClass: MyProtocol {
var featureConfigs: [String : Any]? {
get {
return nil
}
set {
self.featureConfigs = newValue
}
}
}
Subclass
class MySubclass: MyBaseClass {
override var featureConfigs: [String : Any]? {
get {
return ["feature-specific-configuration":""]
}
set {
self.featureConfigs = newValue
}
}
}
I want to be able to update the values stored in featuresConfig. I understand that doing self.featureConfigs = newValue causes an infinite loop but I am unsure of how to update the featuresConfig dictionary correctly. I've read about and tried some things with Subscript but I couldn't get that to work either. Any suggestions?
In order to be able to set a persistent value for your featureConfigs, it needs to be a stored property, rather than a calculated one (this could also be a private backing variable that you create for this task, as #AMomchilov says). You can then use willSet or didSet to observe changes.
In your subclass, you can override the stored property declaration with a calculated one in order to allow you to update a given value-key pair in the dictionary when it's accessed, using super in order to refer to the superclass's stored property.
For example:
protocol MyProtocol: class {
var featureConfigs: [String: Any]? { get set }
}
class MyBaseClass: MyProtocol {
var featureConfigs: [String : Any]? { // stored property with setter observers
willSet {
print("about to be set")
}
didSet {
print("was set")
}
}
}
class MySubclass: MyBaseClass {
// calculated property that forwards to super's stored property
override var featureConfigs: [String : Any]? {
get {
// injects the updated value of a given key when accessing
guard var config = super.featureConfigs else {return nil}
config["feature-specific-configuration"] = "baz"
return config
}
set {
super.featureConfigs = newValue
}
}
}
let s = MySubclass()
s.featureConfigs = ["foo":"bar"]
print(s.featureConfigs) // Optional(["feature-specific-configuration": "baz", "foo": "bar"])
What you wrote is a computed property. Unlike stored properties, it's not backed by a persistent instance variable.
You can manually create a private instance variable to back the storage of this psuedo-computed-property. You can then give this variable a default value specific to the subclass it belongs to.

Mutating property by its name

With the help of Reflection API I'm getting the properties list for my types.
func inspectedProperties(ignored: [String] = []) -> [Property] {
var properties = [String]()
for child in self.children() {
guard let label = child.label else {
continue
}
properties += [label]
}
return properties.filter { !ignored.contains($0) }
}
This function returns me the names for all properties.
Now I want to mutate a certain property just by knowing its name.
class Fruit {
private dynamic var name = "Apple"
}
If I call Fruit().inspectedProperties() I'll get the following array ["name"].
But is it possible to mutate the variable named "name"?
OK, I found a very simple solution but it is not flexible. Actually, you can use KVO to mutate your data types. For this purpose your models should be subclasses of NSObject to enable KVO features and variables marked as dynamic.
P.S.
typealias Property = String
class Fruit: NSObject {
private dynamic var name = "Apple"
}
Then there is the mutating function.
func mutateProperty<T>(property: Property) -> T -> () {
return { value in
let filtered = self.children().filter { label, value in
if let label = label where label == property {
return true
}
return false
}
guard let child = filtered.first else {
return
}
if let object = self as? NSObject where child.value is T {
object.setValue(value as? AnyObject, forKey: property)
}
}
}
Then try out:
let fruit = Fruit()
fruit.mutateProperty("name")("Google")
print(fruit.name) // "Google"
It works, but if you want to work with value types rather than with reference ones it won't work. There might be some low level solution but I'm not familiar with one. If anyone knows how to, please leave your answer here! :)