Best approach to make class design if we have two class as bellow : - swift

Best approach to make class design if we have two class as below:
class Teacher {
var name
var age
var TechId
}
class Student {
var name
var age
var StdID
}
I try it using that :
class Person {
var name
var age
}
class Student : Person {
var StdID
}
class Teacher : Person {
var TechID
}
But now problem is that student become teacher and vice versa.
Can you any one provided best solutions for that using Swift?

You said:
But now problem is that student become teacher and vice versa.
If you can change back and forth like this, I'd first suggest a concrete and distinct object to capture what precisely is transitioning from one to the other:
class Person {
var name: String
var age: Int
}
Note, in your example, you are considering this Person to be what other languages consider to be an abstract/virtual class. But I'm suggesting that you want to make this a concrete object (the actual person) that you instantiate.
The question then becomes how you represent "student" and "teacher". One simple pattern is to consider it a question of membership in some relevant collection:
typealias StudentID = String
var students: [StudentID: Person]
typealias TeacherID = String
var teachers: [TeacherID: Person]
In that case, transitioning from a student to a teacher (or vice versa) is merely a question of adding/removing from the appropriate dictionaries.
The above is a bit constrained, though. For example, what if you wanted to keep track of more student-specific properties (e.g. enrollment date, etc.) or teacher-specific properties (e.g. hire date, social security number, annual salary, etc.). This suggests you might want specific types for these student and teacher types:
class Student {
let studentID: String
let person: Person
}
class Teacher {
let teacherID: String
let person: Person
}
And then your students and teachers collections become simple arrays:
var students: [Student]
var teachers: [Teacher]
But by making the Person a property (rather than a base class), if you know which person is associated with a particular "student id", you can now associate that person with a particular "teacher id", too. But the idea is the same, it's merely a question of membership in the appropriate collection/type, not an issue of trying to change the inherent type of the person.
The alternative is a protocol oriented pattern:
protocol Person {
var name: String { get }
var age: Int { get }
}
struct Student: Person {
let studentID: String
var name: String
var age: Int
init(name: String, age: Int) {
studentID = UUID().uuidString
self.name = name
self.age = age
}
init(person: Person) {
self.init(name: person.name, age: person.age)
}
}
struct Teacher: Person {
let teacherID: String
var name: String
var age: Int
init(name: String, age: Int) {
teacherID = UUID().uuidString
self.name = name
self.age = age
}
init(person: Person) {
self.init(name: person.name, age: person.age)
}
}
This captures your notion that Person is an abstract type that simply conforms to having certain properties. It avoids any ambiguity that Person is not a type, itself, but merely a protocol to which types have to conform. You can only instantiate concrete Student and Teacher objects.
But then, if you want to create a Teacher from a Student, you can do:
let fred = Student(name: "Fred", age: 22)
let teacher = Teacher(person: fred)
Note, though, that this doesn’t “change” fred into a Teacher, but rather creates a new Teacher whose Person properties are copies of those of fred.

For Swift, I would recommend something like this:
protocol Person {
var name: String { get }
var age: Int { get }
}
struct Teacher: Person {
let id: Int
let name: String
let age: Int
}
struct Student: Person {
let id: Int
let name: String
let age: Int
}
Use a Protocol to define the person. And use a Struct for Teacher and Student because once created you would not change their details as a Struct is immutable. Both Teacher and Student conform to Person protocol.
To test if a person is a Teacher or Student, you could do this:
func test(person: Person) {
switch person {
case is Teacher:
print("teacher")
case is Student:
print("student")
default:
preconditionFailure("Unknown person type")
}
}

Related

Inheritance from non-protocol type 'View' SwiftUI + UIKit [duplicate]

