Updating variable value from dictionary and best practice - swift

As I progress into my Swift education, the time as come for me to ask for help about best practice and code optimization.
My app has become more and more complex every day and the current situation is as follows: I started using dictionaries and arguments to use a single function that can process a lot of different variables depending on the situation, which seems to be better practice than using 5 different functions that will do the same thing only with different variables.
I now have two files as follows:
Main1.swift:
class Main1 {
static var value1 : Int = 1
func updateValue(_ value: String) {
let dict : [String : Int] = ["value1": Main1.value1]
let dict1 = dict[value]
guard var value = dict1 else { return }
value = value + 1 // <- trying to update `static var value1`'s value from 1 to 2 here
print(value)
}
}
Main2.swift:
class Main2 {
func updateValue(_ value: String) {
let dict : [String : Int] = ["value1": Main1.value1] // <- thinking `value1` would now be 2
let dict1 = dict[value]
guard var value = dict1 else { return }
value = value + 1 // <- trying to get 3 here
print(value)
}
}
These classes are simplified versions of my code but the logic is the same: I am trying to use variables loaded from dictionaries and update their values to be used in another file and function:
Main1().updateValue("value1") //2
Main2().updateValue("value1") //2 <--- I need 3 here!
-> What exactly am I trying to achieve here?
To update the reference (static var value1 : Int = 1) value while accessing it through the convenience of a dictionary (or different method but you get the point about convenience).
In fact I am trying to do Main1.value1 = Main1.value1 + 1 while accessing Main1.value1 through a dictionary, which is impossible because I am not accessing the reference here.
I know this can't work, I have 3 different copies of value here but I don't how to update the variable value without using another global variable... I need your help to find a better logic.
I am open to any suggestion or thinking. I am not asking for code solution (which would be great anyway) but I'd love to have my education re-centered a little bit, I am starting to lose myself learning all by myself and the frustration comes from that I don't know what to be looking for anymore.

EDIT BASED ON COMMENTS
As per the comments below, here's a potential solution:
class Main1 {
static var dict: [String: Int] = ["value1": 1]
func updateValue(_ key: String) {
guard var value = dict[key] else { return }
value = value + 1
print(value)
dict[key] = value
}
}
ORIGINAL ANSWER
In Swift, [String : Int], String and Int are value types, as opposed to their Objective-C counterparts NSDictionary, NSString and NSNumber, which are reference types.
This means that when you do guard var value = dict1 else { return }, value is now a copy of what the dictionary contained, not a reference to that piece of data inside the dictionary.
So when you do value = value + 1 you're setting the new variables value, but not the contents of the dictionary.
Following your logic, you need to put value back into the dictionary, like this:
func updateValue(_ value: String) {
var dict : [String : Int] = ["value1": Main1.value1] // <- Change this to a var
let dict1 = dict[value]
guard var intValue = dict1 else { return }
intValue = intValue + 1 // <- trying to update `static var value1`'s value from 1 to 2 here
print(intValue)
dict[value] = intValue // <- ADD THIS
}

Related

Swift: How to keep updating a Dictionary inside another Dictionary correctly?

This is a little hard to explain, but I'll try my best. I am trying to update a Dictionary inside another Dictionary properly. The following code almost does what I need.
var dictionary = Dictionary<String, [Int : Int]>()
func handleStatsValue(tag: Int ) {
let currentValue: Int = dictionary["Score"]?[tag] ?? 0
dictionary["Score"] = [
tag : currentValue + 1
]
}
However, it seems the dictionary is overridden when the tag value changes (e.g. 1 to 2). I need Dictionary to have multiple dictionaries inside of it. Any tips or suggestions are deeply appreciated.
Edit: I'm trying to have multiple dictionaries nested inside a dictionary. It seems whenever the tag value is changed, the dictionary is overridden.
One way to write this would be:
func handleStatsValue(tag: Int) {
dictionary["Score", default: [:]][tag, default: 0] += 1
}
or, written without [_:default:]
func handleStatsValue(tag: Int) {
var scoreDictionary = dictionary["Score"] ?? [:]
scoreDictionary[tag] = (scoreDictionary[tag] ?? 0) + 1
dictionary["Score"] = scoreDictionary
}
However, it's not a good idea to use nested dictionaries to keep your data. Use a custom struct instead and try to avoid tags too:
struct DataModel {
var score: [Int: Int] = [:]
}
I think you need something like this to either increase the value for an existing tag or add a new tag if it doesn't exist
func handleStatsValue(tag: Int ) {
if var innerDict = dictionary["Score"] {
if let value = innerDict[tag] {
innerDict[tag] = value + 1
} else {
innerDict[tag] = 1
}
dictionary["Score"] = innerDict
}
}
Although the code looks a bit strange with the hardcoded key "Score", maybe it would be better to have multiple simple dictionaries instead, like
var score: [Int, Int]()
or if you prefer
var score = Dictionary<Int, Int>()

