What’s New in Swift 4?

Swift/Tip 2017. 6. 27. 14:24
반응형

[최종 수정일 : 2017.06.21]

원문 : https://www.raywenderlich.com/163857/whats-new-swift-4

What’s New in Swift 4?

Swift 4는 애플에서 2017년 가을에 베타버젼을 출시할 예정인 최신 주요 릴리즈 버젼입니다. 핵심 초점은 Swift 3 소스와의 호환성을 제공하고 ABI(application binary interface) 안정성을 위해 노력하는 것입니다.

이 글에서 코드에 가장 많은 영향을 줄수 있는 Swift의 변경사항을 중점적으로 설명합니다. 그리고 그것으로 시작합니다!

시작하기(Getting Started)

Swift 4는 Xcode 9에 포함되어있습니다. Xcode 9의 최신 버젼을 애플의 개발자 포탈에서 다운로드 받을수 있습니다. (활성 개발자 계정이 있어야 합니다). 각 Xcode 베타버전은 출시 시점의 최신 Swift 4 스냅샷을 번들로 제공합니다.

읽는 동안에, [SE-xxxx] 형식의 링크를 보게 될것입니다. 이 링크를 클릭하면 관련된 Swift 개선 제안서(Evolution proposal)로 이동합니다. 어떤 주제에 대해서 더 자세히 배우고 싶으면, 그것들을 체크아웃 해야합니다.

플레이그라운드에서 각 Swift 4 기능을 사용하거나 업데이트 하는 것이 좋습니다. 여러분의 머릿속에 있는 지식을 단단하게 만들고 각 주제에 대해서 깊이 파고 드는 능력을 부여할것입니다. 예제를 확장하고/중단하는 것을 시도해 보세요. 재미있게 해보세요.

주의
이 글은 각 Xcode 베타에 업데이트 될것입니다. 다른 Swift 스냅샷을 사용하는 경우 여기 코드가 동작하지 않을 수도 있습니다.

Swift 4로 마이그레이션하기(Migrating to Swift 4)

Swift 3에서 4로 마이그레이션하는 것은 2.2에서 3.0보다 훨씬 덜 복잡할 것입니다. 일반적으로, 대부분의 변경사항은 부가적인것이고 개인적인 변경이 필요하지 않습니다. 왜냐하면, Swift 마이그레이션 도구는 변경된 대부분을 처리해 줄 것입니다.

Xcode 9는 Swift 3의 중간 버젼인 Swift 3.2 와 Swift 4 모두 지원합니다. 프로젝트의 각 대상은 필요한 경우 마이그레이션 하여 Swift 3.2 또는 Swift 4 일수 있습니다. Swift 3.2로 변환하는 것이 완전히 자유롭지는 않으며, - 새로운 SDK와 호환성을 가지기 위해 코드를 업데이트 해야할 필요가 있고 Swift가 아직 안정적인 ABI가 아니기 때문에 Xcode 9에 의존성 있도록 다시 컴파일 해야합니다.

Swift 4로 마이그레이션할 준비가 되면, Xcode는 다시 한번 도울수 있는 마이그레이션 도구를 제공합니다. Xcode 에서, Edit/Convert/To Current Swift Syntax…로 이동하여 변환 도구를 띄워줍니다.

변환할 대상을 선택한 후에, Xcode는 Objective-C 에 대한 기본 설정을 표시합니다. 추론을 제한하여 바이너리 크기를 줄이려면 권장 옵션을 선택하세요(이 주제에 대해서 더 많은 것은, 아래의 @obj 추론 제한(Limiting @objc Inference)을 보세요)

코드에서 어떻게 변경될지 예상하는 것을 잘 이해하기위해, 먼저 Swift 4에서 API 변경사항을 다룰 것입니다.

API 변경사항(API Changes)

Swift 4에 도입된 추가 기능으로 넘어가기 전에, 기존 API에 어떤 변화/개선이 있는지 살펴보겠습니다.

문자열(Strings)

String은 Swift 4에서 많은 사랑을 받을 자격이 있습니다. 이 제안에는 많은 변경사항이 포함되어 있으며, 가장 큰 것을 변경해 봅시다. SE-0163

향수(nostalgic)를 느끼는 경우, 문자열은 이전 Swift 2.0과 같은 컬렉션입니다. 이 변경은 String에 characters배열이 필요한 것을 제거합니다. 이제 String객체를 직접 반복할 수 있습니다.

