티스토리 뷰
최종 프로젝트를 진행하다가 위 사진처럼
알림 탭에 예약내역과 사장님의 공지사항을 한 번에 보여줘야하는 상황이 생겼는데
2가지 다른 타입의 Model을 합쳐 하나의 ForEach문에 돌릴 때 어떤 Hashable을 사용해야할 지 몰라 에러가 났다.
구글링을 해보니... Swift에서 직접적으로 해시 테이블을 구현해서 사용할 일이 거의 없다고ㅎㅎ
그런데 사용할 필요가 없지만 커스텀하여 사용했다.ㅋㅋㅋㅋㅋㅋ
왜 사용했을까를 곰곰히 생각해보다가 Hashable이라는 것 자체를 잘 몰라서
그 땐 이게 최선이었던 것 같다.
우선 Hash라는 것부터 알아봐야겠다.
1. 해쉬란 데이터를 다루는 기법 중 하나
2. 데이터를 간단한 숫자로 표현
3. 임의의 크기를 가진 데이터(Key)를 고정된 크기의 데이터(Value)로 변화시켜 저장
4. 2개의 데이터를 비교할 때, 데이터가 동일하면 각 데이터의 해쉬값 동일
(해쉬 값이 동일하다고 데이터가 동일하다는 참이 아닐 수도 있다고 한다. 해쉬 값은 일정 크기의 Int값으로 유한하기 때문이다.)
.
.
.
오.. 데이터를 비교하기 위해 혹은 저장하기 위해 사용하는 거구나...
그럼 Hashable은?
공식문서에는 정수 해쉬 값을 생성하기 위해 Hasher로 해쉬될 수 있는 유형이다.
또한 Hashable은 Equatable이라는 프로토콜을 채택하고 있네?!
그래서 값 비교가 가능했구나...
일단 그래서 초기에 코드를 짤 때에는 시간이 촉박해서
우선 되게만 하려고 Hashable을 커스텀해서 사용을 했는데
지금 여유가 있을 때 다시 생각해보니 이미 Hashable 자체가 이 역할을 해주는 거여서 커스텀할 필요가 아예 없었는데
커스텀할 시간에 조금 더 차분히 살펴볼 걸 그랬다...
삽질과 나의 한심함을 느꼈다.
그래서 코드를 찬찬히 살펴보니 ReservationModel에는 Hashable이 채택되어있지 않네?ㅎㅎㅎㅎㅎ
덕분에 Hashable이 이런 식으로 구성되어있구나를 알아서 기록해본다.
커스텀하기 위해 식별할 수 있게 Identifiable, 그리고 Hashable 채택한 TestProtocol을 만들고
비교할 수 있게.. static func == (lhs: Self, rhs: Self) -> Bool { } 를 만들었네유...
근데 여러분은 삽질의 시간을 보는 것일 뿐... 바로 Hashable을 가져다쓰면 됩니다.
protocol TestProtocol: Identifiable, Hashable {
var classification: String { get set }
var id: String { get set }
// 예약 현황
var shopId : String { get set }
var userId : String { get set } // 이메일 형식으로 들어옴
var reservedTime : Date { get set }
var state : String { get set }
// 샵 노티스 공지 사항
var category : String { get set }
var shopName : String { get set }
var date : Date { get set }
var title : String { get set }
var body: String { get set }
static func == (lhs: Self, rhs: Self) -> Bool
}
extension TestProtocol {
static func == (lhs: Self, rhs: Self) -> Bool {
return lhs.id == rhs.id
}
}
이렇게 만들어주고 ShopNotice와 ReservationModel을 합치기 위한... Model 작업을 해준다.
물론 이 MergedType도 Hashable을 통해 값 비교도 하고,, 데이터를 저장하기도 하고... 해야한다.
각각의 모델들은 다르고 값이 없을 수도 있기 때문에 옵셔널 처리할거다.
struct MergedType: Hashable {
let shopNotice: ShopNotice?
let reservation: ReservationModel?
let type: String
let date: Date
func calculateTime(_ date: Date) -> String {
let format = DateFormatter()
format.locale = Locale(identifier: "ko_KR")
format.dateFormat = "M월 d일"
return getTimeName(Int(Date().timeIntervalSince(date)), format.string(from: date))
}
func getTimeName(_ time: Int, _ date: String) -> String {
let result = time / 60
switch result {
case 0:
return "방금"
case 1 ... 59:
return "\(result)분 전"
case 60 ... 1439:
return "\(result / 60)시간 전"
default:
return date
}
}
}
func mergedArray() -> [MergedType] {
var result: [MergedType] = []
for notice in filteredMyNotice() {
result.append(MergedType(shopNotice: notice, reservation: nil, type: "notice", date: notice.date))
}
for reservation in filteredMyReservation() {
result.append(MergedType(shopNotice: nil, reservation: reservation, type: "reservation", date: reservation.reservedTime))
}
return result.sorted(by: {$0.date > $1.date} )
}
=================================================================================
하지만 이렇게 안써도 되고 아래 Model로 끝났다.
struct MergedType: Hashable {
let shopNotice: ShopNotice?
let reservation: ReservationModel?
let type: String
let date: Date
func calculateTime(_ date: Date) -> String {
let format = DateFormatter()
format.locale = Locale(identifier: "ko_KR")
format.dateFormat = "M월 d일"
return getTimeName(Int(Date().timeIntervalSince(date)), format.string(from: date))
}
func getTimeName(_ time: Int, _ date: String) -> String {
let result = time / 60
switch result {
case 0:
return "방금"
case 1 ... 59:
return "\(result)분 전"
case 60 ... 1439:
return "\(result / 60)시간 전"
default:
return date
}
}
}
func mergedArray() -> [MergedType] {
var result: [MergedType] = []
for notice in filteredMyNotice() {
result.append(MergedType(shopNotice: notice, reservation: nil, type: "notice", date: notice.date))
}
for reservation in filteredMyReservation() {
result.append(MergedType(shopNotice: nil, reservation: reservation, type: "reservation", date: reservation.reservedTime))
}
return result.sorted(by: {$0.date > $1.date} )
}
다시 깔끔해진 나의 코드들
쓴 코드들을 왜 리팩토링을 해야하는 지 알겠다.
이 2개로 깔끔하게 끝날 수 있었다...
'에러 해결 일지 > 프로젝트 에러 해결' 카테고리의 다른 글
네트워크 통신 때 Escaping Closure의 역할.. 조금은 알 것도 같다. (0) | 2023.04.17 |
---|---|
Realm 갈아치우기 (0) | 2023.03.03 |
파이어베이스 스토리지에 사진이 업로드 및 다운로드 안될 때 방법 2가지 (0) | 2023.01.13 |
Cannot assign to property: 'self' is immutable (0) | 2022.12.05 |
SwiftUI: The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions (0) | 2022.12.05 |