BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ ニュース Using Go in Native macOS Apps with MacDriver

Using Go in Native macOS Apps with MacDriver

原文(投稿日:2021/02/10)へのリンク

MacDriverは、GoとObjective-Cの相互互換性の実現を目指す最新プロジェクトだ。これによってmacOS、さらに可能性としてはiOSのアプリをGoで記述できるようになると同時に、Objective-CやSwiftで書かれたmacOSアプリでGoコードを再利用することも可能になる。

MacDriverの基礎となるのは、Objective-CランタイムのGoによるラッパであるobjcパッケージである。これにより、実行時に使用可能な任意のクラスあるいはユーティリティメソッドにアクセスできるようになる。Objective-C独特の、実行時にクラスを生成したり拡張したりすることも可能だ。以下に示すのは、すべてのmacOSアプリとiOSアプリに必要なAppDelegateクラスを定義し、インスタンスを生成する方法の例である。

cls := objc.NewClass("AppDelegate", "NSObject")
cls.AddMethod("applicationDidFinishLaunching:", func(app objc.Object) {
	fmt.Println("Launched!")
})
objc.RegisterClass(cls)

delegate := objc.Get("AppDelegate").Alloc().Init()
app := objc.Get("NSApplication").Get("sharedApplication")
app.Set("delegate:", delegate)
app.Send("run")

objcパッケージ以外にもMacDriverには、macOS SDKに属するいくつかのフレームワークが含まれており、それらのAPIをネイティブなGoライブラリのように使用することができる。Foundationフレームワークに対応するcoreパッケージ、AppKitに対応するcocoaパッケージ、WebKitフレームワークに対応するwebkitパッケージなどがその内容だ。現時点では、各フレームワークでサポートされているクラス数は限られているが、必要に応じて新たなクラスやパッケージを追加する予定だ、とJeff Lindsay氏は述べている。ただし、バインディングは手書きで開発されているため、時間を要するかも知れない。

MacDriverの最後のコンポーネントは、ネイティブなmacOSアプリケーションからGoコードの呼び出しを可能にするブリッジシステムである。MacDriverでは、Goコードを分離したプロセス内で実行した上で、その間でコピー可能な構造体を宣言的に記述および変更するための、高レベルの抽象化を提供する、というアプローチを採用している。

作者であるJeff Lindsay氏に話を聞くことができた。

InfoQ: MacDriverを開発しようと思った動機を教えてください。

Jeff Lindsay: 以前からObject-Cランタイム、特にAppleのAPIやフレームワークとGoとのバインディングが実現できないものか、とずっと試していました。Cocoaを使ったネイティブGUIプログラミングだけでなく、すべてのAppleフレームワークに対してです。メニューバーの生成や基本的なウィンドウの生成といった、特定のAPIを一時的にバインディングする方法はいくつかあったのですが、それらは十分なものではありませんでした。それらの中にはクロスプラットフォームの実現を目指しているものも多く、素晴らしいことではあるのですが、一部のプラットフォーム特有のAPIが提供できていなかったり、さらに不都合なことに、併用について考慮されていなかったりしたのです。Cocoaを含むGUIフレームワークの多くはメインスレッドを所有しようとします。それぞれのライブラリがそれを独占的に所有することを前提にしているのです。

Objective-Cの素晴らしいところは、動的ランタイムをCライブラリの形式で持っていることです。従って、それに対して汎用的なバインディングを作れば、低レベルのグラフィックAPIや、M1 Mac以前はiOSでのみ使用可能であったNeural Engine APIなどを含む、すべてのAPIにアクセスする道が開けることになります。AppleのプラットフォームはすべてObjective-Cランタイム上に構築されているので、iOS、tvOSなどのフレームワークを使用するGoコードを書くことも不可能ではありません。

理論的には、Cライブラリであることが、このプロジェクトを簡単なものにしてくれます。というのも、Goにはすでにcgoがあるので、Cライブラリとの統合やリンクは比較的容易なのです。問題になるのは、cgoが可変引数のC関数呼び出しをサポートしていないことです。そして、Objective-Cランタイムで最も使用されている関数はメソッド呼び出しであり、これは可変引数なのです。最終的に、これを解決するプロジェクトを放棄された状態で見つけたのですが、Goコンパイラの完成度の向上や構文の厳密化のため、いくつかの部分が使えなくなっていました。もう一度使えるものにならないかと取り組んだのですが、その後、本当に必要になるまで1年ほど放置していました。

