Swift: Variable of Optional Dictionary inside Class: Shows Brackets in Output - swift

I would like to make an Optional Dictionary Variable inside of a Class so that when I create an instance I don't HAVE to add that argument...
example
class Stuff {
var dictionary: [String: Double]?
init(dictionary: [String:Double]?=["":0]){
}
}
var instance1 = Stuff(dictionary:["length":1.5])
var array: [Stuff] = [instance1, instance2]
let arraySet = (array[indexPath.row])
let X = arraySet.dictionary!
cell.Label.text = "\(X)"
This works but when i assign it to a cell.label.text the output shows Brackets...
"[length:1.5]"
How can i get rid of the brackets???

It's quite complicated to get rid of the brackets, because the key/value pair must be extracted out of the dictionary:
class Stuff {
var dictionary: [String: Double]?
init(dictionary: [String:Double]?=["":0.0]){
self.dictionary = dictionary
}
}
let instance = Stuff(dictionary:["length":1.5])
if let dict = instance.dictionary where dict.count > 0 {
let key = dict.keys.first!
let value = dict[key]!
label.text = "\(key) : \(value)"
}
If you create the dictionary with non-optional literals, consider to declare the variable dictionary as non-optional

Related

Swift update value inside Dictionary<String, Any> in an Array

I have an array of Dictionaries, like so
var myArray: [Dictionary<String, Any>] = [Dictionary<String, Any>]()
Somewhere in my class i add values there, like so:
var myDict = Dictionary<String, Any>()
myDict["Id"] = "someUniqueId"
myDict["v1"] = -42
self.myArray.append(myDict)
Then later i try to update the v1 inside this dict like so:
self.myArray.first(where: { $0["Id"] == "someUniqueId" })?["v1"] = -56
However, the compiler tells me:
Cannot assign through subscript: function call returns immutable value
Any way i can modify the value in array outside of copying the whole dictionary, removing it from array, and re-inserting?
UPDATE This dictionary is not strongly typed because what is inside the dictionary, both types and fieldnames, are controlled by the user in an external system. E.g. we download some metadata and then render controls for this dynamic data.
This is because Swift Dictionary is a value type.
Use a custom class or you can use NSMutableDictionary for this purpose (because NSDictionary is immutable).
var myArray = [NSMutableDictionary]()
var myDict = NSMutableDictionary()
myDict["Id"] = "someUniqueId"
myDict["v1"] = -42
myArray.append(myDict)
myArray.first(where: { $0["Id"] as? String == "someUniqueId" })?["v1"] = -56
print(myArray)
// [{
// Id = someUniqueId;
// v1 = "-56";
// }]
You can make it easier for yourself by wrapping your dictionary in a simple class
class Config {
private var dict: [String: Any]
init(_ dict: [String: Any]) {
self.dict = dict
}
func valueFor(_ key: String) -> Any? {
return dict[key]
}
func setValue(_ value: Any, forKey key: String) {
dict[key] = value
}
}
and use that in your array
var myArray = [Config]()
And use it like this
array.filter { $0.valueFor("Id") as? String == "someUniqueId" }
.first?.setValue(-56, forKey: "v1")
That way you can avoid using classes like NSMutableDictionary and keeping it more pure Swift. Another advantage with a custom class is that you can add functionality to it to simplify your code using it, so for instance if you will be looking up dictionaries by "Id" a lot we can add a computed property for it
var id: String? {
return valueFor("Id") as? String
}
and then the example above would become
array.filter { $0.id == "someUniqueId" }.first?.setValue(-56, forKey: "v1")

How to get some data out of an array in swift?

Here is my problem: I have an plist file, which have a simple strut:
root object is an array, and there are 2 sections in the array, each of them are dictionary including 2 pair key-value data:
and I'm going to create a tableView to show the datas, but I can't get the content out of the array :
here is how i declared my dataArray:
var plistArray = [AnyObject]()
can some one help me?
You need to properly cast at each level:
if let innerArray = plistArray[0] as? [AnyObject] {
if let dataDic = innerArray[indexPath.row] as? [String:String] {
if let imageName = dataDic["Pic"] {
cell?.imageView?.image = UIImage(named: imageName)
}
}
}
But why are you using AnyObject when you know what the plist contains? Use proper types. You know it's an array of arrays of dictionaries with String keys and String values.
var plistArray = [[[String:String]]]()
Then all you need is:
if let imageName = plistArray[0][indexPath.row]["Pic"] {
cell?.imageView?.image = UIImage(named: imageName)
}

How to update key of dictionary in Swift 3

