Why does the Sendable type is not checked when defining the generic struct of Sendable types - ios

I'm going through
https://developer.apple.com/videos/play/wwdc2021/10133/
video, and I've created exmple to test Sendable type.
Why does this piece of code nod raise and compiler error?
class Author {
var firstName: String = ""
var secondName: String = ""
var notifCenter: NotificationCenter = NotificationCenter.default
}
struct Pair<T, U> where T: Sendable, U: Sendable {
var first: T
var second: U
}
var k = Pair<Int, Author>(first: 5, second: Author())
I've intentionally added notifCenter stored property to the class of Author not to allow for some compiler magic. Author is clearly not the Sendable type yet the compile doesn't seem to have a problem with it.

Related

Variable name in dot notation in Swift

I want to use variable names in dot notation in Swift.
I'm wrestling with trying to make sense of JSON in Swift, and having a really hard time doing things that are so easy in JS (like cycling through objects, etc).
I have variables of custom types:
var questionConfiguration: QuestionConfiguration
var priorityConfiguration: PriorityConfiguration
Each of these types has an attribute, displayOrder, I want to access this dynamically. I have tried various things, including the following (i is a variable of type Int):
var configs = [self.questionConfiguration, self.priorityConfiguration]
print([configs[i]].displayOrder)
var configs = ["questionConfiguration", "priorityConfiguration"]
print([configs[i]].displayOrder)
How can I achieve this?
EDIT:
struct QuestionConfiguration: Codable {
var displayOrder: Int
}
struct PriorityConfiguration: Codable {
var displayOrder: Int
}
You can create a protocol for same property name then you can access it easily
protocol SameProp {
var displayOrder: Int {get set}
}
class QuestionConfiguration : SameProp {
var displayOrder : Int = 4
}
class PriorityConfiguration : SameProp {
var displayOrder : Int = 6
}
var questionConfiguration: QuestionConfiguration = QuestionConfiguration()
var priorityConfiguration: PriorityConfiguration = PriorityConfiguration()
var configs = [questionConfiguration, priorityConfiguration] as [SameProp]
for elements in configs{
print(elements.displayOrder) // will print 4 and 6
}
Swift is very particular about data types and I suspect you are experiencing challenges because of it.
When you do this:
var configs = [self.questionConfiguration, self.priorityConfiguration]
In Swift, all the elements in an array have to have the same type. You've not given Swift any hints about what types this array should contain so the compiler has to infer a type. QuestionConfiguration and PriorityConfiguration are two different types. So the compiler is likely to infer that configs is of type [Any] also called Array<Any>.
Then you try something like this:
configs[i].displayOrder (not sure why your code has the extra brackets)
But configs[i] is inferred to be of type Any and that type doesn't have a displayOrder property so the compiler complains.
You need to tell the compiler that these types share some properties. One way to do that would be to add a protocol that describes what they share:
protocol DisplayOrderable {
var displayOrder: Int { get }
}
struct QuestionConfiguration: DisplayOrderable, Codable {
var displayOrder: Int
}
struct PriorityConfiguration: DisplayOrderable, Codable {
var displayOrder: Int
}
then you could use
var configs: [any DisplayOrderable] = [self.questionConfiguration, self.priorityConfiguration]
And configs[i].displayOrder should work.

What's the difference between : and = in swift

