収穫の秋
毎年9月末の「秋のApple祭り」は、毎年恒例になった感があります[1] 。新しいiOS、新しいiPhone、そして新しいXcode。iOS 9は日本時間9月17日未明にリリースされ、Xcode 7もそれとほぼ同時にApp Storeでの配布が開始されました。同時にリリースされる予定だったwatchOS 2のリリースこそ5日遅れましたが、あとは9月25日にiPhoneが届けば祭りもクライマックスといったところでしょうか。本稿が読者のみなさんの手に届くころには、iOS 9もiPhone 6s/6s plusもすっかり馴染んでいることでしょう。
しかしXcode 7、そしてSwift 2はどうでしょうか? 以前紹介したとおり、今年中にはAppleはSwiftのオープン化を公約しています。Swift 2に習熟するには絶好の期間かもしれません。
そのXcode、本物ですか?
そのXcodeですが、気になるニュースが1つ。中国で改竄され配布された通称XcodeGhost で開発、デプロイされたアプリが、マルウェアに感染した状態でApp Storeに登場したのです。
Apple IDさえあればβ版も無料で入手でき、ましてや安定版がMac App Storeから無料で入手できるXcodeのパチモノをわざわざ使うなんて一見ありえなさそうですが、さすがGreat Firewall of Chinaの向こうだけあって、かの国ではAppleの公式サーバとの接続も日本のように安定しているとも言えず、よって(違法に)二次配布するサイトも点在しているようです。飛行機を乗っ取るために空港を乗っ取るようなもので、不謹慎ながら感嘆を禁じ得ませんでした。
Appleもこの件に関しては、異例の注意喚起 を行っています。
そのXcodeが本物か否かは、Terminal.appでspctl --assess --verbose /Applications/Xcode.app
を実行すればOKです。Xcodeの巨大さゆえ、コマンドが終了するのにしばらく待たされるのですが、問題なければ
/Applications/Xcode.app: accepted
source=Mac App Store
のようにacceptedを含んだ表示が出るはずです。
Apple「すべてのSwift 1.xプロジェクトを、生まれる前に消し去りたい」
それでは気を取り直して本題へ。iPhone 6sのキャッチコピーは、「 唯一変わったのは、そのすべて。」だそうですが、Swiftの変貌ぶりはそれどころではありません。なにしろSwift 1.xのコードが全然そのまま動かないのですから。Python 2.xとPython 3.xは別言語ですが、Swift 1.xとSwift 2.xの別言語ぶりはそれをも凌駕します。Python 3の普及が遅れている一番の理由はまさにそこ、「 同じ言語じゃない」という点にあるのですが、AppleはそれをXcodeでひっくり返しました。Swift 1.xで書かれたプロジェクトを開くと、XcodeがSwift 2.xに翻訳してくれるのです。その一部始終は前々(第7回 )に紹介したとおりですが、まるで「すべてのSwift 1.xプロジェクトを、生まれる前に消し去りたい」といわんばかりのこの機能には「そんな祈りが叶うとすれば、それは時間干渉なんてレベルじゃない。因果律そのものに対する反逆だ」とインキュベーターでなくとも叫びたくもなるというものです。Python 3どころかPerl 6とか、「 人類には早すぎる言語」との付き合いが浅からぬ筆者には、「 さすがApple!おれたちにできない事を平然とやってのけるッそこにシビれる!あこがれるゥ!」という冷やかしすら無理で、ただ伏してメメタァするしかありませんでした。
前々回で私は「Swift 2への移行は正式版が出た後で、ただしSwift 2正式化以後はSwift 1の後方互換性サポートも捨てるのがよさそうです。筆者がGitHubに上げているプロジェクトはそうするつもりです」と述べましたが、ほぼそのようになりました。Swift 2正式版がXcodeとともにリリースされてまだ一週間も経っていないのに、筆者はすでにSwift 1がどうだったか思い出せなくなりつつあります。
What's new in Swift 2
というわけで、後方非互換性はXcode 7の力であっさりと乗り越えてしまいましたが、これからがやっと俺たちの戦い。Swift 2は何が変わったのでしょうか? 完全に書き直されたThe Swift Programming Language でそれを追うのは、サン・ピエトロ寺院を見てコロッセオがどうだったかを思い起こすほど難しいかもしれません(コロッセオは石切場だったのです。これ豆) 。筆者の場合、Swift 1から2への変遷を追うのに一番ありがたかったのはWWDC 2015のビデオ、What's new in Swift でした。本稿でもそれを改めて駆け足で追いかけた後、くり紹介していく予定です。逆に、println()
からprint()
など、Xcode 7が自動で片付けてしまう変更点に関しては、軽く触れるにとどめます。
最低限文化的なenum
Swiftのenum
は、enumというよりむしろunion(共用体)に近いもので、本当はSwift 1の頃からもっとバリバリ使いたかったのですが、class
やstruct
と比べると微妙に使いづらいものでした。
デフォルトのdescription debugDescriptionが直感的に
たとえば、
enum Langs {
case C, Cplusplu, ObjectiveC, Swift
}
というenumがあったとして、print(Langs.Swift)
はそのままではそっけなく(Enum Value)
と表示されるだけでした。“ Swift” と表示させるためには、いちいち、
extension Langs : Printable {
var description: String {
switch self {
case C:
return "C"
case Cplusplus:
return "C++"
case ObjectiveC:
return "Objective-C"
case Swift:
return "Swift"
}
}
}
などという具合にdescription
プロパティを定義しておく必要があったのですが、これと同様のことを自動でやってくれます。
総称型enumがまともに
Swift 1では次のコードがクラッシュしていました。
enum Either<L,R> {
case Left(L)
case Right(R)
}
総称型のEnumでは型変数が1つしか取れなかったので、
enum Optional<T> {
case None
case Some(T)
}
は定義できてもEitherのような型は作れなかったのですが、やっと作れるようになりました。
再帰的enumが可能に
Swift 1では、これもだめでした。
enum Cons<T> {
case Nil
case Atom(T)
case Pair(Cons, Cons)
}
enum
は値型(value type)なので、「 固定的なサイズを持たなければならないのに、自己参照していては無限にメモリが必要になってしまう」とはSwiftのパパ、Chris Lattnerの弁。「 今のデバイスでは無理。まあ来年あたりなら」というジョークが見事に滑っていましたが、indirect
を次のようにつけることで再帰的enumが実現可能になりました(図1 ) 。
enum Cons<T> {
case Nil
case Atom(T)
indirect case Pair(Cons, Cons)
}
図1 再帰的enum
もしくは、
indirect enum Cons<T> {
case Nil
case Atom(T)
case Pair(Cons, Cons)
}
ここまでそろっていたなら、筆者もswiftjson をenumベースで実装したんですがねえ……。
repeat { } while
Swift 1までの「必ず1回はブロックを実行する」ループはdo { } while
だったのですが、repeat { } while
になりました。do { }
は今後後述のdo { } catch { }
などでも用いられるようになるのと、ブロックの頭を見ただけで条件分岐がブロックの後ろにあることが直感的にわかるということでそうなったようです。
Option Sets
これはSwift 1.2から導入された重複なしの集合、Setとは別物です。
Swift 1ではビットマップのフラグの扱いは、たとえば、
let swiftGoals:Goals = .Refined ¦ .Safe ¦
.Expressive
のように、Bitwise命令で操作していたのですが、わかりづらいということで次のようにOption SetType
プロトコルに適合したstruct
を使ってより直感的にフラグを立てたり折ったりできるようになりました。
struct Goals : OptionSetType {
let rawValue : Int
static let Refined = Goals(rawValue:1)
static let Safe = Goals(rawValue:2)
static let Expressive = Goals(rawValue:4)
}
let swiftGoals:Goals = [.Refined, .Safe,
.Expressive]
if swiftGoals.contains(.Expressive){}
関数とメソッドとラベルと
Swiftでは関数もメソッドもfunc
で定義しますが……、
func f(first:String, second:Bool){ }
swtruct S {
func m(first:String, second:Bool){ }
}
Swift 1では関数は、
f("Swift", second:true)
と最初の引数のラベルが省略可能だったのに対し、
var o = S()
o.m(first:"String", second:true)
といった具合にメソッドで最初の引数のラベルは省略不能でした。Swift 2では関数の場合と同様、どちらもデフォルトでは最初の引数のみ省略という形におさまりました。_をラベルの前につけることでそのラベルを省略可能にできるのは今までどおりです。
使用する変数に警告
これはあくまでもXcodeの機能であってSwift2の仕様ではありませんが、var
で宣言したはずなのに一度も変更を加えていない変数は、let
で定数にするように警告するようになりました。
@testable
ユニットテストの際に1つ困るのは、public
なシンボルにしかアクセスできなかったことですが、ユニットテスト中で、
@testable
import MyApp
と宣言することで、internalなシンボルにもアクセスできるようになりました。
Markdown in Rich Comments
リッチコメントの中でMarkdown記法が使えるようになりました。画像すら入れられます。
guardでガード
if let
記法だと、条件が煩雑だとネストが深くなりがちでした。
if let name = json["name"] {
if let year = json["year"] {
handlePerson(name, year)
} else {
handleError("year is missing")
}
} else {
handleError("name is missing")
}
たとえばPerlだったら、同様の場合はこう書くところです。
my $name = $json->{name} or handlError("name is missing");
my $year = $json->{year} or handlError("year is missing");
handlePerson($name, $year);
どちらが読みやすいかは一目瞭然ですが、これと同様のことがguard
文の導入である程度できるようになりました。
guard let name = json["name"] else {
handleError("name is missing")
}
guard let year = json["year"] else {
handleError("year is missing")
}
handlePerson(name, year)
else { }
がまだうざくはありますが、だいぶすっきりしました。else
というのもやや違和感がありますが、これがないと
guard let something = someFunction
{ /*...*/ }
と書いてしまった場合、
guard let something = someFunction({ /*...*/ })
と解釈されてしまうおそれがあるのでこのような形になったのでしょう。
case、beyond、switch
パターンマッチは便利なものですが、switch
がうざく感じませんでしたか?
switch lang {
case .Swift(let version) where version < 2.0:
updateIt(version)
default: break
}
こういう場合に、if case
が使えるようになりました。
if case .Swift(let version) = lang where
version = < 2.0 {
upadteIt(version)
}
caseはifだけでなくforでも使えます。
for n in numbers where numbers % 2 == 0 {
handleEven(n);
}
if #available
APIバージョンによる条件コンパイルに、#available
が使えるようになりました。
override func awakeFromNib() {
if #available(OSX 10.10.3, *) {
dropButton.springLoaded = true
}
}
今までextensionをかけられたのはstruct
、enum
、class
といった実装を伴った型のみでしたが、protocol
にextension
をかけられるようになりました。
extension CollectionType {
func countIf(match: Element -> Bool) -> Int {
var count = 0
for value in self {
if match(value) { count++ }
}
return count
}
}
これは大進歩です。大進歩なので次回以降詳しく取り上げます。
Exception、ahem、error handling
おそらく巷で最も騒がれているSwift 2の新機能が、try {} catch {}
かもしれません。しかしこれはAppleが慎重にexception
という言葉を避けているように、Javaなどの言語の「例外」からするとかなり限定的な機構です。これも解説は次回以降と、とりあえずこんな感じというものを、あえて何も解説せずに(図2 ) 。
図2 try-catch……例外処理?
String is not just a sequence of characters
String自体がSequenceType
でなくなり、そのように扱いたければ.characters
、.utf8
、.utf16
を必ず使うようになりました。
Swiftによる文字列の扱いもまたそのための記事を割くだけの意味があるものなので、次回以降あらためて取り上げます。
まとめ
そのほかにも、
破壊的ソートだったsort()
がsortInPlace()
になり、非破壊的なsorted()
がsort()
になったり
find()
がindexOf()
になったり
.extend()
が.appendContentsOf()
になったり
などとAppleが変えたい放題変えたSwift 2ですが、世代や性能や機能が変わってもそれがiPhoneであることは見間違えないように、一目見ればそれがSwiftのコードだという特徴をSwift 2は備えているように感じています。シンボルの改名にしても、英語的な洗練を捨ててまで非ネイティブな人でも誤解しにくい名前にしているのは日本のSwiftプログラマにとっては朗報かもしれません。
というわけで今回はSwift 2の変更点と新機能を駆け足で見てきたわけですが、ここで今年の産業界のニュースNo.0ほぼ間違いなしのビッグニュースが飛び込んできました。Appleがいよいよ自動車を再発明する? いえ。VW社のディーゼル不正事件です。EA189という「クリーンディーゼル」の制御コンピュータに、当局がガスチェックしているときだけエンジンの動作モードを変えるという通称“ defeat device” が組み込まれているのを米国環境保護局(EPA)が見つけたというもので、リコール対象50万台、制裁金2兆円というのは序の口で、これまで同エンジンが搭載されたクルマは全世界で1,100万台も売れており、今年上半期トヨタを抜いて世界最大販売台数を達成した同社は、通年でそれを達成する前にそれをはるかに上回る創業以来の危機が到来しています。ソフトウェアとハードウェアの組み合わせで世界を変えるという点で自動車メーカーも実はAppleと同じような立場にあったわけで、それが負の方向に働くとどれほど恐ろしいことになるのか、一エンジニアとして恐々としています。世界を変えるなら、より良い方向に変えていきたいものです。
第1特集
MySQL アプリ開発者の必修5科目
不意なトラブルに困らないためのRDB基礎知識
第2特集
「知りたい」「 使いたい」「 発信したい」をかなえる
OSSソースコードリーディングのススメ
特別企画
企業のシステムを支えるOSとエコシステムの全貌
[特別企画]Red Hat Enterprise Linux 9最新ガイド
短期連載
今さら聞けないSSH
[前編]リモートログインとコマンドの実行
短期連載
MySQLで学ぶ文字コード
[最終回]文字コードのハマりどころTips集
短期連載
新生「Ansible」徹底解説
[4]Playbookの実行環境(基礎編)