書いて覚えるSwift入門

第42回恒例、Apple収穫祭

秋のiPhone祭り

本稿執筆現在は9月、⁠秋のiPhone祭り」の真っ只中。読者のみなさんに届くのはその翌月ですが、筆者はiPhone XSとApple Watch Series 4をちょうど受け取ったところです。9月というのはApple業界人にとってはおそらく最も忙しい月で、今年も図1⁠、

  • 9月14日:Special Event
  • 9月18日:iOS 12 & watchOS 5
  • 9月21日:iPhone XS、Apple Watch Series 4 and Xcode 10
  • 9月25日:macOS Mojave

という具合で原稿を書く暇はどこにあるのでしょうか😂。

図1 Special Event
図1 Special Event

今回はその9月をおさらいしつつ、Xcode 10とともに正式リリースされたSwift 4.2を見ていきます。

高イフォン、永イフォン

昨年のiPhone Xで、ついにiPhoneの値段は$1,000の大台に乗りました。後継機のXSも同様なうえ、ひとまわり大きなXS Maxに至っては、Mac bookに匹敵する高価格です。⁠iPhoneが高いのではなく日本がデフレ」という意見もSNSでは少なからず見受けられましたが、米ドルでも$1,000が大台であるのは同様で、実際iPhoneの平均価格は去年から急上昇していることが図2のグラフではっきりと見てとれます。

図2 Apple製品の平均価格
図2 Apple製品の平均価格

そしてその理由は、図3のグラフから読み取れます。

図3 Apple製品の出荷台数
図3 Apple製品の出荷台数

そう。4年前から頭打ちなのです。iPhoneだけではありません。スマートフォン全体の売り上げ台数が。人々がスマホに飽きたわけではありません。オンにもオフにも、仕事にも娯楽にも。無人島に持って行けるものが1つだけだとしたら、一番選ばれるのは間違いなくスマホです。しかしもう全人類に行き渡ってしまったのです。iOSデバイスだけで累計20億台。Androidも合わせれば、すでに1人に1台。

しかしスマホ以前のガラケーはもっと売れていたのです。最盛期のNokiaは1年ではなく1四半期に1億台以上の端末を売りました。しかし人々はガラケーを捨てスマホを求めました。そうさせるだけの魅力がスマホにあったから。だとしたら今のスマホを捨てさせるだけの魅力を持った新製品を出せ、というのが消費者と投資家に共通する欲求でしょう。

しかし、今秋Appleはついにその欲求にNoと言ったのです。さりげなく、しかし見間違えようなく。Special Eventで最もSpecialだったのは、XSでもSeries 4でもXRでもなく、環境担当VP、Lisa Jacksonのプレゼンテーションでしょう。彼女ははっきり言ったのです。⁠製品はなるべく永く使ってください」図4⁠。

図4 アップル環境担当VP、Lisa Jacksonによるプレゼンテーション
図4 アップル環境担当VP、Lisa Jacksonによるプレゼンテーション

これは陳腐化が前提のデジタルデバイス業界の自己否定にすら思えます。しかし他のハードウェア業界では常識です。乗用車を毎年買い換える人は稀ですし、事故でもない限り廃車にする人はまずいません。中古市場もリサイクル市場も成熟しています。新機種市場で最高額のiPhoneは、中古市場でも最高値で、新品が割高でもライフサイクルコストで見れば割安であるがゆえに売れるという点は、海外における日本車に通じるものがあります。

しかし乗用車と決定的に異なる点が1つあります。スマホはソフトウェアで若返らせることができるのです。いみじくもiOS 12がそれを証明しました。五年前のiPhone 5sもはっきり体感できるほど速くなったのですから。しかしそのためには最新のソフトウェアを受け止められるだけの余裕を持ったハードウェアが必要で、iOSでは64 bit CPUというのが分水嶺になっています。そしてA12 bionicの69億トランジスタというのは、iMac Pro用の18コアXeonに匹敵します。既存のソフトウェアには早すぎるハードウェアを用意し、ソフトウェアが追いついてくるのを待つ。これぞ深慮遠謀というものではないでしょうか。

そう、ソフトウェア。ハードウェアが成熟産業となった今、ITで革新を担うのはソフトウェアなのです。その意味において、ソフトウェアにとってのSwiftは、ハードウェアにとっての64 bit CPUに比肩するターニングポイントではありますまいか。

LinuxでもFoundation!

そのSwiftは、今やApple製品専用ではありません。以前紹介したようにサーバーサイドでも使えるものになってきました。Swift 4.2ではついにimport Foundationできるようになって、#if os()なしにクロスプラットフォームで動くコードが格段に書きやすくなっています。

ただし、それをきちんとインストールした場合。公式の取説には重大な抜けが1つあって、そのままではSwiftは動いてもimport Foundationしようとすると、次のエラーで止まります。

Welcome to Swift version 4.2 (swift-4.2-RELEASE). Type :help for assistance.
  1> import Foundation
error: Couldn't lookup symbols:
  _swift_FORCE_LOAD_$_swiftGlibc

公式のclanglibicu-devに加えて、libcurl4をインストールしておく必要があります。次はUbuntu 18.04の例です。

