Swift

Swift 5.8

GREEN.1229 2023. 4. 4. 09:27

안녕하세요. 그린입니다🍏

이번 포스팅은 지난달 30일에 릴리즈된 Swift 5.8에 대해 어떤것들이 나타났고 변경되었는지 학습해보겠습니다🙋🏻

 

Swift 5.8

2023년 3월 30일 공식적으로 Swift 5.8이 출시되었습니다.

이번 릴리즈에서는 향후 기능의 단편적 채택을 지원하는 기능, 향상된 개발자 환경, Swift-DocC, SPM 및 SwiftSyntax를 포함한 Swift 에코 시스템의 툴 개선, 향상된 Windows 지원 등 언어 및 표준 라이브러리에 대한 주요 사항들이 업데이트 되었습니다!

주로 느껴진것은 새로운 기능들이 막 쏟아져 나왔다기 보다는 기존 Swift의 사용되는 기능들의 개선이 많았던것 같습니다.

릴리즈된 Swift 5.8을 사용하려면 같은 날에 업데이트를 제공한 Xcode 14.3 버전의 사용이 필요합니다.

다만, 일부분의 변경 사항들은 Swift 6을 위한 초석으로 그전에 사용하려면 특정한 컴파일러 플래그가 필요합니다.

-enable-upcoming-feature

이러한 컴파일러 플래그 예시처럼 활성화 시켜야 합니다.

 

자 그럼 본격적으로 어떤것들이 변경되었는지 알아볼까요?

 

ResultBuilder 내 변수에 대한 허용 (SE-0373)

이제 result builder 블록 내부에서 변수들에 대해 허용할 수 있습니다.

쉽게 말해 SwiftUI의 View 프로토콜을 따르는 body는 result Builder를 따르게 되있습니다.

기존에는 아래와 같이 해당 body 내부에서 변수의 선언 및 사용이 불가했다면 이제는 가능하게 되었습니다.

import SwiftUI

struct ContentView: View {
    var body: some View {
        GeometryReader { proxy in
            @Clamped(10...100) var width = proxy.size.width
            Text("\(width)")
        }
    }
}

요런식의 예시로 width 자체가 body 밖에 있는것이 아닌 body 즉, 결과 빌더 내부에 위치할 수 있습니다.

이러한 제공되는 변수들의 종류는 아래와 같습니다.

 

- 초기화 되지 않은 변수

- 기본적으로 초기화된 변수

- 연산 프로퍼티 (computed variables)

- 관측 프로퍼티 (observed variables)

- 프로퍼티 래퍼가 포함된 변수

- lazy 변수

 

사실 거의 뭐 변수를 다 지원한다고 봐도 무방하겠네요!

 

Function Back Deployment (SE-0376)

함수 Back 탑재? 직역하니까 너무 이상하네요ㅎㅎ..

해당 함수에 대해 @available와 비슷하게 어떤 버전 이전으로 사용하게 할 수 있는지 지정해줄 수 있는 어노테이션입니다.

예전에는 어떤 버전 이후부터 사용가능하도록 되있었을때 새로운 버전이 문제를 발생시킬 수도 있고, 그게 꼭 iOS 버전이 아니더라도 새로운 프레임워크 버전일때도 업데이트에 문제가 발생할 수 있습니다.

그렇기에 조금 더 안전성을 보장해줄 수 있죠.

즉, 아래와 같이 사용할 수 있습니다.

extension Temperature {
  @available(toasterOS 1.0, ovenOS 1.0, *)
  @backDeployed(before: toasterOS 2.0, ovenOS 2.0)
  public var degreesFahrenheit: Double {
    return (degreesCelcius * 9 / 5) + 32
  }
}

해당 degreesFahrenheit 변수에 특정하여 toasterOS에 대해 1.0 이후 2.0 이전을 지원하도록 합니다.

쪼오금 더 쉬운 코드로 이해하면 요렇습니다.

extension Text {
    @backDeployed(before: iOS 16.4, macOS 13.3, tvOS 16.4, watchOS 9.4)
    @available(iOS 14.0, macOS 11, tvOS 14.0, watchOS 7.0, *)
    public func monospaced(_ isActive: Bool) -> Text {
        fatalError("Implementation here")
    }
}

이래서 backDeployed라고 이름 지었나봅니다🤔

 

Allow implicit self for weak self captures, after self is unwrapped (SE-0365)

다들 weak self는 잘 아실거라고 생각해요.

