Protocol with Empty Init - swift

I am using a framework that has a 'user' protocol with all the desired properties listed, the protocol also has an empty init method. I need to create a user but any instance I create using the protocol complains that the init doesnt initialize all properties
Framework protocol
public protocol User {
/// Name
public var firstname: String { get set }
/// Lastname
public var lastname: String { get set }
///Init
public init()
}
How would I create my own struct utilizing this protocol and adding values to the params on init ?
thanks in advance!

You can use as following:
struct MyAppUser: User {
// default init coming from protocol
init() {
self.firstname = ""
self.lastname = ""
}
// you can use below init if you want
init(firstName: String, lastName: String) {
self.firstname = firstName
self.lastname = lastName
}
// coming from protocol
var firstname: String
var lastname: String
}
// user 1
var user1 = MyAppUser()
user1.firstname = "Ashis"
user1.lastname = "laha"
print(user1)
// user 2
let user2 = MyAppUser(firstName: "Kunal", lastName: "Pradhan")
print(user2)
Output:
MyAppUser(firstname: "Ashis", lastname: "laha")
MyAppUser(firstname: "Kunal", lastname: "Pradhan")

Related

How to destructure variable into function's parameter?

How can I pass the value of all key from object as functions parameters? like python did.
I have one function getNameInfo with parameter with default value firstName, lastName, age and one object with key firstName, lastName, age how is the best way for me to descture the pbject and pass all value of key into function? in python i can do something like getNameInfo(**a.__dict__.values()) but
class Person {
public var firstName = "firstName"
public var lastName = "lastName"
public var age = 12
public init(firstName: String, lastName: String, age: Int){
self.firstName = firstName
self.lastName = lastName
self.age = age
}
}
let a = Person(firstName: "firstName", lastName: "lastName", age: 12)
func getNameInfo(firstName: String = "i am first", lastName: String = "I am lat", age: Int = 50) {
print("\(fName), \(lName), \(age)")
}
getNameInfo()
// getNameInfo(a) // expect to print out firstName, lastName, 12
There is no similar feature in Swift. The closest version is to use protocols:
protocol NameInfoProviding {
var firstName: String { get }
var lastName: String { get }
var age: Int { get }
}
extension Person: NameInfoProviding {}
func getNameInfo(_ nameInfo: some NameInfoProviding) {
print("\(nameInfo.firstName), \(nameInfo.lastName), \(nameInfo.age)")
}
getNameInfo(a)
But yes, this is a different feature and used in different ways.

Copy one struct object

i have to quite similar struct objects. but one includes more values than the other. As the initial is required for KituraKuery methods i can not modify it but require more information for future processing.
my problem is now, that these struct objects look like this:
struct provider: Codable {
var firstName: String?
var lastName: String?
var email:String?
}
extension provider: Model{
class Persistence {}
}
struct provider2: Codable {
var firstName: String?
var lastName: String?
var email:String?
var providerCategories: [categories]?
}
extension provider: Model{
class Persistence {}
}
what i need is basically a smarter way to copy information from provider to provider2.
what i did as of now is i provided an init to provider2 taking provider as input and adding all values to it.
struct provider2: Codable {
var firstName: String?
var lastName: String?
var email:String?
var providerCategories: [categories]?
init(provider: provider?) {
if let provider = provider{
firstName = provider.firstName
lastName = provider.lastName
email = provider.lastName
}
}
extension provider: Model{
class Persistence {}
}
i however believe this is probably the worst way and there are much better and more lean approaches to it.
I tried myself on protocols but could that not really get to work.
Any input would be great :)
In your approach both Provider and Provider2 struct are tightly coupled to each other. So lets say in future if you want to change Provider struct or you want to initialise Provider2 struct with another struct, then you need to change lot of things.
We can solve the problem easily by decoupling both Provider and Provider2 struct
protocol BasicInfo {
var firstName: String? { get set }
var lastName: String? { get set }
var email:String? { get set }
}
protocol Address {
var address: String? {get set}
}
struct Provider: BasicInfo {
var firstName: String?
var lastName: String?
var email: String?
}
struct Provider2: BasicInfo, Address {
var firstName: String?
var lastName: String?
var email:String?
var address: String?
init(basic: BasicInfo, add: String) {
firstName = basic.firstName
lastName = basic.lastName
email = basic.email
address = add
}
}
//Below are instances of respective struct
let provider1 = Provider(firstName: "Test1", lastName: "TestLast1", email: "test1#gmail.com")
var provider2 = Provider2(basic: provider1, add: "Germany")
In above code i have two different Struct Provider and Provider2. Provider2 contains more variable than Provider (i have just added a single var to demonstrate). Now lets say in future we don't require Provider to fill Provider2, we have a new struct Provider3 which will fill Provider2 values.
struct Provider3: BasicInfo {
var firstName: String?
var lastName: String?
var email: String?
var middleName: String? //new property added
}
//Below are instances of respective struct
let provider3 = Provider3(firstName: "Test1", lastName: "TestLast1", email: "test1#gmail.com")
var provider2 = Provider2(basic: provider3, add: "Germany")
As you see there is no changes in struct Provider2, we have just introduce a new struct, create instance of new struct and passed that instance to Provider2 init method.
You could use extensions for this:
extension provider {
func asProvider2() -> provider2 {
return provider2(firstName: firstName,
lastName: lastName,
email: email,
providerCategories: nil)
}
}
// Then you can use it like this:
let bar = provider(firstName: "foo", lastName: "bar", email: "baz")
let bar2 = bar.asProvider2()

