iOSのSwift,Objective-CでPythonを呼び出す

# iOSでは使えないPython

MacにはPythonが標準で入っているので、Cocoaアプリケーションではlibpythonが簡単に使えますが、iOSにはPythonが入ってないので使うことができません。
いくら調べてもPythonistaのことばっかりで嫌になります。
そんなところ、libpythonを静的ライブラリにしてiOSアプリに組み込んで動かすことができたので、ここで書くのはその方法。

# kivy

kivyという、Pythonでマルチタッチアプリケーションを作成するツールがあり、これはiOSに対応しています。
Pythonのソースコードからライブラリをビルドするのは難しかったので、こいつからlibpythonだけを抜き出して使います。
kivyではPython2.7とPython3.7をサポートしています。ここでは3.7を利用しますが、2.7も基本的には同じやり方でできるはずなので、適宜読み替えてください。

## kivyを使えるようにする

### Python3を標準に

Python2.7にしたい場合はスキップしてください
python3が入っていない場合はhomebrewなどを使ってインストールしてください

```shell
alias python=python3
alias pip=pip3
```

### kivyをダウンロードと必要なもののインストール

```shell
git clone git://github.com/kivy/kivy-ios
cd kivy-ios
pip install -r requirements.txt
xcode-select --install
brew install autoconf automake libtool pkg-config
brew link libtool
sudo pip3 install Cython==0.28.1
```

### ビルド

```shell
./toolchain.py build python3 
./toolchain.py build python3 hostpython 
```

これでPythonのビルドは完了です。
```kivy-ios/dist/lib/```内に

- libcrypto.a
- libffi.a
- libpython3.a
- libssl.a

が生成されているはずです。

# プロジェクトにライブラリをインポート

## 必要なライブラリを追加

"Linked Frameworks and Libraries"に以下の3つを追加してください

- libz.tbd
- libbz2.tbd
- libsqlite3.tbd

## ビルドしたライブラリのインポート

Project NavigatorにFrameworksというグループがあるはずなので、```kivy-ios/dist/lib/```内にある4つのファイルをFinderからドラッグしてFrameworksグループに入れてください。
ダイアログが出てきたら"Copy items if needed"にチェックを入れて"Finish"をクリック。

## python3をプロジェクトにコピー

```kivy-ios/dist/root/python3 ```がpython3の本体みたいなので、これをProject Navigatorにドラッグして、ダイアログが出てきたら"Copy items if needed"と"Create folder references"にチェックを入れて"Finish"をクリック。
Project Navigatorでpython3のフォルダが青いアイコンならOKです

## Header Search Pathsを指定

このままではPythonのヘッダをコンパイラが見つけてくれないので、指定します。
Build Settings内のHeader Search Pathsに```python3```をrecursiveで追加します

<img width="1528" alt="スクリーンショット 2018-12-05 4.20.19.png" src="https://qiita-image-store.s3.amazonaws.com/0/242402/c5d81bc0-79e0-2139-e33f-7552cbe29892.png">


## 最終的にこうなっているはず

<img width="1398" alt="スクリーンショット 2018-12-05 4.23.25.png" src="https://qiita-image-store.s3.amazonaws.com/0/242402/c8f7860c-6a7d-bab9-66ea-1f56916e3d42.png">
<img width="908" alt="スクリーンショット 2018-12-05 4.24.07.png" src="https://qiita-image-store.s3.amazonaws.com/0/242402/29937c58-da6f-8c02-9245-a4c187c76527.png">


# Objective-Cで使用

テストしてないです。

## インクルード
使いたいクラスのヘッダで```#include "Python.h"```

## PYTHONHOMEの指定

Py_Initializeをする前にPythonの標準ライブラリが入っているパス(PYTHON_HOME)を指定しないと標準ライブラリが使えません。なので、下にあるコードをPy_Initializeの前に実行してください。

```Objective-C
NSString *resourcePath = [NSString stringWithFormat:@"%@/python3", [[NSBundle mainBundle] resourcePath], nil];
NSString *python_home = [NSString stringWithFormat:@"PYTHONHOME=%@", resourcePath, nil];
putenv((char *)[python_home UTF8String]);
```

## 実行

[他のアプリケーションへの Python の埋め込み](https://docs.python.org/ja/3/extending/embedding.html)
あとはここに書いてある通りにすればいいです。


# Swiftで使用

##インクルード

Bridging-Headerが必要です。
ない場合は自分で作ってBuild Settingsで指定するか、新しい"C File"を生成すると自動で生成してくれるのでそれを使います。

Bridging-Headerさえ用意できれば、そこに```#include "Python.h"```と書くだけです。

## PYTHONHOMEの指定

Py_Initializeをする前にPythonの標準ライブラリが入っているパス(PYTHON_HOME)を指定しないと標準ライブラリが使えません。なので、下にあるコードをPy_Initializeの前に実行してください。

```Swift
let resourcePath = Bundle.main.resourcePath! + "/python3"
let python_home = "PYTHONHOME=\(resourcePath)" as NSString
putenv(UnsafeMutablePointer(mutating: python_home.utf8String))
```

## 実行

[他のアプリケーションへの Python の埋め込み](https://docs.python.org/ja/3/extending/embedding.html)
あとはここに書いてあるみたいにできます。

ただし、1つ注意点があり、Swiftでは#defineによるマクロが使えないので、#defineで定義された関数が使えません。
例えば、
```PyRun_SimpleString```は
```#define PyRun_SimpleString(s) PyRun_SimpleStringFlags(s, NULL)```
として定義されている関数なので、使うことができません。こういうときは、直接
```PyRun_SimpleStringFlags(s, NULL)```を呼び出す必要があります。

こういうのは[超高水準レイヤ](https://docs.python.jp/3/c-api/veryhigh.html)のような場所で調べたり、ヘッダーファイル内を関数名で検索すればわかりますが、面倒ですね。もしSwiftでライブラリ内のマクロ関数を使う方法を知っている方いたら教えて欲しいです。

# サンプル

<img width="1428" alt="スクリーンショット 2018-12-05 4.42.20.png" src="https://qiita-image-store.s3.amazonaws.com/0/242402/0b19c811-c8e0-d969-99fe-612253e1bb25.png">

標準ライブラリが使えます。
でも、これだとまだpythonを実行するだけで、Swiftとの値のやりとりができてない。
あと、kivyのrecipesを見ると、標準ライブラリ以外にもいくつかライブラリが使えそうだけど、それもまだ試してない。
とりあえず、進捗があったらまた記事を上げます。