Sorry if the title is rather confusing, but I'm curious to know the difference between these two lines:
var title = String()
var title: String
Is one being initialized and one only be declared? Which is more correct?
For example, if I have a struct should I use one of the other?
So the reason I ask this is because I'm learning about how to grab some JSON from a url and then display it in my app. One of the new ways of doing so is using Decodable. So, I have a struct in a model class like so:
struct Videos: Decodable {
var title = String()
var number_of_views : Int
var thumbnail_image_name: String
var channel: Channel
var duration: Int
}
In another class I have this:
URLSession.shared.dataTask(with: url){(data,response,error) in
if(error != nil){
print(error!)
return
}
guard let data = data else { return }
do{
self.Videos2 = try JSONDecoder().decode([Videos].self, from: data)
//self.collectionView?.reloadData()
}catch let jsonErr{
print(jsonErr)
}
}.resume()
So, should I declare or initialize the variables in my struct? I'm assuming I should just declare them like so:
var title: String?
Would that be the correct syntax in my struct?
UPDATE:
I understand this question was more broad then I originally proposed it to be. I'm sorry about that, but thank you so much for all your great answers that clarified a lot up for me.
The difference is that : defines the type of your variable, whereas = assigns an actual value to the variable.
So:
var title = String()
This calls the initializer of the String type, creating a new String instance. It then assigns this value to title. The type of title is inferred to be String because you're assigning an object of type String to it; however, you could also write this line explicitly as:
var title: String = String()
This would mean you are declaring a title variable of type String, and assigning a new String to it.
var title: String
This simply says you're defining a variable of type String. However, you are not assigning a value to it. You will need to assign something to this variable before you use it, or you will get a compile error (and if this is a property rather than just a variable, you'll need to assign it before you get to the end of your type's init() method, unless it's optional with ? after it, in which case it gets implicitly initialized to nil).
EDIT: For your example, I'd probably declare all the variables using let and :, assuming that your JSON provides values for all of those properties. The initializer generated by Decodable should then set all the properties when you create the object. So, something like:
struct Videos: Decodable {
let title: String
let number_of_views : Int
let thumbnail_image_name: String
let channel: Int
let duration: Int
}
This initializes a value
var title = String()
This declares a value but does not initialize it
var title: String
If you attempt to use the latter, such as print(title), you will get a compiler error stating Variable 'title' used before being initialized
It does not matter whether the value is a class or a struct.
The = operator is the assignment operator, it assigns a value to the object on the left of the =
Typically, class or struct properties are declared but not initialized until the init() is called. A simple class might be
class MyClass {
let myProperty: String
init(aString: String) {
self.myProperty = aString
}
}
Whereas inside the scope of a function you may declare a local variable that only lives inside the scope of the function.
func doSomethingToAString(aString: String) -> String {
let extraString = "Something"
let amendedString = aString + extraString
return amendedString
}
In your specific example, the struct synthesizes an initializer that will allow you to initialize the struct with all the values needed to fill your properties. The initializer generated by Decodable should then set all the properties when you create a Videos struct, you will do it something like:
let aVideos = Videos(title: "My Title", number_of_views: 0, thumbnail_image_name: "ImageName", channel: Channel(), duration: 10)
Is one being initialized and one only be declared?
Yes, meaning that the declared cannot be used. If you tried to set a value for it, you would get a compile-time error:
variable 'title' passed by reference before being initialized
Which is more correct?
There is no rule of thumb to determine which is more correct, that would be depends on is there a need to initialize title directly.
On another hand, when it comes to declare properties for a class, saying var title = String() means that you are give title an initial value ("") which means that you are able to create an instance of this class directly, example:
class Foo {
var title = String()
}
let myFoo = Foo()
However, if title declared as var title: String, you will have to implement the init for Foo:
class Foo {
var title: String
init(title: String) {
self.title = title
}
}
let myFoo = Foo(title: "")
Also, you have an option to declare it as lazy:
lazy var title = String()
which means:
A lazy stored property is a property whose initial value is not
calculated until the first time it is used. You indicate a lazy stored
property by writing the lazy modifier before its declaration.
Properties - Lazy Stored Properties

How can I find the type of a property dynamically in swift (Reflection/Mirror)?

