1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Swift SDK for Android をざっくり見ていく

Posted at

Getting Started

doc

やっていること

元がSwiftコードのバイナリをAndroid OS で実行するデモとなっていて 以下をやっている。

  • Swift コードをクロスコンパイルして Android向けのELFを吐き出し
  • 吐き出したELFと Android NDK のC++ の共有ライブラリをemulatorにpush
  • emulator上でshellから ELFを実行

ざっくり準備 と 実行

進め方の詳細は記事をみたほうがよさそう。ここではざっくり書くと

準備

  • SwiftSDK For Android の準備(Swiftのホストツールチェーンもここで必要)
  • Android NDK
  • SwiftSDK For AndroidとAndroid NDK のリンク

実行

// swiftlyで作ったパッケージをAndroid(aarch64)向けにクロスコンパイル
swiftly run swift build --swift-sdk  aarch64-unknown-linux-android28 --static-swift-stdlib

// emulatorに ELFと共有ライブラリ送信して実行
adb push .build/aarch64-unknown-linux-android28/debug/hello /data/local/tmp
adb push $ANDROID_NDK_HOME/toolchains/llvm/prebuilt/*/sysroot/usr/lib/aarch64-linux-android/libc++_shared.so /data/local/tmp/
adb shell /data/local/tmp/hello
// hello

要するにSwift をクロスコンパイルして、AndroidOS向けのバイナリ生成する仕組み。

swift-android-examples

doc

大きく二つ。

  • swift-java を使った,SwiftをAndroidで動かす仕組み
  • JNIを直に使った連携

swift-java による連携

サンプル

出典: https://github.com/swiftlang/swift-android-examples/tree/main/hello-swift-java

自作のシンプルなSwiftライブラリを Androidから呼んでいる。

どういった連携手法なのか

swift-java

  • Swiftで書いた関数や型をAndroid側(Java/Kotlin)に公開するためのビルドツール群とGradleプラグインのセット
  • Swiftのシンボルから必要なJNIバインディングとJavaラッパークラスの自動生成してくれる
import Crypto
#if canImport(FoundationEssentials)
import FoundationEssentials
#else
import Foundation
#endif

public func hash(_ input: String) -> String {
    SHA256.hash(data: Data(input.utf8)).description
}

↑ から ↓。JNIのネイティブメソッドがjavaラッパーとして生成されていて,これをKotlinから参照している。

public final class SwiftHashing {
  static final String LIB_NAME = "SwiftHashing";
  
  static {
    System.loadLibrary(LIB_NAME);
  }
  
  // ==== --------------------------------------------------
  // hash
  
  /**
   * Downcall to Swift:
   * {@snippet lang=swift :
   * public func hash(_ input: String) -> String
   * }
   */
  public static java.lang.String hash(java.lang.String input) {
    return SwiftHashing.$hash(input);
  } // printJavaBindingWrapperMethod(_:_:importedFunc:signaturesOnly:) @ JExtractSwiftLib/JNISwift2JavaGenerator+JavaBindingsPrinting.swift:480
  private static native java.lang.String $hash(java.lang.String input);
  
} 

かなりの部分を自作のgradle タスクで賄っているのが面白い。この辺りに実験的さを感じる
(細かなステータスやアナウンスは把握してないので感想)

preBuildにひっかけ。

preBuild.dependsOn(copyJniLibs)

buildSwiftAllというカスタミタスクに繋いで

// copyJniLibs
def copyJniLibs = tasks.register("copyJniLibs", Copy) {
    dependsOn(buildSwiftAll)
    // ここ以降はビルド済みsoファイル等を必要な場所に移動してたりする。

各Android ABI (arm64-v8a, armeabi-v7a, x86_64) 向けにSwiftコードをビルド。

// buildSwiftAllから path等設定して以下
executable(getSwiftlyPath())
args("run", "swift", "build", "+${swiftVersion}", "--swift-sdk", info.triple)

このswift build の中で JExtractSwiftPlugin というプラグインが動いて JNI連携のjavaが吐き出される様子。

残りは細かいですが、吐き出したsoファイルを特定のフォルダにまとめてJNI連携しやすいようにしているっぽい。

シンプルなJNI連携

doc

swift

ネイティブ連携ということで、Swift側にも見慣れない低レベルなコードが多い。

import Android

@_cdecl("Java_org_example_helloswift_MainActivity_stringFromSwift")
public func MainActivity_stringFromSwift(env: UnsafeMutablePointer<JNIEnv?>, clazz: jclass) -> jstring {
    let hello = ["Hello", "from", "Swift", "❤️"].joined(separator: " ")
    return hello.withCString { ptr in
    	env.pointee!.pointee.NewStringUTF(env, ptr)!
    }
}

連携するための記法ということでざっくり

  • JNI特有の型(jstring, jclass, JNIEnv)
  • C言語互換(@_cdecl, withCStrin)

ビルド

swift-javaの例と同じく, gradle scriptで頑張っている
この辺り( swift-android.gradle.kts

val swiftBuildTask = createSwiftBuildTask(buildTypeName, arch, isDebug)
val copyTask = createCopySwiftLibrariesTask(buildTypeName, arch, isDebug, swiftBuildTask)
...
tasks.findByName("merge${capitalizedVariantName}JniLibFolders")?.let { task ->
task.dependsOn(copyTask)       
  1. Swiftビルドタスクでsoファイル生成libhello-swift-raw-jni-library.so
  2. コピータスクでJNILibsディレクトリに移動
  3. AndroidのJNIマージタスクに依存関係を追加(パイプラインに統合)

という感じ。おそらくこのpluginを公開しようと頑張っているのではないかな?

Android側

.soを初期化時にロードして、JNI関数呼び出し

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        findViewById<TextView>(R.id.sample_text).text = stringFromSwift()
    }

    /**
     * A native method that is implemented by the 'helloswift' native library,
     * which is packaged with this application.
     */
    external fun stringFromSwift(): String

    companion object {
        // Used to load the 'helloswift' library on application startup.
        init {
            System.loadLibrary("helloswift")
        }
    }
}

終わり

  • swift-javaの連携で、swiftコードも馴染みある感じになる。こちらのgradle pluginが充実すると Androidでも使いやすそうではあった(感想)
  • SwiftUI連携は今のところ公式ではなさそうだが、やろうとしているところはある様子

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?