Date Planner 라는 플레이그라운드 앱이 있습니다.
고 녀석을 만드는 중인데 일주일이 코앞에 다가오네요.
놀면서 하는 것 같지는 않은데 생각보다 속도가 안나서 속상..
보기 좋은 코드는 협력 효율을 높이고 코드 가독성도 높여준다.
그래서 나는 보기 좋은 코드를 쓰려고 꽤 노력하는 편인데, 그래서 그런가 속도가 나지 않을 때가 있다.
예컨대 @ViewBuilder
를 쓰면 코드 자체의 가독성은 매우 좋아지지만 그 내부로 어떤 데이터를 전달해야 한다면 파라미터 타입을 고민해야 한다.
Any View
, Binding<Optional>
등등 나를 괴롭히던 요상한 타입들이 너무 많았고 어찌어찌 해결은 했다.
아니면 내가 아직 코드에 익숙하지 않은 것일 수도 있다.
코드를 보면서 고민을 더 오래 하다보면 더 쉬운 코드가 생각나곤 했으니까.
며칠 전에 올렸던 sectionHeaderMaker
뷰빌더는 Any 타입을 받고 타입캐스팅 하는 등 나름 독창적인 해결책을 쓰긴 했어도 그 쓰임 자체가 SwiftUI 표준에 걸맞지는 않다.
SwiftUI에는 뷰 프로토콜 구조체를 위해서 Any View
, some View
등의 오팩 타입이 존재하기 때문에 Any를 쓸 일은 없다.
그 이상한 코드는 아래처럼 변했고, 내가 필요로 하는 쓰임을 제대로 갖추고 있다.
섹션 헤더가 ForEach
안에서 돌지 않아야 하며, 유동적으로 새로운 이벤트 뷰를 만들 수 있어야 하고 마지막으로 내용을 셀에 전달할 수 있어야 했다.
@ViewBuilder
private func sectionHeaderMaker(headerTitle: String, startDay: Int, endDay: Int) -> some View {
let arr = Array(eventEnvManager.allEventDictionary.keys.enumerated())
Section {
ForEach(arr, id: \.element) { _, keys in
if let eachEvent = eventEnvManager.allEventDictionary[keys]! {
switch eachEvent.timeDiff {
case startDay ... endDay:
NavigationLink(destination: {
let _ = print("nav :" + "\(eachEvent)")
MakeNewEventView(isNewEventViewShowing: $isNewEventViewShowing,
eachEvent: eachEvent)
}, label: {
EventCellView(eachStruct: eachEvent, reloadToggle: $reloadToggle)
})
default:
EmptyView()
}
}
}
} header: {
Text(headerTitle)
}
}
@Binding
)바인딩 프로퍼티 래퍼는 그 내부에 옵셔널을 받지 않는다.
당연한 이야기지만 프로퍼티 래퍼로 감싸진 Binding<Optional>
변수는 그 뒤에서 옵셔널 체이닝하더라도 Binding<non-Optional>
타입을 받아올 수 없다.
다 좋은데.. 옵셔널 @State
변수를 바인딩해야 할 때가 문제였다.
애시당초 그런 형태의 바인딩은 API로 없었기 때문에 스택오버플로우를 방황해야 했다.
그러나.. 내가 원하는 해결책은 구하지 못했다.
??
연산자를 오버로딩하거나 옵셔널 타입을 확장하거나 하는 등등의 경우의 수가 내가 원하는 기능에서 맞아떨어지지 않았다.
나는 결국.. 옵셔널을 버려야 했고 불필요한 위치에서 구조체를 불필요하게 생성해야 했다.
이 구조체는 앱이 실행되면서 처음으로 생기는 구조체가 되었는데, 이 구조체로 뭔가 할 수 있다면 해볼 생각이다.
@Binding
은 값을 갖고 있지 않다.
그래서 외부에서 하위 뷰의 Binding
에 직접 접근해봤자 값을 가져올 수 없다.
오직 @State
를 포함한 Source of Truth 속성의 프로퍼티 래퍼들만이 실제 값을 갖는다.
며칠 전에 쓴 글에서 데이터의 휘발과 전달에 대해 고민한 적이 있는데, 내가 아직 프로퍼티 래퍼에 대한 이해가 부족해서 생긴 현상으로 보인다.
고민했던 흔적이 정답과 가까운 건.. 호재인가?
그래서 데이터를 직접 보관하다가 전달해야 하는 뷰에서는 @State
, @StateObject
로 코드를 변경했다.
동작은 하는데 조금 더 기능을 추가하다가 보면 뭔가 다시 손대야 할 것 같다.
지금 만들고 있는 이 Date Planner 앱은 아키텍쳐를 갖고 있지 않다.
뷰모델이 없으며 모델도 사실상 이름만 모델이다.
하나의 중앙 모델이 있는 것도 아니며, 분산 모델들도 서로 제대로 된 커뮤니케이션 허브를 갖고 있지 않다.
데이터의 흐름과 뷰가 어떻게 반영되는지를 중점적으로 설계하고 앱을 만들기 시작해야 한다는 생각을 뼈저리게 하고 있다.
설계에 대한 내 이해도 부족에서 오는 어려움이라 생각하는데, 애초에 뭐가 뷰 모델이 되어야 하고 뭐가 뷰에 있어야 하는지를 잘 모르기 때문인 것 같다.
UIKit을 할 때랑은 완전히 다른 아키텍쳐 구성이나 데이터 소통이 아직까지 내게 어려운 듯하다.
UIKit에선 내 마음대로 컴플리션 핸들러도 써 가면서 데이터 옮기고 그랬는데 SwiftUI는 프로퍼티 래퍼가 따로 있고..
다음 앱을 제작하게 된다면 데이터 흐름을 중점적으로 고민하고 계획해서 더 깔끔하게 코딩에 착수해야겠다.
지금은 너무 중구난방으로 코딩하고 있다는 생각이 머리에서 떠나질 않는다.
![]() |
---|
이게 대체 뭐냐 |
221103