Cannot assign through subscript: 'dict' is a 'let' constant

I have a very simple problem with Swift.
I created this function:
var dictAges : [String: Int] = ["John":40, "Michael":20, "Bob": -16]
func correctAges(dict:[String:Int]) {
for (name, age) in dict {
guard age >= 0 else {
dict[name] = 0
continue
}
}
}
correctAges(dict:dictAges)
But I don't understand the error:
"cannot assign through subscript: 'dict' is a 'let' constant, dict[name] = 0"
How can I solve it?
Input arguments to functions are immutable inside the function body and a Dictionary is a value type, so the dict you see inside the function is actually a copy of the original dictAges. When you call a function with a value type as its input argument, that input argument is passed by value, not passed by reference, hence inside the function you cannot access the original variable.
Either declare the dict input argument as inout or if you'd prefer the more functional approach, return a mutated version of the dictionary from the function.
Functional approach:
var dictAges : [String: Int] = ["John":40, "Michael":20, "Bob": -16]
func correctAges(dict:[String:Int])->[String:Int] {
var mutatedDict = dict
for (name, age) in mutatedDict {
guard age >= 0 else {
mutatedDict[name] = 0
continue
}
}
return mutatedDict
}
let newDict = correctAges(dict:dictAges) //["Michael": 20, "Bob": 0, "John": 40]
Inout version:
func correctAges(dict: inout [String:Int]){
for (name,age) in dict {
guard age >= 0 else {
dict[name] = 0
continue
}
}
}
correctAges(dict: &dictAges) //["Michael": 20, "Bob": 0, "John": 40]
Your expectation that you can mutate a dictionary passed into a function is wrong, for the simple reason that a dictionary in Swift is a value type. And the types inside the dictionary, String and Int, are values too. This means that, in effect, the parameter dict is a copy of the original dictAges. Nothing that you do to dict can have any effect on dictAges.
This is a sign that you should rethink your entire architecture. If it was wrong to enter a negative number as an age, you should have caught that up front, as it was being entered. In effect, your entire use of a dictionary of Ints as the model here is probably incorrect. You should have used a dictionary of Person, where Person is a struct with an age and a setter that prevents an age from being negative in the first place.
If you must have a function that runs through an arbitrary dictionary and fixes the values to be nonnegative, make that a feature of dictionaries themselves:
var dictAges : [String: Int] = ["John":40, "Michael":20, "Bob": -16]
extension Dictionary where Value : SignedInteger {
mutating func fixAges() {
for (k,v) in self {
if v < 0 {
self[k] = 0
}
}
}
}
dictAges.fixAges()
In general, it's not good to mutate a sequence as you're looping through it, which is why you only get an immutable copy of the dictionary in your code.
Swift 4
I think a nicer approach could be to use map, and to take a more functional approach:
var dictAges : [String: Int] = ["John":40, "Michael":20, "Bob": -16]
func correctAges(dict:[String:Int]) -> [String:Int]
{
let corrected = dict.map { (name, age) in age > 0 ? (name, age) : (name, 0) }
return Dictionary(uniqueKeysWithValues: corrected)
}
dictAges = correctAges(dict: dictAges)
print(dictAges) // ["Michael": 20, "Bob": 0, "John": 40]
This way you can reuse this method for any [String:Int] dictionary.

Check if dictionary contains value in Swift

