맛동산이

Swift) Async, Await 사용하는 법 본문

앱/Swift

Swift) Async, Await 사용하는 법

진ddang 2023. 7. 4. 13:35

Async와 await을 사용하기

1. 기존의 코드

override func viewDidLoad() {
    super.viewDidLoad()
    
    self.getData { data in
        self.decode(data: data) { contents in
            print(contents) // "<!doctype html>~~~~
        }
    }
}

func getData(completion: (Data) -> Void) {
    let url = URL(string: "https://zeddios.tistory.com")!
    let data = try! Data(contentsOf: url)
    completion(data)
}

func decode(data: Data, completion: (String) -> Void) {
    let contents = String(data: data, encoding: .utf8)!
    completion(contents)
}

2. completion과 메소드를 async로 전환

func getData() async -> Data {
	let url = URL(string: "https://zeddios.tistory.com")!
  let data = try! Data(contentsOf: url)
	return data
}

func decode(data: Data) async -> String {
	let contents = String(data: data, encoding: .utf8)!
	return contents
}

3. 사용하는 쪽에서 await

func process() async {
	let data = await self.getData()
	let contents = await self.decode(data: data)
	print(contents)
}

4. 해당 비동기를 사용할곳에 Task로 감싸서 붙이기

override func viewDidLoad() {
    super.viewDidLoad()
    
    Task {
        await self.process()
     }
}


Async await을 사용한 코드 예제

private func fetchUsers() async -> [User] {
	guard let url = url else {
		return 
	}
	do {
		let (data, _) = try await UrlSession.shared.data(from: url)
		let user = try JSONDecoder().decode([User].self, from: data)
		return user
	}
	catch {
		return []
	}
}

이러한 코드를 통해서 기존의

viewDidLoad() {
	super.viewDidLoad()
	fetchUsers() { users in 
		self.users = users
			DispatchQueue.main.async {
				self.tableView.reload()
		}
	}
} 

이러한 코드를 다음과 같이 작성이 가능하다. 

viewDidLoad() {
	super.viewDidLoad()
	async { 
		let users = await fetchUsers()
		self.users = users
		DispatchQueue.main.async {
			self.tableView.reload()
	}
}

이러한 코드에서 사실 큰 차이가 없어보이지만

위의 비동기 처리가 되는 코드가 중첩적으로, 콜되면 엄청난 차이를 보인다.

예를들어 다음과 같은 코드가 아름답게 변화할수있다.

viewDidLoad() {
	super.viewDidLoad()
	fetchUsers() { users in 
			getOtherUsers(from: users) { others in
				anotherUsers(from: others) { another in 
				...
			}
		}
	}
} 

이러한 코드가 async await을 사용하면

viewDidLoad() {
	super.viewDidLoad()
	async { 
		let users = await fetchUsers(from: url)
		let otherUsers = await getOtherUsers(from: users)
		let anotherUsers = await anotherUsers(from: otherUsers)
		self.users = anotherUsers
		DispatchQueue.main.async {
			self.tableView.reload()
	}
}

또한 여기에서 Result type을 이용해서 에러핸들링을 해줄수 있다.

enum MyError: Error {
	case failToGet
}

private func fetchUsers() async -> Result<[Users], Error> {
	guard let url = url else {
		.failure(MyError.failToGet)
	}
	do {
		let (data, _) = try await UrlSession.shared.data(from: url)
		let user = try JSONDecoder().decode([User].self, from: data)
		return .success(user)
	}
	catch {
		return .failure(error)
	}
}

viewDidLoad() {
	super.viewDidLoad()
	async { 
		let result = await fetchUsers()
		switch result {
			case .success(let users):
				self.users = users
				DispatchQueue.main.async {
					self.tableView.reload()
				}
			case .failure(let error):
				print(error.localizationDescription)
		}
	}
}

이러한 화려한 아주 깔끔한 코드를 짤수 있도록 도와주는것이 async await이다.


Uploaded by N2T

반응형

' > Swift' 카테고리의 다른 글

Swift) 소수점 다루기  (0) 2023.08.18
Swift) Task, TaskGroup, .task와 dispatchQueue  (0) 2023.07.04
Swift) 프로토콜 Sendable이란  (0) 2023.07.04
Swift) actor와 Actor isolation에 대해서  (0) 2023.07.04
Swift) Return type과 에러처리  (0) 2023.07.04