Swift equivalent of a simple JavaScript object declaration - swift

Considering the following JavaScript object:
const car = {type:"Fiat", model:"500", color:"white"};
What would be the equivalent structure in Swift language, written in such a short line of code?

You can also do:
let car = (type: "Fiat", model: "500", color: "white")
But in my opinion, it is much cleaner to define your objects by using structs.

Swift is a strongly typed language. If you want to initialize an object, you have to define its type first. Javascript is weakly typed and apart of that it doesn't even distinguish between objects and dictionaries (or arrays).
In Swift, you could write
let car = ["type": "Fiat", "model": "500", "color": "white"]
to create a [String: String] dictionary, but that would be a bad idea in this case. Instead, you should properly declare a type:
struct Car {
let type: String
let model: String
let color: String
}
and then initialize it:
let car = Car(type: "Fiat", model: "500", color: "white")
which is almost the same as in Javascript, only with types.
Types give you more power and "shorter" does not mean "better".

Related

How to get a property of a tuple with a string?

I'm starting in Swift, I come from JavaScript, and some things are a bit confusing for me, is there something similar to this in Swift?:
JS:
var user = {name: "Chris Answood"}
user["name"]
JavaScript doesn't have built-in tuple support. You just created an object and get the value of the name property inside this object. You have used user["name"] but user.name would also be possible.
To achieve this in Swift you can create a struct:
struct User {
name: String
}
let user = User(name: "Chris Answood")
print(user.name)
Unlike JavaScript, Swift has support for tuples. This is an easy example:
let user = (firstName: "Chris", lastName: "Answood")
print(user.firstName)

How to add/Update key/values in dict in swift?

func exercise() {
var stockTickers: [String: String] = [
"APPL" : "Apple Inc",
"HOG": "Harley-Davidson Inc",
"BOOM": "Dynamic Materials",
"HEINY": "Heineken",
"BEN": "Franklin Resources Inc"
]
stockTickers["WORK"] = ["Slack Technologies Inc"]
stockTickers["BOOM"] = ["DMC Global Inc"]
print(stockTickers["WORK"]!)
print(stockTickers["BOOM"]!)
}
Error: Cannot assign value of type '[String]' to subscript of type 'String'
I do not understand the error, I'm new to swift, can someone guide me through this and tell mw why I see this error.
Alexander explained what you need to do in abstract terms. (Voted)
Specifically, change
stockTickers["WORK"] = ["Slack Technologies Inc"]
stockTickers["BOOM"] = ["DMC Global Inc"]
To
stockTickers["WORK"] = "Slack Technologies Inc"
stockTickers["BOOM"] = "DMC Global Inc"
The expression ["Slack Technologies Inc"] defines a String array containing a single string. That's not what you want. you defined a dictionary of type [String:String]
If you wanted your dictionary to have String keys and values that contained arrays of strings, you'd have to change the way you declared your dictionary:
var stockTickers: [String: [String]] = [
"APPL" : ["Apple Inc"],
"HOG": ["Harley-Davidson Inc"],
"BOOM": ["Dynamic Materials"],
"HEINY": ["Heineken"],
"BEN": ["Franklin Resources Inc"]
]
["DMC Global Inc"] is an array literal containing a string. It evaluates to a value of type [String] (a.k.a. Array<String>).
Thus the error makes sense: you’re trying to a assign an array of strings in a place where only a String is expected.
Just remove the square brackets.

Swift initialise property at struct initialisation without initialiser

