How to use Do, Try, Catch, and Throws in Swift | Swift Concurrency #1
Result
커스텀하기throws
를 통해 에러 throw
하기thorws
함수의 리턴값을 do catch
를 통해 받기Result
를 리턴하는 함수 구현throw
하는 throws
함수 구현throw
를 받는 try
, try?
, try!
코드 구현 func getTitle3() -> Result<String, Error> {
return isActive ? .success(returnedText) : .failure(DoCatchTryThrowError.getTitleError)
}
func getTitle4() throws -> String {
if isActive {
return returnedText
} else {
throw DoCatchTryThrowError.getTitleError
}
}
Result<Success, Failure>
타입을 통해 switch case
로 핸들링 가능 → Combine
프레임워크 사용 시 sink
의 completion
사용 방법과 같음throws
: String
을 리턴하면서 특정 상황에 따라 에러를 발생시키는 함수 func fetchTitle3() {
let completion = manager.getTitle3()
switch completion {
case .success(let returnedValue):
self.text = returnedValue
case .failure(let error):
self.text = error.localizedDescription
}
}
func fetchTitle4() {
do {
self.text = try manager.getTitle4()
} catch {
self.text = error.localizedDescription
}
}
func fetchTitle5() {
guard let text = try? manager.getTitle4() else {
self.text = DoCatchTryThrowError.getTitleError.localizedDescription
return }
self.text = text
}
getTitle3
가 Result
를 리턴하고 있기 때문에 switch case
를 통해 받을 수 있음getTitle4
가 커스텀 에러 타입을 리턴하고 있기 때문에 do catch
, try?
문을 통해 받을 수 있음try?
는 do catch
블럭이 아니더라도 구현 가능하지만 실패한다면 nil
을 리턴하기 때문에 주의 → 어떤 에러가 리턴되었는지도 확인할 수 없기 때문에 일반적으로 do catch
를 통해 구현하기struct DoCatchTryThrowBootCamp: View {
@StateObject private var viewModel: DoCatchTryThrowBootCampViewModel
init(dataManager: DoCatchTryThrowBootCampProtocol) {
_viewModel = StateObject(wrappedValue: DoCatchTryThrowBootCampViewModel(manager: dataManager))
// Dependency Ingection
}
var body: some View {
NavigationView {
VStack(spacing: 30) {
textBox
activeToggleButton
}
.padding(.horizontal, 10)
.padding(.vertical,100)
.navigationTitle("DoCatchTryThrow")
}
}
}
extension DoCatchTryThrowBootCamp {
private var textBox: some View {
Text(viewModel.text)
.font(.title)
.fontWeight(.bold)
.withDoCatchTryThorwBootCampViewModifier()
.onTapGesture {
viewModel.fetchTitle4()
}
}
private var activeToggleButton: some View {
Button {
viewModel.manager.isActive.toggle()
viewModel.text = "Default Text"
} label: {
Text("Active Toggle : \(viewModel.manager.isActive.description)")
.foregroundColor(.black)
.font(.title)
.fontWeight(.bold)
.withDoCatchTryThorwBootCampViewModifier(backgroundColor: .red.opacity(0.3))
.frame(height: 70)
}
}
}
struct DoCatchTryThrowBootCampViewModifier: ViewModifier {
let backgroundColor: Color
func body(content: Content) -> some View {
content
.frame(maxWidth: .infinity)
.frame(maxHeight: .infinity)
.background(backgroundColor)
.cornerRadius(10)
}
}
extension View {
func withDoCatchTryThorwBootCampViewModifier(backgroundColor: Color = Color.blue.opacity(0.7)) -> some View {
modifier(DoCatchTryThrowBootCampViewModifier(backgroundColor: backgroundColor))
}
}
isAcitve
변수를 토글링하는 하단 버튼class DoCatchTryThrowBootCampViewModel: ObservableObject {
@Published var text: String = "Default Text"
var manager: DoCatchTryThrowBootCampProtocol
init(manager: DoCatchTryThrowBootCampProtocol) {
self.manager = manager
}
func fetchTitle() {
guard let text = manager.getTitle() else { return }
self.text = text
}
func fetchTitle2() {
let returnedValue = manager.getTitle2()
if let text = returnedValue.title {
self.text = text
} else if let error = returnedValue.error {
self.text = error.localizedDescription
}
}
func fetchTitle3() {
let completion = manager.getTitle3()
switch completion {
case .success(let returnedValue):
self.text = returnedValue
case .failure(let error):
self.text = error.localizedDescription
}
}
func fetchTitle4() {
do {
self.text = try manager.getTitle4()
} catch {
self.text = error.localizedDescription
}
}
func fetchTitle5() {
guard let text = try? manager.getTitle4() else {
self.text = DoCatchTryThrowError.getTitleError.localizedDescription
return }
self.text = text
}
func fetchTitle6() {
do {
self.text = try manager.getTitle4()
self.text = try manager.getTitle5()
// getTitle4 -> try success, getTitle5 -> try fail
// then catch block
} catch {
self.text = error.localizedDescription
}
}
func fetchTitle7() {
do {
if let text = try? manager.getTitle5() {
self.text = text
}
self.text = try manager.getTitle4()
// getTitle4 -> try success, getTitle5 -> try fail but returned nil -> return getTitle4's value
} catch {
self.text = error.localizedDescription
}
}
}
DoCatchTryThrowBootCampDataManager
가 아니라 DoCatchTryThrowBootCampDataProtocol
이 가지고 있는 함수만 사용하다는 데 주의import SwiftUI
protocol DoCatchTryThrowBootCampProtocol {
var isActive: Bool { get set}
func getTitle() -> String?
func getTitle2() -> (title: String?, error: Error?)
func getTitle3() -> Result<String, Error>
func getTitle4() throws -> String
func getTitle5() throws -> String
}
enum DoCatchTryThrowError: LocalizedError {
case getTitleError
}
class DoCatchTryThrowBootCampDataManager: DoCatchTryThrowBootCampProtocol {
var isActive: Bool = true
let returnedText: String = "Returned Text"
func getTitle() -> String? {
return isActive ? returnedText : nil
}
func getTitle2() -> (title: String?, error: Error?) {
return isActive ? (returnedText, nil) : (nil, DoCatchTryThrowError.getTitleError)
}
func getTitle3() -> Result<String, Error> {
return isActive ? .success(returnedText) : .failure(DoCatchTryThrowError.getTitleError)
}
func getTitle4() throws -> String {
if isActive {
return returnedText
} else {
throw DoCatchTryThrowError.getTitleError
}
}
func getTitle5() throws -> String {
throw DoCatchTryThrowError.getTitleError
}
}
isActive
토글링을 위한 get set
설정)에러를
throws
하는 함수의 리턴값을 핸들링하는 것은 특히 비동기 코드를 작성할 때 확인해야 할 점이다!