2
1

More than 3 years have passed since last update.

Processing(Java)からSwiftで書いたプログラムを呼び出す

Last updated at Posted at 2020-10-06

Processing を使ってユーティリティ系のソフトを作っていると、どうしてもかゆいところに手がとどかない(システム環境設定系とか)

Swiftで実装すればAPIを叩くのに苦労はしないが、
Java側で Runtime#exec とか ProcessBuilder を使うのはなんか違う気がするので、
どうにかしてして、Javaで実装されたメソッドのようにSwiftで実装した関数を叩きたい!

JNA(Java Native Access)

Processingの実体はPAppletクラスを継承したJavaクラスであるため、
Javaの文法、資産を利用することができる

Javaには、JNI(Java Native Interface)と呼ばれる、C/C++などで実装されたネイティブコードと連携する仕組みがある。
このJNIを利用すれば、Swiftで実装したネイティブコードを呼び出されるのではと思うが、
このJavaとC/C++を繋ぐJNIを書くのがマヂ無理
(パッケージ名を含めた関数名をつけた関数をつくったり...)

※ Swiftの実装を呼び出すには結局SwiftとJavaの間にCのコードを挟まないといけない
Write & call Swift code using Java’s JNI
https://stackoverflow.com/questions/27628385/write-call-swift-code-using-java-s-jni

あー、Javaからネイティブコードを呼びたい
けど、JNIは書きたくない

そんなときは JNA(Java Native Access) を使おう
なんかよしなにやってくれるらしい!

JNAに関してはこのあたりを参照されたし
https://qiita.com/everylittle/items/b888cbec643f14de5ea6

Swiftのソースからダイナミックリンクライブラリを生成

兎にも角にも、Javaから呼び出したいメソッドを作ります

hello.swift
import Darwin.C

@_cdecl("hello")
public func hello() {
    print("Hello")
    fflush(stdout)
}

上のプログラムは標準出力に Hello と表示する関数の実装
ポイントは 1.呼び出し規約を設定する,2.publicにする です

fflush(stdout) がないとprintされないらしいので入れておきましょう
Printf from native code appears only at the end of the java app
https://stackoverrun.com/ja/q/3379588

このままではソースのままなのでコンパイルしましょう
上のプログラムからダイナミックリンクライブラリ(MacOSだとlib*.dylib)を生成します

ダイナミックリンクライブラリを生成
swiftc -emit-library hello.swift

ターミナルで上のコマンドを実行すると lib + ファイル名(拡張子なし) + .dylib
ダイナミックリンクライブラリが生成されます
image.png

nmコマンドで確認すると hello関数がグローバルセクションにあってなんか呼べそうな気がしてきますね

nmコマンドで確認
cha84rakanal$ nm libhello.dylib 
0000000000000e40 T _$S5helloAAyyF
                 U _$S6Darwin6stdoutSpySo7__sFILEVGvg
                 U _$SSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC
                 U _$SSSN
                 U _$Ss27_allocateUninitializedArrayySayxG_BptBwlFyp_Tg5
                 U _$Ss5print_9separator10terminatoryypd_S2StF
                 U _$Ss5print_9separator10terminatoryypd_S2StFfA0_
                 U _$Ss5print_9separator10terminatoryypd_S2StFfA1_
0000000000000fac s ___swift_reflection_version
                 U __swift_FORCE_LOAD_$_swiftDarwin
0000000000001060 s __swift_FORCE_LOAD_$_swiftDarwin_$_hello
                 U _fflush
0000000000000e30 T _hello
                 U _swift_bridgeObjectRelease
                 U _swift_bridgeObjectRetain
                 U dyld_stub_binder
cha84rakanal$ 

実際にJava側からSwiftで実装した関数を呼び出す

さて実際にProcessing(Java)側から先程作った Hello関数 を呼び出してみます
スケッチのdataディレクトリに先に生成したダイナミックリンクライブラリを入れて、次のスケッチで動かします!

JNAProcessing.pde
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;

public interface SwiftLib extends Library {
    void hello();
}

SwiftLib mylib;

void setup(){
    mylib = (SwiftLib) Native.loadLibrary(dataPath("libhello.dylib"), SwiftLib.class);
    mylib.hello();
}

void draw(){
}

ポイントは、

  1. JNA関連のライブラリをインポート
  2. com.sun.jna.Libraryを継承したインターフェースを定義(中に作成した関数の定義)
  3. Native#loadLibrary でダイナミックリンクライブラリを読み込み、ライブラリ名だけだと、Javaが参照しているダイナミックライブラリのパスをみにいく、絶対パスで入れられると確実

これでHelloと表示されたらOK! あとは何でも好きな処理をSwiftで実装するだけ!
それでは、よいProcessing Life

image.png

2
1
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
2
1