Swift generics subclass behaviour in Array - arrays

So I walk against this problem.
I have a BaseModel with some variables inside and a few normal "final" Models that inherit from the BaseModel.
If I have an array with class BaseModel with inside a few final models, and then loop trough those models and sent them to a function that uses generics. And check the class of the generics I get back they are from class BaseModel instead of the final class model that I need. How can I retrieve in a dynamic way the class of the final model. And of course I could cast them, but I would love to find a better solution.
See example in the playground I made:
import UIKit
class BaseModel {
var id: Int
init(id: Int) {
self.id = id
}
}
final class PersonModel: BaseModel {
var firstName: String
var surname: String
init(id: Int, firstName: String, surname:String) {
self.firstName = firstName
self.surname = surname
super.init(id: id)
}
}
func genericsTest<C: BaseModel>(with object: C) {
print("Class type: \(C.self)")
}
var person0 = PersonModel(id: 0, firstName: "John", surname: "Doe")
genericsTest(with: person0)
// This prints: "Class type: PersonModel"
var person1 = PersonModel(id: 1, firstName: "John1", surname: "Doe")
var person2 = PersonModel(id: 2, firstName: "John2", surname: "Doe")
var person3 = PersonModel(id: 3, firstName: "John3", surname: "Doe")
var personArray: [BaseModel] = [person0, person1, person2, person3]
for person in personArray {
print("Class type in for loop: \(person.self)")
// This prints: "Class type in for loop: __lldb_expr_195.PersonModel"
genericsTest(with: person)
// This prints: "Class type: BaseModel"
}
So in the for loop if I print the object.self I get the class I expect but in the function with generics do the same call I get the base class.
Edit 1:
So if it would be possible to make Generic C the type of the final model I would be able to create thing like:
func genericsTest<C: BaseModel>(with type: C.Type, and object: C) {
let test = KeyPath<C, Int>
}
Just as example

Have a look at this
Question
You define a array of type 'BaseModel' so in your for loop it basically is saying
for let person: BaseModel in personArray
Making person of type BaseModel
In the function:
func genericsTest<C: BaseModel>(with object: C) {
print("Class type: \(C.self)")
}
You are using the type, in contrary in this code:
print("Class type in for loop: \(model.self)")
You are using the object. If you change the C to object you will get the same result.
Do remember if you going to use this in you project to determine what type of object this is. You need to seriously go back to your drawing board. Because this is not OOP!

Related

Build an array based on another class Constructor

I want to create an array of Custom types in Swift based on another class. How can I pass the array count to the Shop class constructor below?
(count of customer array should be cnt in Shop class init)
class Customer {
var id:Int
var name:String
var balance:Double=500
init(Id:Int,Name:String, Balance: Double) {
id=Id
name=Name
balance=Balance
}
}
class Shop {
let cus:[Customer]
init(cnt:Int) {
//
}
}
I am not sure what is the exact problem. From what I understood, you are simply trying to do something like this:
let customers = [Customer]()
customers.append(Customer(Id: 1, Name: "Mike", Balance: 100))
customers.append(Customer(Id: 2, Name: "Sara", Balance: 150))
customers.append(Customer(Id: 3, Name: "Dan", Balance: 120))
let shop = Shop(cnt: customers.count)
Edit: Based on your comment, you seem to want to populate the cus array in the Shop class based on the passed parameter to the constructor. Which is a weird decision because you want to create an array of a class with multiple properties (Id, Name, Balance) knowing only the count. What data will go to these properties?
I will assume you want to create a fake array of arbitrary Customer class.
class Shop {
var cus: [Customer]
init(cnt: Int) {
cus = [Customer]()
for i in 0..<cnt {
cus.append(Customer(Id: i, Name: "name_\(i)", Balance: Double(i*100)))
}
}
}

Class Array Error - ''Type of expression is ambiguous without more context''

I'm pretty new to coding Swift, so please excuse me if this error can be simply fixed!
I created an array, and created a class for each variable. My class looks like this -
class name {
var Name: name?
}
class subject {
var Subject: subject?
}
class grade {
var Grade: grade?
}
And my Array is the following -
var arr = [
[
name: "Koester",
subject: "Science",
grade: "9A"
],
[
name: "Koester",
subject: "Science",
grade: "9B"
]
]
On the Line 8 of the second block of code, "[" I get an error that says -
"Type of expression is ambiguous without more context"
I've tried checking all the other posts related to this "Type of expression is ambiguous without more context" error, but I couldn't find one that was directly related to a variable array, and it seems like I'm the first one that's getting the error inside the array itself.
Thanks!
What I understood from your question is your requirement is you need a class which should have properties which will store the student data and you need to add that data into Array.
Check below code:
Create a calls For StudentDetail like:
class StudentDetail {
//Create Student Properties
var name: String?
var subject: String?
var grade: String?
//Initialise properties this way
init(name: String, subject: String, grade: String) {
self.name = name
self.subject = subject
self.grade = grade
}
}
Now create a class object like:
var user1Detail = StudentDetail(name: "Koester", subject: "Science", grade: "9A")
var user2Detail = StudentDetail(name: "Koester", subject: "Science", grade: "9B")
Now you can store it this way in Array:
var arr = [StudentDetail]()
arr.append(user1Detail)
arr.append(user2Detail)
Now your arr will have student detail and you can access properties of it.
Hope this will help.