let galaxy = "Milky Way 🐮"
for char in galaxy {
  print(char)
}

String을 통해 논리적인 반복을 할뿐만 아니라, Sequence와 Collection으로부터 부가기능(bells and whistles)을 얻을수 있습니다.

galaxy.count       // 11
galaxy.isEmpty     // false
galaxy.dropFirst() // "ilky Way 🐮"
String(galaxy.reversed()) // "🐮 yaW ykliM"

// Filter out any none ASCII characters
galaxy.filter { char in
  let isASCII = char.unicodeScalars.reduce(true, { $0 && $1.isASCII })
  return isASCII
} // "Milky Way "

위의 ASCII 예제는 Character에 대한 작은 개선을 보여줍니다. 이제 Character로 부터 Unicode-ScalarView를 직접 접근할 수 있습니다. 아마도, 새로운 String 인스턴스가 필요했었습니다. SE-0178

다른 추가기능은 StringProtocol입니다. 그것은 이전에 String으로 선언된 기능 대부분을 선언합니다. 부분(slices) 작업 방식을 개선하기 위해 변경하였습니다. Swift 4는 String에서 서브시퀀스를 참조하는 Substring 타입을 추가하였습니다.

String과 Substring 모두 StringProtocol을 구현하여 거의 동일한 기능을 제공합니다.

// Grab a subsequence of String
let endIndex = galaxy.index(galaxy.startIndex, offsetBy: 3)
var milkSubstring = galaxy[galaxy.startIndex...endIndex]   // "Milk"
type(of: milkSubstring)   // Substring.Type

// Concatenate a String onto a Substring
milkSubstring += "🥛"     // "Milk"

// Create a String from a Substring
let milkString = String(milkSubstring) // "Milk🥛"

또 다른 큰 개선점은 String이 문자단위 클러스터(grapheme clusters) 해석하는 방법입니다. 이 해결책은 유니코드 9의 개정으로 부터 해결책이 나왔습니다. 이전에는 여러 코드 포인트로 구성된 유니코드 문자의 수가 1보다 컸습니다. 일반적인으로 이런 상황은 피부색상을 선택하는 이모디콘일때 발생합니다. 전후의 동작을 보여주는 몇가지 예가 있습니다.

"👩‍💻".count // Now: 1, Before: 2
"👍🏽".count // Now: 1, Before: 2
"👨‍❤️‍💋‍👨".count // Now: 1, Before, 3

이것은 문자열 선언문(String Menifesto)에 언급된 변경사항의 일부분입니다. 미래에 볼것을 기대하는 원래 동기와 제안된 해결책에 관해서 읽을 수 있습니다.

딕셔너리와 세트(Dictionary and Set)

Collection 타입에 관해, Set과 Dictionary는 언제나 직관적이지 않습니다, 운이 좋게도, Swift 팀은 SE-0165로 필요한 많은 사랑을 주었습니다.

시퀀스 기반의 초기화(Sequence Based Initialization)

목록의 첫번째 키-값(key-value) 쌍(튜플)으로 시퀀스로 사전을 만드는 기능입니다.

let nearestStarNames = ["Proxima Centauri", "Alpha Centauri A", "Alpha Centauri B", "Barnard's Star", "Wolf 359"]
let nearestStarDistances = [4.24, 4.37, 4.37, 5.96, 7.78]

// Dictionary from sequence of keys-values
let starDistanceDict = Dictionary(uniqueKeysWithValues: zip(nearestStarNames, nearestStarDistances)) 
// ["Wolf 359": 7.78, "Alpha Centauri B": 4.37, "Proxima Centauri": 4.24, "Alpha Centauri A": 4.37, "Barnard's Star": 5.96]
중복 키 해결(Duplicate Key Resolution)

이제 원하는 데로 중복된 키를 사용하여 딕셔너리를 초기화 할 수 있습니다. 아무말도 없이 키-값 쌍을 덮어 씌우는 것을 피하는데 도움이 됩니다.

// Random vote of people's favorite stars
let favoriteStarVotes = ["Alpha Centauri A", "Wolf 359", "Alpha Centauri A", "Barnard's Star"]

// Merging keys with closure for conflicts
let mergedKeysAndValues = Dictionary(zip(favoriteStarVotes, repeatElement(1, count: favoriteStarVotes.count)), uniquingKeysWith: +) // ["Barnard's Star": 1, "Alpha Centauri A": 2, "Wolf 359": 1]

