Apple Developer Documentation
developer.apple.com
5. Handling Errors
앱을 개발하는 동안 구문 또는 의미론적 오류가 발생하여 수정되었을 수 있다. 예를 들어 네트워크 연결이 끊어져서 파일에서 데이터를 읽는 것을 실패할 수 있다. 이런 문제가 발생했을 때 안내를 제공해서 오류를 해결하는 방법에 대해 알아보자!
Section 1. Add an Error Wrapper Structure
항상 작업을 완료하거나 출력을 생성하는 기능이 있을 수 있다. 파일에 읽기 권한이 없거나 지정한 경로에 없을 수도 있다. 아니면 인코딩 형식이 올바르지 않을 수도 있다.
작업이 실패했을 때 무엇이 오류를 야기했는지 사용자에게 이해시켜주는 것이 도움이 된다.
import Foundation
struct ErrorWrapper: Identifiable {
let id: UUID
let error: Error
let guidance: String
init(id: UUID = UUID(), error: Error, guidance: String) {
self.id = id
self.error = error
self.guidance = guidance
}
}
Model 그룹에 ErrorWrapper 를 생성하고 고유한 id 값을 제공하기 위해 Identifiable을 추가한다.
오류 프로토콜을 사용해서 오류 처리 속성을 명시적으로 할당할 수 있다. 그리고 init 을 한다.
Section 2. Create an Error View
사용자에게 에러를 보여주고 설명해주는 view를 생성해보자.
struct ErrorView_Previews: PreviewProvider {
enum SampleError: Error {
case errorRequired
}
static var wrapper: ErrorWrapper {
ErrorWrapper(error: SampleError.errorRequired,
guidance: "You can safely ignore this error.")
}
static var previews: some View {
ErrorView(errorWrapper: wrapper)
}
}
가끔 SwiftUI previews를 효과적으로 사용하기 위해서 리얼한 샘플 데이터가 필요할 수 있다. 그래서 이렇게 preview에 sample 에러를 넣어준다.
우선 샘플 에러를 보내기 위한 enum을 만들고, errorRequired property를 사용해 에러를 초기화하는 정적 프로퍼티를 만든다. 그 다음 값을 preview에 전달한다.
struct ErrorView: View {
let errorWrapper: ErrorWrapper
var body: some View {
VStack {
Text("An error has occurred!")
.font(.title)
.padding(.bottom)
Text(errorWrapper.error.localizedDescription)
.font(.headline)
Text(errorWrapper.guidance)
.font(.caption)
.padding(.top)
Spacer()
}
.padding()
.background(.ultraThinMaterial)
.cornerRadius(16)
}
}
error는 localizedDescription을 제공한다.
Section 3. Report Errors
모달을 통해서 앱 화면의 흐름을 방해하는 것은 좋지 않지만 에러는 예외이다. 데이터 바인딩을 사용해서 오류 보기의 모달 표시를 트리거한다.
struct ErrorView: View {
let errorWrapper: ErrorWrapper
@Environment(\\.dismiss) private var dismiss
var body: some View {
NavigationView {
...
}
...
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("Dismiss") {
dismiss()
}
}
}
}
}
@Enviroment 프로퍼티를 사용함으로써 view의 environment stores를 읽을 수 있다. view의 presentation 모드나 scene phase,visibility, color scheme 같은..
이 케이스에서는 view의 dismiss structure을 허용하고 view를 dismiss하기 위한 함수같은 것을 부른다.
ToolbarItem의 Button안에 dismiss를 추가한다. dismiss 는 구조체이다. 만약 구조체가 callAsFunction()을 포함한다면 그것을 함수처럼 부를 수 있다.
struct ScrumdingerApp: App {
@StateObject private var store = ScrumStore()
@State private var errorWrapper: ErrorWrapper?
var body: some Scene {
WindowGroup {
NavigationView {
ScrumsView(scrums: $store.scrums) {
Task {
do {
try await ScrumStore.save(scrums: store.scrums)
} catch {
errorWrapper = ErrorWrapper(error: error, guidance: "Try again later.")
}
}
}
}
.task {
do {
store.scrums = try await ScrumStore.load()
} catch {
errorWrapper = ErrorWrapper(error: error, guidance: "Scrumdinger will load sample data and continue.")
}
}
.sheet(item: $errorWrapper, onDismiss: {
store.scrums = DailyScrum.sampleData
}) { wrapper in
ErrorView(errorWrapper: wrapper)
}
}
}
}
메인 App에 errorWrapper 라는 상태 변수를 옵셔널로 선언한다. 옵셔널로 선언했기 때문에 기본 값은 nil이고 이 상태 변수에 값이 할당되면 SwiftUI는 View를 업데이트한다.
이후 errorWrapper를 바인딩하는 sheet 를 추가한다. 모달 시트는 모달이 닫혔을 때 실행하게 하는 클로저를 제공한다. 그리고 모달 시트에 표시할 view를 제공하기 위한 클로저도 제공한다.
에러가 발생했을 때에 errorView에 wrapper를 제공하고 임시로 sampleData를 저장소의 스크럼에 넣는다.
Section 4. Simulate Data Corruption
이 섹션은 Terminal을 통해서 앱의 샌드박스에 들어간 다음 데이터를 조작한다.
그렇게 했을 때 앱에 발생하는 errorView를 확인해본다. 그리고 에러창을 dismiss 했을 때 샘플 데이터가 다시 채워져서 정상적으로 돌아온 것을 확인할 수 있다. 자세한 것은 직접 찾아가서 알아보도록 !
'iOS > iOS App Dev Tutorials' 카테고리의 다른 글
[iOS App Dev Tutorials] SwiftUI - Drawing (0) | 2022.05.18 |
---|---|
[iOS App Dev Tutorials] SwiftUI - Persistence and Concurrency (4) (0) | 2022.05.18 |
[iOS App Dev Tutorials] SwiftUI - Persistence and Concurrency (3) (0) | 2022.05.18 |
[iOS App Dev Tutorials] SwiftUI - Persistence and Concurrency (2) (0) | 2022.05.18 |
[iOS App Dev Tutorials] SwiftUI - Persistence and Concurrency (1) (0) | 2022.05.18 |