Where is the property that do not conform to the protocol goes? - swift

Teacher & TeamMate are two protocols. The class Coach conforms to those protocols.
protocol Teacher {
var firstName: String { get }
var lastName: String { get }
var title: String { get }
}
protocol TeamMate {
var firstName: String { get }
func role()
}
class Coach: Teacher, TeamMate {
var firstName: String
var lastName: String
var title: String
func role() {
print("coach the team")
}
init(firstName: String, lastName: String, title: String){
self.firstName = firstName
self.lastName = lastName
self.title = title
}
}
var member: TeamMate = Coach(firstName: "Izumi", lastName: "Yakuza", title: "LA Coach")
I've created a variable named member of type coach meanwhile it conforms to the TeamMate type (Please correct me if the description is not accurate)
I need to initialise all the properties defined in the Coach class when I create the member object. I.e.(firstName: "Izumi", lastName: "Yakuza", title: "LA Coach"). However, in the end, there is only one property firstName and one method role() inside the member instance.
Question: How the properties (lastName: "Yakuza", title: "LA Coach") were processed? Is that they were created firstly and then cut away or just hidden?
Thanks a lot for your kind help and time.

I've created a variable named member of type coach meanwhile it conforms to the TeamMate type
var member: TeamMate = Coach(firstName: "Izumi", lastName: "Yakuza", title: "LA Coach")
I suspect this line of code isn't doing what you think it's doing. Your member variable isn't of type Coach, you've upcast to the abstract type TeamMate (a protocol which Coach conforms to). Therefore when using your variable, you can only access members that TeamMate defines (firstName and role()) – which is the exact behaviour you're seeing.
Unless you need to you shouldn't upcast your variables, as upcasting loses type information. In most cases, you should just let Swift infer the correct type for you. In your case member should almost certainly be of type Coach (you can always freely pass it to anything expecting a TeamMate, as Coach conforms to this protocol).
Therefore, you just want to drop the explicit type annotation:
var member = Coach(firstName: "Izumi", lastName: "Yakuza", title: "LA Coach")
You should also make it a let constant if you don't plan on mutating it.

Related

Is there any way to make the method return a mutable value?

as shown in the code below:
struct Person {
var name: String
}
struct Group {
var person: Person
func callAsFunction() -> Person {
// Person is immutable value
person
}
}
var james = Person(name: "James")
var group = Group(person: james)
group().name = "Wong" //ERROR: Cannot assign to property: function call returns immutable value
group() return an immutable value, that can't be changed! So Is there any way to make the callAsFunction() method return a mutable value?
Thanks ;)
Updated:
My idea is to transfer all the calls and visits of the Group to the Person object in the Group, just like using Person directly.
I can't use dynamicMemberLookup because I don't know what method or property there will be in Person. For example, there may be 100 methods and properties in Person (not only one name property as demonstrated), and it is impossible for me to write 100 subscript methods with dynamicMemberLookup.
My needs are a bit like proxy objects in the Ruby language. Accessing an object (Group) actually accesses another object (Person) inside it, as if the Group does not exist.
ruby proxy patterns:
https://refactoring.guru/design-patterns/proxy/ruby/example
CallAsFunction is the closest implementation so far, but requires that Person cannot be a Struct, otherwise it cannot be assigned to its properties.
Maybe it's not possible to implement this feature in Swift yet?
You're using the wrong dynamic method. What you want is dynamicMemberLookup. Watch closely. First, the preparation:
struct Person {
var name: String
}
#dynamicMemberLookup
struct Group {
var person: Person
subscript(dynamicMember kp:WritableKeyPath<Person,String>) -> String {
get { self.person[keyPath:kp] }
set { self.person[keyPath:kp] = newValue }
}
}
Now look at what that allows you to say:
var group = Group(person: Person(name: "James"))
group.name = "Wong"
print(group.person) // Person(name: "Wong")
Do you see? We set the name of the Group even though it has no name property, and the result was that we set the name of the Group's person which does have a name property.
The callAsFunction simply returns (a copy of the) Person, which is a value type. You cannot then mutate the property of it like that. It is equivalent to the following:
struct Person {
var name: String
}
Person(name: "Foo").name = "Bar"
That returns the same error:
If Person was a reference type, it would have worked, but not for a value type. And even if you took your value type, and first assigned it to a variable before mutating it, you would only be mutating your copy, not the original.
If you want the behavior you want, you would use a #dynamicMemberLookup as suggested by matt (+1) and outlined in SE-0195.
You said:
I can't use dynamicMemberLookup because I don't know what method or property there will be in Person. For example, there may be 100 methods and properties in Person (not only one name property as demonstrated), and it is impossible for me to write 100 subscript methods with dynamicMemberLookup.
You do not need “100 subscript methods.” It is the motivating idea behind #dynamicMemberLookup, namely that the properties will be determined dynamically. E.g., here is Person with two properties, but Group only has the one #dynamicMemberLookup.
struct Person {
var name: String
var city: String
}
#dynamicMemberLookup
struct Group {
var person: Person
subscript(dynamicMember keyPath: WritableKeyPath<Person, String>) -> String {
get { person[keyPath: keyPath] }
set { person[keyPath: keyPath] = newValue }
}
}
var group = Group(person: Person(name: "James", city: "New York"))
group.name = "Wong"
group.city = "Los Angeles"
print(group.person) // Person(name: "Wong", city: "Los Angeles")
If you want to handle different types, make it generic:
struct Person {
var name: String
var city: String
var age: Int
}
#dynamicMemberLookup
struct Group {
var person: Person
subscript<T>(dynamicMember keyPath: WritableKeyPath<Person, T>) -> T {
get { person[keyPath: keyPath] }
set { person[keyPath: keyPath] = newValue }
}
}
And
var group = Group(person: Person(name: "James", city: "New York", age: 41))
group.name = "Wong"
group.city = "Los Angeles"
group.age = 42
print(group.person) // Person(name: "Wong", city: "Los Angeles", age: 42)