Keep value semantics on structs (containing an array of reference types)

Let's say (just as an example) I have a class Person and a struct House:
class Person {
var name: String
var lastName: String
init(name: String, lastName: String) {
self.name = name
self.lastName = lastName
}
}
struct House {
var address: String
var squareMeters: Float
var owner: Person
init(withAddress address: String, squareMeters: Float, andOwner owner: Person) {
self.address = address
self.squareMeters = squareMeters
self.owner = owner
}
}
To keep the value semantics on House is pretty easy in this case, if I'm not mistaken I can just do something like:
struct House {
var address: String
var squareMeters: Float
private var owner: Person //owner is now private
init(withAddress address: String, squareMeters: Float, andOwner owner: Person) {
self.address = address
self.squareMeters = squareMeters
//init now creates a copy of the owner passed
self.owner = Person(name: owner.name, lastName: owner.lastName)
}
//every time the owner gets modified from the outside we make sure to create a copy if needed
private var ownerToWrite: Person {
mutating get {
if !isKnownUniquelyReferenced(&owner) {
owner = Person(name: owner.name, lastName: owner.lastName)
}
return owner
}
}
var ownerName: String {
get {
return owner.name
}
mutating set {
ownerToWrite.name = newValue
}
}
var ownerLastName: String {
get {
return owner.name
}
mutating set {
return ownerToWrite.lastName = newValue
}
}
}
Ok, this should work. But what if House contained an array of Person? For instance:
struct House {
var address: String
var squareMeters: Float
var tenants: [Person]
init(withAddress address: String, squareMeters: Float, andTenants tenants: [Person]) {
self.address = address
self.squareMeters = squareMeters
self.tenants = tenants
}
}
How could I maintain the value semantics on the new House struct? Thank you in advance.

init new class instance with args object parameter which conforms to a protocol

I've a class and a protocol into myModel.swift
public protocol IModelArgs{
var name: String { get set};
var surname: String { get set};
}
public class Model {
var name: String;
var surname: String;
init(args: IModelArgs) {
self.name = args.name;
self.surname = args.surname;
}
}
IModelArgs is the protocol of arguments object passed to Model constructor.
Into another file I need to create the instance of Model class, but I'm not able to pass args object to constructor: What I'm wrong?
let myM = Model(args: ("T1","T2"));
The main problem in your case that ("T1","T2") is a tuple and not the object that conform your protocol. In your case it should look like this:
struct ArgsObject: IModelArgs {
var name: String
var surname: String
}
let myM = Model(args: ArgsObject(name: "someName", surname: "someSurname"))
But if you want to use the protocol only to pass an object to the constructor, you do not need to do this. Create struct for it like this:
struct ArgsObject {
let name: String
let surname: String
}
class Model {
var name: String
var surname: String
init(args: ArgsObject) {
self.name = args.name
self.surname = args.surname
}
}
let myM = Model(args: ArgsObject(name: "someName", surname: "someSurname"))
Few optional comments
Don't use ; and protocol names like ISomething, it's not the Java

How do I create a class that requires an instance variable to be set?

public class User : NSObject {
var id: Int //will throw an error during build
var name: String?
override init(){
}
convenience init(id: Int, name: String?){
self.id = id
self.name = name
}
}
I want to create a user class. The id should non-optional. However, my above code does not work unless I change the line to:
var id: Int = 0
I don't want to do this. Is there a better way?
Delete the word convenience! Convenience is exactly the opposite of what you want. You want this to be a designated initializer. Like this:
public class User : NSObject {
var id: Int
var name: String?
init(id: Int, name: String?){
self.id = id
self.name = name
super.init()
}
}
This forces the creator of a User to supply an id, which is exactly what you want.
public class User {
let id: Int
var name: String?
init(id: Int, name: String? = nil) {
self.id = id
self.name = name
}
}
let user = User(id: 3) // works without errors
As you can see, I changed some things. You probably don't really need to subclass NSObject, and I don't think you'll want to change a user's id after initialisation, so it makes more sense to make it a let constant.