어떤 이스케이핑 클로저가 있다고 가정해보고 아래 코드를 보시죠.

class TimerController {
    var timer: Timer?
    var fireCount = 0

    init() {
        timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] timer in
            guard let self else { return }
            print("Timer has fired \(self.fireCount) times")
            self.fireCount += 1
        }
    }
}

기존 Swif 5.7까지는 이렇게 fireCount 프로퍼티를 클로저 블럭 내부에서 사용할때 weak self로 캡처하고 해당 self를 옵셔널 언래핑하더라도 꼭 위 코드처럼 fireCount에 self.를 붙여줘야 했습니다.

 

그런데 이제 그러지 않아도 weak self 캡쳐를 하고 self를 언래핑 해준다면 암시적으로 self를 허용해줄 수 있습니다.

즉 아래와 같이 fireCount에 self.를 붙이지 않아도 이제는 컴파일 된다는 말이죠!

class TimerController {
    var timer: Timer?
    var fireCount = 0

    init() {
        timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] timer in
            guard let self else { return }
            print("Timer has fired \(fireCount) times")
            fireCount += 1
        }
    }
}

조금 더 편해졌습니다🎉

위 언래핑 방법뿐 아니라 if let, while let 등 정말 다양한 언래핑만 거치면 암시적으로 self를 활성화 해줍니다.

만약 언래핑을 하지 않았다면 아래와 같이 오류가 납니다.

button.tapHandler = { [weak self] in
    // error: explicit use of 'self' is required when 'self' is optional,
    // to make control flow explicit
    // fix-it: reference 'self?.' explicitly
    dismiss()
}

그런데 이것과 관련해서 Swift 5.3의 SE-0269 업데이트를 보면 동일한 내용의 업데이트가 있습니다.

https://github.com/apple/swift-evolution/blob/main/proposals/0269-implicit-self-explicit-capture.md

 

GitHub - apple/swift-evolution: This maintains proposals for changes and user-visible enhancements to the Swift Programming Lang

This maintains proposals for changes and user-visible enhancements to the Swift Programming Language. - GitHub - apple/swift-evolution: This maintains proposals for changes and user-visible enhance...

github.com

이번 업데이트도 해당 업데이트의 영향을 받아 더 보완을 시킨것인데요.

여기서 보면 이전 업데이트는 weak self가 아닌 self 캡쳐일때만 활용되었습니다.

즉, 이번에는 기존을 디벨롭하여 weak self에도 기능을 붙였다고 이해하면 좋을것 같네요!

 

Concise magic file names (SE-0274)

간결한 매직 파일 이름이라고 해석할 수 있습니다.

우선 해당 기능은 Swif 5.8에서 업데이트 되었지만 기본적으로 활성화 되있지는 않습니다.

그렇기에 만약 활성화 하려면 새로운 컴파일러 플래그를 추가해야 합니다.

즉, Xcode의 기타 Swift 플래그에 #file 플래그를 추가해야 하죠.

-enable-upcoming-feature ConciseMagicFile

자 그럼 이게 무슨 기능인지 보시죠!

기존 SE-0274 업데이트 후 #file 식별자를 사용할 수 있게 되었습니다.

해당 파일 식별자를 사용할때 파일에 대한 전체 경로를 포함해야 했습니다.

/Users/twostraws/Desktop/WhatsNewInSwift/WhatsNewInSwift/ContentView.swift

그런데 이런 방법은 불필요하게 길고 공개되지 않아도 될 부분이기에 이번 업데이트를 통해 개선되었습니다.

즉, 전체 패스보다 해당 파일 이름으로 간결하게 제공될 수 있는 점이죠.

이제는 해당 기능을 활성화 한다면 두가지 방법으로 사용할 수 있습니다.

