iOSDC Japan 2019でXCFrameworksのLTをします

こんにちは。iOSDC Japan 2019の0日目参加のため東京に上陸しております id:ikesyo です。明日1日目の16:15からTrack Bで『5分でわかる!Xcode 11から使えるXCFrameworks』というタイトルでLTをします。

どういう内容かは紹介文を読んでいただきつつ、先月行われたスピーカーディナーでの1分間ピッチの動画も上がっているので、タイトルや紹介文でLTの内容にちょっと興味が出てきたら、ぜひ動画も見てみて、当日LTを聴きに行くかどうかの参考にしてもらえればと思います。

ちなみに私はiOSDC Japanで4年連続4回目のLT登壇(自分調べではただ一人!)となります。今年もこの場で発表できて嬉しい・楽しい気持ちも持ちつつ、少しでも有益な情報を持ち帰ってもらえればと思います。それでは会場でお会いしましょう!🤗

Xcode 11で導入されるxcframeworkのディレクトリ構造

WWDC19でお目見えしたXcode 11ですが、ライブラリ・フレームワークの配布形態として .xcframework という新しいフォーマットが導入されています。

https://developer.apple.com/documentation/xcode_release_notes/xcode_11_beta_release_notes/#3318305

XCFrameworks make it possible to bundle a binary framework or library for multiple platforms —including iOS devices, iOS simulators, and UIKit for Mac — into a single distributable .xcframework bundle that your developers can use within their own applications. An .xcframework bundle can be added to an Xcode target’s Link Libraries phase and Xcode uses the right platform’s version of the included framework or library at build time. Creation of XCFrameworks is supported from the command line using xcodebuild -create-xcframework. Frameworks or libraries bundled in an XCFramework should be built with the Build Libraries for Distribution build setting set to YES. (49948269)

このフォーマットの導入は、リリースノートにも書かれているようにUIKit for Mac(Project Catalyst)と関係していそうです。それだけではなく、Swift 5.1でのModule Stability*1サポートによる、Swiftで書かれたフレームワークのバイナリ配布とも関連した動きに見えます。

これまでは、各種SDKなどでiOS用のフレームワーク.framework形式)を配布する場合、iOSシミュレーター用のframeworkと、iOSバイス用のframeworkをそれぞれビルドしてから、frameworkのバンドル内に含まれるバイナリをlipoコマンドで結合して1つのframeworkにする、という手段が取られていました。Carthageでframeworkをビルドする時もこうした処理が行われています。

ただしこの方法には少し問題があり、iOSアプリをApp Storeに提出する際のバリデーションで、アプリに含まれるframeworkのバイナリにシミュレーター用のCPUアーキテクチャーのスライスが含まれているとバリデーションで蹴られてしまいます。そのためストア提出用のリリースビルドを行う際には、lipoコマンドでシミュレーター用のスライスを取り除く必要があります。Carthageではこの問題の対処として、アプリにframeworkを組み込む際にcarthage copy-frameworksコマンドを使うようになっています。

一方、新しい.xcframework形式では、複数の.frameworkを1つのバンドル(ディレクトリ)にまとめる構造となっています。この構造では、アプリをデバイス向けにビルドする時にはxcframework内のデバイス用のframework、シミュレーター向けにビルドする時にはシミュレーター用のframeworkを使うことができるので、ストア提出用のアプリにシミュレーター用のスライスが含まれてしまうということが起きなくなります。Carthageでもxcframeworkがサポートされると、copy-frameworksコマンドを使用する必要がなくなることが期待できます*2

実際に.xcframeworkを生成してディレクトリ構造を確認してみました。シミュレーター用・デバイス用・UIKit for Mac用のframeworkは先にビルドした上で、xcframeworkの生成は次のコマンドで行えます:

$ xcodebuild -create-framework \
    -framework iphonesimulator/Himotoki.framework \
    -framework iphoneos/Himotoki.framework \
    -framework uikitformac/Himotoki.framework \
    -output Himotoki.xcframework

ディレクトリ構造は次のようになっていました。