I need to use two classes with the same name in swift 5. For this, I have created those two classes in two different modules, but I am confused on how to use both the classes in an UIViewController
one of my class is Person which is in models > student module and another class is Person with is in the models module
I have tried importing class like
import class models.student.Person
class BookViewController: UIViewController {
var students:[Person] = [] //it should call models.student.Person
var people: [Person] = [] //it should call models.Person
...
but above Person class is pointing to models.Person only, It is not pointing to models.student.Person
Person class in models > Person.swift is
import Foundation
// MARK: - Person
public struct Person: Codable {
public let firstName: String?
public let lastName: String?
public let address: String?
public let phone: String?
enum CodingKeys: String, CodingKey {
case firstName = "first_name"
case lastName = "last_name"
case address = "address"
case phone = "phone"
}
public init(firstName: String?, lastName: String?, address: String?, phone: String?) {
self.firstName = firstName
self.lastName = lastName
self.address = address
self.phone = phone
}
}
and the models.student.Person.swift is
import Foundation
// MARK: - Person
public struct Person: Codable {
public let fullName: String?
public let educationalQualification: String?
public let college: String?
enum CodingKeys: String, CodingKey {
case fullName = "full_name"
case educationalQualification = "educational_qualification"
case college = "college"
}
public init(fullName: String?, educationalQualification: String?, college: String?) {
self.fullName = fullName
self.educationalQualification = educationalQualification
self.college = college
}
}
I need both the class in my BookViewController
I can't change the name of the class to a different one, I should use the same class name.
You could try typealias:
typealias StudentPerson = yourStudentFramework.Person
and then use it like:
import yourStudentFramework
class BookViewController: UIViewController {
var students:[StudentPerson] = [] //it should call yourStudentFramework.Person
var people: [Person] = [] //it should call models.Person
...

How to convert one struct to another with same variables Swift (iOS)?

I need help with converting one object to another. Might have searched 10-20 website didn't find any good answer.
public struct UniversityJoinChatViewModel {
public let id: Int?
public let name: String?
public init(nameOfModel model : UniversityGroupChatItem?) {
self.id = model?.id;
self.name = model?.name;
}
}
public struct UniversityGroupChatItem: Codable {
public let id: Int?
public let name: String?
public init(id: Int?, name: String?) {
self.id = id
self.name = name
}
}
I did this:
let say I have value UniversityGroupChatItem in variable universityGroupChatItem and my universityGroupChatItem contains is not nil and contains value. I tried this it did not work.
universityJoinChatViewModel = (universityGroupChatItem) as! UniversityJoinChatViewModel
The app crashed.
Then I tried:
map and
compactmap
None worked.
I am not getting how to convert UniversityGroupChatItem struct to UniversityJoinChatViewModel struct.
I do not understand how to convert one struct to another struct both has same number name variables.
You can't force cast one object into another (no matter struct, class, enum ect.) even if they are fully the same inside
You need to implement inits where one object takes fields from another one.
map is function to sequences, if You have only 1 object just init it with another one
Examples:
public struct UniversityJoinChatViewModel {
public let id: Int?
public let name: String?
public init(nameOfModel model : UniversityGroupChatItem?) {
self.id = model?.id;
self.name = model?.name;
}
}
public struct UniversityGroupChatItem: Codable {
public let id: Int?
public let name: String?
public init(id: Int?, name: String?) {
self.id = id
self.name = name
}
}
let universityGroupChatItem = UniversityGroupChatItem(id: 0, name: "name")
let universityJoinChatViewModel = UniversityJoinChatViewModel(nameOfModel: universityGroupChatItem)
let groupArray = Array(repeating: universityGroupChatItem, count: 10)
let joinArray = groupArray.map(UniversityJoinChatViewModel.init(nameOfModel:))
In your case, you already have the constructor that can help you to achieve what you want, so instead of trying to cast the object, create a new one:
universityJoinChatViewModel = UniversityJoinChatViewModel(nameOfModel: universityGroupChatItem)

Creating a unique ID for an object in YapDatabase

Suppose I have a collection of Users in a YapDatabase. How do I assign each user a unique identifier upon creation?
struct User: Codable{
public var name: String
private var id: ???
}
I've seen UUID in the Apple developer documentation. Having done some reasearch, it seems more for use in distributed systems, athough I suppose it could work. Are there other alternatives? Is there a "standard" method for giving objects a unique id in a relational database?
Possible variants:
1) String (unique everywhere & forever)
struct User: Codable{
public var name: String
private var id: String // = UUID().uuidString
}
2) Int (unique inside your users context, enough in majority of cases)
struct User: Codable{
public var name: String
private var id: Int = 0 // = lastUser.id + 1, where 0 is a new non stored
}

