'multi-line string'에 해당하는 글 1건

제목: What's new in Swift 4.0

This article was translated with permission from the English original, What's new in Swift 4.0? on Hacking with Swift .

새로운 스위프트4의 새로운 점을 배우기위한 실제 예제 코드: 새로운 인코딩과 디코딩, 똑똑해진 키패스(keypaths), 다열 문자열(multi-line string) 등이 있습니다!

스위프트4.0이 모두의 인기있는 앱 개발 언어로 새로 배포되었고, 간단하고 안전한 코드를 작성할 수 있는 다양한 기능들을 소개했다. 스위프트 3.0이 발표되었을때 그 극적인 변화에 비하면 즐겁게 받아드릴 수 있을 것인데, 실제로 대부분 변화는 현재 존재하는 스위프트 코드와 완전히 호환이 되게 만든 것이다. 따라서 여러분이 약간의 변경사항을 원한다면, 그렇게 오래 걸리지 않을 것이다.

주의: 스위프트4는 아직 활발히 개발중이다. 나는 여기서 몇가지 유용한 새 기능을 골랐고, 이것들은 현재 모두 구현되었으며 시도해볼 수 있다. 마지막 배포가 있기 전의 그 달에 더 많은 기능이 나올 것임을 기억하라.

이 글이 마음에 들었다면, 아래의 것들도 흥미있을 수 있을 것이다.

스위프트스러운 인코딩과 디코딩
값 타입이 멋지다는 사실은 알고있지만, NSCoding과같은 Objective-C API와 인터렉트하기는 힘들다는 것도 알고있다. 둘을 이어주는 레이어를 만들거나 클래스에 넣어 사용해야한다. 두가지 방법 다 좋지만은 않다. 더욱 나쁜것은, 클래스에 넣어 변경할지라도 직접 인코딩과 디코딩 메소드를 작성해야하며, 이것은 고통스러운 에러를 야기할 수 있다.

스위프트4는 특수한 코드 없이 커스텀 데이터 타입을 시리얼라이즈하고 디-시리얼라이즈하게해주는 Codable 프로토콜을 소개했다. 여러분의 값 타입 손실에대해 더이상 걱정하지 않아도 된다. 더 나은점은 어떻게 데이터를 시리얼라이즈 할지 정할 수 있다는 것이다. 기존의 프로퍼티 리스트 양식을 사용할 수도 있고 JSON도 가능하다.

여러분이 읽고 있는 것이 맞는 말이다: 스위프트4는 특별한 코드 없이 여러분의 커스텀 데이터 타입을 JSON으로 시리얼라이즈 해준다.

이 얼마나 아름다운 광경인지 지켜보자. 먼저, 여기에 커스텀 데이터 타입이 있고, 그 인스턴스가 있다.
struct Language: Codable {
   var name: String
   var version: Int
}

let swift = Language(name: "Swift", version: 4)
let php = Language(name: "PHP", version: 7)
let perl = Language(name: "Perl", version: 6)
Language 구조체에 Codable 프로토콜을 따르게 한 것을 볼 수 있을 것이다. 이 간단한 추가사항만으로 아래처럼 JSON의 Data 표현으로 변환할 수 있다.
let encoder = JSONEncoder()
if let encoded = try? encoder.encode(swift) {
   // save encoded somewhere
}
스위프트는 자동으로 여러분의 데이터 타입 안에있는 모든 프로퍼티들을 인코딩할 것이다. 여러분은 딱히 할 일이 없다.

