Hi I am new to swift and writing iOS application. I would like to know how can I better use generics to pass my result data.
I have one delegate which gives data of person from server
protocol PersonDataProvider {
func dataReceived(_ result: PersonResult)
}
Result can have success and fail status through enumeration.
enum PersonResult {
case success
case networkFailed
}
Below class calls server api and fetch data, passes back
class MyNetworkClass {
var personDataProvider: PersonDataProvider
func getDataFromServer() {
personDataProvider.dataReceived(.success)
}
}
Below is viewcontroller where i subscribe provider
class MyViewController: PersonDataProvider {
func dataReceived(_ result: PersonResult) {
switch result {
case .success:
print("success")
case .networkFailed:
print("no network")
}
}
}
Now, I would like send extra information with success block which can be anything like below data model. It may have any type.
class Employer:Person {
let id
let salary
}
class Student:Person {
let id
let rollNumber
}
How can I achieve that? Can i define associate type in protocol and achieve if yes how?
How can I subscriber "MyViewController" of "PersonDataProvider" can define type of result he expects from "success" block?
Use an associated value as part of the .success
case.
Let's say you have a superclass Person for your types of people. Then define your enum like this:
enum PersonResult {
case success(Person)
case networkFailed
}
Now when you hand back a .success
value you must supply a Person to go with it:
func getDataFromServer() {
let person = Student.init(...) // initialize, or fetch, or whatever
personDataProvider.dataReceived(.success(person))
}
Here is a complete fake example that mocks up the whole thing; you'll have to change the details, but the point is, this compiles:
enum PersonResult {
case success(Person)
case networkFailed
}
protocol PersonDataProvider {
func dataReceived(_ result: PersonResult)
}
class MyNetworkClass {
var personDataProvider: PersonDataProvider!
func getDataFromServer() {
let person = Student.init(id: 3, rollNumber: 10)
personDataProvider.dataReceived(.success(person))
}
}
class Person {}
class Employer:Person {
let id : Int
let salary : Int
init(id:Int, salary:Int) {
self.id = id; self.salary = salary
}
}
class Student:Person {
let id : Int
let rollNumber : Int
init(id:Int, rollNumber:Int) {
self.id = id; self.rollNumber = rollNumber
}
}
You have to move away from delegation pattern to achieve something like what you want. Instead you will need a generic enum
of Result type and your networking class
which is generic to your data model type.
enum Result<T> {
case success(T)
case networkFailed
}
class MyNetworkClass<T: Person> {
func getDataFromServer(completion: @escaping (Result<T>)-> Void) {
// here goes your implementation (data fetching)
// for demonstration purpose I just initialized object
if let student = Student(id: 112233, rollNumber: 25) as? T {
completion(Result.success(student))
} else if let employer = Employer(id: 445566, salary: 5200) as? T {
completion(Result.success(employer))
} else {
completion(Result.networkFailed)
}
}
}
Then your usage would be:
MyNetworkClass().getDataFromServer { (result: Result<Student>) in // you can also use Employer type
switch result {
case .success(let student):
print(student.id)
case .networkFailed:
print("Error")
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.