기억할것: Swift는 여전히 ABI-안정성이 지원되지 않음!
이 글을 읽기 전에 모두가 알아야 할 사실은 Swift가 여전히 ABI에 안정적이지 않다는 사실이다. 이 말은 이전 버전의 Swift 컴파일러로 만든 바이너리가 현재 버전과 호환이 되지 않는다는 의미이다. 좀 더 알아듣기 쉽게 설명하자면 여러분의 모든 파일과 의존성들은 같은 버전의 Swift로 컴파일해야한다는 의미이다: 다른 버전의 Swift로 작성된 코드들은 합칠 수 없다.
애플은 현재 최신버전의 Swift3.0을 깨부수면서 (약속한것은 아니지만) 이것을 해결하려고 노력중에 있으며 Swift4에서 ABI 안정성을 지원할 계획이다.
2.3으로 가야할까 3.0으로 가야할까?
만약 여러분이 우리 VTS처럼 중요한 Swift 코드베이스를 가지고 있다면 아래에 해답이 있다.
그렇다. 두가지 다이다.
여러분의 Swift 코드베이스가 지저분하지 않다면 2.3을 생략하고 3.0으로 넘어가면 된다.
아래에 염두할 것들이다.
- 의존성은 중요하다. 여러분의 의존성이 Swift2.3이나 Swift3.0을 지원하는지 확인해야한다. 대부분 주요 라이브러리/프레임워크들은 둘 다 지원하고 있다(Alamofire, Charts). 그러나 기억해야할 것은 Swift3.0을 업그레이드 하는 방법은 하나밖에 없다. Swift3.0으로 새 개발을 시작하고 나면 2.3버전의 모든 의존성이 업데이트 없이는 작동하지 않을 것이다.
- 8.x 버전의 Xcode는 Swift2.3 지원을 멈출 것이다. Swift2.3은 Swift3.0으로 업그레이드 하기 힘든 프로젝트를 위한 매개 단계의 경향이 있다.
- 만약 여러 프로젝트에 걸쳐진 개인의 CocoaPods이 있다면 먼저 이것부터 업그레이드 해야한다.
2.3으로 업그레이드 할 수 있으나 어느정도 지연이 될지도 모른다. Xcode9가 배포되기 전에 Swift3으로 갈아타야하기 때문이다.
Swift2.2에서 2.3으로 마이그레이션 이슈
마이그레이터는 아마 Range<T>를 CountableRange<T>로 변환할 것이다. Swift3에는 오직 CountableRange<T> 밖에 없기 때문이다.
마이그레이터는 dispatch_queue_set_specific() 사용을 위해 /*Migrator FIXME: Use a variable of type DispatchSpecificKey*/ 주석을 달아 줄 것이다. 이것은 미래의 Swift3으로 마이그레이션 하는 것과 관련이 있다.
Swift2.x에서 Swift3.0으로의 마이그레이션 이슈
Swift의 3.0버전은 "모든걸 깨부순(break everything)" 배포이다. Swift3은 모든 것을 깨부수고 언어적 긴 용어가 필요없는 모든 것을 없앨 것이다. 따라서 많은 변화가 생기고, 프로젝트를 안정화시키기위해 그 많은 것들을 고쳐야할 것이다.
Swift2.3
Swift2.3은 굉장히 마이너한 업데이트이다. 어떤 식으로 마이너할까? 사실 딱 한가지가 바뀌었다. 컴파일타임 nullability가 바뀐 애플 SDK의 Objective-C 코드를 체크하는 것이다.
몇몇 마이너한 이름 재정의과 수많은 옵셔널 생성자와 같은 다른 변화들은 이제 옵셔널 대신에 필요한 오브젝트를 반환한다. 이것은 번들로부터 nib을 인스턴스화하는 것과 같은 것들에 적용됐다.
Swift3.0
(인지할만한)주요 변화들
private의 정의는 fileprivate로 바뀌었다.
공식적으로 private라 불리던 것이 이제 fileprivate로 바뀌었다. fileprivate 변수는 extension으로 접근할 수 있다. private 변수는 클래스에서 extension으로 접근할 수 없다.
public의 정의는 open으로 바뀌었다.
현재 클래스나 파라미터 public 정의는 두가지 기능을 제공한다.
- 외부모듈이 클래스나 맴버를 사용할 수 있다.
- 외부모듈이 클래스나 맴버를 오버라이드 할 수 있다.
Swift3에서의 public은 외부적으로 사용가능함이지, 오버라이드 가능함은 아니다.
이전의 이 기능은 open으로 되었다.
동사와 명사
함수 이름이 -d(과거형)으로 끝나는 것들은 그 오브젝트의 새 인스턴스를 반환한다.
이러한 변화는 reverse와 reversed, enumerate와 enumerated 등에 적용한다.
Objective-C의 불리언은 이제 is라는 단어로 시작되고 Foundation 타입은 더이상 접두에 NS를 사용하지 않는다(NSString처럼 String과 충돌이 일어나는 경우들은 빼고 말이다.).
Foundation 타입은 Swift의 let과 var 선언에서 아지 잘 동작할 것이다.
맴버로 불러오기
설명이 달린 C 함수들을 메소드로 부를 수 있다. 임포터(importer)는 보통 이러한 맵핑을 자동으로 추론할 수 있아며 Swift에 예전의 C-API를 자연스럽고 네이티브하게 사용할 수 있게 해준다. Case and point, CoreGraphics API. 참고로 CoreGraphics는 이번 배포때 공식적으로 업데이트 되지 않았다.
케멀케이스(CamelCase) 변화들
열거형이나 프로퍼티에서 머리문자(CG, NS등)로 시작하는 것들이 UpperCamelCase에서 LowerCamelCase로 대체되었다.
// Before
let red = UIColor.redColor().CGColor
// After
let red = UIColor.red.cgColor
(개인적으로 가장 작은 변화라 생각되는)상태절 변화
이제부터는 guard, if, while절에서 where 키워드를 사용할 수 없다. where 키워드는 for-in-where 형태의 for문에서는 사용할 수있다.
case 절에서도 마찬가지로 바뀌었다.
첫번째 인자의 문자 일관성
이렇게 이해하면 쉽다: 첫번째 파라미터의 이름은 디폴트로 필요하다.
(내가 좋아하는 변화인)묵시적으로 언랩핑된 옵셔널 다루기
여러분이 여러 언어(shared-language)로 프로젝트를 할 때 이 점 덕분에 마이그레이션 작업이 의미있어진다고 생각이 든다. 그것이 무엇일까?
이전에 ImplicitlyUnwrappedType! 프로퍼티를 가진 Objective-C 타입이 이제 WrappedType?로 되었다. 아래의 경우를 제외하고 모두 적용된다.
더 나은 Objective-C API 변환
네이밍이 더 명확해졌다.
최신식 디스패치
컬랙션 타입이 새로운 모델을 가지게 됨
이전에 컬랙션 타입에서 한 인덱스에서 시작하여 탐색할 때는 index의 successor 메소드를 사용해야 했다. 이제는 이 책임이 컬랙션으로 넘어가게 되었다. c.index(after:index) 이런식으로 작성한다. 컬랙션은 이제 어떤 comparable 타입의 인덱스를 가진다.
(아래 부분은 Range 오브젝트를 손수 만들때 적용된다. 보통 이렇게 할 일은 드물거라 생각된다.)
이런 변화에서 사이드 이팩트로서 Range가 여러 타입으로 쪼개어졌다(Range, ClosedRange, CountableRange, CountableClosedRange) ClosedRanged는 이제 그 타입(0...Int8.max)의 최대값 범위를 포함한다. Range와 ClosedRange는 더이상 반복(iterate)을 할 수 없다. 그 의무로서 오직 Comparable 오브젝트만 필요하다. 따라서 Range<String>을 만들 수 있다.
Objective-C의 id는 Swift의 Any 타입으로 불러와진다.
애플의 말을 인용하자면
이제부터 id는 'AnyObject'가 아닌 'Any'로 불러오기 때문에, 이전에 여러분이 'AnyObject'로 동적인 검색을 수행하는 곳에서 애러가 뜰 수도 있다.
(인식하지 못할지도 모르는)작은 변화들
옵셔널 비교연산자가 제거됨
현재 nil 타입은 비교가능하다.
이 점은 아래와같이 버그를 만들기 쉽다.
이제는 이것들을 비교하기 전에 반드시 언랩핑을 해주어야한다.
이것이 굉장히 좋은 점 중 하나이기도 하지만 유닛테스트를 망가뜨릴지도 모른다.
클러저 파라미터 이름과 레이블
수많은 클로저 파라미터 이름이 재정의되고 선택적으로 바뀌었다.
flatten이 join으로 명칭이 바뀌었다.
UnsafePointer<T> 다루기
오브젝트가 아닌 것들의 포인터 타입의 nullability는 옵셔널을 사용해 표현할 수 있다.
부동 소숫점 값을 반올림하는 기능은 이제 그 값이 가지고 있다.
이전에는 부동 소숫점을 반올림하기 위해 전역의 C함수(float나 ceil)을 사용할 수 있었다. 아직 이 함수들도 사용가능하나 디프리케이트 될지 고려되고 있다.
대신 아래와 같이 사용할 수 있다.
추가적인 반올림 기준이다.
- toNearestOrAwayFromZero
- toNearestOrEven
- towardZero
- awayFromZero
제네릭 타입 에일리어스
연산자 정의 문법의 변화
Objective-C의 상수는 이제 Swift 타입이다.
이제 Objective-C inter-op 식의 문자열을 사용할 수 없다.
굳이 걱정하지 않을 정도로 작은 것
NSError의 Bridging이 강화되었다.
nulTerminatedUTF8CString이 utf8CString으로 이름이 바뀜
문자열의 UnicodeScalar 생성자 중복을 제거
실패할 수 있는 UnicodeScalar 생성자는 이제 옵셔널을 반환한다.
더이상 튜플 splatting이 안된다.
더이상 curry한 func 선언 문법이 안된다.
이 모든 변화가 Swift3에서 일나날까?
아니다.
아직 이번 배포를 위해 검토하는 계획 안이다.(옮긴이: Swift3은 16.9.13에 정식 배포가 되었는데, 이 글은 16.8.31에 쓰여졌다) 몇 계획은 Swift3.x에서 달라질 것이고, 우리가 일반적으로 사용하지 않는 API 호출의 제거/병합과 같은 아주 마이너한 변화들은 포함시키지 않았다.
추가로 나는 이 개별적인 변화를 깊게 볼 필요가 없었으며, 대신 각 변화에대한 높은 수준에서 개괄적인 설명을 하였다. 몇 변화는 그 영역에서 파급표과를 가지고 있으며, 여러분이 이러한 변화에 관심이 있으면 Swift EVO project를 한번 방문해 보아라.
이번 배포에서 검토 진행/대기중인 변화들
- Sequence-기반 생성자를 추가하고 Dictionary에 메소드를 합친다.
- 약 참조(weak reference)에서 강 참조(strong reference)로 self를 업그레이드하여 옵셔널 바인딩을 사용할 수 있게 해준다.
- 표준 라이브러리에 AnyHashable을 추가한다.
3.x 배포에서 바뀐것들
- 순환 알고리즘
- .self 제거
- 커스텀 Objective-C 표현을 제공하는 것을 Swift 타입에서 허용
- 동적 케스터로부터 연결 변환 동작을 제거
- Sequence end-operation 이름을 합리화
보너스
제거되어서 우리 모두가 기쁜 것들
- Swift 언어에서 where문 제거
- 인스턴스 맴버를 접근하기 위해 self가 필요함
- extension에서 접근 변경자 제거