SwiftUI Optional, JSON decode, Error, Async, Await

Ryan Cho·2025년 6월 24일

Optional(nill)

Swift에서는 optional한 값에 대해 값이 존재하거나 nill로 표현한다.

let arry: [String] = ["A"]
print(arr[0]) // "A"
print(arr.first) // Optional("A")

let arry2: [String] = []
print(arry2[0]) // error
print(arry2.first) // nill

Optional Unwrapping

if-let , guard-let 패턴 지향

//Optional Unwapping
// 1. nill-coalescing -> 기본값 넣어줌
let number1: Int? = nil

func printNumber(number: Int){
    print(number)
}

printNumber(number: number1 ?? 1)


// 2. forced-unwrapping -> 위험하게 벗기기 테스트용으로만 쓰고 걍 쓰지마 아오
//printNumber(number: number1!)

// 3. optional binding(if-let) -> 조심히 벗김 지향해라
if let num = number1 {
    printNumber(number: num)
} else {
    print("실패")
}

// 4. optional binding2(guard-let) -> 조심히 벗김2 지향해라
// 함수 내에서만 사용가능
func guardLetEx(){
    guard let num = number1 else {return} //guard-let은 성공하면 아래로
    printNumber(number: num)
}

api요청 JSON decode처리

본 예제는 플레이그라운드에서 기능 구현 목적만을 위함

import UIKit

Task {
    let url = URL(string: "https://gvec03gvkf.execute-api.ap-northeast-2.amazonaws.com/")! //optional한 값이라 임시로 타입강제

    let (data,_) = try! await URLSession.shared.data(from: url)
    // do/catch 블록을 사용해야하지만 임시로 try! 로 강제 처리
    // URLSession.shared.data의 return 값이 (Data,URLResponse) 형태기 떄문에 (data,_) 이런식으로 값 할당. 구조분해할당처럼 느끼면 될듯
    
    let decoder = JSONDecoder()
    let decodedData = try! decoder.decode(DramaCollections.self, from: data) // 임시로 try!로 강제 처리
    print(decodedData) // JSON데이터 출력 성공
    
}


struct DramaCollections: Decodable {
    var bigBanner: String
    var dramas: [Dramas]
    
    enum CodingKeys: String, CodingKey { // 응답 데이터의 필드가 대문자 상수처럼 표기되어서 카멜케이스로 변환
        case bigBanner = "BIG_BANNER"
        case dramas = "DRAMAS"
    }
}

struct Dramas: Decodable {
    var categoryTitle: String
    var posters: [String]
    
    enum CodingKeys: String, CodingKey {
        case categoryTitle = "CATEGORY_TITLE"
        case posters = "POSTERS"
    }
}

Error Handling

// Error Handling (안전한 방법)

func tryEx(){
    //try구문이 존재하면 반드시 do-catch 블록으로 에러핸들링
    do{
        let url = URL(string: "https://www.naver.com")! // url은 URL? 타입임 임시로 unwrap optional을 위해 !강제사용
        let data = try Data(contentsOf: url) // contentsOf는 throws를 발생시키므로, 이런경우 반드시 try 필요
        let fileContents = String(data: data, encoding: .utf8)
    } catch {
        print("에러발생: \(error)")
    }
    
}

// do-catch 안쓰고싶니?
func tryEx2(){
    let url = URL(string: "https://www.naver.com")! // url은 URL? 타입임 임시로 unwrap optional을 위해 !강제사용
    guard let data = try? Data(contentsOf: url) else{return} // else에 에러핸들링을 해도됨
    print("data", data)
    let fileContents = String(data: data, encoding: .utf8)
    print(fileContents)
}

// 에러핸들링을 사용하는 부분에서 결정하려면 (Error Propagation)
func tryEx3() throws { // throws를 명시
    let url = URL(string: "https://www.naver.com")!
    let data = try Data(contentsOf: url)
    let fileContents = String(data: data, encoding: .utf8)
    print(fileContents)
}

    do {
        try tryEx3() // 사용하는 부분에서 try 선언
    } catch {
        print("에러다") // 외부에서 에러 핸들링
    }

Async, Await, Task

Swift언어는 JavaScript(싱글스레드)와 다르게 멀티스레드를 지원한다.
이 환경에서 비동기처리(네트워크 요청 등)은 서브 스레드로 작업을 옮기는 방식으로 진행된다.
메인 스레드에서 가장 중요한 UI를 그리는 등의 일을 하고, 비동기 처리가 필요한 부분은 필요에 따라 서브 스레드로 할당할 수 있다.

Task

기본적으로 Task블록 안에 코드를 작성하면 서브 스레드로 작업을 옮긴다.

var count = 0
print("start")
Task {
	for i in 0 ... 500000 {
    	count += 1
    }
}
print("result", count)
print("end")

이런 경우 콘솔은 "start" "end" "result, (count)" 순으로 출력된다.

async가 지정된 경우

기본적으로 async 키워드를 나타내는 함수들이 있다.
예를들어

Task.sleep(nanoseconds) 

이런 함수는 async를 나타내고 (타입정의에서 확인 가능)
사용할 경우 await 키워드와 함께 Task블록에 작성하면 된다.

Task{
	print("start")
	await Task.sleep(3000000000)
    print("end")
}

이 경우 async키워드, Task블록을 사용하지 않을 경우 syntax error가 발생

profile
From frontend to fullstack

0개의 댓글