RealmSwift LinkingObjects and Decodable

I have a Realm model class that I need to be Decodable so I can serialize it from JSON and save it to database. Every PortfolioItem is associated with one Product and at some point I need to get to PortfolioItem from Product via inverse relationship. That's why I have LinkingObjects property. The problem is when I try to conform to Decodable protocol. The compiler is giving me an error Cannot automatically synthesize 'Decodable' because 'LinkingObjects<PortfolioItem>' does not conform to 'Decodable' . How to deal with this? I found very little about LinkingObjects and Decodable online and I have no idea how to approach this.
class PortfolioItem: Object {
#objc dynamic var id: String = ""
#objc dynamic var productId: String = ""
#objc dynamic public var product: Product?
convenience init(id: String, productId: String) {
self.init()
self.id = id
}
}
final class Product: Object, Decodable {
#objc dynamic var id: String = ""
#objc dynamic var name: String = ""
private let portfolioItems = LinkingObjects(fromType: PortfolioItem.self, property: "product")
public var portfolioItem: PortfolioItem? {
return portfolioItems.first
}
convenience init(id: String, name: String) {
self.init()
self.id = id
}
}
Big thanks to Chris Shaw for helping me figure this out. I wrote a more in-depth article how to setup Decodable and LinkingObjects, look HERE.
Well unless I'm missing something then the LinkingObjects properties does not need to be included in the decoding.
My assumption here is that you're receiving JSON from some online source, where the JSON for a Product consists of { id: "", name: "" }. As long as you're creating the PortfolioItem correctly with associated Product, then the resulting LinkingObjects property is the result of a dynamic query within Realm (and will thus work without any JSON source).
I'm not in a position to test compile an answer today, but you should be able to use CodingKeys to simply exclude that property, i.e. by adding this to Product:-
private enum CodingKeys: String, CodingKey {
case id
case name
}
Also, unrelated, but note that your convenience init function are not initialising all properties that you're passing in.

Swift Protocols: Difference between { get } and { get set } with concrete examples?

I'm pretty new to Swift, and although I've read Apple's documentation and many topics and threads about this, I still can't understand what's the difference between { get } and { get set }. I mean, I'm looking for an explanation with a concrete example.
Like, for example:
protocol PersonProtocol {
var firstName: String { get }
var lastName: String { get set }
}
What would be the actual difference between these two properties? I tried to play with these properties in a playground:
struct Person: PersonProtocol {
var firstName: String
var lastName: String
}
var p = Person(firstName: "John", lastName: "Lennon")
print(p.firstName) // John
print(p.lastName) // Lennon
p.firstName = "Paul"
p.lastName = "McCartney"
print(p.firstName) // Paul
print(p.lastName) // McCartney
Did not help... Thanks for your help.
You are creating a variable of type Person and there are no restrictions on that struct. If you instead create a variable of type PersonProtocol then firstName will be read only
var p1: PersonProtocol = Person(firstName: "John", lastName: "Lennon")
print(p1.firstName) // John
print(p1.lastName) // Lennon
p1.firstName = "Paul" <== error: cannot assign to property: 'firstName' is a get-only property
protocol — is a requirement of some minimal interface of the type implementing it.
var name: Type { get } requires type to have property with at least a getter (accessible from outside of the type, not private), i.e. outside code should be able to read value of the property. In the implementing type it could be let name: Type, var name: Type, private(set) var name: Type, fileprivate(set) var name: Type, etc.
var name: Type { get set } requires type to have property with both accessible getter and setter, i.e. outside code should be able to read and write to the property. Here only var name: Type would be allowed.
If protocol requires for getter but you also provide a setter — it's not against protocol requirements.
But if protocol requires for both getter and setter — you must provide both, and not having any of them won't be valid implementation.
Your Person class defined both properties as var(with accessible getter and setter) therefore you can change them both. But PersonProtocol haven't required ability to set firstName.
And as #JoakimDanielson shows, if you will use just interface required by protocol you won't be to change the firstName value.