Why does accessing a String parameter of a subclassed NSManagedObject with a parent relationship crash?

I have generated classes for two core data entities. The first is called Address and is an abstract entity. The second is called Person, and it inherits from Address. I've added a few example managed attributes for the purpose of this test. And i've added a non-managed String property to the Person class. Accessing the string property of the Person class will crash. Why does this crash?
The Address and Person classes are automatically generated by Xcode, with the exception of the extra parameter: let foo = "Foo"
If i modify the code to make Person inherit from NSManagedObject directly instead of Address, then the code works and doesn't crash.
Automatically generated Address class:
#objc(Address)
public class Address: NSManagedObject {
}
extension Address {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Address> {
return NSFetchRequest<Address>(entityName: "Address")
}
#NSManaged public var street: String?
#NSManaged public var city: String?
}
Automatically generated person class with the exception of the "foo" parameter:
#objc(Person)
public class Person: Address {
public let foo = "Foo" //added this parameter
}
extension Person {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Person> {
return NSFetchRequest<Person>(entityName: "Person")
}
#NSManaged public var name: String?
}
problem code
let person = Person(context: context)
print(person.foo) //doesn't crash, but prints empty line instead of value
print("VALUE:\(person.foo):") //crashes with Thread 1: EXC_BAD_ACCESS (code=1, address=0x18)
UPDATE:
if foo is defined as
public let foo: String? = "Foo"
then the print statements don't crash, instead they interpret the value as 'nil' and print that.
So my question becomes: Why is this value which is assigned as a constant being reset to nil under the covers?
I have two hand-waving explanations why you are getting nil:
Managed objects don't function very well until they are inserted.
Your foo is a what I would call a constant stored property. I made up the name because, red flag, I cannot find any examples of it in the Swift book chapter on Properties
Put these two together and you get an edge case that doesn't work.
That being said, I'm kind of surprised that your foo setting does not work, because foo is not a managed property (that is, it is not in the data model). If I make such a constant stored property in a regular, non-managed object…
public class Animal {
public let foo: String! = "Foo"
}
it reads back later as expected.
So, if you can accept that this edge case just doesn't work in Core Data, you can move on to several more normal ways that do work.
One way is to declare foo as a var and assign a value to in awakeFromInsert() which is, as I alluded to earlier, after insertion. In Core Data, awakeFromInsert() is one of your friends…
#objc(Person)
public class Person: Address {
public var foo: String!
override public func awakeFromInsert() {
foo = "Foo"
}
}
Another way that works is as a computed property…
#objc(Person)
public class Person: Address {
public var foo : String { return "Foo" }
}
And, finally, the most logical way, since foo is constant for all instances, is to make it a type property…
#objc(Person)
public class Person: Address {
static var foo: String = "Foo"
}
but of course if you do this you must reference it as Person.foo instead of person.foo.

swift create record and add record into relationship

I working with swift 4 for macOS.
I use core data with two entities:
Person (in relationship with Book)
Book (in relationship with Person)
NSManagedObject Person
import CoreData
#objc(Person)
public class Person: NSManagedObject {
#NSManaged public var firstName: String!
#NSManaged public var books: NSSet
}
NSManagedObject Book
import CoreData
#objc(Book)
public class Book: NSManagedObject {
#NSManaged public var bookName: String!
#NSManaged public var date: DateString!
#NSManaged public var person: Person?
}
with this code i create a new person record:
let entity = NSEntityDescription.entity(forEntityName: "Person", in: context)
let newPerson = NSManagedObject(entity: entity!, insertInto: context) as! Person
newPerson.firstName = "Max"
do {
try context.save()
} catch {print(error)}
now i would like to create directly a new record into Book if i create a new person record => bookName="Test" , date=Date()
is this possible with my used code newPerson.books = ?
or have i use the same code "new person record" (little bit modified) to create a book record?
You can't make the new Book creation automatic. You need to have code that creates the instance. You have a couple of options:
Do it just like your code above. Any time you create a new Person, follow it with code that creates a Book.
Override awakeFromInsert on your Person class. In your override, add the code to create a new Book. Now you can create a new Person using your current code, and each new Person will create a new Book when you first create it.