위 코드는 중복된 키를 해결하기 위해 충돌하는 두 값을 더하는 +축약으로 zip를 사용합니다.

주의
zip에 익숙하지 않으면, 애플의 Swift 문서에서 쉽게 배울수 있습니다.

필터링(Filtering)

Dictionary와 Set은 원래 타입의 새로운 객체로 필터링된 결과를 가질수 있습니다.

// Filtering results into dictionary rather than array of tuples
let closeStars = starDistanceDict.filter { $0.value < 5.0 }
closeStars // Dictionary: ["Proxima Centauri": 4.24, "Alpha Centauri A": 4.37, "Alpha Centauri B": 4.37]
딕셔너리 맵핑(Dictionary Mapping)

Dictionary 값을 직접 맵핑하는데 매우 유용한 메소드를 얻었습니다.

// Mapping values directly resulting in a dictionary
let mappedCloseStars = closeStars.mapValues { "\($0)" }
mappedCloseStars // ["Proxima Centauri": "4.24", "Alpha Centauri A": "4.37", "Alpha Centauri B": "4.37"]
딕셔너리 기본 값(Dictionary Default Values)

Dictionary에서 값에 접근하는 일반적인 방법은 nil 결합 연산자를 사용하여 값이 nil인 경우 기본 값을 지정합니다. Swift 4 에서, 훨씬더 명확해지고 줄 변환(line mutation)에서 굉장한 것을 할수 있게 해줍니다.

// Subscript with a default value
let siriusDistance = mappedCloseStars["Wolf 359", default: "unknown"] // "unknown"

// Subscript with a default value used for mutating
var starWordsCount: [String: Int] = [:]
for starName in nearestStarNames {
  let numWords = starName.split(separator: " ").count
  starWordsCount[starName, default: 0] += numWords // Amazing 
}
starWordsCount // ["Wolf 359": 2, "Alpha Centauri B": 3, "Proxima Centauri": 2, "Alpha Centauri A": 3, "Barnard's Star": 2]

이전에는 이러한 타입의 변환은 if-let 문으로 감싸는 것이 필요했었습니다. Swift 4에서는 모든것이 한 줄로 가능합니다.

딕셔너리 그룹화(Dictionary Grouping)

놀랍게도 유용한 추가기능은 시퀀스로부터 딕셔너리를 초기화하고 버킷(buckets: [])으로 그룹화 하는 기능입니다.

// Grouping sequences by computed key
let starsByFirstLetter = Dictionary(grouping: nearestStarNames) { $0.first! }

// ["B": ["Barnard's Star"], "A": ["Alpha Centauri A", "Alpha Centauri B"], "W": ["Wolf 359"], "P": ["Proxima Centauri"]]

이것은 특정 패턴으로 데이터를 그룹화 할때 좋습니다.

용량 예약하기(Reserving Capacity)

Sequence와 Dictionary모두 용량을 명시적으로 예약할 수 있습니다.

// Improved Set/Dictionary capacity reservation
starWordsCount.capacity  // 6
starWordsCount.reserveCapacity(20) // reserves at _least_ 20 elements of capacity
starWordsCount.capacity // 24

이러한 타입의 재할당은 비용이 많이 드는 작업이 될수 있습니다. reserveCapacity(_:) 사용은 얼마나 많은 데이터를 저장할 필요가 있는지 생각할때 성능을 향상시키는 쉬운 방법입니다.

그것은 많은 양의 정보였으며, 두 타입 모두 확실히 확인하고 이러한 추가기능을 사용해서 코드를 멋지게 꾸미는 방법을 찾아보세요.

비공개 접근 수정자(Private Access Modifier)

Swift 3의 요소중에 fileprivate의 추가를 좋아하지 않았습니다. 이론적으로는 훌륭하지만 실제로는 사용법이 혼란스러울수 있습니다. 목표는 멤버 내에서 비공개를 사용하는 것이었고, 같은 파일에서 멤버간에 접근 권한을 공유하려는 상황에서 드물게 fileprivate를 사용합니다.

문제는 Swift가 확장 사용하여 코드를 논리 그룹으로 나누는 것을 권장한다는 것입니다. 확장은 원래 멤버 선원 밖에서 구성되며, 확장에서 fileprivate이 필요하게 됩니다.

