端的にいうと
Apple開発者カンファレンス直前、Swift言語専用機能にみるAppple製品アプリ開発を魅力的にしたいAppleの成果Swift Macrosについての読み物。
1) ネイティブ/クロスプラットフォームの先にあるアプリ?
スマートフォン普及とモバイルアプリ開発が人気となって10年を超えました。アプリ開発もプラットフォーマー(モバイルではAppleとGoogle)が提供する開発キット(SDK)を使用する"ネイティブアプリ"ばかりではなく、その他の以外の選択肢(クロスプラットフォーム、Webアプリ)が登場、加えて2020年代初頭にはGPT経由を使用して開発委託をできるだけ避けてアプリ開発を進める試みもあらわれてきております。
GPTの活用が進むとアプリを開発する際の選択肢は誰がアプリの元を生成しても揺らぎが少ないアプリが構築できるかであり、ネイティブかクロスプラットフォームは重要度は下がるかもしれません。
プラットフォーム側のAppleとしては誰がアプリの元を生成しても揺らぎが少ないアプリが構築できる事に努めれば良いというシンプルが答えが出てくるはずです。そんな話をとっかかとし、四半世紀以上の歴史を持つAppleの開発環境におけるここ数年の取り組みを見てみます。
2) アプリ開発が煩雑になるのは?
先ほど誰がアプリの元を生成しても揺らぎが少ないアプリが構築できるかという話をしましたが一旦GPTは置いておき、機能を実現する際、遭遇するトラブルを示してみます。
- a) 記述する量が多い
- b) 複数の箇所に記述が分散している
aはアプリの元となるソースコードの量が多くなると不具合が混入する確率が高くなりアプリの揺らぎにつながります。
開発中に実装ミスというのは人間が開発する以上避けられませんが、正しく動いていたコードでもメンテナンスに伴う修正の際に意図しないキー入力で問題を含めたことに気がつかないといった意図しない変更は気づかないものです。
開発/修正時の意図しない不具合の混入はテストコードやコードレビューによって防げますが根本となるソースコードの総量が少なければそもそも問題が発生する機会を減らすことができます。
同じ機能を実現する際にソースコードの量が少ないほどアプリの揺らぎは少ないものになります。
bはアプリを構成するのはソースコード(から生成したプログラム)だけでは完結せずそのほかのリソース(UI、アプリ情報、ローカライズ情報) が存在し、場合によってはソースコードに記載されている内容をほかのリソースに関連づけする必要があります。
例としてはアプリが起動する際に最初に呼ばれる箇所(エントリーポイント)をアプリ情報に記述する、1990年代後半からの統合開発環境を使ったRAD(Rapid Application Development) ではソースコードとUIのInput/Outputを紐づけるといった事が該当します。
ソースコードとそのほかのリソースを関連付けさせるというのは開発者ごとの知識とノウハウに依存しがちです。ソースコードとほかのリソースの関連付けに関しても記述する箇所が減ることが理想です。
3) Swift Attributes - Swift言語の準標準機能
Appleはプログラミング言語SwiftをApple製品を開発するための言語として推し進めています。前任のObjective-Cから四半世紀ぶりの新しい言語で2010年代のWebプログラミングを参考にする、や並行性(concurrency)を考慮した言語となっています。また広く開発者の注目を集めるべくオープンソースとして公開され2024年現在Windows11やUbuntuでも動作可能です。
開発者の興味を惹く一方でAppleは営利団体なのでオープンソースとしてのSwiftに仕様変更を加えず、アプリ開発に利用できるSwift言語のサブシステムとしてSwift Attributeを提供しています。Swift Attributes はSwift言語のみに対応し、Swift言語を構成する語彙に属性を付加することで言語仕様を改変せずアプリ開発機能を提供しています。
https://docs.swift.org/swift-book/documentation/the-swift-programming-language/attributes/
身近な例としては、@main です。@main はアプリが起動するエントリーポイントであるクラス(もしくは構造体)であることを示しますが。アプリの構築タイミングで別のコードに変換されます。
iOSアプリでは以下のようなコードが展開されます。
// AppDelegateクラスををアプリの開始とする @main class AppDelegate: UIResponder, UIApplicationDelegate { … }
アプリの構築タイミングで上記の処理は以下の内容に近しいものに変換されます。サンプルはObjective-C でのmain() の記述方法となります。
int main(int argc, char * argv[]) { NSString * appDelegateClassName; @autoreleasepool { // Setup code that might create autoreleased objects goes here. appDelegateClassName = NSStringFromClass([AppDelegate class]); } return UIApplicationMain(argc, argv, nil, appDelegateClassName); }
アプリ作成時に記述すべき冗長なコードを@mainだけで記述する事ができます。
仕組みとしてはC言語/C++でのmacro処理(ビルド直前に行われる簡易スクリプトを使った変換処理)に近い働き、アプリのビルド中にソースコードを展開します、macro処理と違ってSwift AttributesはSwift言語を厳密に扱います。例えばクラス、構造体に対して指定可能な@main は変換対象がクラスか構造体かを判断しソースコードを展開します。
@main struct Test { static func main(){ switch CommandLine.arguments { default: break } } }
4) @propertyWrapper - Swift言語の準標準機能をさらに拡張?
Appleは必要な分だけSwift Attributesに機能を追加してきましたが、目的別のフレームワーク(例:地図表示、グラフ表示)全て向けに機能を追加するのは無理があると見たのか、Swift Attributesでできる機能を一部公開しました。一部公開された機能の名称は@propertyWrapper で2019年に公開されました。@propertyWrapperの役割としてはソースコード中のクラスや構造体内の要素をカスタマイズできます。
@propertyWrapper struct SomeWrapper { var wrappedValue: Int var someValue: Double init() { self.wrappedValue = 100 self.someValue = 12.3 } init(wrappedValue: Int) { self.wrappedValue = wrappedValue self.someValue = 45.6 } init(wrappedValue value: Int, custom: Double) { self.wrappedValue = value self.someValue = custom } } struct SomeStruct { // Uses init() @SomeWrapper var a: Int // Uses init(wrappedValue:) @SomeWrapper var b = 10 // Both use init(wrappedValue:custom:) @SomeWrapper(custom: 98.7) var c = 30 @SomeWrapper(wrappedValue: 30, custom: 98.7) var d }
上記サンプルコードではSomeWrapper で記述された煩雑な処理をSomeStruct内で@SomeWrapper として属性として指定できる事でソースコードを簡潔に記述することに成功しています。
ソースコードを簡潔に記述できる一方、Swift Attributesという準標準機能内に、目的を絞ったカスタマイズ機能が提供されており、なぜこのタイミングなのかという疑問が湧いてきます。
@propertyWrapperが追加された疑問に対してAppleは同年2019年に答えています。
Appleの新しいユーザーインターフェイス構築用のフレームワークSwiftUIにてSwift Attributesの@propertyWrapperを用いてSwift Attributesに直接機能を追加することを回避に使用していたのでした。追加されたも機能の代表的なものは以下となります。
- @State
- @Binding
- @ObservedObject
- @StateObject (2020年に追加)
上記機能はSwiftUI に直接追加されたわけではなく同年に公開されたフレームワークCombine Frameworkに追加、SwiftUIはCombine Frameworkを使っているという程をとっています。
2024年5月現在から2019年を鑑みるに、当時はSwiftUI がApple社内で極秘で開発されていた都合上、Swift言語に変更を加えるとオープンソースという都合上、機能の必要性について議論すると開発しているものが判明してしまうため順標準機能である程度Appleが自由にできるSwift Attributesに限定的なカスタマイズ機能を提供していきました。
結果としては簡潔なコードを記述できる機能を提供できていますが綺麗とは言えない、機能の二階建といった状態が2022年まで続いていました。
5) Swift Macros の登場
Appleは必要な分だけSwift Attributesに機能を追加し、Swift Attributesでも足りない部分を限定的なカスタマイズ機能を追加して簡潔なコードを書く試行錯誤を繰り返してきました。
2023年Appleは開発者会議(WWDC)の場で簡潔なコードを書くための新しいフレームワークSwift Macrosを公開しました。
https://docs.swift.org/swift-book/documentation/the-swift-programming-language/macros/
Swift MacrosはSwift Attributesが実現してきた@〜が付けられた属性や#を付加したマクロをカスタマイズできるので利用する側はSwift Macrosと使っているのかSwift Attributesを使っているかを意識することなく簡潔なコードを記述できます。ただしSwift Macros経由で生成されたコードは展開内容を確認することができます。Swift の機能を強化する目的がある以上、他言語(Objective-C)はサポートされていません。
Swift Macrosの登場によりSwift Attributesで提供してきた機能そのものをApple/Apple以外の開発者関係なく利用できるようになり、Appleとしては順標準機能であるSwift Attributesに機能を追加する必要性は低くなりました。
Swift Macrosの登場によってAppleの提供するフレームワークにもSwift Macrosを採用したフレレームワークが登場しています。2024年5月現在で著名なものは以下となります。
- Observation | Apple Developer Documentation
- SwiftData - Xcode - Apple Developer
- App Intents | Apple Developer Documentation
ObservationはSwiftUIで利用可能なフレームワークで2019年のSwiftUI登場時の機能を整理したもの。SwiftDataはAppleが提供する内部DB/クラウドDB用のフレームワークをSwift言語向けに更新したものです。AppIntentsはアプリの外部機能(ショートカットなど)を提供する仕組みで前任のSiriKitやCustomIntent をSwift言語向けに整理したものです。いずれもSwift Macros を採用した例となります。
6) Swift Macros を学ぶ必要はあるのか?
簡潔なコードを記述できる仕組みを提供するSwift Macrosですが一般的なアプリを開発する開発者が知っておかなければならない知識ではありません。Swift MacrosはiOSプラットフォームに開発キット(SDK)を提供側での理解が必要な機能となります。利用者側としては簡潔のコードが書ける仕組みがSwift Attriobutesに由来するものなのかSwift Macrosに由来したものかを理解する必要はありませんし、Appleとしても簡潔なコードを記述できるように両者の境目をできるだけ曖昧にしています。
ただし不具合調などの際はSwift Macrosで簡潔に記述したコードは開発ツール(Xcode) で展開することを覚えておいても良いかもしれません。
まとめ
2010年代後半にAppleが新しいプログラミング言語Swiftを発表以後、年月をかけて揺らぎの少ないソースコードでアプリを開発できるように整備してきました。
Appleの都合でSwift言語の根幹を改変せずにアプリ開発に利便性を高めるべくサブシステム(Swift Attributes) に機能を追加するなど紆余曲折を続けていましたが、2023年に公開されたSwift MacrosによりApple/サードパーティー隔てず簡潔なコードを記述できるようになりアプリを開発する際の揺らぎを減らす取り組みが進んでいます。
最後までお読みいただき感謝です。
ユニファでは、一緒にはたらく仲間を募集しています!