Just simple task. I've got a dictionary var types = [Int : String]() which inits like an empty and after some user actions it fills with data. According to emptiness or some specific data in this dictionary I enable/disable a button in UI.
Check for emptiness is easy, but how to check if dictionary contains certain value?
Compiler suggested me a placeholder with predicate:
types.contains(predicate: ((Int, String)) throws -> Bool>)
Since you only want to check for existance of a given value, you can apply the contains method for the values properties of your dictionary (given native Swift dictionary), e.g.
var types: [Int : String] = [1: "foo", 2: "bar"]
print(types.values.contains("foo")) // true
As mentioned in #njuri: answer, making use of the values property of the dictionary can seemingly yield an overhead (I have not verified this myself) w.r.t. just checking the contains predicate directly against the value entry in the key-value tuple of each Dictionary element. Since Swift is fast, this shouldn't be an issue, however, unless you're working with a huge dictionary. Anyway, if you'd like to avoid using the values property, you could have a look at the alternatives given in the forementioned answer, or, use another alternative (Dictionary extension) as follows:
extension Dictionary where Value: Equatable {
func containsValue(value : Value) -> Bool {
return self.contains { $0.1 == value }
}
}
types.containsValue("foo") // true
types.containsValue("baz") // false
I wrote a function which is using contains method on dictionary.
Your specific case:
let dic : [Int : String] = [1 : "a", 2 : "b"]
func dictionary(dict : [Int : String], containsValue value : String)->Bool{
let contains = dict.contains { (_,v) -> Bool in
return v == value
}
return contains
}
let c = dictionary(dic, containsValue: "c") // false
let a = dictionary(dic, containsValue: "a") // true
Generic:
extension Dictionary{
func containsValue<T : Equatable>(value : T)->Bool{
let contains = self.contains { (k, v) -> Bool in
if let v = v as? T where v == value{
return true
}
return false
}
return contains
}
}
I've tested this function against dictionary.values.contains() and it is roughly two times faster.
If you want to check if already contains a value this would be the way:
if !yourDictionary.values.contains("Zero") {
yourDictionary[newItemKey] = newItemValue; //addNewItem
}
else {
print("this value already exists");
}
And this one if you want to check if the key exists:
You get the item to add to your dictionary.
Check if the item's key already exists
If it doesn't, append the item or enable the button.
//1
let newItemKey = 0
let newItemValue = "Zero"
//2
let keyExists = yourDictionary[newItemKey] != nil
//3
if !keyExists {
yourDictionary[newItemKey] = newItemValue; //addNewItem
}
else {
print("This key already exists");
}
The dictionary getter returns an optional value.
let dictionary = ["ben": "says hi"]
let containsAlpha = dictionary["alpha"] != nil
let containsBen = dictionary["ben"] != nil

Native Swift Dictionary by Reference

What I'm trying to do:
a class that has several (say 10) instance variables of dictionary type (mutable var).
a method that (depending on arguments, etc.) picks a dictionary an updates it.
In ObjC, this is fairly easily accomplished using NSMutableDictionary. In Swift, this is more tricky, since the dictionary gets copied on into the local variable.
I think the best way to explain what I'm trying to achieve is via a code sample:
class MyClass {
/// There are several dictionaries as instance variables
var dict1: [String : String] = [ : ]
var dict2: [String : String] = [ : ]
// ...
/// This method should change a value in one of the dictionaries,
/// depending on the argument.
func changeDictAtIndex(index: Int) {
var dict: [String : String]
if index == 0 {
dict = dict1
}else{
dict = dict2
}
dict["OK"] = "KO"
// This won't work since Swift copies the dictionary into
// the local variable, which gets destroyed at the end of
// the scope...
}
}
let obj = MyClass()
obj.changeDictAtIndex(0)
obj.dict1 // Still empty.
Question: Is there a native way to do this (native meaning without using NSMutableDictionary)?
P.S.: I'm aware of the inout attribute, but that works AFAIK only with function arguments, which doesn't really solve anything...
EDIT:
I'm currently solving this via closure:
var dictSetter: (key: String, value: String) -> Void
if index == 0 {
dictSetter = { self.dict1[$0] = $1 }
}else{
dictSetter = { self.dict2[$0] = $1 }
}
dictSetter(key: "OK", value: "KO")
As you may already aware, you can use inout to solve the problem
func updateDict(inout dict: [String : String]) {
dict["OK"] = "KO"
}
func changeDictAtIndex(index: Int) {
if index == 0 {
updateDict(&dict1)
}else{
updateDict(&dict2)
}
}
Question: Is there a native way to do this (native meaning without using NSMutableDictionary)?
I have rewritten your class, note the changes:
Different syntax for empty dictionary
ChangeDictAtIndex function now takes in a dictionary you want to replace.
The instance variables are being set to the passed in dict.
I would look at the Apple's The Swift Programming Language (Swift 2.1) section on the basics and collection types.
class MyClass {
// There are several dictionaries as instance variables
var dict1 = [String : String]()
var dict2 = [String : String]()
func changeDictAtIndex(index: Int, dict: [String : String]) {
if index == 0 {
dict1 = dict
} else {
dict2 = dict
}
}
}
Usage:
let obj = MyClass()
obj.changeDictAtIndex(0, dict: ["MyKey" : "MyValue"])
print(obj.dict1)

