SwiftUI View에 ViewModel 여러 개 가지기

글쓴이 연유 날짜

일반적인 경우로 생각할 수 있는지는 모르겠지만, 코딩을 하다보니, 뷰 하나에 기능에 따라 ViewModel을 분기시켜서 연동되게 하고 싶은 경우가 생겼습니다.

보통 스위프트에서 이러한 문제를 풀기 위해서는 Protocol을 참조하게 하거나, Subclassing으로 해결할 수 있을 것 같습니다. 상황에 따라 다르겠지만, Subclassing의 경우에는 기본 구현을 통해 변화를 주기는 편리하지만, Overriding으로 코드가 수정되고, 기능이 추가될 수록 파악하기 어려운 점이 있어서 Protocol을 이용해서 해결해보려고 했습니다. Protocol의 경우에는 인터페이스로 정의한 것만 구현해주면 다른 클래스와의 상호 작용 문제가 없어서 장기적으로 볼 때, 더 깔끔하다고 생각합니다.

Protocol을 이용해 구현

그러나 프로토콜을 이용해서 구현하려고 하니 즉각적으로 몇 가지 문제점을 만날 수 있었습니다.

PropertyWrapper의 사용성이 저하

PropertyWrapper를 Protocol의 정의에 사용할 수 없습니다. 왜냐하면 PropertyWrapper는 직접적으로 우리가 구현하는 게 아니라, 래퍼형 자료를 편의 생성하는 데 도움을 주는 예약어이기 때문입니다. 스위프트 포럼에서도 이야기가 나왔지만, 구체적인 구현 방법은 아직 논의중인 것 같습니다.

차선책으로 Protocol에 value, published, publisher를 모두 선언한다음 구현해주는 방법이 있습니다만, 가독성이 저하될 뿐만 아니라, PropertyWrapper가 제공하는 예약어도 사용할 수 없습니다. @Published property wrapper를 이용해서 많은 publisher를 사용할 수 밖에 없는 SwiftUI 프로그래밍에서 이는 매우 불편합니다.

Observed(State)Object와 EnvironmentObject로 사용

위의 PropertyWrapper 문제를 안고 간다고 하더라도, 근본적으로 SwiftUI 환경에서 사용하기 어려운 부분이 발생합니다. Protocol을 만들고, ObservableObject를 추가적으로 채택해야 하는데,

ObservableObject에서 Self에 대한 규약 조건이 있으므로, 실제 구현을 property로 주는 것이 아닌, protocol을 property로 주는 것이 원칙적으로 불가능하게 됩니다. 함수나 계산 프로퍼티가 아니므로 OpaqueType을 사용할 수도 없습니다.

2번에 더해서 environobject는 아예 프레임워크에서 인젝션해주므로 시도해볼 수도 없다.

더욱이 EnvironmentObject는 외부에서 주입을 받아 사용하게 되기 때문에 해결 방법 조차 고민해보기 어렵습니다.

Subclassing

위의 문제들이 발생했기 때문에 차선책으로 Subclassing을 통해 문제를 해결하려고 했습니다. 다른 문제는 특별히 발견하지 못 했지만, Subclassing시에 EnvironmentObejct 주입을 주의해야합니다. 만약, Subclassing한 오브젝트를 그대로 주입했다면 아래와 같은 에러를 만날 수도 있습니다.

이러한 문제가 발생한다면, 주입할 때, 업캐스팅을 해주면 됩니다.

카테고리: 미분류