Trying to convert object mapper model to array

I'm new to Swift and have asked a few questions that I was somewhat confused about how to get the correct type for my object, but I believe I've figured it out.
I have a User object that is an Object Mapper model.
I am trying to convert this Object Mapper model to an Array.
https://github.com/Hearst-DD/ObjectMapper
However I am getting the error Cannot specialize a non-generic definition
Here is the variable I am trying to cast:
fileprivate var userDataSource = Mapper<User>().Array<Any>(User)
And an extension for its definition:
extension AccountViewController: GetUserDelegate {
func getUserSuccess(user: User) {
self.userDataSource = User
}
}
The original mapping to a model is done here:
guard let user = Mapper<User>().map(JSONObject: value)
The user class looks like this:
class User: Mappable {
required init?(map: Map) {
}
init() {}
var id: Int?
var firstName: String?
var lastName: String?
var displayName: String?
var image: URL?
var about: String?
var email: String?
var password: String?
var authToken: String?
I can include more if desired.
What am I misunderstanding here? None of the examples I'm seeing for this error really seem to apply to my situation.
Desired ultimate output:
["Name", "Bob Jim"],
["MC #", "1234567"],
["Company Name", "Bob's Truckin"],
["Truck Type", "Flat Bed"],
["Cell", "(555) 555-5555"],
["Dispatch", "(999) 999-9999"],
["Favorite Destinations", "Los Angeles"]
I gather you want an array of Users as the datasource,
I'd declare it like any other array:
var userDataSource = [User]()
or
var userDataSource = Array<User>()
And to update the datasource,
extension AccountViewController: GetUserDelegate {
func getUserSuccess(user: User) {
self.userDataSource.append(user)
}
}

Array is nil after appending to it

I have 2 Types, first is Car with property feature and it of the second type Feature.
I have a property cars: [Car], when I append new car to it, it returns nil.
I have created a sample in the snippet below:
class Car {
let make: String
var features: Feature?
let id: String
init(make: String, features: Feature?, id: String) {
self.make = make
self.features = features
self.id = id
}
}
class Feature {
let convertible: String
init(convertible: String) {
self.convertible = convertible
}
}
var cars: [Car]?
var feature: Feature?
let featureElement = Feature(convertible: "yes")
feature = featureElement
let car = Car(make: "SomeMaked", features: feature, id: "ID")
cars?.append(car)
print(cars)
My question is: Shouldn't the array increase its count after appening to it? What am I missing?
Please note that this code is just sample, so ignore typos and coding convention.
You haven't initialized your array.
Replace your var cars: [Car]? with var cars: [Car]? = []

How to print elements of one class using variable of another class?

I have created two classes a Person class and a food class. Now i am making list of all the food items consumed in a year by a particular person. I want to print all of them separated by commas
Here's my code
class Food {
let name: String
var EatenBy: Person?
init(name: String){
self.name = name
}
}
And my Person class is as follows
class Person {
var name: String
var foods: [Food] = []
lazy var foodNames: () -> String = {
return ""
}
init(name: String){
self.name = name
}
func adopt(food: Food){
foods.append(food)
food.EatenBy = self
}
}
Now i want to create different food items using the Food class and then assign it to a person who have consumed them and store it in an array foods.
var person = Person(name: "Test")
var pasta = Food(name: "pasta")
Can anyone help me out how can i use the objects created using Food class and assign it to a object created in the Person class and append them in the foods array ?
My final aim is to print all the elements in the foods array separating them using commas or spaces or such, which i guess can be easily done by looping each of the element through a for loop ?
Using a Person instance, you can access its properties (including the foods array by the dot syntax: person.foods.
For printing the food names, you should declare foodNames as a computed property. As its return value, just use map to get the names of each Food instance, then use [String].joined(separator: ",") to join the contents of the array of Strings into a single String.
class Person {
var name: String
var foods: [Food] = []
var foodNames:String {
return foods.map{$0.name}.joined(separator: ",")
}
init(name: String){
self.name = name
}
func adopt(food: Food){
foods.append(food)
food.EatenBy = self
}
}
class Food {
let name: String
var EatenBy: Person?
init(name: String){
self.name = name
}
}
let john = Person(name: "John")
let pasta = Food(name: "pasta")
john.foods = [pasta, Food(name: "tuna")] //assign an array to the variable directly
john.foods.append(Food(name: "tomato")) //or append the existing array
john.adopt(food: Food(name: "bread")) //you can also use your _adopt_ function to add an element to the _foods_ array of a _Person_ instance
john.foodNames // "pasta, tuna, tomato,bread"
It looks like you are missing some basic concepts of how Swift works (like using the dot syntax to access class properties), so I would recommend reading The Swift Programming Language Guide as a start, it is a really useful starting point for a beginner Swift developer.

Resources