FamilyActivityPicker: Crash Handling #3

DoyleHWorks·2025년 4월 5일

FamilyActivityPicker: Crash Handling in UIKit context #2 에서 살펴본대로, .familyActivityPicker 메서드를 활용하면 우회방법을 통한 Crash Handling을 구현하기가 어려울 것 같아서, 새로 SwiftUI 뷰를 구성해주기로 했다. 해보니까 생각보다 어렵지 않았다.

구현 코드

import SwiftUI
import FamilyControls

struct FamilyActivityPickerView: View {
    
    @Environment(\.dismiss) private var dismiss
    @Binding var selection: FamilyActivitySelection
    @State private var tempSelection: FamilyActivitySelection
    
    init(selection: Binding<FamilyActivitySelection>) {
        self._selection = selection
        self._tempSelection = State(initialValue: selection.wrappedValue)
    }
    
    var body: some View {
        VStack(spacing: 0) {
            HStack {
                Button("취소") {
                    dismiss()
                }
                Spacer()
                Button("저장") {
                    selection = tempSelection
                }
            }
            .padding()
            .background(Color(UIColor.systemBackground))
            .foregroundStyle(.white)
            
            FamilyActivityPicker(selection: $tempSelection)
        }
        .ignoresSafeArea(edges: .bottom)
    }
}

tempSelection을 물게 했더니, 항목을 선택했을 때 바로 닫혀버리는 문제가 사라졌다.

FamilyActivityPicker가 UIKit Context에서도 잘 작동하도록 이와 같이 구성해주었다.
이를 불러오는 부분은 아래와 같이 구현되어있다:

    private func appPicker() {
        // UIKit 기반인 ViewController에서 SwiftUI 기반의 View를 불러오기 위한 임시 변수
        let tempSelectionBinding = Binding<FamilyActivitySelection>(
            get: { [weak self] in self?.familyActivitySelection ?? .init() },
            set: { [weak self] in self?.familyActivitySelection = $0 }
        )
        
        let hostingVC = UIHostingController(
            rootView: FamilyActivityPickerView(selection: tempSelectionBinding)
        )
        
        // hostingVC 설정 갱신을 통해 실제 구현
        let selectionBinding = Binding<FamilyActivitySelection>(
            get: { [weak self] in self?.familyActivitySelection ?? .init() },
            set: { [weak self] newSelection in
                self?.familyActivitySelection = newSelection
                self?.viewModel.blockListRelay.accept(newSelection)

                hostingVC.dismiss(animated: true)
            }
        )
        
        let pickerView = FamilyActivityPickerView(selection: selectionBinding)
        hostingVC.rootView = pickerView
        
        hostingVC.modalPresentationStyle = .overFullScreen
        hostingVC.view.backgroundColor = .clear
        
        self.isFamilyActivityPickerPresented = true
        self.present(hostingVC, animated: true, completion: nil)
    }

새로 발생한 문제점들:
1. 취소와 저장 버튼에 뒤따라오는 FamilyActivityPicker가 늦어서 끊어져보이는 문제 (Opal은 로딩스크린을 집어넣어서 해결함)
2. 불러왔을 때 애초에 크래쉬가 발생한 경우: 기존 사용하려던 우회방법을 사용하면 해결되기는 하는데.. UX 측면에서 상당히 나쁠 것 같다.

새로 발생한 문제는 별개로 해결한다 치고, 이제 Crash Handling을 적용해주면 된다. 기존에 참고한 자료에 따르면 ZStack으로 화면 뒤편에 미리 알럿창을 띄워두고 1초마다 업데이트 해주는 우회방법을 사용하면 되는데..

그 전에 디버깅을 하다가 발견한 오류에 조금 주목해보기로 했다.

어쩌면 FamilyControls.ActivityPickerRemoteViewError 라는 녀석을 감지만 할 수 있다면 트리거로 사용할 수 있지 않을까? Opal 개발자는 저걸 발견해서 사용하고 있을지도 모른다.

profile
Reciprocity lies in knowing enough

0개의 댓글