$ tree Himotoki.xcframework
Himotoki.xcframework
├── Info.plist
├── ios-armv7_arm64
│   └── Himotoki.framework
│       ├── Headers
│       │   ├── Himotoki-Swift.h
│       │   └── Himotoki.h
│       ├── Himotoki
│       ├── Info.plist
│       └── Modules
│           ├── Himotoki.swiftmodule
│           │   ├── arm.swiftdoc
│           │   ├── arm.swiftinterface
│           │   ├── arm.swiftmodule
│           │   ├── arm64-apple-ios.swiftdoc
│           │   ├── arm64-apple-ios.swiftinterface
│           │   ├── arm64-apple-ios.swiftmodule
│           │   ├── arm64.swiftdoc
│           │   ├── arm64.swiftinterface
│           │   ├── arm64.swiftmodule
│           │   ├── armv7-apple-ios.swiftdoc
│           │   ├── armv7-apple-ios.swiftinterface
│           │   ├── armv7-apple-ios.swiftmodule
│           │   ├── armv7.swiftdoc
│           │   ├── armv7.swiftinterface
│           │   └── armv7.swiftmodule
│           └── module.modulemap
├── ios-i386_x86_64-simulator
│   └── Himotoki.framework
│       ├── Headers
│       │   ├── Himotoki-Swift.h
│       │   └── Himotoki.h
│       ├── Himotoki
│       ├── Info.plist
│       ├── Modules
│       │   ├── Himotoki.swiftmodule
│       │   │   ├── i386-apple-ios-simulator.swiftdoc
│       │   │   ├── i386-apple-ios-simulator.swiftinterface
│       │   │   ├── i386-apple-ios-simulator.swiftmodule
│       │   │   ├── i386.swiftdoc
│       │   │   ├── i386.swiftinterface
│       │   │   ├── i386.swiftmodule
│       │   │   ├── x86_64-apple-ios-simulator.swiftdoc
│       │   │   ├── x86_64-apple-ios-simulator.swiftinterface
│       │   │   ├── x86_64-apple-ios-simulator.swiftmodule
│       │   │   ├── x86_64.swiftdoc
│       │   │   ├── x86_64.swiftinterface
│       │   │   └── x86_64.swiftmodule
│       │   └── module.modulemap
│       └── _CodeSignature
│           └── CodeResources
└── ios-x86_64-uikitformac
    └── Himotoki.framework
        ├── Headers -> Versions/Current/Headers
        ├── Himotoki -> Versions/Current/Himotoki
        ├── Modules -> Versions/Current/Modules
        ├── Resources -> Versions/Current/Resources
        └── Versions
            ├── A
            │   ├── Headers
            │   │   ├── Himotoki-Swift.h
            │   │   └── Himotoki.h
            │   ├── Himotoki
            │   ├── Modules
            │   │   ├── Himotoki.swiftmodule
            │   │   │   ├── x86_64-apple-ios-macabi.swiftdoc
            │   │   │   ├── x86_64-apple-ios-macabi.swiftinterface
            │   │   │   ├── x86_64-apple-ios-macabi.swiftmodule
            │   │   │   ├── x86_64.swiftdoc
            │   │   │   ├── x86_64.swiftinterface
            │   │   │   └── x86_64.swiftmodule
            │   │   └── module.modulemap
            │   └── Resources
            │       └── Info.plist
            └── Current -> A

ios-armv7_arm64にデバイス用のframeworkが、ios-x86_64-simulatorにシミュレーター用のframeworkが、そしてios-x86_64-uikitformacにUIKit for Mac用のframeworkが含まれていることが分かります。

