Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- 리팩토링
- UITextView
- SWIFT
- collectionview
- ribs
- tableView
- Clean Code
- swiftUI
- Human interface guide
- Refactoring
- RxCocoa
- Xcode
- uitableview
- 리펙터링
- rxswift
- 클린 코드
- uiscrollview
- clean architecture
- 리펙토링
- Protocol
- UICollectionView
- 스위프트
- ios
- 애니메이션
- HIG
- Observable
- combine
- map
- swift documentation
- MVVM
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] protocol 심화 - (protocol 응용, existential any, generics, cannot conform to 'Hashable', 의존성 분리) 본문
iOS 응용 (swift)
[iOS - swift] protocol 심화 - (protocol 응용, existential any, generics, cannot conform to 'Hashable', 의존성 분리)
jake-kim 2023. 4. 30. 23:53existential any 개념
- protocol에서 generic을 지정해줄 때 any 키워드를 사용하여 편리하게 generic 처리를 할 수 있는 것
protocol ABC {
associatedtype MyType
}
// 기존 - 제네릭 선언해야함
func printValue<T: ABC>(type: T) {
print(type)
}
// any 키워드 사용 - 제네릭을 따로 선언해주지 않아도 됨
func printValue2(type: any ABC) {
print(type)
}
protocol 타입 다루기
- DIP원칙과 테스트에 용이한 구성을 위해서 데이터 모델을 protocol로 참조되게 설정
ex) 모델이 MyItem이고, 이 모델은 MyItemable 프로토콜을 따르게 한 후 사용하는쪽에서는 MyItemable 프로토콜로만 참조되게 구현
protocol MyItemable: Hashable {
var id: UUID { get }
var value: Int { get }
}
struct MyItem: MyItemable {
let id = UUID()
let value: Int
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
}
- 만약 UITableViewDiffableDataSource를 사용한다면, 이 안에 Hashable 타입을 넣어서 초기화가 필요
- 프로토콜을 따르지 않게하면 아래처럼 MyItem하면 되지만 protocol로 표현하고 싶은 경우?
import UIKit
final class MyDataSource: UITableViewDiffableDataSource<Int, MyItem> {
}
- protocol로 변경한 경우 에러 발생
final class MyDataSource: UITableViewDiffableDataSource<Int, MyItemable> { // Error!
}
- 사전지식) MyItemable로 넣었지만 암묵적으로 any MyItemable로 선언됨
- any로 선언되는 이유는 hashable안에 있는 Hasher의 combine 타입이 제네릭이라 any가 필요한 것
- 문제) MyItemable은 분명 정의하는 곳에서 Hashable을 따르게 했지만, UITableViewDiffableDataSource에서 사용하면 Hashable을 준수하지 않다고 표출
- 프로토콜을 타입으로 사용할 때, 정의할 때 당시 사용했던 protocol (Hashable)은 적용되지 않는 버그가 존재
protocol MyItemable: Hashable {
var id: UUID { get }
var value: Int { get }
}
- 해결책
- 현재 MyItemable 부분에 구현체를 넣어주어야 하는 상황
- Hashable만을 따르고 있는 AnyHashable을 준수하는 Wrapper를 만들어서 그 안은 MyItemable을 가지고 있도록 구현하면 완료
import UIKit
enum MyItemableWrapper: Hashable {
case item(any MyItemable)
func hash(into hasher: inout Hasher) {
switch self {
case let .item(item):
hasher.combine(item.id)
}
}
static func == (lhs: Self, rhs: Self) -> Bool {
switch (lhs, rhs) {
case let (.item(lhsItem), .item(rhsItem)):
return lhsItem.id == rhsItem.id
}
}
}
final class MyDataSource: UITableViewDiffableDataSource<Int, MyItemableWrapper> {
}
- 사용하는 쪽 - 구체적인 타입 의존없이 프로토콜을 wrapping하고 있는 타입 그대로 사용 가능
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let tableView = UITableView()
let dataSource = MyDataSource(tableView: tableView) { tableView, indexPath, itemIdentifier in
switch itemIdentifier {
case let .item(item):
print(item.value)
}
return UITableViewCell()
}
}
}
* 전체 코드: https://github.com/JK0369/ExAny
'iOS 응용 (swift)' 카테고리의 다른 글
Comments