뷰모델에서 데이터를 작업하는 과정에서 에러가 발생할 수 있다. 그럴 경우, 뷰에 Alert를 띄워서 사용자에게 에러가 발생했다는 것을 알리려고 한다. 에러가 발생할 수 있는 모든 뷰에 해당 기능을 넣을 것이다. 재사용할 수 있도록 ViewModifier로 만들고 extension에 추가하려고 한다.
익스텐션은 구조체, 클래스, 열거형, 트로토콜 타입에 새로운 기능을 추가할 수 있다. 연산 프로퍼티, 메서드 등을 추가할 수 있다.
*protocol: 프로토콜은 특정 역할을 수행하기 위한 메서드, 프로퍼티, 기타 요구사항 등의 청사진을 정의한다.
Apple Developer Documentation - modifier
Applies a modifier to a view and returns a new view.
뷰에 modifier를 적용하고 새로운 뷰를 반환한다.
Apple Developer Documentation - ViewModifier
A modifier that you apply to a view or another view modifier, producing a different version of the original value.
뷰나 또다른 뷰 modifier에 적용해서 기본 값과 다른 버전을 만든다. 재사용할 modifier를 만들어 뷰에 적용할 수 있다.
struct BorderedCaption: ViewModifier {
func body(content: Content) -> some View {
content
.font(.caption2)
.padding(10)
.overlay(
RoundedRectangle(cornerRadius: 15)
.stroke(lineWidth: 1)
)
.foregroundColor(Color.blue)
}
}
뷰에 바로 적용할 수도 있지만, 기본적으로 modifier(_ :)를 사용해 뷰에 대한 익스텐션을 정의한다.
extension View {
func borderedCaption() -> some View {
modifier(BorderedCaption())
}
}
아래와 비슷하게 캡션을 적용할 수 있다.
Image(systemName: "bus")
.resizable()
.frame(width:50, height:50)
Text("Downtown Bus")
.borderedCaption()
재사용할 ErrorAlertModifier를 만들었다. 그리고 isPresented와 message 텍스트를 받는다. 에러 메세지를 확인하는 용도로 버튼은 .cancel 스타일로 하나를 넣었다. isPresented는 Binding타입이어야 함에 주의하자.
struct ErrorAlertModifier: ViewModifier {
var isPresented: Binding<Bool>
let message: String
func body(content: Content) -> some View {
content.alert(isPresented: isPresented) {
Alert(title: Text("Error"),
message: Text(message),
dismissButton: .cancel(Text("OK")))
}
}
}
뷰에 showErrorMessage 메서드를 추가한다. 이 함수는 showAlert과 message를 받아 자신에 ErrorAlertModifier를 추가한다.
extension View {
func showErrorMessage(showAlert: Binding<Bool>, message: String) -> some View {
self.modifier(ErrorAlertModifier(isPresented: showAlert, message: message))
}
}
뷰모델에서 Alert를 띄울지에 대한 showErrorAlert와 에러메세지를 저장하는 errorMessage 변수들을 만든다. 에러가 발생했는지(값이 변경되었는지) 감시해야 하므로 @Published를 추가한다.
@Published var showErrorAlert = false
@Published var errorMessage = ""
그리고 에러가 발생한다면 error의 메세지를 errorMessage에 저장한다. 뷰모델 내에서 파이어베이스에서 현재 유저를 fetch하는 함수 코드 예시이다. error가 발생시에 showErrorAlert를 true로 저장하고 에러 메세지도 저장하였다.
func fetchUser() {
guard let uid = userSession?.uid else { return }
COLLECTION_USERS.document(uid).getDocument { snapshot, error in
if let (errorMessage) = error?.localizedDescription {
self.showErrorAlert = true
self.errorMessage = errorMessage
return
}
guard let user = try? snapshot?.data(as: User.self) else { return }
self.currentUser = user
}
}
뷰에서 showErrorMessage 익스텐션을 사용한다. showErrorMessage에 이전에 저장한 showErrorAlert와 errorMessage를 넣는다.
struct LoginView: View {
@EnvironmentObject var viewModel: AuthViewModel
var body: some View {
NavigationView {
...
}
.showErrorMessage(showAlert: $viewModel.showErrorAlert, message: viewModel.errorMessage)
}
}
에러가 발생하면 해당 에러 메세지를 띄우는 것을 확인할 수 있다.