またトップレベルのInfo.plistの内容は次のようになっていました。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>AvailableLibraries</key>
    <array>
        <dict>
            <key>LibraryIdentifier</key>
            <string>ios-x86_64-uikitformac</string>
            <key>LibraryPath</key>
            <string>Himotoki.framework</string>
            <key>SupportedArchitectures</key>
            <array>
                <string>x86_64</string>
            </array>
            <key>SupportedPlatform</key>
            <string>ios</string>
            <key>SupportedPlatformVariant</key>
            <string>uikitformac</string>
        </dict>
        <dict>
            <key>LibraryIdentifier</key>
            <string>ios-armv7_arm64</string>
            <key>LibraryPath</key>
            <string>Himotoki.framework</string>
            <key>SupportedArchitectures</key>
            <array>
                <string>armv7</string>
                <string>arm64</string>
            </array>
            <key>SupportedPlatform</key>
            <string>ios</string>
        </dict>
        <dict>
            <key>LibraryIdentifier</key>
            <string>ios-i386_x86_64-simulator</string>
            <key>LibraryPath</key>
            <string>Himotoki.framework</string>
            <key>SupportedArchitectures</key>
            <array>
                <string>i386</string>
                <string>x86_64</string>
            </array>
            <key>SupportedPlatform</key>
            <string>ios</string>
            <key>SupportedPlatformVariant</key>
            <string>simulator</string>
        </dict>
    </array>
    <key>CFBundlePackageType</key>
    <string>XFWK</string>
    <key>XCFrameworkFormatVersion</key>
    <string>1.0</string>
</dict>
</plist>

xcodebuild -create-xcframeworkコマンドの-frameworkオプションの数を増やすことで、ここにmacOS、tvOS、watchOS用のビルドも追加することができるはずです。

また.frameworkだけでなく、.aのstatic libraryや.dylibのdynamic libraryとそれ用のヘッダーをバンドルすることもできるようになっています。コマンドのヘルプの出力も載せておきます。

$ xcodebuild -create-xcframework -help
OVERVIEW: Utility for packaging multiple build configurations of a given library or framework into a single xcframework.

USAGE:
xcodebuild -create-xcframework -framework <path> [-framework <path>...] -output <path>
xcodebuild -create-xcframework -library <path> [-headers <path>] [-library <path> [-headers <path>]...] -output <path>

OPTIONS:
-framework <path>         Adds a framework from the given <path>.
-library <path>           Adds a static or dynamic library from the given <path>.
-headers <path>           Adds the headers from the given <path>. Only applicable with -library.
-output <path>            The <path> to write the xcframework to.
-help                     Show this help content.

今後の各種ツールやSDKでのサポートに期待したいですね。

CocoaPods 1.7.0からSpecsのソースにCDNを使えるようになります

先日リリースされたCocoaPods 1.7.0.beta.1で、実験的機能としてSpecsのソース(パッケージ定義であるpodspecファイルのセントラルリポジトリ)にCDNを利用できるようになりました。

今までは、ソースとして https://github.com/CocoaPods/Specs のGitリポジトリが使用されており、初期セットアップ時のクローンや、日々のアップデートに結構な時間が掛かっていましたが、ソースのCDN化によりこうした時間が削減できるようになります(必要なデータだけダウンロードされてローカルにキャッシュもされる)。

また、ライブラリの最新バージョンを使おうとしたらローカルのSpecsが更新されていなくて、その都度$ pod repo update(もしくは$ pod install --repo-updateのオプション付き実行)をしないといけない、というのもなくなるはずです。

Beta期間中の結果によっては、1.7.0の正式リリース時点でCDNソースがデフォルトになるようです。

Depending on the results and stability, we hope that starting with 1.7.0, CocoaPods will no longer require users to clone the master specs repo in order to get started.

この機能を有効にするには、1.7.0.beta.1にアップデートし、プロジェクトのPodfileの先頭に次の1行を足すだけです。

# source 'https://github.com/CocoaPods/Specs' 既存のこの行があれば削除かコメントアウトしましょう
source 'https://cdn.jsdelivr.net/cocoa/'

ということでどんどん試していきましょう!💪

既存iOSアプリへのReact Native導入とcocoapods-fix-react-native

  • 既存iOSアプリへのReact Native導入は公式にドキュメントが用意されている
  • その手順に従うと、React Nativeの依存解決にはCocoaPodsを使うようになっている
    • 一方 react-native init で新規にプロジェクトを作成する場合は、各ライブラリがstatic libraryとしてxcodeprojに直接設定されるようになっている
  • React NativeのCocoaPodsサポートではsubspecが使われているが、subspecではヘッダーのディレクトリが分割されないので、static libraryとして組み込む場合とは一部ヘッダーのインポート方法が異なってビルドエラーになる
    • 他にもエラーになる要因はいくつかある
  • そのような問題を解消してくれるのが https://github.com/orta/cocoapods-fix-react-native/ というCocoaPods用のプラグイン