이제 여러분도 나와같이 오랫동안 NSCoding을 사용해왔다면 뭔가 의심스러울 것이다. 정말로 제대로 동작할까? 그리고 이게 동작한다는 것을 어떻게 확신할 수 있을까? 음, Data 오브젝트를 문자열로 만드는 코드를 추가해서 한번 출력해보자. 그리고 다시 Language 인스턴스로 디코딩 시켜, 읽을 수 있는지 보자.
if let encoded = try? encoder.encode(swift) {
   if let json = String(data: encoded, encoding: .utf8) {
       print(json)
   }

   let decoder = JSONDecoder()
   if let decoded = try? decoder.decode(Language.self, from: encoded) {
       print(decoded.name)
   }
}
디코딩이 타입캐스팅을 필요로 하지 않는다는 점을 인지하자. 첫번째 파라미터로 데이터 타입 이름을 주면, 스위프트는 여기서 리턴 타입을 추론한다.

JSONEncoder와 그 프로퍼티 리스트에 짝인 PropertyListEncoder는 이 부분이 어떻게 동작할지 수많은 커스터마이징 옵션을 가진다. 빽빽한(compact) JSON을 원하는지, 출력하지 좋은(pretty-printed) JSON을 원하는지, ISO8601 데이터를 원하는지, 유닉스 epoch 데이터를 원하는지, 바이너리 프로퍼티 리스트를 사용하고 싶은지 XML을 사용하고 싶은지 정할 수 있다. 다른 옵션들에대한 더 많은 정보를 원한다면  the Swift Evolution proposal for this new feature을 확인해보자.

다열 문자열 리터럴
스위프트에서 다열 문자열로 쓰려면 항상 문자열 안에 \n을 써서 개행을 했었어야 했다. 코드상에서 보기에 좋아보이지 않지만 사용자에게는 올바르게 보여진다. 다행히 스위프트4에서 새로 소개된 다열 문자열 리터럴 문법은 문자열 보간법같은 기능도 쓸 수 있는 채로, 자유롭게 개행을 할 수 있게하고 이스케이핑 없이 따옴표를 사용할 수 있다.