Get a Swift Variable's Actual Name as String

So I am trying to get the Actual Variable Name as String in Swift, but have not found a way to do so... or maybe I am looking at this problem and solution in a bad angle.
So this is basically what I want to do:
var appId: String? = nil
//This is true, since appId is actually the name of the var appId
if( appId.getVarName = "appId"){
appId = "CommandoFurball"
}
Unfortunately I have not been able to find in apple docs anything that is close to this but this:
varobj.self or reflect(var).summary
however, this gives information of what is inside the variable itself or the type of the variable in this case being String and I want the Actual name of the Variable.
This is officially supported in Swift 3 using #keyPath()
https://github.com/apple/swift-evolution/blob/master/proposals/0062-objc-keypaths.md
Example usage would look like:
NSPredicate(format: "%K == %#", #keyPath(Person.firstName), "Wendy")
In Swift 4 we have something even better: \KeyPath notation
https://github.com/apple/swift-evolution/blob/master/proposals/0161-key-paths.md
NSPredicate(format: "%K == %#", \Person.mother.firstName, "Wendy")
// or
let keyPath = \Person.mother.firstName
NSPredicate(format: "%K == %#", keyPath, "Andrew")
The shorthand is a welcome addition, and being able to reference keypaths from a variable is extremely powerful
As per the updated from this answer, it is supported in Swift 3 via #keyPath
NSPredicate(format: "%K == %#", #keyPath(Person.firstName), "Andrew")
This is my solution
class Test {
var name: String = "Ido"
var lastName: String = "Cohen"
}
let t = Test()
let mirror = Mirror(reflecting: t)
for child in mirror.children {
print(child.label ?? "")
}
print will be
name
lastName
This works:
struct s {
var x:Int = 1
var y:Int = 2
var z:Int = 3
}
var xyz = s()
let m = Mirror(reflecting: xyz)
print(m.description)
print(m.children.count)
for p in m.children {
print(p.label as Any)
}
I've come up with a swift solution, however unfortunately it doesn't work with Ints, Floats, and Doubles I believe.
func propertyNameFor(inout item : AnyObject) -> String{
let listMemAdd = unsafeAddressOf(item)
let propertyName = Mirror(reflecting: self).children.filter { (child: (label: String?, value: Any)) -> Bool in
if let value = child.value as? AnyObject {
return listMemAdd == unsafeAddressOf(value)
}
return false
}.flatMap {
return $0.label!
}.first ?? ""
return propertyName
}
var mutableObject : AnyObject = object
let propertyName = MyClass().propertyNameFor(&mutableObject)
It compares memory addresses for an object's properties and sees if any match.
The reason it doesn't work for Ints, Floats, and Doubles because they're not of type anyobject, although you can pass them as anyobject, when you do so they get converted to NSNumbers. therefore the memory address changes. they talk about it here.
For my app, it didn't hinder me at all because I only needed it for custom classes. So maybe someone will find this useful. If anyone can make this work with the other datatypes then that would be pretty cool.
Completing the accepted answer for extensions:
The property needs to be #objc.
var appId: String? {
....
}
You need to use #keyPath syntax, \ notation is not supported yet for extensions.
#keyPath(YourClass.appId)
The best solution is Here
From given link
import Foundation
extension NSObject {
//
// Retrieves an array of property names found on the current object
// using Objective-C runtime functions for introspection:
// https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html
//
func propertyNames() -> Array<String> {
var results: Array<String> = [];
// retrieve the properties via the class_copyPropertyList function
var count: UInt32 = 0;
var myClass: AnyClass = self.classForCoder;
var properties = class_copyPropertyList(myClass, &count);
// iterate each objc_property_t struct
for var i: UInt32 = 0; i < count; i++ {
var property = properties[Int(i)];
// retrieve the property name by calling property_getName function
var cname = property_getName(property);
// covert the c string into a Swift string
var name = String.fromCString(cname);
results.append(name!);
}
// release objc_property_t structs
free(properties);
return results;
}
}