In swift, structs have an automatically generated memberwise initializer.
This means the following struct can be initialised without me having to write an init.
struct Activity {
let name: String
let desc: String
let category: Category
let subcategory: Subcategory
let emoji: Character
let coordinate: CLLocationCoordinate2D
let creationTime: Date = Date()
let activityTime: Date
let id: UUID = UUID()
var comments: [Comment] = []
}
I have one single property called emojiwhich is computed by the subcategory. In other words, the value for emoji depends on the value of subcategory.
However this means that the value of emoji can only be assigned after the initialisation of subcategory.
How should I do this in code?
Approach 1:
Provide my own initialiser
init(name: String, desc: String, category: Category, subcategory: Subcategory,
coordinate: CLLocationCoordinate2D, activityTime: Date) {
self.name = name
self.desc = desc
self.category = category
self.subcategory = subcategory
self.coordinate = coordinate
self.activityTime = activityTime
self.emoji = AllCategories.categories[category]?[subcategory] ?? "❌"
}
I don't like this approach as it adds a lot of unecessary code that will only grow if I add more properties... I would like to use the generated initialiser of the struct. On the other hand, the code is still very simple.
Approach 2:
Use a lazy varthat is only computed when called.
lazy var emoji: Character = {
AllCategories.categories[category]?[subcategory] ?? "❌"
}()
I also don't really like this approach, I find it overly complex for what I am trying to do. Also this makes emojia varinstead of letwhich it is not, I want it to remain a constant. On the other hand, I can continue using the automatically generated initialiser.
Questions:
What other possibilities do I have?
If there are none, which of the 2 approches is the best?
This sounds like a great chance to use computed properties:
var emoji: Character {
AllCategories.categories[category]?[subcategory] ?? "❌"
}
Although it is declared a var, you can't actually set it. It's just how computed properties must be declared.
The expression AllCategories.categories[category]?[subcategory] ?? "❌" will be evaluated every time you use the property. It's not a too time-consuming expression, so IMO it's fine.

Why use an initializer? [duplicate]

This question already has answers here:
Why do we need to specify init method?
(3 answers)
Closed 6 years ago.
Newbie, coming from PHP. Be gentle.
Here's a swift struct with an initializer
struct Book {
let title: String
let author: String
let price: String
let pubDate: String
init(title: String, author: String, price: String, pubDate: String){
self.title = title
self.author = author
self.price = price
self.pubDate = pubDate
}
}
let HarryPotter = Book(title: "Harry Potter", author: "JK Rowling",
price: "30$", pubDate: "January 10th, 1998")
And here's a swift struct without an intializer
struct Book {
let title: String
let author: String
let price: String
let pubDate: String
}
let HarryPotter = Book(title: "Harry Potter", author: "JK Rowling",
price: "30$", pubDate: "January 10th, 1998")
If these both do the same thing, then what is the advantage of using an initializer?
In the second case, you're still using an initializer. You're using the default initializer, which was generated for you because you haven't specified any of your own.
Swift provides a default initializer for any structure or class that provides default values for all of its properties and does not provide at least one initializer itself. The default initializer simply creates a new instance with all of its properties set to their default values.
To answer the more general question of what initializes for: they encapsulate the initialization of an instance, and guarantee that you can never obtain an instance in a "half-baked" state. They're Swift's equivalent of PHP Constructors.
In your specific case, nothing, because all your initialiser is doing is setting the values. But, it's possible that you could have other code in the initialiser which actually does something.
If you want to include some logic beforehand you could do that in the constructor/initializer.
This is a bit of a guess since I'm a Ruby developer rather than Swift, but there might be times when you would want to run more (arbitrary) code inside of your initializer besides just assigning the argument values to instance variables. For example:
def initialize(attr)
#attr = attr
puts "I'm initializing!"
end
In the above case I've assigned the attribute and printed to stdout. I'm guessing in Swift all of that attribute assignment is being done automatically so if all you want to do is assign attributes you can avoid writing the initializer yourself and let Swift handle it.

How do I put different types in a dictionary in the Swift Language?