Swift 4는 타입과 타입의 모든 확장간에 동일한 접근제어 범위를 공유하여 원래 의도를 실현합니다. 이것은 동일 파일 내에서만 유효합니다. SE-0169

struct SpaceCraft {
  private let warpCode: String

  init(warpCode: String) {
    self.warpCode = warpCode
  }
}

extension SpaceCraft {
  func goToWarpSpeed(warpCode: String) {
    if warpCode == self.warpCode { // Error in Swift 3 unless warpCode is fileprivate
      print("Do it Scotty!")
    }
  }
}

let enterprise = SpaceCraft(warpCode: "KirkIsCool")
//enterprise.warpCode  // error: 'warpCode' is inaccessible due to 'private' protection level
enterprise.goToWarpSpeed(warpCode: "KirkIsCool") // "Do it Scotty!"

이렇게 하면 코드 구조를 임시조치(bandaid) 하는 것보다 의도된 목적으로 fileprivate를 사용할 수 있습니다.

API 추가(API Additions)

이제 Swift 4의 새로운 멋진 기능을 살펴보겠습니다. 단순히 추가하는 것처럼 이러한 변경으로 기존 코드가 변경되지 않아야 합니다.

보관과 직렬화(Archival and Serialization)

지금까지 Swift에서 사용자정의 타입을 직렬화(serialize)하고 보관(archive)하려면 많은 수의 작업을 해야 합니다. class타입의 경우 NSObject를 서브클래스하고 NSCoding프로토콜을 구현해야 합니다.

struct와 enum과 같은 값 타입은 NSObject와 NSCoding을 확장할 수 있는 서브객체를 생성하는 것과 같은 작업이 필요했습니다.

Swift 4는 세가지 Swift 타입 모두에 직렬화를 제공하여 문제를 해결합니다. SE-0166

struct CuriosityLog: Codable {
  enum Discovery: String, Codable {
    case rock, water, martian
  }

  var sol: Int
  var discoveries: [Discovery]
}

// Create a log entry for Mars sol 42
let logSol42 = CuriosityLog(sol: 42, discoveries: [.rock, .rock, .rock, .rock])

이 예제에서 Swift 타입을 인코딩 가능하고 디코딩 가능한 것으로 만드는데 필요한 것은 Codable프로토콜을 구현하는 것을 알 수 있습니다. 모든 프로퍼티들이 Codable한 경우에, 프로토콜 구현은 컴파일러에 의해 자동으로 생성됩니다.

객체를 실제 인코딩하려면, 인코더로 전달해야 합니다. Swift 인코더는 Swift 4에서 적극적으로 구현되어 있습니다. 각 다른 스키마에 따라 객체를 인코딩 합니다. SE-0167 (주의 이 제안은 아직 개발중입니다)

let jsonEncoder = JSONEncoder() // One currently available encoder

// Encode the data
let jsonData = try jsonEncoder.encode(logSol42)
// Create a String from the data
let jsonString = String(data: jsonData, encoding: .utf8) // "{"sol":42,"discoveries":["rock","rock","rock","rock"]}"

이것은 객체를 가져와서 자동으로 JSON 객체로 인코딩 했습니다. JSONEncoder가 출력을 사용자정의하기 위해 노출하는 프로퍼티를 확인하세요.

이 프로세스의 마지막 부분은 데이터를 실제 객체로 다시 디코딩하는 것입니다.

let jsonDecoder = JSONDecoder() // Pair decoder to JSONEncoder

// Attempt to decode the data to a CuriosityLog object
let decodedLog = try jsonDecoder.decode(CuriosityLog.self, from: jsonData)
decodedLog.sol         // 42
decodedLog.discoveries // [rock, rock, rock, rock]

Swift 4 인코딩/디코딩을 사용하면 @objc 프로토콜의 오버헤드와 제한 사항에 의존하지 않고 Swift에서 타입 안정성을 기대할 수 있습니다.

키-값 코딩(Key-Value Coding)

Swift에서 함수는 클로저이기 때문에 지금까지 함수를 호출하지 않고 참조 할수 있었습니다. 프로퍼티의 기본 데이터에 실제로 접근하지 않고 프로퍼티의 참조를 유지하는 것을 할 수 없었습니다.

Swift 4에 추가된 매우 흥미로운 것은 인스턴스의 기본 값을 가져오거나 설정하기 위해 타입의 키 경로를 참조하는 기능입니다. SE-0161

