관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] 2. swift 5.8 정리 - optional generic, lazy var, @backDeployed 본문

iOS 응용 (swift)

[iOS - swift] 2. swift 5.8 정리 - optional generic, lazy var, @backDeployed

jake-kim 2023. 4. 10. 01:30

1. swift 5.8 정리 - implicit self, magic file name, collection downcast

2. swift 5.8 정리 - optional generic, lazy var, @backDeployed < 

Swift5.8 Release

  • 2023년 3월 30일 출시
  • Xcode 14.3부터 사용 가능

optional generic

  • SE-0375
  • any 프로토콜 타입의 값을 제네릭 파라미터로 넘길때 Optional 타입도 가능
  • Swift5.7에서는 any 타입의 값을 함수 제네릭 매개변수 넘길 때, 타입은 non-optional만 가능했지만 swift5.8에서는 Optional 선언도 가능

ex) swfit5.7에서 에러 발생하지만 swift5.8에서는 에러 x

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 {
    let value = optionalDouble(number) // error: type 'any Numeric' cannot conform to 'Numeric'
    print(value)
}
  • 만약 swift5.7에서 사용한다면 Optional 불가
    • swift5.7에서는 아래처럼 수정이 필요
func optionalDouble<T: Numeric>(_ number: T) -> T {
...

Result Builder에서 lazy var 사용 가능

  • SE-0373
  • swift5.7에서는 result builder 내부에서 lazy var를 사용하면 아래처럼 오류가 발생하지만 5.8는 사용 가능
// refer: https://www.hackingwithswift.com/articles/256/whats-new-in-swift-5-8

enum UserState {
    case subscriber, nonsubscriber, unknown
}

struct User {
    var id: UUID
    var username: String
}

struct ContentView: View {
    @State private var state = UserState.unknown

    var body: some View {
        VStack {
            lazy var user = fetchUsername() // // error: cannot declare local lazy variable in result builder

            switch state {
            case .subscriber:
                Text("Hello, \(user.username)")
            case .nonsubscriber:
                Text("Hello, \(user.username)")
                Button("Subscribe now") {
                    startSubscription(for: user)
                }
            case .unknown:
                Text("Sign up today!")
            }
        }
        .padding()
    }

    // Example function that would do complex work
    func fetchUsername() -> User {
        User(id: UUID(), username: "Anonymous")
    }

    func startSubscription(for user: User) {
        print("Starting subscription…")
    }
}

@backDeployed

  • SE-0376
  • 원래는 @available로 "~버전 이상"의 정보를 제공했다면, 이제는 ~버전 이하에서만 동작하도록 해줄수있는 것
extension String {
    @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 someValue() -> Self {
        "value"
    }
}
  • @backDeployed가 왜 나왔는지?
    • 특정 함수가 기존에 @available(iOS 14.0, *)으로 선언되어 있을때, 만약 iOS 15부터 새로운 함수가 생긴 경우, @backDeployed가 없다면 아래처럼 작업
@available(iOS 14.0, *)
func someFunc() {

}
  • iOS 15부터 새로운 함수가 등장) 기존 함수 안에 if문으로 분기를 태워야함
@available(iOS 14.0, *)
func someFunc() {
    if #available(iOS 15.0, *) {
        print("새로운 함수 호출")
    } else {
    	print("기존 함수 호출")
    }
}
  • 만약 backDeployed를 사용하면 함수 내부에서 분기를 태우지 않아도 함수를 새로 추가하여 구현이 가능
    • 사용자가 iOS15에서 기존의 someFunc를 사용할때 backDeployed에 의해서 알아차리게됨
    • 함수 안에서 분기문을 치지 않아도 되므로 더욱 깔끔한 코드 작성이 가능
public extension String {
    @backDeployed(before: iOS 16.4)
    @available(iOS 14.0, *)
    func someFunc() {
    }
    
    @available(iOS 15.0, *)
    func someNewFunc() {
    }
}

* 참고

https://online.swiftplayground.run/

https://github.com/apple/swift-evolution/blob/main/proposals/0373-vars-without-limits-in-result-builders.md

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

https://github.com/apple/swift-evolution/blob/main/proposals/0375-opening-existential-optional.md

https://github.com/apple/swift-evolution/blob/main/proposals/0376-function-back-deployment.md

https://www.swift.org/swift-evolution/#?version=5.8 

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

Comments