각각의 다른 구조를 가진 모델들을 하나의 재활용되는 화면에서 사용하려는 경우 타입 에러가 발생한다
하나의 재활용되는 화면에서 구조가 다른 모델들의 데이터를 보여주는 방법을 찾을 것이다
다음과 같은 두 모델이 존재하고 화면 A와 B의 레이아웃이 동일하다고 가정한다
화면 A에서는 Employee의 이름과 나이를 보여주고 화면 B에서는 Employee 수, Company의 이름, 그리고 CEO 이름을 보여주려고 한다
struct Employee {
let name: String
let age: Int
}
struct Company {
let name: String
let ceo: String
let employee: [Employee]
}
일반적으로 Company의 데이터를 가지고 화면을 만든다면 이런 방법이 될 것이다
struct CardView: View {
let data: Company // Comapny type only
var body: some View {
VStack(alignment: .leading, spacing: 8) {
Text(data.name)
.font(.headline)
HStack {
Label("\(data.employee.count)", systemImage: "person.3")
Spacer()
Label("\(data.ceo)", systemImage: "laurel.leading")
}
.font(.caption)
}
.padding()
}
}
하지만 Employee 타입의 데이터는 사용할 수 없게 된다
그래서 먼저 각각의 모델에 extension을 만들어 데이터에 접근할 수 있도록 한다
extension Employee {
var getName: String {
return name
}
var getAge: Int {
return age
}
}
extension Company {
var getName: String {
return name
}
var getCEO: String {
return ceo
}
var getEmployeeCount: Int {
return employee.count
}
}
예를 들어 Employee에서 getName은 직원의 이름을 Company에서 getName은 회사의 이름을 가져오는 것이다
그래도 여전히 데이터 타입 문제는 해결되지 않는다
이때 우리가 사용할 수 있는 방법은 바로 Protocol 이다
protocol CardViewData {
var getName: String { get }
var getAge: Int? { get }
var getCEO: String? { get }
var getEmployeeCount: Int? { get }
}
모델이 Protocol을 따르게 하면 두 데이터 구조가 달라도 데이터에 접근이 가능하기 때문에 하나의 화면에서 사용될 수 있다
최종적인 모델의 모습은 이렇다
extension Employee: CardViewData {
var getName: String {
return name
}
var getAge: Int? {
return age
}
var getCEO: String? {
return nil
}
var getEmployeeCount: Int? {
return nil
}
}
extension Company: CardViewData {
var getName: String {
return name
}
var getCEO: String? {
return ceo
}
var getEmployeeCount: Int? {
return employee.count
}
var getAge: Int? {
return nil
}
}
Protocol을 따르는 extension을 사용하면 아래와 같이 구조가 다른 두 데이터를 한 화면에서 모두 사용할 수 있게 된다
struct CardView: View {
let data: CardViewData // created protocol
var body: some View {
VStack(alignment: .leading, spacing: 8) {
Text(data.getName)
.font(.headline)
HStack {
if let employeeCount = data.getEmployeeCount {
Label("\(employeeCount)", systemImage: "person.3")
Spacer()
if let ceo = data.getCEO {
Label("\(ceo)", systemImage: "laurel.leading")
}
}
else {
Label("\(data.getAge!)", systemImage: "person.3")
}
}
}
.font(.caption)
.padding()
}
}