문자열 리터럴을 시작하기 위해서는 쌍따옴표를 3개(""") 적고 리턴키를 누른다. 그 다음 여러분이 원하는 만큼 문자열을 쳐내는데, 변수나 개행도 가능하며, 문자열이 끝날때는 리턴키를 누르고 쌍따옴표를 세개 치면 된다.

문자열 리터럴에는 두가지 중요한 규칙이 있기 때문에 리턴키를 누르는 것에대해 이야기하고 싶다. """으로 문자열을 열때, 여러분의 문자열 내용은 반드시 새로운 라인에서 시작해야한다. 그리고 """로 다열 문자열을 끝낼때는 새로운 줄에서 """을 쳐야한다.

여기에 예시가 있다.
let longString = """
When you write a string that spans multiple
lines make sure you start its content on a
line all of its own, and end it with three
quotes also on a line of their own.
Multi-line strings also let you write "quote marks"
freely inside your strings, which is great!
"""
이것은 정의에서 몇몇 개행이 들어있는 새로운 문자열을 만든 것이다. 훨씬 읽고 쓰기 쉽다.

더 많은 정보를 원한다면 the Swift Evolution proposal for this new feature을 확인해보자.

키-밸류 코딩을위한 키패스 증진(Improved keypaths for key-value coding)
Objective-C에서 사랑했던 기능 중 하나는 직접 접근하는 것이 아닌 동적으로 프로퍼티를 참조하는 능력이다. 이것은 "주어진 오브젝트 X에 내가 읽고 싶은 프로퍼티가 있어"라고 말할 수 있다. 이런 참조 방법을 키패스(keypath)라 부르고 이것은 직접 프로퍼티에 접근하는 것과 구별되는데, 여기서는 실제로 값을 읽고 쓰는 것이 아니라 슬며시 감춰놨다가 나중에 사용하는 것이다.

여러분이 아직 키패스를 사용해보지 않았다면 기존의 스위프트 메소드를 이용해서 어떤식으로 동작했는지 보여주겠다. Starship이라는 구조체를 하나 정의하고 Crew라는 구조체를 정의하자. 그리고 각각의 인스턴스를 하나씩 만든다.
// an example struct
struct Crew {
   var name: String
   var rank: String
}

// another example struct, this time with a method
struct Starship {
   var name: String
   var maxWarp: Double
   var captain: Crew

   func goToMaximumWarp() {
       print("\(name) is now travelling at warp \(maxWarp)")
   }
}

// create instances of those two structs
let janeway = Crew(name: "Kathryn Janeway", rank: "Captain")
let voyager = Starship(name: "Voyager", maxWarp: 9.975, captain: janeway)

// grab a reference to the goToMaximumWarp() method
let enterWarp = voyager.goToMaximumWarp

// call that reference
enterWarp()
스위프트에서는 함수가 일급 타입이므로 마지막 두 줄에서 goToMaximumWarp() 메소드를 enterWarp으로 참조하게 만들 수 있다. 그리고 나중에 우리가 원할때 언제든지 사용할 수 있다. 여기서 문제는 이와같은 것을 프로퍼티에는 못한다는 것이다. "불가피한 상황이 생겼을때 확인할 수 있도록 captain의 이름 프로퍼티를 참조하는 것을 만들어라"고 말할 수 없다. 스위프트는 단지 직접 프로퍼티를 읽어서 원래의 값을 얻어내가 때문이다.

이것은 키패스로 고칠 수 있는데, 우리의 enterWarp() 코드같은 프러퍼티를 참조하며 호출되지 않은 것(uninvoked references to properties)이다. 이제 참조를 호출하면 현재 값을 얻어낸다. 그러나 나중에 참조를 호출하면 나중에 값을 얻어낸다. 프로퍼티의 수를 통해 파헤쳐볼 수 있고, 스위프트는 올바른 타입을 받나냈는지 확인하기위해 타입 추론을 이용한다.

스위프트 에볼루션 커뮤니티는 이 키패스 문법이 올바른지에대해 꽤 오랫동안 토론해왔었는데, 이것이 스위프트 코드와 시각적으로 달라 보일 필요가 있었기 때문이다. 결국 백슬래시를 사용한 문법이 되었다. \Starship.name, \Starship.maxWarp, \Starship.captain.name. 이 두가지를 변수에 할당하여 여러분이 원하는 언제나 어떤 Starship 인스턴스에서도 사용할 수 있다. 예제를 보자.
let nameKeyPath = \Starship.name
let maxWarpKeyPath = \Starship.maxWarp
let captainName = \Starship.captain.name

let starshipName = voyager[keyPath: nameKeyPath]
let starshipMaxWarp = voyager[keyPath: maxWarpKeyPath]
let starshipCaptain = voyager[keyPath: captainName]
스위프트는 타입 추론이 가능하기 때문에 starshipName 문자열과 starshipMaxWarp 더블형을 만들 것이다. 이 예제의 세번째처럼 프로퍼티의 프로퍼티라도 스위프트는 역시 올바르게 이해할 것이다.

나중에는 런타임중에도 배열 인덱스를 접근할 수 있거나 문자열로부터 키패스를 만들어낼 수 있는 계획이 있다. 더 많은 정보를 원한다면 the Swift Evolution proposal for this new feature을 확인해보자.

약간의 광고
If you're enjoying this article, you might like my free Natural Swift video. It gives you 75 minutes of hands-on coding that teaches functional programming, protocol-oriented programming, and value types, and you can download it for free with no obligation or catches – just click here.

And now back to your regularly scheduled broadcast…

딕셔너리 기능 증진
스위프트4에서 한가지 더 흥미로운 프로퍼절은 딕셔너리를 더욱 강력하게 만들고 특정 상황에 여러분이 예상한대로 동작하게 만드는 기능이었다.

간단한 예제로 시작해보자. 스위프트3에서 딕셔너리를 필터링 하는 것은 새로운 딕셔너리를 반환하지 았다. 대신 키/값 래이블과 함께 튜플 배열을 반환했었다. 예제이다.
let cities = ["Shanghai": 24_256_800, "Karachi": 23_500_000, "Beijing": 21_516_000, "Seoul": 9_995_000];
let massiveCities = cities.filter { $0.value > 10_000_000 }
이후에는 더이상 딕셔너리가 아니므로 massiveCities["Shanghai"]로 읽을 수 없다. 대신 massiveCities[0].value를 사용해야했는데, 별로 좋아보이지 않는다.

스위프트4부터는 더욱 여러분이 생각한대로 동작한다. 여기서는 새로운 딕셔너리를 반환해준다. 명백하게 이것은 튜플-배열 리턴 타입에 의존하고있는 현재 코드를 변경해야 할 것이다.

비슷하게, 딕셔너리에서 map() 메소드도 많은 사람들이 원하던 방식대로 되지 않았었다. 전달되면 키-값 튜플을 받고, 배열에 추가하기위해 한 값을 반환할 수 있다. 예제를 보자.
let populations = cities.map { $0.value * 2 }
이것은 스위프트4에서 바뀌지 않았지만, mapValues()라 불리는 새 메소드가 추가되었다. 이것은 더욱더 유용할 것으로 보이는데, 값을 변경하고 기존의 키를 이용해 딕셔너리에 다시 값을 넣어둘 수 있게 해준다.

예를들어, 이 코드는 모든 도시 인구를 100만으로 나누고 문자열로 만든다. 그리고 다시 Shanghai, Karachi, Seoul의 같은 키로 새로운 딕셔너리에 넣는다.
let roundedCities = cities.mapValues { "\($0 / 1_000_000) million people" }
(여기서 여러분은 실수로 중복할 수도 있기 때문에 딕셔너리 키를 맵핑시키는게 안전하지 않다고 생각할 수 있다.)

쉽게, 내가 좋아하는 새로운 딕셔너리 추가사항은 grouping 생성자이다. 이것은 시퀀스를 여러분이 원하는대로 그룹화한 시퀀스의 딕셔너리로 만들어준다. 계속해서 cities 예제로가서, 도시 이름의 배열을 얻어내기위해 cities.keys를 사용할 수 있으며, 첫글자로 그룹화한다.
let groupedCities = Dictionary(grouping: cities.keys) { $0.characters.first! }
print(groupedCities)
// ["B": ["Beijing"], "S": ["Shanghai", "Seoul"], "K": ["Karachi"]]
대신 아래처럼 그 이름의 길이에따라 도시들을 그룹화 시킬 수 있다.
let groupedCities = Dictionary(grouping: cities.keys) { $0.count }
print(groupedCities)
// [5: ["Seoul"], 7: ["Karachi", "Beijing"], 8: ["Shanghai"]]
마지막으로, 딕셔너리 키에 접근할 수 있고 키에 해당하는 값이 없다면 디폴트 값을 제공할 수 있다.
let person = ["name": "Taylor", "city": "Nashville"]
let name = person["name", default: "Anonymous"]
이제 경험이 좀 있는 개발자들은 nil-coalescing으로 사용하는게 더 났다고 주장할 것이다. 나도 동의한다. 현재 버전의 스위프트것을 사용하는 것 대신에 아래처럼도 쓸 수 있다.
let name = person["name"] ?? "Anonymous"
그러나 그냥 읽는데는 문제가 없지만, 딕셔너리 값을 수정할때는 동작하지 않는다. 적절하게 딕셔너리 값을 수정할 수 없는데, 그 키로 접근하면 옵셔널을 반환하기 때문이다. 키가 존재하지 않을 수도 있다. 스위프트4의 디폴트 딕셔너리 값으로 여러분은 더욱 적합한 코드를 작성할 수 있다. 아래처럼 말이다.
var favoriteTVShows = ["Red Dwarf", "Blackadder", "Fawlty Towers", "Red Dwarf"]
var favoriteCounts = [String: Int]()

for show in favoriteTVShows {
   favoriteCounts[show, default: 0] += 1
}
이 코드는 favoriteTVShows에 있는 모든 문자열을 돌면서, favoriteCounts라는 딕셔너리를 사용하여 각 항목이 나타날때마다 카운팅을 한다. 코드 한줄에서 딕셔너리를 수정할 수 있는데, 딕셔너리에는 항상 값을 가질거라는 것을 알기 때문이다. 디폴트값의 0이든 이전에 카운팅되었던 것에따라 더 높은 숫자든.

더 많은 정보를 원한다면 the Swift Evolution proposal for these new features을 확인해보자.

문자열은 다시 컬랙션이다!
작은 변화지만 많은 사람들을 행복하게 만든다. 문자열이 다시 컬랙션이 되었다. 이 의미는, 이것을 뒤집거나, 문자를 하나씩 돌거나, map()하거나 flatMap()하는 등이 가능하다. 예제를 보자.
let quote = "It is a truth universally acknowledged that new Swift versions bring new features."
let reversed = quote.reversed()

for letter in quote {
   print(letter)
}
이 변경은 String Manifesto라 불리는 다양한 개정사항의 부분에 소개되어 있다.

한쪽만의 범위(One-sided ranges)
가장 최신은 아니지만 최근에, 스위프트4는 파이썬같은 한쪽만 컬랙션 자르기를 소개했다. 한쪽이 빠지면 자동으로 컬랙션의 시작이나 끝을 추론한다. 이 변경은 현재 코드에 영향을 주지 않는다. 현재 연산자에 새로운 부분이기 때문에 잠재적인 손상을 걱정하지 않아도 된다.

아래에 예제가 있다.
let characters = ["Dr Horrible", "Captain Hammer", "Penny", "Bad Horse", "Moist"]
let bigParts = characters[..<3]
let smallParts = characters[3...]
print(bigParts)
// ["Dr Horrible", "Captain Hammer", "Penny"]
print(smallParts)
// ["Bad Horse", "Moist"]
더 많은 정보를 원한다면 the Swift Evolution proposal for this new feature을 확인해보자.

아직 더 남은게 있다...
스위프트4를 탑재한 Xcode의 첫번째 배포판은 6월에 iOS11, tvOS11, watchOS4, macOS와함께 나올것으로 보인다(역자: 이미 나왔습니다). 지금까지 우리가 본 것은 특히 명확하게 이미 나오기로 약속되었고, 팀은 가능한 추가할 수 있도록 스위프트4를 만들려고 노력하고 있다. 주로 기존에 있는 것을 고치거나 수정하지 않도록 새로운 기능을 추가하는 것이 업그레이드하기 편하기 해줄 수 있고, 고맙게도 이 언어를 위한 새로운 안정성의 시작의 신호가 있다.

스위프트 에볼루션이 때론 혼돈속에 빠지기도 했지만(접근 수준같은 부분), 스위프트4는 애플의 커뮤니티 방법을 다시 검증했다. 나는 위에 몇몇 스위프트 에볼루션을 링크 걸어두었는데, 각각의 것들은 커뮤니티 여론의 도움으로 광범위하게 토론되었다. 애플 엔지니어가 변경을 강행하지 않고 분별력있게 하였으며, 무엇이 이미 스마트하고 엘레강트한 언어인지 다듬기위해 고려했다.

한가지 지연된 기능은 ABI 호환성인데, 이것은 개발자들에게 컴파일된 라이브러리를 배포할 수 있게 해줄 것이다. 오늘 스위프트에 몇가지 핵심 기능이 구현되지 않고 남아있다. 다행히도 스위프트5가 되기 전에 만나볼 수 있을 것이다...


이 블로그는 공부하고 공유하는 목적으로 운영되고 있습니다. 번역글에대한 피드백은 언제나 환영이며, 좋은글 추천도 함께 받고 있습니다. 피드백은 

으로 보내주시면 됩니다.




WRITTEN BY
tucan.dev
개인 iOS 개발, tucan9389

,