반응형

Hacking with Swift 사이트의 강좌 번역본입니다.

[원문 : https://www.hackingwithswift.com/articles/228/whats-new-in-swift-5-4]

What’s new in Swift 5.4

Swift 5.4는 오류가 있는 표현식에서 더 나은 코드 완성과 컴파일 속도를 높이는 것을 포함한 몇 가지 큰 컴파일 개선을 가져왔습니다. 그러나, 몇가지 중요한 새 기능과 개선사항도 추가되었으므로, 여기에서 자세히 살펴봅시다.

** 팁 ** : 샘플 코드를 시험해보고 싶으면 Xcode playground 다운로드 할 수 있습니다.

암시적 멤버 구문이 개선됨(Improved implicit member syntax)

SE-0287는 암시적 멤버 표현식을 사용하는 Swift의 능력을 향상시킴으로써, 정확한 하나의 정적 멤버를 지원하는 대신에 그것들의 체인(chains)을 만들 수 있습니다.

Swift는 항상 간단한 표현식에 대한 암시적 멤버 구문을 사용하는 능력이 있고, 예를들어 SwiftUI에서 일부 텍스트 색상을 지정하는데 Color.red 대신 .red를 사용할 수 있습니다.

struct ContentView1: View {
    var body: some View {
        Text("You're not my supervisor!")
            .foregroundColor(.red)
    }
}

Swift 5.4 이전에는 좀 더 복잡한 표현식에서는 동작되지 않았습니다. 예를들어, 빨간 색상을 약간(slightly) 투명하게 하려면 다음과 같이 작성해야 합니다.

struct ContentView2: View {
    var body: some View {
        Text("You're not my supervisor!")
            .foregroundColor(Color.red.opacity(0.5))
    }
}

Swift 5.4 부터(onwards) 컴파일러는 다중 체인 멤버(multiple chained members)를 이해할수 있고, Color 타입을 추론할 수 있는 것을 의미합니다.

struct ContentView3: View {
    var body: some View {
        Text("You're not my supervisor!")
            .foregroundColor(.red.opacity(0.5))
    }
}

함수에서의 여러개의 가변 매개변수(Multiple variadic parameters in functions)

SE-0284는 가변 매개변수를 따르는 모든 매개변수가 라벨을 가지는 한 함수, 첨자, 초기화에 여러개의 가변 매개변수를 사용하도록 하는 기능을 도입했습니다. Swift 5.4 전에는, 이 상황에서는 하나의 가변 매개변수만 사용할 수 있었습니다.

따라서, 이 개선으로 축구 경기중에 골이 득점된 시간을 저장하는 가변 매개변수를 받아들이는 함수를 작성할 수 있고, 두번째 가변 매개변수는 득점한 선수의 이름을 기록할 수 있습니다.

func summarizeGoals(times: Int..., players: String...) {
    let joinedNames = ListFormatter.localizedString(byJoining: players)
    let joinedTimes = ListFormatter.localizedString(byJoining: times.map(String.init))

    print("\(times.count) goals where scored by \(joinedNames) at the follow minutes: \(joinedTimes)")
}

이 함수를 호출하기 위해서, 2개의 값 세트를 가변 매개변수로 제공하고, 첫번째 가변 이후에 모든 매개변수가 라벨이 있는지를 확인합니다.

summarizeGoals(times: 18, 33, 55, 90, players: "Dani", "Jamie", "Roy")

결과 빌더(Result builders)

함수 빌더(Function builders)는 비공식적으로 Swift 5.1 에서 도입되었지만, Swift 5.4에서 이르기까지 논의하고 개선하기 위해서 SE-0289와 같은 Swift Evolution 제안 프로세스를 공식적으로 진행했습니다. 그 과정에서 실제 목적을 더 잘 반영하기 위해서 결과 빌더(result builders)로 이름이 바뀌었고, 새로운 기능까지 가졌습니다.

우선, 가장 중요한 부분입니다: 결과 빌더(result builders)를 사용해서 선택한 순서를 전달해서 단계적으로 새로운 값을 만듭니다. SwiftUI의 뷰 생성 시스템의 광범위한 부분은 차지하고 있으므로, 내부에 다양한 뷰가 있는 VStack이 있을때, Swift는 이들을 내부 TupleView 타입으로 자동으로 그룹화해서 VStack의 단일 자식으로 저장할 수 있습니다 - 일련의 뷰들을 단일 뷰로 변환합니다.

결과 빌더(result builders)에 대해서 자세히 살펴볼 가치(deserve)가 있지만, 최소한 몇개의 작은 코드 예제를 제공해서 실제 동작하는 것을 확인할 수 있습니다.

다음은 단일 문자열을 반환하는 함수입니다.

func makeSentence1() -> String {
    "Why settle for a Duke when you can have a Prince?"
}

print(makeSentence1())

그것은 잘 동작하비만, 함께 결합하고자 하는 문자열이 여러개 있으면 어떨까요? SwiftUI 처럼 모두 개별적으로 제공하고 Swift가 해결하도록 할 수 있습니다.

// This is invalid Swift, and will not compile.
// func makeSentence2() -> String {
//     "Why settle for a Duke"
//     "when you can have"
//     "a Prince?"
// }

그 자체로는, Swift가 우리가 의미하는 것을 더 이상 이해하지 못하기 때문에 해당 코드는 동작하지 않습니다. 하지만, 다음과 같이 원하는 변환을 사용해서 여러개의 문자열을 하나의 문자열로 변환하는 방법을 이해하는 결과 빌더(result builder)를 만들 수 있습니다.

@resultBuilder
struct SimpleStringBuilder {
    static func buildBlock(_ parts: String...) -> String {
        parts.joined(separator: "\n")
    }
}

적은 양의 코드이지만, 살펴볼 것이 많습니다.

  • @resultBuilder 속성(attribute)은 SwiftUI에게 다음에 오는 타입이 결과 빌더(result builder)로 처리되는 것을 알려줍니다. 이전에는 이 동작을 @_functionBuilder로 사용했었으며, 일반 용도로 설계되지 않았음을 보여주는 밑줄(underscore)이 있습니다.
  • 모든 결과 빌더(result builder)는 반드시 하나의 정적 메소드 builderBlack()를 제공해야 하며, 일종의 데이터를 가지고 변환해야 합니다. 위 예제는 0개 이상의 문자열을 가지고, 결합하고 단일 문자열로 다시 보냅니다.
  • 마지막 결과는 SimpleStringBuilder 구조체가 결과 빌더(result builder)가 되는 것이며, 문자열 결합이 필요한 어디서든지 @SimpleStringBuilder를 사용할 수 있다는 것을 의미합니다.

다음과 같이 SimpleStringBuilder.buildBlack()를 직접 사용하는 것을 멈출수 있습니다.

let joined = SimpleStringBuilder.buildBlock(
    "Why settle for a Duke",
    "when you can have",
    "a Prince?"
)

print(joined)

그러나, SimpleStringBuilder구조체와 함께 @resultBuilder 주석(annotation)을 사용했기 때문에 다음과 같은 함수에 적용할 수 있습니다.

@SimpleStringBuilder func makeSentence3() -> String {
    "Why settle for a Duke"
    "when you can have"
    "a Prince?"
}

print(makeSentence3())

각 문자열의 끝부분에 콤마(commas)가 더 이상 필요하지 않은 것을 주의하세요 - @resultBuilder SimpleStringBuilder를 사용해서 makeSentence()에서 각 구문을 단일 문자열로 자동으로 변환합니다.

실제로 결과 빌더(result builders)는 빌더 타입에 더 많은 메소드를 추가해서 훨씬 더 많은 작업을 처리할 수 있습니다. 예를들어, 데이터를 변환하는 방법을 설명하는 2개의 추가적인 메소드를 추가해서 SimpleStringBuilder에 if / else 를 지원하도록 할 수 있습니다. 코드에서 문자열을 변환하지 않으므로, 바로 다시 보낼 수 있습니다.

@resultBuilder
struct ConditionalStringBuilder {
    static func buildBlock(_ parts: String...) -> String {
        parts.joined(separator: "\n")
    }

    static func buildEither(first component: String) -> String {
        return component
    }

    static func buildEither(second component: String) -> String {
        return component
    }
}

거의 작업하지 않을 것을 알지만, 이제 함수에서 조건부를 사용할 수 있습니다.

@ConditionalStringBuilder func makeSentence4() -> String {
    "Why settle for a Duke"
    "when you can have"

    if Bool.random() {
        "a Prince?"
    } else {
        "a King?"
    }
}

print(makeSentence4())

마찬가지로, 빌더 타입에 buildArray() 메소드를 추가해서 반복문을 지원하는 것을 추가할 수 있습니다.

@resultBuilder
struct ComplexStringBuilder {
    static func buildBlock(_ parts: String...) -> String {
        parts.joined(separator: "\n")
    }

    static func buildEither(first component: String) -> String {
        return component
    }

    static func buildEither(second component: String) -> String {
        return component
    }

    static func buildArray(_ components: [String]) -> String {
        components.joined(separator: "\n")
    }
}

그리고 이제 for 반복문을 사용할 수 있습니다.

@ComplexStringBuilder func countDown() -> String {
    for i in (0...10).reversed() {
        "\(i)…"
    }

    "Lift off!"
}

print(countDown())

결과 빌더(result builder) 시스템이 거의 모든 것을 작업하기 때문에 마법처럼 느껴지고, 비록 예제가 매우 간단하지만 결과 빌더(result builders)가 Swift에 가져다준 놀라운 힘을 느껴보길 바랍니다.

Swift 5.4는 저장된 프로퍼티에 배치된 속성을 지원하기 위해 결과 빌더(result builder) 시스템을 확장한다는 덛붙일 필요가 있으며, 결과 빌더(result builder)를 적용할 수 있도록 구조체에 대해 암시적 멤버단위 초기화를 자동으로 조정합니다.

이는 다음과 같이 결과 빌더(result builders)를 사용하는 사용자정의 SwiftUI 뷰에 특히 유용합니다.

struct CustomVStack<Content: View>: View {
    @ViewBuilder let content: Content

    var body: some View {
        VStack {
            // custom functionality here
            content
        }
    }
}

결과 빌더(result builders)에 대해 더 발전된, 실제 예제를 보고자 하는 경우에는 GitHub에 있는 Awesome Function Builders 저장소를 확인하세요.

이제 로컬 함수가 오버로딩을 지원(Local functions now support overloading)

SR-10069는 로컬 컨텍스트에서 함수를 오버로드 할 수 있는 기능을 요청했으며, 실제로 중첩된 함수가 오버로드 될 수 있음으로 의미하므로 Swift가 사용되는 타입에 따라 실행할 함수를 고를 수 있습니다.

예를들어, 간단한 쿠키를 만들고 싶은 경우 다음과 같이 코드를 작성할 수 있습니다.

struct Butter { }
struct Flour { }
struct Sugar { }

func makeCookies() {
    func add(item: Butter) {
        print("Adding butter…")
    }

    func add(item: Flour) {
        print("Adding flour…")
    }

    func add(item: Sugar) {
        print("Adding sugar…")
    }

    add(item: Butter())
    add(item: Flour())
    add(item: Sugar())
}

Swift 5.4 이전에는(prior), 3개의 add() 메소드가 makeCookies() 내부에 중첩되지 않은 경우에만 오버로드될 수 있었지만, Swift 5.4 부터는(onwards) 이 경우에도 함수 오버로딩이 지원됩니다.

이제 로컬 변수에 프로퍼티 래퍼를 지원(Property wrappers are now supported for local variables)

프로퍼티 래퍼(property wrappers)는 프로퍼티에 기능을 추가하기 위해 쉽고 재사용이 가능하도록 프로퍼티에 추가하는 방법으로 Swift 5.1에서 처음 도입되었지만, Swift 5.4에서는 함수에서 지경 변수로 사용하는 것을 지원하도록 동작이 확장되었습니다.

예를들어, 값이 0 미만이 되지 않도록 하는 프로퍼티 래퍼(property wrapper)를 만들 수 있습니다.

@propertyWrapper struct NonNegative<T: Numeric & Comparable> {
    var value: T

    var wrappedValue: T {
        get { value }

        set {
            if newValue < 0 {
                value = 0
            } else {
                value = newValue
            }
        }
    }

    init(wrappedValue: T) {
        if wrappedValue < 0 {
            self.value = 0
        } else {
            self.value = wrappedValue
        }
    }
}

그리고 Swift 5.4 부터는 프로퍼티에 연결하는 것이아니라 일반 함수 내에서 프로퍼티 래퍼를 사용할 수 있습니다. 예를들어, 플레이어가 점수를 얻거나 잃을 수 있는 게임을 작성할 수 있지만, 점수가 0 미만이 되어서는 안됩니다.

func playGame() {
    @NonNegative var score = 0

    // player was correct
    score += 4

    // player was correct again
    score += 8

    // player got one wrong
    score -= 15

    // player got another one wrong
    score -= 16

    print(score)
}

패키지는 이제 실행가능한 대상을 선언할 수 있음(Packages can now declare executable targets)

SE-0294는 Swift Package Manager를 사용하는 앱에 대한 새로운 대상(target) 옵션이 추가되어, 실행 가능한 대상을 명시적으로 선언할 수 있습니다.

이는 Swift Package Manager와 잘 어울리지 않았기 때문에, SE-0281(@main을 사용해서 프로그램 진입점을 표시)을 사용하고자 하는 사람들에게 특히 중요합니다. - 항상 main.swift 파일을 찾습니다.

이러한 변화로, 이제 main.swift를 제거하고 대신 @main을 사용할 수 있습니다

주의 : 이러한 새로운 기능을 사용하기 위해서 // swift-tools-version:5.4 Package.swift 파일에 지정해줘야 합니다.

반응형

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

Type의 문자열 이름 사용하기  (0) 2022.04.01
What’s new in Swift 5.6  (0) 2022.03.21
Swift version과 Xcode version  (0) 2022.02.09
What’s new in Swift 5.5  (0) 2022.01.25
What’s new in Swift 5.3  (0) 2021.04.15
What’s new in Swift 5.2  (0) 2021.04.15
How to use opaque return types in Swift 5.1  (0) 2019.12.18
What’s new in Swift 5.1  (0) 2019.12.17
Posted by 까칠코더
,