So let's say I have a class like this:
class Employee: NSObject {
var id: String?
var someArray: [Employee]?
}
I use reflection to get the property names:
let employee = Employee()
let mirror = Mirror(reflecting: employee)
propertyNames = mirror.children.flatMap { $0.label }
// ["businessUnitId", "someArray"]
so far so good! Now i need to be able to figure out the type of each of these properties, so if I do the employee.valueForKey("someArray"), it won't work because it only gives me the AnyObject type. What would be the best way to do this? Specially for the array, I need to be able to dynamically tell that the array contains type of Employee.
You don't need to inherit from NSObject (unless you have a good reason to).
class Employee {
var id: String?
var someArray: [Employee]?
}
let employee = Employee()
for property in Mirror(reflecting: employee).children {
print("name: \(property.label) type: \(type(of: property.value))")
}
Output
name: Optional("id") type: Optional<String>
name: Optional("someArray") type: Optional<Array<Employee>>
This also works with Structs
If you are inheriting from NSObject, you can use some of the methods provided by NSCoding and the Objective-C runtime:
let employee = Employee()
employee.someArray = []
employee.valueForKey("someArray")?.classForCoder // NSArray.Type
employee.valueForKey("someArray")?.classForKeyedArchiver // NSArray.Type
employee.valueForKey("someArray")?.superclass // _SwiftNativeNSArrayWithContiguousStorage.Type
employee.valueForKey("someArray")!.dynamicType

In swift, when one property has no default value, the default values held by other properties become useless?

In swift, I have the following code:
struct Person {
var name: String
var nose: Int = 1
}
var mike = Person(name: "mike", nose: 1)
var john = Person(name: "john") //error: missing argument for parameter 'nose' in call
Although nose has a default value, I have to specify a value for it. My question is, does default value of nose become totally useless and I can simply write the declaration as the following?
struct Person {
var name: String
var nose: Int
}
Thanks!
From The Swift Programming Language
Structure types automatically receive a memberwise initializer if they
do not define any of their own custom initializers. This is true even
if the structure’s stored properties do not have default values.
The memberwise initializer is a shorthand way to initialize the member
properties of new structure instances. Initial values for the
properties of the new instance can be passed to the memberwise
initializer by name.
As you haven't declared any initialisers for your struct, the only initialiser that is available is the memberwise initialiser.
You can declare an initialiser that just takes a name in order to use the default value for nose. This then requires you to explicitly declare the memberwise initialiser -
struct Person {
var name: String
var nose: Int = 1
init(_ name:String) {
self.name=name;
}
init (name:String, nose:Int){
self.name=name;
self.nose=nose;
}
}
var mike = Person(name: "mike", nose: 1)
var john = Person("john")
I like this form because it is so flexible:
struct Person {
var name: String
var nose: Int
init(name: String = "Anon", nose: Int = 1) {
self.name=name;
self.nose=nose;
}
}
var sally = Person(nose: 2, name: "Sally")
var anon1 = Person(nose: 1)
var anon2 = Person()
var mike = Person(name: "mike", nose: 1)
var john = Person(name: "john")
Note 5 different initialiser combinations possible. Essentially it allows you to treat Person like a person builder (Builder pattern).

Creating array with struct in swift

So. I have tried to create a person with struct in Swift and i'm wondering how to create an array using an instance of my struct.
Can anybody tell me how to do this?
struct Person{
var name: String
var boyOrGirl: Bool
init(names: String, bOg: Bool){
self.name = names
self.boyOrGirl = bOg
}
}
var personArray: Person = [["Heine",true], ["Magnus",true]]
An instance of Person is created as:
Person(names: "Heine", bOg: true)
There are 2 errors instead in your code:
var personArray: Person = [["Heine",true], ["Magnus",true]]
^^^^^^ ^^^^^^^^^^^^^^
personArray should be an array, whereas you declared it as Person
what you are doing here is adding an array containing a string and a boolean
The correct syntax is:
var personArray: [Person] = [Person(names: "Heine", bOg: true), Person(names: "Magnus",bOg: true)]
Note that the variable type [Person] can be omitted because the compiler can infer the type from the value assigned to the personArray variable:
var personArray = [Person(names: "Heine", bOg: true), Person(names: "Magnus",bOg: true)]
You'd use:
var personArray: [Person] = [Person(name:"Heine",bOg:true), Person(name:"Magnus",bOg:true)]
or, since the array type can be inferred, even:
var personArray = [Person(name:"Heine",bOg:true), Person(name:"Magnus",bOg:true)]

Resources