Swift only allows a dictionary to contain a single type.
Here's the definition that is taken from the Swift book:
A dictionary is a container that stores multiple values of the same type
[...]
They differ from Objective-C’s NSDictionary and NSMutableDictionary classes, which can use any kind of object as their keys and values and do not provide any information about the nature of these objects.
If that’s the case then how are we going to create nested dictionaries?
Imagine we have a plist that holds String, Array and Dictionary items in it . If I’m allowed to hold only the same of type of items (either string, array etc.) then how am I going to use different types of items stored in the plist?
How do I put different types in the same dictionary in Swift?
You can achieve plist-like nested structures using Any type for dictionary values which is Swift's somewhat counterpart to Objective-C's id type but can also hold value types.
var response = Dictionary<String, Any>()
response["user"] = ["Login": "Power Ranger", "Password": "Mighty Morfin'"]
response["status"] = 200
EDIT:
Any seems to be better than AnyObject because in the above code response["status"] is of type Swift.Int, while using value type of AnyObject it is __NSCFNumber.
As has been suggested, you can use the Any type to represent a plist dictionary's values. But then how do you work with the data? Cast every value any time you look it up from the dictionary? That's really messy. A better, more type-safe way to model a plist would be to take advantage of Swift's enums, also known as algebraic data types or discriminated unions. They let you specify exactly what types are permitted in the dictionary and avoid ever having to cast. Here's an implementation, explained:
// An atomic (i.e. non-collection) data type in a plist.
enum PListNode {
case PLN_String(String)
case PLN_Integer(Int)
case PLN_Float(Double)
case PLN_Bool(Bool)
case PLN_Date(CFDate)
case PLN_Data(CFData)
}
At the most atomic level, only the above data types may be stored in a plist. Each 'node' in the plist can ultimately can only be one of these types. So we create an enum which lets us specify this.
// A value that can be stored in a plist Dictionary's key-value pair.
enum PListValue {
case PLV_Node(PListNode)
case PLV_Array(PListNode[])
case PLV_Dictionary(Dictionary<String, Box<PListValue>>)
}
typealias PList = Dictionary<String, Box<PListValue>>
A plist is basically a dictionary of key-value pairs, and each value can be either an atomic (i.e. non-collection) value; or it can be an array of atomic values; or it can be a dictionary of string-plist value pairs. The above enum expresses these constraints, and the typealias gives the plist type an easy-to-remember name.
Given the above types, we can completely express any given plist in a type-safe way, e.g.:
// Example translated from
// https://developer.apple.com/library/Mac/documentation/Darwin/Reference/ManPages/man5/plist.5.html
let myPlist: PList = [
"Year Of Birth": Box(PLV_Node(PLN_Integer(1965)))
, "Pets Names": Box(PLV_Array([]))
, "Picture": Box(PLV_Node(PLN_Data(...)))
, "City of Birth": Box(PLV_Node(PLN_String("Springfield")))
, "Name": Box(PLV_Node(PLN_String("John Doe")))
, "Kids Names": Box(
PLV_Array([PLN_String("John"), PLN_String("Kyra")])
)
]
What it means to be type-safe here is that you can process any given plist using a switch statement and cover all possibilities without the need for any casting. You're eliminating a whole class of potential runtime errors. E.g.:
// See https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Enumerations.html#//apple_ref/doc/uid/TP40014097-CH12-XID_189 for explanation
switch myPlist["Year Of Birth"] {
case Box(.PLV_Node(let plvNodeValue)):
...
case Box(.PLV_Array(let plvArrayValue)):
...
case Box(.PLV_Dictionary(let plvDictionaryValue)):
...
}
Note that it's necessary to wrap up recursive data structures in a 'box' (a pointer to the actual value) to keep their sizes finite.
NSObject works for my case while "Any" does not
var d:Dictionary<String,NSObject> = [:]
d["key1"] = "ddd"
d["key2"] = 111 //OK
NSLog("%#", d) //OK
var d2:Dictionary = Dictionary<String,Any>()
d2["key1"] = "ddd"
d2["key2"] = 111
NSLog("%#", d2) //I got error here
Use NSMutableDictionary like this :
var dictInfo : NSMutableDictionary = [ "lang_key": "1"]
dictInfo["food_type"] = lbl_TypeOfFood.text
dictInfo["search_text"] = txt_Search.text
dictInfo["date"] = lbl_Date.text
dictInfo["opening_hours"] = lbl_OpeningHours.text
hope this will work fine .
Use: Dictionary<String, AnyObject>
var dict: Dictionary<String, AnyObject> = [
"number": 1,
"string": "Hello",
]
NSMutableDictionary to Dictionary works like a charm and will allow you to put different types in a Dictionary in the Swift Language:
let nsMutableDictionary = NSMutableDictionary()
nsMutableDictionary[NSFontAttributeName] = UIFont(name: "HelveticaNeue", size: 12.0)!
nsMutableDictionary[NSForegroundColorAttributeName] = UIColor.redColor()
let dictionary: Dictionary<NSObject, AnyObject> = nsMutableDictionary
self.attributedPlaceholder = NSAttributedString(string: textParam, attributes: dictionary)
let dictionary : Dictionary = [
"key": "value",
"key2": 2,
"key3": NSString(),
2: "test",
]
One can specify types which restricts the dictionary
let dictionary : Dictionary<String, String> = [
"key": "value",
"key2": 2, // This errors
]