struct Lightsaber {
  enum Color {
    case blue, green, red
  }
  let color: Color
}

class ForceUser {
  var name: String
  var lightsaber: Lightsaber
  var master: ForceUser?

  init(name: String, lightsaber: Lightsaber, master: ForceUser? = nil) {
    self.name = name
    self.lightsaber = lightsaber
    self.master = master
  }
}

let sidious = ForceUser(name: "Darth Sidious", lightsaber: Lightsaber(color: .red))
let obiwan = ForceUser(name: "Obi-Wan Kenobi", lightsaber: Lightsaber(color: .blue))
let anakin = ForceUser(name: "Anakin Skywalker", lightsaber: Lightsaber(color: .blue), master: obiwan)

다음은 이름, 광선검, 마스터를 설정하여 강제로 사용자 인스턴스 몇개를 생성하였습니다.
키 경로를 만드려면, 간단하게 프로퍼티 백슬러쉬(back-slash \) 뒤에 관심있는 프로퍼티를 사용하면 됩니다.

// Create reference to the ForceUser.name key path
let nameKeyPath = \ForceUser.name

// Access the value from key path on instance
let obiwanName = obiwan[keyPath: nameKeyPath]  // "Obi-Wan Kenobi"

이 인스턴스에서, ForceUser의 name프로퍼티에 대한 키 패스(key path)를 생성하였습니다. 그리고 나서 키 패스(key path)를 새로운 서브스크립트 keyPath로 전달하여 사용합니다. 이 서브스크립트는 이제 기본적으로 모든 타입에서 사용할 수 있습니다.

키 패스를 사용하여 서브 객체에 내려가거나(drill down), 프로퍼티를 설정하고, 키 경로 참조를 조립하는(build off) 예제가 더 있습니다.

// Use keypath directly inline and to drill down to sub objects
let anakinSaberColor = anakin[keyPath: \ForceUser.lightsaber.color]  // blue

// Access a property on the object returned by key path
let masterKeyPath = \ForceUser.master
let anakinMasterName = anakin[keyPath: masterKeyPath]?.name  // "Obi-Wan Kenobi"

// Change Anakin to the dark side using key path as a setter
anakin[keyPath: masterKeyPath] = sidious
anakin.master?.name // Darth Sidious

// Note: not currently working, but works in some situations
// Append a key path to an existing path
//let masterNameKeyPath = masterKeyPath.appending(path: \ForceUser.name)
//anakin[keyPath: masterKeyPath] // "Darth Sidious"

Swift에서 키 패스의 아름다움은 강력하게 입력된다는 것입니다. Objective-C 문자열 스타일의 실수는 더이상 없습니다.

여러줄의 문자열 원문(Multi-line String Literals)

