こんにちは!Swift愛好会&Kotlin愛好会主催の@jollyjoesterです。本書を手に取っていただき、ありがとうございます。
本書はSwiftを愛するメンバーが集まるSwift愛好会とKotlinを愛するメンバーが集まるKotlin愛好会の有志メンバーがネタを持ち寄って作成した初の技術同人です。言語についての話にとどまらず、Swift/Kotlinに関連の深いプラットフォームやサービスについての話など多岐にわたるネタについて盛り込んでいます。
各章は独立しています。著者紹介でそれぞれの章の概要を紹介しますので、興味を持った章から読み進めてください。楽しんでいただけるととても嬉しいです。
ドーモ!jollyjoesterです。ついにSwift 5がリリースされました。Swift 5は2019年3月にリリースされたばかりのSwiftの最新バージョンです。Swift 5にはABI安定化やStringの再実装、実行時のメモリへの排他アクセスの強制、動的に呼び出し可能な型のサポートなど、様々な進化が詰まっています。しかし、それらは理解するには難しいのと普通に実装している分には意識しなくていい部分が多かったりします。
本章では、Swift5で「書き方が変わった」、「新しい書き方ができるようになった」ようなわかりやすい部分をピックアップしてゆるふわに紹介していこうと思います。新しいSwift 5を楽しく使っていきましょう!なお、筆者はiOSアプリ開発者であり、検証はXcode10.2を用いています。
Swiftでは文字列を表すための区切り文字としてダブルクォーテーション(")を使用します。この文字列の中で特殊な文字を使うときはバックスラッシュ(\)を用いてエスケープをする必要があります。
例えば「Hello, Swift "5" has come!」というダブルクォーテーション入りの文字列を表示させたい場合、ソースコード上では下記のようにエスケープしなければなりません。
print("Hello, Swift \"5\" has come!")
でももし文字列中にエスケープが必要な文字が多く含まれていたらとてもめんどくさいですよね?ということでSwift 5からRaw Text*1が導入されました。
[*1] SE-0200 Enhancing String Literals Delimiters to Support Raw Text
Raw Textは#を用いて下記のように記述します。
print(#"Hello, Swift "5" has come!"#)
Raw Textを用いるとエスケープの必要がなくなり、表示したい文字列そのままを指定することができるようになります。表示したい文字列をどこかからコピペしてくる必要がある場合などに便利ですね。特殊文字を多く扱う場合の多い正規表現を扱うときに特に便利そうです。エスケープめんどくせぇ!と思ったらRaw Textの出番と覚えておきましょう!
なお、Raw Textという仕組みは多くの他の言語にもある仕組みで、SwiftのRaw TextはRustのRaw Textの影響を受けているようです。
isMultiple(of:)というメソッドで整数がある数の倍数であるかどうかを判別できるようになりました。*2
[*2] SE-0225 Adding isMultiple to BinaryInteger
これまである整数が偶数か奇数かを判別するのに「2で割った余りが0か否か」で判別していた方も多いと思います。
let num = 3
if num % 2 == 0 {
print("even")
} else {
print("odd")
}
これが下記のように書けるようになります。
let num = 3
if num.isMultiple(of: 2) {
print("even")
} else {
print("odd")
}
2の倍数だったら偶数、じゃなかったら奇数と1 step省いて書けるようになりました。ちなみにisMultiple(of:)の提案者は偶数奇数を判定するisEvenやisOddも提案していましたが、isMultiple(of:)のみが承認されたようです。
みんな大好きFizzBuzzも素直にかけます。
(1...100).forEach{
switch $0 {
case let x where x.isMultiple(of: 3*5):
print("FizzBuzz")
case let x where x.isMultiple(of: 3):
print("Fizz")
case let x where x.isMultiple(of: 5):
print("Buzz")
default:
print($0)
}
}
一般的なテキスト処理と高度なテキスト処理の両方のユースケースをサポートするためにUnicode.scalar*3に様々なプロパティが追加されました。
[*3] SE-0211 Add Unicode Properties to Unicode.Scalar
例えばisEmojiというプロパティを使って文字列から絵文字だけを取り出すことができます。
let message = "Happy🎉birthday🎂"
message.unicodeScalars.filter { $0.properties.isEmoji }
// "🎉🎂" // 紙面作成の都合で印字されませんが、手元で試してみてください
他にもisLowercase, isUppercase, isDash, isQuotationMarkなど様々なプロパティがあるので遊んでみると面白いです。
前述のUnicode ScalarへのプロパティはUnicodeに詳しい人が高度な操作をするためのものでしたが、もうちょっとわかりやすいものについてはCharacterのプロパティ*4にも追加されています。
[*4] SE-0221 Character Properties
例えば文字が数字かどうか判定することができます。
let char: Character = "3"
char.isNumber
// true
他にもisASCII, isWhitespaceなどが追加されています。
RangeがCodableに適合しました*5。これまでString, Int, Doubleなどの標準ライブラリの型と、Date, Data, URLなどのFoundationの型、およびこれらの型を要素に持つOptional, Array, DictionaryがCodableに適合していましたがRangeも適合した結果、例えば下記のように年齢、身長、温度など一定の幅のある数値を扱う際に便利になりました。
struct Toy : Codable {
let name: String
let ageRange: Range<Int>
}
let car = Toy(name: "car", ageRange: Range(4...6))
let data = try! JSONEncoder().encode(car)
let json = String(data: data, encoding: .utf8)
// "{"name":"car","ageRange":[4,16]}"
[*5] SE-0239 Add Codable conformance to Range types
compactMapValues*6はDictionaryの値の型変換やnilの除去ができるものです。
[*6] SE-0218 Introduce compactMapValues to Dictionary
理解のためにArrayのcompactMapから復習していきましょう。Arrayにはnilを取り除く操作であるcompactMapがあります。
["1", "2", nil, "4", "5"].compactMap{ $0 }
// ["1", "2", "4", "5"]
これを利用して型変換を一括で行い、型変換に失敗したものは取り除いたArrayを得ることもできます。例えば下記は各要素をInt型に変換してできなかった要素は取り除いたArrayが得られます。
["1", "2", "A", "4", "5"].compactMap{ Int($0) }
// [1, 2, 4, 5]
// "A"はIntに型変換できないので取り除かれた
これのDictionary版がcompactMapValuesです。Dictionaryの値がnilである要素を取り除いたDictionaryを得ることができます。
["kei": "6", "rei": "4", "rui": nil].compactMapValues{ $0 }
// ["rei": "4", "kei": "6"]
Dictionaryの値を型変換し、型変換できなかった要素を取り除いたDictionaryを得ます。
["kei": "6", "rei": "4", "rui": "baby"].compactMapValues{ Int($0) }
// ["kei": 6, "rei": 4]
// "baby"はIntに型変換できないので取り除かれた
なお、余談ですがcompactMapValuesに貢献したのは@d_dateさんという日本人です。Swiftにどうやって新機能を提案していくかに興味がある方は彼の発信する情報を追ってみると良いでしょう。
Swiftの標準ライブラリにResult*7型が追加されました。
[*7] Add Result to the Standard Library
すごく雑に言うとResult型はOptional型の親戚です。Optional型は「値がある or nil」を表す型ですが、Result型は「成功 or 失敗」を表す型です。
それぞれの宣言の必要な部分を抜き出して見てみると類似性を感じ取れます。
enum Optional<Wrapped> {
case none
case some(Wrapped)
}
enum Result<Success, Failure> where Failure : Error {
case success(Success)
case failure(Failure)
}
Resultは特に非同期処理におけるエラーハンドリングに用います。
例えばResult型を返す処理があったとして
func someMethod() -> Result<Data, Error>
下記のようにエラーハンドリングします。
switch someMethod() {
case .success(let data):
// 成功:dataを用いる処理
case .failure(let error):
// 失敗:エラーハンドリング
}
いくつかパッと試してわかりやすい機能を中心にSwift 5の新機能を紹介しました。もしこの中で1つでも興味がわいた機能があったのであればぜひ「Swift Programming Language Evolution」*8を覗いてみてください。「どうしてその機能を追加したいと思ったのか?」「その機能を追加する価値はどのようなものか?」「どのように設計して追加すると良いか?」などSwiftに機能が追加されていく様がリアルに感じられます。理解するのはだいぶ難しいですが、きっとSwiftをより楽しめるようになるでしょう。
それでは楽しいSwift Lifeを!