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)
Related
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".
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.
When designing classes/structs in Swift, what are the best practices for identifying when you should use an Optional type?
For example, lets say I'm mapping a JSON response to a Photo object and there is a property photographerName which is just a string. Sometimes we receive empty strings for the field photographerName by the response.
Should my Photo object use a String? type and assign it to nil? Or use a String type and set it to an empty string?
It really depends on the application architecture and data structures that you going to use in the application.
There is no rule that will describe how properly use optional chaining in the application architecture.
At first, we should know what is optional chaining.
Optional chaining is a process for querying and calling properties,
methods, and subscripts on an optional that might currently be nil. If
the optional contains a value, the property, method, or subscript call
succeeds; if the optional is nil, the property, method, or subscript
call returns nil. Multiple queries can be chained together, and the
entire chain fails gracefully if any link in the chain is nil.
Based on my experience, I am using optional values in my data structure when I clearly could not define if my model could or could not have some relationship or property.
Example:
struct User {
let name: String
let email: String
var friends: [String]?
init(name: String, email: String) {
self.name = name
self.email = email
}
}
let user = User(name: "Oleg", email: "Oleg#email.com")
I know that user will have name and email but I do not know if he would have any friends. Also, in this way, I could prevent my code from execution of operation on nil objects.
In case that you describe it really depends on the architecture of the application and requirements. For example, should we show placeholder if photographerName is empty or not? Are any additional operation that are using photographerName property?
It´s up to you what you like to use, but if you receive a response with a property that can be nil then you should use optional and handle it in the code.
Something like this:
struct Photo {
let photographerName: String?
init(photographerName: String? = nil) {
self.photographerName = photographerName
}
}
And then in your code to check if photographerName has a value:
if let photographerName = photo.photographerName {
// use photographerName in here
}
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.
I think I have programmed myself into a corner, but I'm hoping you all know a way out. I have a class...
class Card {
var order: Int? = -1
var tag: String = "0"
var comment: String?
var data : [String: NSNumber]
}
Ideally everything would be in data, which is a few strings and lots of numbers. I started with [String, String] but I found I was writing lots of code to cast and convert when I wanted to (say) compare one of those numbers to zero. Changing it to [String, NSNumber] simplified all that code, but now my tableViewDataSource becomes very complex because some of the data is in data and some is in a separate property like comment. I even tried [String, Any], but then everything had to be cast all the time to do anything.
I have a feeling I am missing something fundamental here. When working with NSTableViews, is there a simple way to use Swift properties that I'm missing? valueForKey: does not work, there's no easy way to do a reflection-like solution I know of, etc. Any suggestions?
You can only bind dynamic properties, and your class needs to inherit from NSObject or implement NSObjectProtocol. Additionally, nilable value-types aren't allowed, so you cannot bind Int?
ie.:
class Card: NSObject {
dynamic var order: Int = -1
dynamic var tag: String = "0"
dynamic var comment: String?
dynamic var data: [String: NSNumber]
}