// New behavior, when enabled
print(#file)

// Old behavior, when needed
print(#filePath)

#file을 통해 간결한 파일 이름을 통해 사용할 수 있고, #filePath는 기존 #file 역할과 동일하게 전체 경로를 넣어 사용할 수 있습니다.

 

Opening existential arguments to optional parameters (SE-0375)

옵셔널한 파라미터에 대한 존재하는 인자 열기라는 내용으로 해석되는데요. 

난해하네요🥲

코드를 보면서 차근차근 알아보시죠!

func optionalDouble<T: Numeric>(_ number: T?) -> T {
    let numberToDouble = number ?? 0
    return  numberToDouble * 2
}

let first = 1
let second = 2.0
let third: Float = 3

let numbers: [any Numeric] = [first, second, third]

for number in numbers {
    print(optionalDouble(number))
}

해당 옵셔널한 제네릭 T를 인자로 받는 optionalDouble 메서드가 있고 이 메서드의 number를 사용하는 코드가 있습니다.

이 코드는 Swift 5.7에서는 컴파일되지 않고 "Type 'any Numeric' can't conform to 'Numeric'" 이라고 오류가 납니다.

즉, T는 Numeric으로 옵셔널이 아닌데 옵셔널이 들어올 수 있는 가능성처럼 T?로 파라미터를 선언했기 때문이겠죠?

Numeric 타입을 옵셔널로 변경해도 되지만 그런 작업없이 Swift 5.8에서는 지원해줍니다.

불일치 될 수 있는 부분은 언어 자체적으로 편리하게 해결해줬네요👍

 

Collection downcasts in cast patterns are now supported

캐스트 패턴의 컬렉션 다운 캐스트가 지원됩니다.

class Pet { }
class Dog: Pet {
    func bark() { print("Woof!") }
}

func bark(using pets: [Pet]) {
    switch pets {
    case let pets as [Dog]:
        for pet in pets {
            pet.bark()
        }
    default:
        print("No barking today.")
    }
}

요 코드는 Swift 5.7에서는 컴파일이 되지 않고 Swift 5.8에서는 컴파일이 되는 코드입니다.

Dog는 Pet을 상속받고 있습니다.

bark 메서드를 보면 파라미터 타입으로 [Pet]이 들어오고 내부에서는 as [Dog]로 형 변환 즉, 다운 캐스트를 해주고 있습니다.

그러다보니 Swift 5.8 이전에서는 캐스트 패턴에서 다운캐스트된 컬렉션이 구현되지 않는다고 오류가 납니다.

즉 아래와 같이 [Dog]에 대해 명시적으로 다운캐스팅을 하고 사용하도록 유도하죠.

if let dogs = pets as? [Dog {
  ...
}

이렇게도 사용할 수 있지만 불편하기도 하고 불일치스럽기도 해서 이 부분이 Swift 5.8 업데이트에 포함된것 같습니다.

즉 컬렉션 다운캐스트가 지원되게끔 말이죠!

 

Document Sorting as Stable (SE-0372)

안정적인 문서 정렬을 제공해줍니다.

Swift의 정렬 알고리즘이 Swift 5 이전에 안정적으로 발전되었지만 문서는 여전히 업데이트 된적이 없다고 하네요!?

즉 sort() 메서드 있죠?

그 메서드는 Swift 5 이후부터 충분히 오랫동안 안정적이였음에도 불구하고 문서에는 요렇게 나와 있어요.

The sorting algorithm is not guaranteed to be stable. A stable sort preserves the relative order of elements that compare as equal.

즉 이 문구만 보면 여전히 정렬 알고리즘이 안정적이지 않겠네? 라고 오해할 수 있죠.

그래서, 기능 추가는 아니고 이번 업데이트는 Doc 변경입니다.

- /// The sorting algorithm is not guaranteed to be stable. A stable sort
+ /// The sorting algorithm is guaranteed to be stable. A stable sort
  /// preserves the relative order of elements that compare as equal.

요렇게 문서가 정렬 알고리즘 안정적입니다~ 하고 변경되었습니다.

 

마무리

위에 소개한것 외에도 StaticBigInt를 통해 더 큰 정수 표현하도록 업데이트된 내용도 있고, 버퍼 슬라이스 개선 및 조건부 컴파일 등 몇개 더 다양한 업데이트 내용이 있습니다!

관심 있으신 분들은 아래 공식 릴리즈 문서 Swift Evolution Appendix을 참고해서 보시면 좋을것 같아요ㅎㅎ

이번 버전 업데이트는 정말 놀랍거나 신박한 기능의 추가보다는 내실을 다잡는? 그런 업데이트 버전이였던것 같습니다🙋🏻

 

[참고 자료]

https://www.swift.org/blog/swift-5.8-released/

 

Swift 5.8 Released!

Swift 5.8 is now officially released! 🎉 This release includes major additions to the language and standard library, including hasFeature to support piecemeal adoption of upcoming features, an improved developer experience, improvements to tools in the S

www.swift.org

https://www.hackingwithswift.com/articles/256/whats-new-in-swift-5-8

 

What's new in Swift 5.8

Back-deployable APIs, more implicit self upgrades, improved result builders, and more!

www.hackingwithswift.com