<h1><center> Swift Macros [1주차] </center></h1>
###### tags: `💻 WWDC 스터디`
> [color=#724cd1][name=데릭]
> [Platforms State of the Union - wwdc2023](https://www.youtube.com/watch?v=sby0V96sF7s)
> WWDC 2023 Session 중 하나인 `Swift Macros`에 대해 알아보자
## 개요
Swift Macros를 도입하여 더 쉽고 올바르게 사용할 수 있는 새로운 종류의 API를 잠금 해제?
- 매크로는 Swift 방식으로 수행되는 것
> 매크로는 코드 구조를 사용하여 프로젝트와 함께 빌드된 새 코드를 생성하는 주석이다.
```swift
@CaseDetection
enum Topic {
case diversity
case mentorship
var isDiversity: Bool {
if case .diversity = self { true }
else { false }
}
var isMentorship: Bool {
if case .mentorship = self { true }
else { false }
}
}
```
- 매크로는 코드에 속성으로 첨부되거나 해시 기호가 있는 독립형일 수 있다.
```swift
@CaseDetection
enum Topic {
case diversity
case mentorship
}
#assert(x == y - 1)
```
매크로는 API가 언어의 일부인 것처럼 느끼게 하며 주석만으로 새 API를 사용할 수 있는 많은 방법들이 있다.
매크로는 생성된 코드가 프로젝트의 일부인 것처럼 느껴지는 생명력을 얻는다.
# 예제
> 유효한 URL 문자열을 확인하는 URL Macro를 만든다
```swift
import Foundation
import MyCoolMacros
enum Topic {
case diversity
case mentorship
var url: URL {
switch self {
case .diversity: #URL("https://www.swift.org/diversity/")
URL(string: "https://www.swift.org/diversity/")!
case .mentorship: #URL("https://www.swift.org/mentorship")
}
}
}
```
- 매크로는 Swift 코드를 생성하기 때문에 Xcode의 `Expand Macro`기능을 사용하여 정확히 무엇을 하는지 볼 수 있다.
case .diversity를 보면 URL 이니셔라이져를 호출하고 결과를 강제로 언랩핑한다. **그러나** URL 매크로는 그 이상을 수행한다. **컴파일 타임에서 문자열이 유효한 URL인지 확인할 수 있다.**
예를 들어, URL에는 공백이 있을 수 없으므로 공백을 추가하면 매크로가 사용자 지정 오류 메시지를 제공하여 문자열에서 공백을 자동으로 제거하는 수정 프로그램을 제공한다.
-> 이렇게 하면 내 앱이 실행 중일 때 오류가 발생했을 수 있는 상황을 방지하고 코드를 작성할 때 실수한 내용을 바로 수정할 수 있다.
모든 매크로에는 올바른 코드를 작성하는 데 도움이 되는 사용자 지정 피드백 기능이 있다.
```swift
import Foundation
import MyCoolMacros
struct SwiftWebsite {
func fetchContent(_ topic: Topic, completion: @escaping (Result<String, Error>) -> Void) {
let task = URLSession.shared.dataTask(with: topic.url) { ... }
task.resume()
}
}
let website = SwiftWebsite()
let task = Task {
var content: String? = ""
website.fetchContent(.diversity) { result in
if case .success(let result) = result {
content = result
}
}
if let content {
print(content)
}
}
await task.value
```
비동기 작업을 수행 완료하고 핸들러를 사용하는 fetchContent라는 함수가 있지만 async/await를 사용하고 싶다면 어떻게 해야 할까?
- fetchContent 함수를 구현하는 대신 AddAsync 매크로를 연결할 수 있다.
```swift
@AddAsync
func fetchContent(_ topic: Topic, completion: @escaping (Result<String, Error>) -> Void) {
let task = URLSession.shared.dataTask(with: topic.url) { ... }
task.resume()
}
```
이제 fetchContent를 호출할 때, CompletionHandler를 전달하는 대신 결과를 기다릴 수 있다.
```swift
let task = Task {
let content = try? await website.fetchContent(.diversity)
if let content {
print(content)
}
}
```
비동기 함수를 실행하면 Xcode가 Source Editor에서 바로 매크로를 확장합니다.
```swift
func fetchContent(_ topic: Topic) async throws -> String {
try await withCheckedThrowingContinuation { continuation in
fetchContent(topic) { returnValue in
switch returnValue {
case .success(let value):
continuation.resume(returning: value)
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}
```
Swift는 오픈 소스로 개발되었기 때문에 개발자는 이미 매크로를 사용하여 Assertion 실패에 대한 설명을 자동으로 생성하고 사용자 지정 가능한 기본 프로토콜 적합성을 제공하는 등 가장 일반적으로 요청되는 일부 언어 기능을 구축하고 있다.
```swift
#assert
@CustomCodable
@
```