$ sudo apt-get install clang libicu-dev libcurl4
$ wget https://swift.org/builds/swift-4.2-
release/ubuntu1804/swift-4.2-RELEASE/swift-4.2-RELEASE-ubuntu18.04.tar.gz
$ tar xvpf swift-4.2-RELEASE-ubuntu18.04.tar.gz
$ export PATH=̃/swift-4.2-RELEASE-ubuntu18.04/usr/bin:$PATH

Foundationが使えるようになったことで以前の、

#if os(iOS) || os(watchOS) || os(tvOS)
import Darwin
// ...
#else
import Glibc
// ...
#endif

は陳腐化しました。今後はなるべくFoundationを共通で使うようにして、どうしても切り替えが必要な場合は、

#if canImport(Darwin)
// Appleプラットフォーム
#else
// Linux、FreeBSD…
#endif

とするとよいでしょう。

.hashValue -> .hash(into:)

Hashableプロトコルの必須プロパティが、.hashValue:Intから.hash(into:)に変わったことも特記すべきでしょう。これは解説よりも実例を見たほうがわかりやすと思います。swift-sionでは次のように変更しています。

変更前
extension SION : Hashable {
        public var hashValue: Int {
        switch self {
        case .Error(let m):         fatalError("\(m)")
        case .Nil:                  return NSNull().hashValue
        case .Bool(let v):          return v.hashValue
        case .Int(let v):           return v.hashValue
        case .Double(let v):        return v.hashValue
        case .Date(let v):          return v.hashValue
        case .String(let v):        return v.hashValue
        case .Data(let v):          return v.hashValue
        case .Ext(let v):           return v.hashValue
        case .Array(let v):         return "\(v)".hashValue // will be fixed in Swift 4.2
        case .Dictionary(let v):    return "\(v)".hashValue // will be fixed in Swift 4.2
        }
    }
}
変更後
extension SION : Hashable {
    public func hash(into hasher: inout Hasher) {
        switch self {
        case .Error(let m):     fatalError("\(m)")
        case .Nil:              NSNull().hash(into:&hasher)
        case .Bool(let v):      v.hash(into:&hasher)
        case .Int(let v):       v.hash(into:&hasher)
        case .Double(let v):    v.hash(into:&hasher)
        case .Date(let v):      v.hash(into:&hasher)
        case .String(let v):    v.hash(into:&hasher)
        case .Data(let v):      v.hash(into:&hasher)
        case .Ext(let v):       v.hash(into:&hasher)
        case .Array(let a):     for e in a {
            e.hash(into:&hasher)
            }
        case .Dictionary(let d):for k in d.keys.sorted(by:{$0.hashValue 

とくに変わったのが、ArrayDictionaryの場合。以前は全部文字列化してその.hashValueをとっていたのに対し、Swift 4.2では要素ごとに.hash()しています。ここで注目して欲しいのがDictionaryの場合。なぜキーをソートしているかと言えば、本来Dictionaryは順不同だから。["one":1,"zero":0] == ["zero":0,"one":1]ですが、順序が異なるので同じように文字列化されるとは限りません。Swiftの現在の実装ではどちらも["one":1,"zero":0]と辞書順にソートされてから文字列化されますが、それはあくまでSION.descriptionが明示的にそうしているからであって、Dictionary一般で成り立つとは言えません。

equally random

Swift 4.1まで言語標準サポートがなかったおかげでいらぬ苦労を強いられていた乱数値も、4.2以降は数値型でサポートされます。

以前
#if os(iOS)    os(tvOS)    os(watchOS) ||
os(macOS)
  return Int(arc4random())
#else
  return random() // or Int(rand())
#endif
以後
let randomIntFrom0To10 = Int.random(in: 0 ..

ほかにも変更点はいくつかありますが、Xcode 10 はSwift 4.1 と4.0 もサポートしており、Edit以下のConvertメニューである程度自動変更もしてくれます図5⁠。ぜひとも活用して行きましょう。

図5 Xcode 10メニュー画面
図5 Xcode 10メニュー画面

次回予告

次回は、今回には間に合わなかったmacOSMojaveに触れたのち、⁠定常運転」つまりSwiftを書いて覚えていくことにします。とくにプロトコルという概念は、耳学問だとなにかわからないのに書いていくうちにチョトデキルようになっていくという点が実に味わい深く、食欲の秋にもってこいの食材です。

Software Design

本誌最新号をチェック!
Software Design 2022年9月号

2022年8月18日発売
B5判/192ページ
定価1,342円
(本体1,220円+税10%)

  • 第1特集
    MySQL アプリ開発者の必修5科目
    不意なトラブルに困らないためのRDB基礎知識
  • 第2特集
    「知りたい」⁠使いたい」⁠発信したい」をかなえる
    OSSソースコードリーディングのススメ
  • 特別企画
    企業のシステムを支えるOSとエコシステムの全貌
    [特別企画]Red Hat Enterprise Linux 9最新ガイド
  • 短期連載
    今さら聞けないSSH
    [前編]リモートログインとコマンドの実行
  • 短期連載
    MySQLで学ぶ文字コード
    [最終回]文字コードのハマりどころTips集
  • 短期連載
    新生「Ansible」徹底解説
    [4]Playbookの実行環境(基礎編)

おすすめ記事

記事・ニュース一覧