Swift가 짜쳐서 문서 스펙을 보고 있는데, 5.0에서 5.1까지 변화된 사항을 짧은 코멘트와 코드로 정리해보았습니다. 최신 스펙의 변경점에서 향후 Swift가 지향하는 방향에 대해서 제가 Swift 꼬꼬마라서 알 순 없지만 사용성에 많은 신경을 쓴다는 것은 확실히 알 수 있습니다. 그리고 Chris Lattner (https://github.com/lattner)가 Google로 갔다는데 흠...?

5.1의 변화

SE-0068 Expanding Swift Self to class members and value types

self가 아니다. Self다.

// 클래스에 사용
class MyClass {
    class var name: String {
        return "unknow name"
    }

    func show1() {
        print("name is \(MyClass.name)")
    }

    func show2() {
        print("name is \(Self.name)")
    }

}

class YourClass: MyClass {
    override class var name: String {
        return "your name"
    }
}

// 구조체도 가능
protocol MyType {
    static var name: String { get }
    func show()
}

extension MyType {
    func show() {
        print("My name is \(Self.name)")
    }
}

struct John: MyType {
    static var name = "John"
}

struct Conrad: MyType {
    static var name = "Conrad"
}

let j = John()
j.show()    // "John"

let c = Conrad()
c.show()    // "Conrad"

SE-0240 Ordered Collection Diffing

순서만 보장된다면! 비교 뿐만 아니라 복원도 가능하다!

let alpha1 = ["a", "b", "c"]
let alpha2 = ["a", "c", "d"]
let diff = alpha1.difference(from: alpha2)
// remove(offset: 2, element: "d", associatedWith: nil)
// insert(offset: 1, element: "b", associatedWith: nil)

for d in diff {
    switch d {
    case .remove(let offset, _, _):
        alpha2.remove(at: offset)
    case .insert(let offset, let element, _):
        alpha2.insert(element, at: offset)
    }
}
// alpha1 == alpha2    // true

SE-0242 Synthesize default values for the memberwise initializer

생성자를 쉽게 만들면, 객체도 쉽게 만들 수 있으려나?

struct Dog {
  var age: Int = 0
  var name: String

  // The generated memberwise init:
  init(age: Int = 0, name: String)
}

// This now works
let sparky = Dog(name: "Sparky") // Dog(age: 0, name: "Sparky")

SE-0244 Opaque Result Types

정적 컴파일을 속여서 얻는 이득이 뭘까?

// 컴파일 에러
func someList() -> Collection {
    return [1, 2, 3]
}

// 안 컴파일 에러
func someList() -> some Collection {
    return [1, 2, 3]
}

SE-0245 Add an Array Initializer with Access to Uninitialized Storage / SE-0247 Contiguous Strings

한 덩어리의 메모리가 필요한 이유, O(1)을 향한 간절함이냐?

let list = [Int](unsafeUninitializedCapacity: 5) { (buffer, initializedCount) in
    initializedCount = 5
}

SE-0252 Key Path Member Lookup

이걸 어디 쓰나 싶은데, 잘 사용하면 흠?

@dynamicMemberLookup
struct Car<T> {
    private let p: T

    init(_ p1: T) {
        p = p1
    }

    subscript<U>(dynamicMember keyPath: WritableKeyPath<T,U>) -> U {
        return p[keyPath: keyPath]
    }
}

struct Hyundai {
    var name = "Sonata"
}

let a = Car(Hyundai())
print(a.name)

SE-0254 Static and class subscripts

KV를 쓰는게?

struct HTTPStatus {
    private static var map = [Int: String]()

    static subscript(_ code: Int) -> String? {
        get {
            map[code]
        }
        set {
            map[code] = newValue
        }
    }
}

HTTPStatus[200] = "Ok"
HTTPStatus[404] = "Not Found"
HTTPStatus[500] = "Internal Server Error"

print(HTTPStatus[404])    //Not Found

SE-0255 Implicit returns from single-expression functions

줄일 수 있는 건 다 줄인다일까? 아니면 당연해서 일까?

func whatIsThree() -> Int {
    1 + 2
}

SE-0261 Identifiable Protocol

DB인줄...

struct Korean: Identifiable {
    let id: String
    let name: String
    init(name: String, juminNumber: String) {
        self.name = name
        self.id = juminNumber
    }
}

let me = Korean(name: "오랭이", juminNumber: "123456-7890123")
let you = Korean(name: "홍길동", juminNumber: "334455-1122334")

SE-0258 Property Wrappers

긴말하지 않겠다. 이것은 어노테이션!

fileprivate struct Store {
    static private var stores: [String: Any?] = [:]

    static func value(key: String) -> Any? {
        return stores[key] ?? nil
    }
    static func set(key: String, value: Any?) {
        stores.updateValue(value, forKey: key)
    }
}

@propertyWrapper
struct Storage<T> {
    private let key: String
    init(_ key: String) {
        self.key = key
    }
    var wrappedValue: T? {
        get {
            return Store.value(key: key) as? T
        }
        set {
            Store.set(key: key, value: newValue)
        }
    }
}

class A {
    @Storage("has_visited_app")
    var hasVisitedApp: Bool?
}

SE-0248 String Gaps and Missing APIs

UTF 관련 메소드가 추가되었다는데, 지금? 흠..., 흠터레스팅

extension Unicode.UTF16 {
  /// Returns a Boolean value indicating whether the specified code unit is a
  /// high or low surrogate code unit.
  public static func isSurrogate(_ x: CodeUnit) -> Bool

  /// Returns whether the given code unit represents an ASCII scalar
  public static func isASCII(_ x: CodeUnit) -> Bool
}

이하 생략

5.0의 변화

SE-0192 Handling Future Enum Cases

모든 catch-all case를 만족하던가 아니면 경고를 발생시켜라

enum ModernLang{
    case deno
    case rust
    case swift
}

func printModernLang(moderLang: moderLang) {
    switch modernLang {
        case .deno:
            print("hello, Deno!")
        case .rust:
            print("hello, rust!")
        @unknown default:
            print("Hello, everyOne?")
    }
}

SE-0200 Enhancing String Literals Delimiters to Support Raw Text

#를 사용한 문자열 추가(interpolation), 문자열에 관한 내용은 언제나 감사하지만 너무 많아서 문제랄까?

let hello = #"Hello! "Swift""#
print(#" Swift is "Modern Lang!", \#(hello) "#)

SE-0215 Conform Never to Equatable and Hashable

말 장난인가? "...or use Never for its Error to represent something that never errors."

extension Never: Equatable {
  public static func == (lhs: Never, rhs: Never) -> Bool {
    switch (lhs, rhs) {
    }
  }
}

extension Never: Hashable {
  public func hash(into hasher: inout Hasher) {
  }
}

SE-0216 Introduce user-defined dynamically “callable” types

우리는 확장할 것이다.

@dynamicCallable @dynamicMemberLookup
struct JSValue {
  // JavaScript doesn't have keyword arguments.
  @discardableResult
  func dynamicallyCall(withArguments: [JSValue]) -> JSValue { ... }

  // This is a `@dynamicMemberLookup` requirement.
  subscript(dynamicMember member: JSValue) -> JSValue {...}

  // ... other stuff ...
}

SE-0218 Introduce compactMapValues to Dictionary

Syntax Sugar의 끝판왕

let d: [String: String?] = ["a": "1", "b": nil, "c": "3"]
let r1 = d.filter { $0.value != nil }.mapValues { $0! }
let r2 = d.reduce(into: [String: String]()) { (result, item) in result[item.key] = item.value }
// r1 == r2 == ["a": "1", "c": "3"]

let d: [String: String?] = ["a": "1", "b": nil, "c": "3"]
let r4 = d.compactMapValues({$0})
// r4 == ["a": "1", "c": "3"]

SE-0224 Support ‘less than’ operator in compilation conditions

<, >, <=, >= 중 사용하지 않는 것은?

// Swift < 5.0
#if !compiler(>=4.2)
// This will only be executed if the Swift compiler version is less than 4.2.
#endif

// Swift >= 5.0
#if compiler(<4.2)
// This will only be executed if the Swift compiler version is less than 4.2.
#endif

SE-0225 Adding isMultiple to BinaryInteger

나머지 연산자는 이제 안녕인가요?

// Swift < 5.0
cell.contentView.backgroundColor = indexPath.row % 2 == 0 ? .gray : .white

// Swift >= 5.0
cell.contentView.backgroundColor = indexPath.row.isEven ? .gray : .white

SE-0234 Remove Sequence.SubSequence

Swift 5.0이 Swift 4.x와 호환이 되지 않는건 성능 때문

// 이런거 이제 안됨
extension Sequence {
 func dropTwo() -> SubSequence {
   return dropFirst(2)
 }
}

SE-0235 Add Result to the Standard Library

Alamofire의 Result는 삭제되는데...

enum Result<Value, Error> {
    case success(Value)
    case failure(Error)
}