ということで、既存iOSアプリにReact Nativeを導入したい場合はcocoapods-fix-react-nativeプラグインを使っていきましょう 💪

『iOS 11 Programming』の執筆に参加しました&一般販売開始

技術書クラウドファンディングPEAKSのプロジェクトとして成立した『iOS 11 Programming』に、執筆陣の一人として参加させていただき、先日完成を迎えることができました。また本日からクラウドファンディング参加者に限らない一般販売が開始されました。

iOS 11 Programming

iOS 11 Programming

  • 著者:堤 修一,吉田 悠一,池田 翔,坂田 晃一,加藤 尋樹,川邉 雄介,岸川克己,所 友太,永野 哲久,加藤 寛人,
  • 発行日:2017年11月16日
  • 対応フォーマット:製本版,PDF
  • PEAKSで購入する

個人的には昨年の『WEB+DB PRESS Vol.96』の特集に続き、二度目の執筆活動となりました。この時は特集約30ページを一人で書き上げるのにかなり苦労した思い出があります。今回は一冊の技術書ですが、1・2章程度の粒度で分担して書くのは、単著を書くよりもかなりハードルが低く、また他の著者陣が書き上げていくのに刺激されて自分もやらなければ!という気持ちにさせてくれるので、とてもいいやり方だなぁと感じました。

これを読んだ皆さんも応募フォームから書いてみたい、読んでみたい、といったリクエストをどんどん投げつけてみましょう!

第4章を書きました

僕は「第4章 Swift 4の新機能とアップデート」を担当しました。Codable では実装の自動生成が大きなポイントの1つですが、仮にもSwiftコミッターなので、コンパイラーの自動生成部分のコードも読み込みながら挙動を把握し、丁寧に解説しようとしました。自動生成と手動実装を下手に混ぜると危ういといった注意点も書きたかったことの1つですね。

またSmart KeyPathsについては、基本的な使い方はそんなに困らないだろうという前提で、より掘り下げたクラス階層やジェネリクス的な視点、read-onlyかread-writeか、といった点に着目しています。

すでにSwift 4に移行済みの人も少なくないとは思いますが、これらの機能はSwift 3.2モードでも使用可能です。なのでSwift 4に移行前でもどんどん使っていって、皆で色んな知見を溜めていけるといいですね。

あわせて読みたい

どうぞお買い求めください 🙏

関西モバイルアプリ研究会 in 関東で発表しました #関モバ

普段は京都や大阪で開催されている関モバが東京に上陸した!ということで発表してきました。

UISearchBar / UISearchController は結構カスタマイズしにくかったり、iOS 10iOS 11で見た目が大きく変わり得たり、iOS 11で追加された UINavigationItem.searchController によるナビゲーションバーとの統合も実運用で困る箇所があったりと、皆さんどうですか?という話をしたかったのです。会場での結論としては、カスタマイズする必要があれば、これらを使うのを諦めて UITextField をそのまま使って自分でカスタマイズする方がいいですね、という話に落ち着いてよかったよかった 😇

他の発表内容はこちらからどうぞ 👇

未承諾広告

はてなではスマートフォンアプリエンジニアを絶賛募集中です!

Carthage 0.13/0.14をリリースしていました

前回のエントリーに引き続き、Carthage 0.13 および 0.14をリリースしていました。どちらもバグFixメインのリリースですが、0.13ではcarthage other-commandとすると$PATHに存在するcarthage-other-commandを実行するという、いわゆるサブコマンドの機構を実装しました。Carthageのプラグイン的なツールを作るのにぜひ活用してもらえればと思います。

ikesyo.hatenablog.com

今後もリリース時には内容紹介のエントリーを書いて日本語での説明を増やしていきたい所存です!