많은 프로그래밍 언어의 공통적인 특징은 여러줄의 문자열 원문을 생성할 수 있는 것입니다. Swift 4에서는 3개의 따옴표(""")로 텍스트를 래핑하여 간단하고 유용한 구문을 추가합니다. SE-0168

let star = "⭐️"
let introString = """
  A long time ago in a galaxy far,
  far away....

  You could write multi-lined strings
  without "escaping" single quotes.

  The indentation of the closing quotes
       below deside where the text line
  begins.

  You can even dynamically add values
  from properties: \(star)
  """
print(introString) // prints the string exactly as written above with the value of star

이것은 XML/JSON 메시지를 작성할때나 UI에 긴 형식의 텍스트를 작성할때 매우 유용합니다.

단방향 범위(One-Sided Ranges)

자세한 정보를 줄이고, 가독성을 높이기 위해, 표준라이브러리는 단방향 범위를 사용하여 시작과 끝 인덱스를 추론할 수 있습니다. SE-0172

한가지 방법은 하나의 인덱스로부터 컬렉션의 시작이나 끝 인덱스 범위를 만드는 것입니다.

// Collection Subscript
var planets = ["Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"]
let outsideAsteroidBelt = planets[4...] // Before: planets[4..<planets.endIndex]
let firstThree = planets[..<4]          // Before: planets[planets.startIndex..<4]

보다시피, 단방향 범위는 시작이나 끝 인덱스를 명시적으로 지정해야 할 필요를 줄여줍니다.

무한 시퀀스(Infinite Sequence)

시작 인덱스가 셀수 있는 타입일때 무한 시퀀스(Sequence)를 정의 할 수 있습니다.

// Infinite range: 1...infinity
var numberedPlanets = Array(zip(1..., planets))
print(numberedPlanets) // [(1, "Mercury"), (2, "Venus"), ..., (8, "Neptune")]

planets.append("Pluto")
numberedPlanets = Array(zip(1..., planets))
print(numberedPlanets) // [(1, "Mercury"), (2, "Venus"), ..., (9, "Pluto")]

패턴 일치(Pattern Matching)

단방형 범위에 대한 다른 큰 용도는 패턴 일치입니다.

// Pattern matching

func temperature(planetNumber: Int) {
  switch planetNumber {
  case ...2: // anything less than or equal to 2
    print("Too hot")
  case 4...: // anything greater than or equal to 4
    print("Too cold")
  default:
    print("Justtttt right")
  }
}

temperature(planetNumber: 3) // Earth

제네릭 서브스크립트(Generic Subscripts)

서브스크립트는 직관적인 방법으로 데이터 타입에 접근하도록 구성되는 중요한 부분입니다. 유용성을 높이기 위해, 서브스크립트은 이제 제네릭을 사용할 수 있습니다. SE-0148

struct GenericDictionary<Key: Hashable, Value> {
  private var data: [Key: Value]

  init(data: [Key: Value]) {
    self.data = data
  }

  subscript<T>(key: Key) -> T? {
    return data[key] as? T
  }
}

예제에서, 반환 타입은 제네릭입니다. 이러한 제네릭 서브스크립트를 다음과 같이 사용할 수 있습니다.

// Dictionary of type: [String: Any]
var earthData = GenericDictionary(data: ["name": "Earth", "population": 7500000000, "moons": 1])

// Automatically infers return type without "as? String"
let name: String? = earthData["name"]

// Automatically infers return type without "as? Int"
let population: Int? = earthData["population"]

반환 타입이 제네릭일 뿐만아니라, 실제 서브스크립트 타입이 제네릭 일수 있습니다.

extension GenericDictionary {
  subscript<Keys: Sequence>(keys: Keys) -> [Value] where Keys.Iterator.Element == Key {
    var values: [Value] = []
    for key in keys {
      if let value = data[key] {
        values.append(value)
      }
    }
    return values
  }
}

// Array subscript value
let nameAndMoons = earthData[["moons", "name"]]        // [1, "Earth"]
// Set subscript value
let nameAndMoons2 = earthData[Set(["moons", "name"])]  // [1, "Earth"]

예제에서, 두가지 다른 시퀀스(Sequence) 타입(Array와 Set)을 전달하면 각각의 값으로 된 배열의 결과를 볼수 있습니다.

잡동사니(Miscellaneous)

Swift 4에서 가장 많은 변화를 처리합니다. 이제 더 작은 부분을 좀 더 빠르게 살펴봅시다.

MutableCollection.swapAt(::)

MutableCollection은 이제 변경가능한 메소드 swapAt(_:_:)메소드를 가집니다: 지정된 인덱스의 값을 바꿉니다 SE-0173

// Very basic bubble sort with an in-place swap
func bubbleSort<T: Comparable>(_ array: [T]) -> [T] {
  var sortedArray = array
  for i in 0..<sortedArray.count - 1 {
    for j in 1..<sortedArray.count {
      if sortedArray[j-1] > sortedArray[j] {
        sortedArray.swapAt(j-1, j) // New MutableCollection method
      }
    }
  }
  return sortedArray
}

bubbleSort([4, 3, 2, 1, 0]) // [0, 1, 2, 3, 4]

연관 타입 제약(Associated Type Constraints)

이제 where절을 사용해서 연관된 타입을 제한할 수 있습니다. SE-0142

protocol MyProtocol {
  associatedtype Element
  associatedtype SubSequence : Sequence where SubSequence.Iterator.Element == Iterator.Element
}

프로토콜 제한을 사용하여, 많은 associatedtype선언은 고생하지 않고 직접 제한할 수 있습니다.

클래스와 프로토콜 존재(Class and Protocol Existential)

마침내 Objective-C에서 Swift로 변경된 기능은 클래스 뿐만아니라 일련의 프로토콜을 준수하는 타입을 정의하는 기능입니다. SE-0156

protocol MyProtocol { }
class View { }
class ViewSubclass: View, MyProtocol { }

class MyClass {
  var delegate: (View & MyProtocol)?
}

let myClass = MyClass()
//myClass.delegate = View() // error: cannot assign value of type 'View' to type '(View & MyProtocol)?'
myClass.delegate = ViewSubclass()

@objc 추론 제한하기(Limiting @objc Inference)

Swift API를 Objective-C에 노출시키기 위해, @objc 컴파일러 속성을 사용합니다. 대부분의 경우 Swift 컴파일러가 추론했었습니다. 전체 추론에는 3가지 중요한 이슈가 있습니다:

  1. 바이너리의 크기가 늘어날 가능성이 있음
  2. 언제 @objc가 되는지 추론되는 것이 명확하지 않음
  3. 실수로 Objective-C 셀렉터(selector) 충돌이 생길 확률이 커집니다.

Swift 4는 @objc의 추론을 제한함으로써 이것을 해결합니다SE-0160. 이것은 Objective-C의 전체 동적으로 처리하길 원할때 @objc를 명시적으로 사용해야 합니다.

이러한 변경해야 하는 몇가지 예제는 private메소드, dynamic선언, NSObject서브클래스의 모든 메소드를 포함합니다.

NSNumber Bridging

NSNumber와 Swift 숫자들(numbers)간에 많은 고약한(funky) 동작들이 있어서 그 언어가 오랫동안 잊혀지지 않습니다. 운이 좋게도 Swift 4에서 이러한 버그들을 날려버립니다SE-0170.

다음은 동작의 예제를 보여주는 예입니다.

let n = NSNumber(value: 999)
let v = n as? UInt8 // Swift 4: nil, Swift 3: 231

Swift 3에서의 숫자가 오버플로우 되면 이상한 동작을 보이며, 0에서 시작합니다. 예제에서 999 % 28 = 231 입니다.

Swift 4는 숫자가 포함된 타입에서 안전하게 표현될수 있는 경우에만 강제 옵셔널 형변환(casting)으로 값을 반환하도록 하여 문제를 해결합니다.

Swift 패키지 매니져(Swift Package Manager)

지난 몇개월동안 Swift Package Manager에 대한 여러가지 업데이트가 있었습니다. 가장 큰 변화중 일부는 다음과 같습니다.

  • 브런치(branch) 또는 커밋(commit) 해쉬(hash)에 종속성 제공
  • 수용가능한 패키지 버젼의 많은 제어
  • 직관적이지 않는 고정 명령을 좀더 일반적인 해결 패턴으로 교체
  • 컴파일에 사용되는 Swift 버젼을 정의하는 기능
  • 각 대상에 대한 소스파일의 위치 지정

이것들은 모두 SPM을 얻을 수 있는 중요한 단계입니다. SPM에 대한 갈길이 멀지만, 적극적으로 참여함으로써 우리 모두를 도울수 있습니다.

최근에 무엇이 제안되었는지 개요를 보려면 Swift Package Manager Update를 체크아웃하세요.

아직도 진행중(Still In Progress)

이 문서를 작성할 당시에도 여전히 15개의 제안서가 큐에 있습니다. 이러한 것들을 자세히 살며보고 싶으면 Swift Evolution Proposals를 체크아웃하고 Accepted로 필터링하세요.

이 모든 것들을 지금 보지 않고, Xcode 9의 새로운 베타버젼마다 업데이트 할 것입니다.

여기에서 어디로 가야하나요?(Where to Go From Here?)

Swift 언어는 실제로 성장하고 수년동안 성숙해 왔습니다. 제안 프로세스와 커뮤니티 참여로 인해 변경사항이 파이프라인(pipeline)을 따라 내려가는지 추적하는 것이 매우 쉬워졌습니다. 또한 우리중 누구라도 진화에 직접적인 영향을 줄수 있습니다.

Swift 4의 이러한 변화로 인해 우리는 마침내 ABI의 안정성에 가까워져 있습니다. Swift 버젼의 업그레이드의 어려움은 점점 줄어들고 있습니다. 빌드 성능과 도구는 매우 향상 되고 있습니다. 애플 생태계 외부에서 Swift의 사용은 점점 더 실용적이 되고 있습니다. 그리고 생각하기에, 우리는 아마도 직관적인 구현에서 벗어난 String의 몇가지를 완전히 재작성할 것입니다.

Swift에는 훨씬 더 많은 것이 있습니다. 계속 진행되는 모든 변경사항을 확인하려면 다음 자료를 확인하세요.


반응형
Posted by 까칠코더
,