if (userFollowingArray[indexPath.row]["watcher_following"] as! Bool) == false {
let dict : NSMutableDictionary = userFollowingArray[indexPath.row] as! NSMutableDictionary
print(dict)
dict["watcher_following"] = 1
self.userFollowingArray[indexPath.row] = dict as! Dictionary<String, Any>
}
I want to update watcher_following key with true/false but it throws an exception.
[_TtGCs26_SwiftDeferredNSDictionarySSP__
_swift_setObject:forKeyedSubscript:]: unrecognized selector sent to instance
Do not use NSMutable... collection types in Swift
Declare userFollowingArray as Swift Array
var userFollowingArray = [[String:Any]]()
Get the Dictionary as variable
var dict = userFollowingArray[indexPath.row]
Check the value
if dict["watcher_following"] as! Bool == false {
Update it if necessary and assign the dictionary back to the array.
dict["watcher_following"] = true
userFollowingArray[indexPath.row] = dict
}
The last step is necessary due to value semantics
It's much more convenient to use a custom struct or class rather than a dicionary.
Declare var for mutable types, let is only for immutable/constant types. not needed to use objective c NSMutableDictionary type.
if dict["watcher_following"] as! Bool == false {
var dict : Dictionary<String, Any> = userFollowingArray[indexPath.row] as! Dictionary<String, Any>
dict["watcher_following"] = 1
self.userFollowingArray[indexPath.row] = dict as! Dictionary<String, Any>
}

Comparing non optional value leads to error

I am on point where I gotta compare non optional value with nil. But I can't do it because Xcode says:
Comparing non-optional value of type 'Int' to nil always returns false
So I created Struct and then made variable: var products: [Product] = []
How I am able to compare it with nil?:
if products[indexPath.row].snusPortions == nil
{
cell.snusPortionsAmountLabel.text = "N/A"
}else
{
cell.snusPortionsAmountLabel.text = String(products[indexPath.row].snusPortions)
}
I've assigned values to them like this:
let ref = FIRDatabase.database().reference().child("Snuses").queryOrdered(byChild: "Brand").queryEqual(toValue: brandName)
ref.observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.exists(){
let enumerator = snapshot.children
while let thisProduct = enumerator.nextObject() as? FIRDataSnapshot
{
print(thisProduct.value) // So I may see what the data is like and know how to extract it
// Chances are you'd have to create a dictionary
let thisProductDict = thisProduct.value as! [String:AnyObject]
let productName = thisProductDict["Products"] as! String
let snusPortions = thisProductDict["PortionsCan"] as? Int
let productObject = Product(snusProductTitle: productName, snusNicotine: snusNicotine, snusPortions: snusPortions!, snusFlavor: snusFlavor, snusWeight: snusWeight!, snusShippingWeight: snusShippingWeight, snusProductImageURL: productURL)
self.products.append(productObject)
print(self.products)
}
self.tableView.reloadData()
}
})
This is Product struct:
struct Product {
var snusProductTitle: String
init()
{
snusProductTitle = ""
}
init(snusProductTitle: String){
self.snusProductTitle = snusProductTitle
}
}
While testing it says snusPortions is nil but I said to make it "N/A" if it is nil, why?
It sounds like you are confusing yourself between the local variable snusPortions and the Product property snusPortions.
In your Product definition, the property snusPortions is an Int. It can never be nil. Hence, in this code:
if products[indexPath.row].snusPortions == nil
... this Product's snusPortions will never be nil, and we will never set the text to "N/A".
Now let's look at your other code:
let snusPortions = thisProductDict["PortionsCan"] as? Int
This is a completely different snusPortions. It can be nil, namely, if thisProductDict lacks a "PortionsCan" key or if its value is not castable to Int.

How to write inside a dictionary entry in NSUserDefaults without using temporary variables?

I have this code:
var address = Dictionary<String, AnyObject?>();
address["address1"] = "Here";
address["address2"] = "There";
...
let defaults = NSUserDefaults.standardUserDefaults();
var data = defaults.valueForKey("active_user")! as! Dictionary<String, AnyObject?>
data["address"] = address;
defaults.setValue(data, forKey: "active_user");
defaults.synchronize();
I want to change it into like this:
var address = Dictionary<String, AnyObject?>();
address["address1"] = "Here";
address["address2"] = "There";
...
let defaults = NSUserDefaults.standardUserDefaults();
defaults["active_user"]!["address"]! = address;
defaults.synchronize();
Is this possible? How can I do that?
This is somewhat possible by extending NSUserDefaults to have a subscript. See below:
extension NSUserDefaults {
subscript(key: String) -> Any {
get {
return value(forKey: key)
}
set {
setValue(newValue, forKey: key)
}
}
}
This can then be used like so:
let defaults = NSUserDefaults.standardUserDefaults()
defaults["myKey"] = "someValue"
let myValue = defaults["myKey"]
One limitation is that you won't be able to modify nested collections as you've done in your example. You'll always have to make a manual assignment back to the user defaults object to save something.
// this won't work
defaults["active_user"]!["address"]! = address;
// do this instead
let user = defaults["active_user"]!
user["address"] = address
defaults["active_user"] = user
Edit:
I figured out a way to overload the subscript so you can modify nested collections, with slightly different but very clean syntax. Only works at one level of nesting, but I think it could be taken further. Add this to your NSUserDefaults extension:
subscript(firstKey: String, secondKey: String) -> Any? {
get {
if let dict = value(forKey: firstKey) as? [String: Any] {
return dict[secondKey]
}
return nil
}
set {
if let dict = value(forKey: firstKey) as? [String: Any] {
dict[secondKey] = newValue
setValue(dict, forKey: firstKey)
}
}
}
Then you can do this:
defaults["active_user", "address"] = "some address"
let address = defaults["active_user", "address"]