私のおもなユースケースが、本格的なネイティブアプリを開発することではなかったからです。アプリのインターフェースの大部分を構築可能なElectronスタイルのWebビューウィンドウと、ネイティブなメニュー、systrayアイコン、システム通知といった、Electronが備えているクロスプラットフォーム機能を、まずは実現したいと思っていました。それができた上で、すべてのネイティブなAPIへのアクセスができれば(あるいはその可能性があれば)、このアプローチには最高の価値がある、と考えていたのです。ですから、本当の意味でのGUIフレームワークではないかも知れませんが、それを構築するための優れた基盤を提供しているとは思います。同じように、MacDriverはAppleに非常に特化していますが、そのAPIのサブセットを、Electron風のクロスプラットフォームライブラリとして、Linux版やWindows版を含めた宣言的リソースとして公開するための開発をしています。これらは別プロジェクトになる予定です。

InfoQ: MacDriverを実務に使用する上で、完成度はどの程度なのでしょうか?

Lindsay: 現時点では実運用レベルとは言えません。使用することは可能ですが、この種のシステムで難しいのは、データを共有する2つの異なるメモリ管理システム間の調和を維持することです。ほぼ完成してはいるのですが、最初の0.1リリースをカットするには、私自身か他の誰かがNSAutoreleasePoolに関する開発を完成させて、メモリの安全性に関する注意点を資料化しなければなりません。

MacDriverには事実上3つのレイヤがあります。最初はMacDriverの肉に当たる部分で、Object-C内のものを動的にコールするためのObjective-Cランタイムバインディングが含まれています。これはRPC形式のAPIの一種で、非常にパワフルではありますが、Goのフィーリングや型安全性の面では十分ではありません。第2のレイヤはオプションで、特定のAppleフレームワークAPIをラップする型やメソッドを提供するGoパッケージで構成されています。最初は自動生成するつもりでした。ですが、私のこれまでの経験から、コード生成に取り掛かる前に、まずは手作業による開発によって適切なAPI記法を見付けると同時に、特定の問題(例えば構造体を返すメソッドには特別な扱いが必要になります)を解決しておいた方がよいのです。Appleはこのような問題を意識していて、BridgeSupportファイルとしてAPIのスキーマデータを公開してはいるのですが、年月を重ねた結果として、それらを最新に保つことを優先課題とはしなくなっているようです。従って今は、私と他の方々が、必要に応じてこれらを手作業で追加している、という状態です。幸運にもこれは1:1のAPIマッピングなので、"設計"作業はほぼゼロになります。Appleの資料を読んで、ただちに新たなメソッドや型のラッパを追加すればよいのです。

第3のレイヤは試験的なもので、ウィンドウやメニュー、通知などの共通的なクロスプラットフォームリソースを宣言的に記述することを重視した、まったく異なるAPIを試す場所になっています。このレイヤは、将来的に考えているWindowsやLinux用の相当プロジェクトの基盤になる予定です。

従ってここには非常に力を注いでいるのですが、まだ極めて初期の段階なので、簡単なものから重要なものまで、コントリビューションを歓迎しています。

InfoQ: 対象とするフレームワークの拡張や、iOSアプリ開発を対象にするような計画はありますか?

Lindsay: そうしたいのですが、このパッケージは私が最終的に構想しているものの一部品ですので、現時点ではコミュニティや新たなコントリビュータに任せたいと思っています。

InfoQ: 1.0へ向けてのロードマップを教えてください。

Lindsay: よい質問ですね!ですが実際には、安定化、文書化、プロセス、ラッパの追加、スコープの明確化、といったもの以外の回答はありません。1.0リリースに向けて、人々が何を望んでいるのか(そして何を支援したいのか)をブレインストームするために、GitHub上でディスカッションを始めています。残念ながら私はObject-Cのエキスパートではない(その歴史に完全に取りつかれてはいますが)ので、ニュアンス的な部分においては、私よりも造詣の深い人たちに大きく頼っています。あなたがObjective-C開発者でGoにも興味があるのならば、ぜひ手伝ってください!

MacDriverはGitHubからフォーク可能だ。

この記事に星をつける

おすすめ度
スタイル

BT