잔망스러운 신입의 주간 iOS 개발 일지 #5주차 - Swift 5에서 변경된게 뭐야?

이것, 저것 Feb 08, 2020

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)
}
Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.