Literal Convertibles in Swift

I want to know how Literal Convertibles work in Swift. The little I know is that the fact that, in var myInteger = 5, myInteger magically becomes an Int is because Int adopts a protocol, ExpressibleByIntegerLiteral and we don't have to do var myInteger = Int(5). Similarly String, Array, Dictionary etc all conform to some Literal protocols.
My Question is
Am I right in my little understanding of Literal Convertibles?
How can we implement these in our own types. For example
class Employee {
var name: String
var salary: Int
// rest of class functionality ...
}
How can I implement Literal Protocols to do var employee :Employee = "John Doe" which will automatically assign "John Doe" to employee's name property.
You are partially correct in your understanding of the various ExpressibleBy...Literal protocols. When the Swift compiler parses your source code into an Abstract Syntax Tree, it already identified what literal represents what data type: 5 is a literal of type Int, ["name": "John"] is a literal of type Dictionary, etc. Apple makes the base type conform to these protocols for the sake of completeness.
You can adopt these protocols to give your class an opportunity to be initialized from a compile-time constant. But the use case is pretty narrow and I don't see how it applies to your particular situation.
For example, if you want to make your class conform to ExpressibleByStringLiteral, add an initializer to set all your properties from a String:
class Employee: ExpressibleByStringLiteral {
typealias StringLiteralType = String
var name: String
var salary: Int
required init(stringLiteral value: StringLiteralType) {
let components = value.components(separatedBy: "|")
self.name = components[0]
self.salary = Int(components[1])!
}
}
Then you can init your class like this:
let employee1: Employee = "John Smith|50000"
But if you dream about about writing something like this, it's not allowed:
let str = "Jane Doe|60000"
let employee2: Employee = str // error
And if you pass in the wrong data type for salary, it will be a run time error instead of a compile-time error:
let employee3: Employee = "Michael Davis|x" // you won't know this until you run the app
TL, DR: it is a very bad idea to abuse these ExpressibleBy...Literal types.
This can be a scenario to work with Convertibles in custom types.
struct Employee : ExpressibleByStringLiteral {
var name: String = ""
init() {}
init(stringLiteral name: String) {
self.name = name
}
}
func reportName(_ employee: Employee) {
print("Name of employee is \(employee.name)")
}
reportName("John Doe") //Name of employee is John Doe

Why I can change/reassigned a constant value that Instantiated from a class

I've created the following class
class Person {
var firstName: String
var lastName: String
init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
func fullName() -> String {
return "\(firstName) \(lastName)"
}
}
Then I instantiated a constant value from the class
let john = Person(firstName: "Johnny", lastName: "Applessed")
Question: Why I can change the content of the variable john? Isn't it a constant? Can someone explain that for me, thanks a lot.
john.firstName = "John"
print(john.firstName) // -> John
As #Wain has said – it's due to the nature of reference types. The instance being a let constant only means you cannot assign a new reference to it – but says nothing about the actual mutability of the instance itself.
If you change your class to a struct, you'll see how the behaviour differs with value types, as changing a property changes the actual value of your Person – therefore you are unable to do so if it's a let constant. However I somewhat doubt you'll want to make your Person a struct, as two people with the same name shouldn't be considered to be the same person.
If you only wish your properties to be assigned upon initialisation (and then read-only for the lifetime of the instance), then I would recommend making them let constants (instead of making their setters private). This will ensure that you cannot even change their value from within your class, once assigned.
The rule is as long you give a property a value before the super.init() call – you can make it a let constant (in this case, you just have to assign them in the initialiser before using self).
class Person {
let firstName: String
let lastName: String
init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
...
The class instance itself is a constant, so you can't change it to reference another instance, but the instance is mutable because it's properties are created as vars.
Change firstName to have a private setter and see what you can do:
private(set) var firstName: String
When you're using a constant instance of a class in swift, doesn't mean you can't change the class attributes. It' means you can't instantiate a new object in this constant
let person = Person(firstName: "Johnny", lastName: "Appleseed")
person = Person(firstName: "John", lastName: "Appleseed") //--->It gets error: Cannor assign to value: 'person' is a 'let' constant
But you can create a constant inside class and set this values in the init
class Person {
let firstName: String
let lastName: String
init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
func fullName() -> String {
return "\(firstName) \(lastName)"
}
}
//Tip: Don't init the class constants in declaration time or will get the same above error. Just init this constants at constructor/initialization of class.
And Now you have the expected result you want, even if create a 'var' instance of this object
var person = Person(firstName: "Johnny", lastName: "Appleseed")
person.firstName = "John" //--->It gets error: Cannor assign to value: 'person' is a 'let' constant
person = Person(firstName: "John", lastName: "Snow")
person.firstName = "Johnny" //--->It gets error: Cannor assign to value: 'person' is a 'let' constant
Your thinking was not wrong, but a little confuse cause you would be totally right if it was a struct instead a class.