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
...
Related
I have some trouble to init() a class in another class.
I've been looking if I can find a solution in here but I wasn't able to.
If I write super.init() there comes another error because the function isn't existing.
I don't know where I have to initialize it.
I'd prefer to init the Address class in the open init from the Contact class but if I do so I can't access the Address class.
I think that it isn't a big mistake but I'm not able to find it.
open class Contact: Identifiable {
public var id: Int64?
var FirstName: String
var Name: String
var Phone: String
var Mail: String
var Birth: String
var News: Bool
open class Adress: Contact {
var Street: String
var Number: String
var PostalCode: String
var City: String
var Country: String // Error:'super.init' isn't called on all paths before returning from initializer
//If I add the Super.Init() there is an error because Super.Init() isn't existing and I don't know where to create it.
init(Street: String, Number: String, PostalCode: String, City: String, Country: String) {
self.Street=Street
self.Number=Number
self.PostalCode=PostalCode
self.City=City
self.Country=Country
}
}
public init(id: Int64, Firstname: String, Name: String, Phone: String, Mail: String, Birth: String, News: Bool) {
self.id = id
self.FirstName = Name
self.Name = Name
self.Phone = Phone
self.Mail = Mail
self.Birth = Birth
self.News = false
}
}
Technically you could do it this way:
class Contact: Identifiable {
public var id: Int64
var name: String
class Address: Contact {
var street: String
init(street: String, contact: Contact) {
self.street = street
super.init(id: contact.id, name: contact.name)
}
}
public init(id: Int64, name: String) {
self.id = id
self.name = name
}
}
let contact = Contact(id: 1, name: "name")
let address = Contact.Address(street: "street", contact: contact)
But maybe you want to model it in a way that a Contact has an Address:
class Contact: Identifiable {
public var id: Int64
var name: String
var address: Address
public init(id: Int64, name: String, address: Address) {
self.id = id
self.name = name
self.address = address
}
}
class Address: Identifiable {
var id: Int64
var street: String
public init(id: Int64, street: String) {
self.id = id
self.street = street
}
}
let address = Address(id: 1, street: "street")
let contact = Contact(id: 2, name: "name", address: address)
When creating a class, what is the difference between these 2 implementations of creating a custom class in regards to security. They both work the exact same as far as I've used them, but have heard using private variables is the proper way to create classes
class Person {
var name:String
var age:Int
init(name: String, age: Int){
self.name = name
self.age = age
}
}
and
class Person {
private var _name: String
private var _age: Int
var name: String {
set {
self._name = newValue
} get {
return _name
}
}
var age: Int {
set {
self._age = newValue
} get {
return _age
}
}
init(name: String, age: Int){
self._name = name
self._age = age
}
}
private is definitely recommended to use but only in cases when you want that kind of privacy for your properties.
Your second code is not ensuring any kind of privacy. That's just adding more code unnecessarily.
There can be a scenario where you want to keep the write access private to the type (class/struct) and allow read access outside the type. For that you can use private(set) access modifier with your properties, i.e.
class Person {
private(set) var name: String
private(set) var age: Int
init(name: String, age: Int){
self.name = name
self.age = age
}
}
Ok I thought its not a major issue but I am wrong. Currently I am working on a project where I get huge chunk of JSON return. I am fetching those and making my model. Now in my model I am checking if there any value is nil by guard statement. Here is a sample of my model:
import Foundation
import SwiftyJSON
class profileModel {
var _phone_no: String?
var _full_name: String?
var _image: String?
var _email: String?
var _profile_pic: String?
var _rating: Int?
var _dob: String?
var _gender: String?
var _firstName: String?
var _lastName: String?
required init?(phone_no: String, full_name: String, image: String, email: String, profile_pic: String, rating: Int, dob: String, gender: String, firstName: String, lastName: String) {
self._phone_no = phone_no
self._full_name = full_name
self._image = image
self._email = email
self._profile_pic = profile_pic
self._rating = rating
self._dob = dob
self._gender = gender
self._firstName = firstName
self._lastName = lastName
}
convenience init?(json: JSON){
guard let phone_no = json["phone_no"].string,
let full_name = json["full_name"].string,
let image = json["profile_pic"].string,
let email = json["email"].string,
let profile_pic = json["profile_pic"].string,
let rating = json["rating"].int,
let dob = json["dob"].string,
let gender = json["gender"].string,
let firstName = json["first_name"].string,
let lastName = json["last_name"].string else {
print("Profile Detail Model Error")
return nil
}
self.init(phone_no: phone_no, full_name: full_name, image: image, email: email, profile_pic: profile_pic, rating: rating, dob: dob, gender: gender, firstName: firstName, lastName: lastName)
}
}
But how can I prevent crashes when any key is missing from JSON return? Seems like when I check both key & values the class got really really big, there must be some better way.
Making properties optionals is a good approach, however you can take advantage of the new Codable from Swift 4 where you can parse JSON to any data model that conformance to Codable.
In your case you can write the model like this:
class ProfileModel: Codable {
var phone_no: String?
var full_name: String?
var profile_pic: String?
var email: String?
// var profile_pic: String?
var rating: String?
var dob: String?
var gender: String?
var first_name: String?
var last_name: String?
}
And when you need to decode from the server use:
let profile = try JSONDecoder().decode(ProfileModel.self, from: json1)
If you get an array of "Profile" just change the above line to:
let profiles = try JSONDecoder().decode([ProfileModel].self, from: json1)
There is no need to use the library SwiftyJSON any more.
You should have a look at the Codable protocol: The following Playground shows what happens, when you try to parse a Json, that is missing a particular key.
//: Playground - noun: a place where people can play
import Foundation
At first, we create our ProfileModel class and mock a related json.
class ProfileModel: Codable {
//...
var _firstName: String?
var _lastName: String?
}
let profile = ProfileModel()
profile._firstName = "Hans"
profile._lastName = "Peter"
let json = try! JSONEncoder().encode(profile)
Parsing works as expected:
do {
let profileAgain = try JSONDecoder().decode(ProfileModel.self, from: json)
print(profileAgain._firstName) // "Optional("Hans")\n"
print(profileAgain._lastName) // "Optional("Peter")\n"
} catch {
print("something went wrong")
}
So what happens, when we add another property to our class (_phone_no), that is not included in our Json? Nothing really changes, if this new property is optional:
class AnotherProfileModel: Codable {
//...
var _firstName: String?
var _lastName: String?
var _phone_no: Int?
}
do {
let anotherProfile = try JSONDecoder().decode(AnotherProfileModel.self, from: json)
print(anotherProfile._firstName) // "Optional("Hans")\n"
print(anotherProfile._lastName) // "Optional("Peter")\n"
print(anotherProfile._phone_no) // "nil\n"
} catch {
print("something went wrong")
}
But if this property is not an optional, the decoder will throw an error:
class AndYetAnotherProfileModel: Codable {
//...
var _firstName: String?
var _lastName: String?
var _phone_no: Int
}
do {
let andYetAnotherProfileModel = try JSONDecoder().decode(AndYetAnotherProfileModel.self, from: json)
} catch {
print("something went wrong") // "something went wrong\n"
}
I hope this working example will help you, to get a better understanding of the Codable protocol :)
I need to use struct in my custom framework, how can the following be done?
Here my framework:
MyFramework.framework
public class SomeClass : NSObject {
public struct myDetails {
public var firstName: String?
public var lastName: String?
public init(fName: String? LastName lName: String?) {
self.firstName = fName
self.lastName = lName
}//init end
}// struct end
public func fillStruct(inout myStruct: myDetails) {
myStruct.firstName = "John"
myStruct.lastName = "Devid"
}
}// class end
Here's my Application:
// ViewController.swift
import MyFramework
var refObj: SomeClass?
override func ViewDidLoad() {
refObj = SomeClass()
let tempStruct = refObj.myDetails(fName: nil LastName lName: nil) // I CANT ABLE ACCESS LIKE THIS.
refObj.fillStruct(&tempStruct)
}
What I want, is my application to send the data to the struct and my framework to fill the struct and finish the function.
How can this be achieved?
I have an object that might look something like this...
class User {
var username: String?
}
I'd like to have users that could either be a student or a teacher. At the moment, I've added in properties to the User class like this...
class User {
var username: String?
// student properties
var year: Int?
// teacher properties
var department: String?
}
I'm sure I should be using inheritance here, but I'm worried that's going to make the control flow a bit complicated. For example, as part of the login function, I do this...
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.user = User(delegate: self)
appDelegate.user!.load_from_user_defaults()
Or to get something from the current user, I would do this...
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
var username = appDelegate.user.username
How would I use inheritance, in this situation, given that I wouldn't know if it was a teacher or a student logging in? Is it easier to stick to the way I'm doing it?
Some options:
Super/Sub class pattern (inheritance) where you use downcast to check if it is a Student or a Teacher.
Protocols work the same as the Super/Sub pattern but a type can conform to many protocols but can only inherit from one super.
if both Student and Teacher have the same properties you can also just add a property (a bool or an enum) to determine which is which.
create an enum instead of a protocol or super class that has a rawValue of User and has cases for both Teacher and Student. (this is complicated)
Use an enum with associated values.
Every method has it's own benefits and drawbacks.
If you want to be able to pass a User object to different functions in your app, you want to have some conformance/inheritance.
If you have some conformance/inheritance, you can load the user with a method of your choosing and then downcast as such:
if let student = user as? Student {
// do student stuffs
} else if let teacher = user as? Teacher {
// do teacher stuffs
}
Option 1, regular inheritance :
class User {
var username: String?
}
class Student : User {
// student properties
var year: Int?
}
class Teacher : User {
// teacher properties
var department: String?
}
Option 2, conform to instead of inherit from, AKA Protcols :
protocol User : class {
var username: String? { get set }
}
class Student : User {
var username: String?
// student properties
var year: Int?
}
class Teacher : User {
var username: String?
// teacher properties
var department: String?
}
Option 3, UserType property:
enum UserType {
case Student
case Teacher
}
class User {
var username: String?
// student properties, if it is a teacher we leave this blank
var year: Int?
// teacher properties,, if it is a student we leave this blank
var department: String?
var type : UserType?
}
Option 4, enum with User as rawValue:
class User: Equatable,StringLiteralConvertible {
var username: String?
// student properties
var year: Int?
// teacher properties
var department: String?
var type : String?
init(withType type:String) {
self.type = type
}
required convenience init(stringLiteral value: String) {
self.init(withType: value)
}
required convenience init(extendedGraphemeClusterLiteral value: String) {
self.init(withType: value)
}
required convenience init(unicodeScalarLiteral value: String) {
self.init(withType: value)
}
}
func ==(lhs:User,rhs:User) -> Bool {
if lhs.username == rhs.username && lhs.department == rhs.department && lhs.year == rhs.year && lhs.type == rhs.type {
return true
} else {
return false
}
}
enum UserType : User {
case Student = "Student"
case Teacher = "Teacher"
}
let user = UserType.Teacher
Option 5, enum with associated values:
class User {
var username: String?
}
class Student {
// student properties
var year: Int?
}
class Teacher {
// teacher properties
var department: String?
}
enum UserType {
case student(Student)
case teacher(Teacher)
}
"Could be either this or that": In Swift, you use an enumeration. One class for student, one class for teacher, one enumeration for "student or teacher".