WKWebView からPythonista3 でJavaScript とお戯れあそばせ

この記事は、[Pythonista3 Advent Calendar 2022](https://qiita.com/advent-calendar/2022/pythonista3) の24日目の記事です。

https://qiita.com/advent-calendar/2022/pythonista3

一方的な偏った目線で、Pythonista3 を紹介していきます。

ほぼ毎日iPhone（Pythonista3）で、コーディングをしている者です。よろしくお願いします。

以下、私の2022年12月時点の環境です。

```sysInfo.log
--- SYSTEM INFORMATION ---
* Pythonista 3.3 (330025), Default interpreter 3.6.1
* iOS 16.1.1, model iPhone12,1, resolution (portrait) 828.0 x 1792.0 @ 2.0
```

他の環境(iPad や端末の種類、iOS のバージョン違い)では、意図としない挙動(エラーになる)なる場合もあります。ご了承ください。

ちなみに、`model iPhone12,1` は、iPhone11 です。

## この記事でわかること

- `ui` モジュールの`ui.WebView` について
- Pythonista3 でWKWebView を使う方法
- Pythonista3 でのJavaScript 開発のconsole について

## Web ブラウザってすごいね。って話

パソコンにもスマホにも、しれっと標準搭載されているWeb ブラウザ。

インストールせずとも使えちゃうって、改めて考えてみたら凄いことですね。

### Pythonista3 で、Web ブラウザを呼び出す

`ui` モジュールには、`ui.WebView` というブラウザを出せるView があります。

URL でサイトにアクセスするのはもちろん、`.html` ファイルを読み込み表示もできます。

[ui.webview | ui — Native GUI for iOS — Python 3.6.1 documentation](https://omz-software.com/pythonista/docs/ios/ui.html#webview)

https://omz-software.com/pythonista/docs/ios/ui.html#webview

### UIWebView とWKWebView （それからSFSafariViewController）

[UIWebViewを使用するAppのアップデート期限の延長 - 最新ニュース - Apple Developer](https://developer.apple.com/jp/news/?id=edwud51q)

https://developer.apple.com/jp/news/?id=edwud51q

UIWebView にあまり馴染みがないかもしれませんが、Pythonista3 に関しては大いにあります。

#### Pythonista3 とUIWebView

```py
import ui

#import pdbg

wv = ui.WebView()
print(wv.objc_instance.webView())
#pdbg.state(wv.objc_instance.webView())

```

`ui.WebView` より`objc_util` のインスタンスを確認すると:

```適度に改行
<UIWebView: 0x148913380; frame = (0 0; 100 100);
  autoresize = W+H;
  backgroundColor = UIExtendedSRGBColorSpace 0.741176 0.741176 0.760784 1;
  layer = <CALayer: 0x280484b60>
>

```

別に、Pythonista3 がUIWebView 使っててどうということはないのですが、なるほど！って感じですね。

（あ、アプリのこ、更新を、、、）

#### WKWebView をPythonista3 で！？

[mikaelho/pythonista-webview: WKWebView implementation for Pythonista](https://github.com/mikaelho/pythonista-webview)

https://github.com/mikaelho/pythonista-webview

有志の方が、`objc_util` よりWKWebView が呼び出せるモジュールを公開してくれています。

インストールの方法として、README には`pip` の方法があります。

しかし、実体は1ファイル（`wkwebview.py` ）なので、コピーして貼り付けでも問題ありません。

私の場合は、console 関係でエラーが出てしまうので、フォークして（ほんの）一部の書き換えたものを使っています。

[pome-ta/pythonista-webview: WKWebView implementation for Pythonista](https://github.com/pome-ta/pythonista-webview)

https://github.com/pome-ta/pythonista-webview

#### SFSafariViewController について（ちょっとだけ）

[Appにおけるウェブビューを実現するには、WKWebViewとSFSafariViewControllerのどちらを使うべきですか - 見つける - Apple Developer](https://developer.apple.com/jp/news/?id=trjs0tcd)

https://developer.apple.com/jp/news/?id=trjs0tcd

普段使わないので、紹介のみです。

[Pythonista/SafariViewController.py at master · tdamdouni/Pythonista](https://github.com/tdamdouni/Pythonista/blob/master/omz/SafariViewController.py)

https://github.com/tdamdouni/Pythonista/blob/master/omz/SafariViewController.py

サンプルコードがありますが、起動してみると「ああ、なるほど。これか」という見た目のView でサイトを閲覧できます。

開発ニーズにより、適宜使い分けするのが良さそうですね。

## `wkwebview.py` を使った開発

`ui.WebView` すら使ったことがない方には、酷な内容かもしれませんがWKWebView を`wkwebview.py` より呼び出して使う開発をしていきます。

素敵にモジュール化されているので、`ui.WebView` に近しい使い方になっています。

感覚を掴むために、`ui.WebView` で一通り操作してから、`wkwebview.py` モジュールにチャレンジしてもいいかもしれません。

また、JavaScript を知っている前提で話を進めてしまいます。なるべく、参考になりそうなリンクを提示します。

### ディレクトリ構成

今後、以下の構成を想定し解説を進めます。

```
.
├── main.py            <- Pythonista3 でView を呼び出す部分
├── docs               <- html の世界
│   ├── index.html
│   ├── css
│   │   └── style.css
│   └── js
│       └── main.js
└── wkwebview.py       <- リポジトリのを直接貼り付けしている 
```

ただただ、私が使いやすい構成です。使いやすく変更いただいても構いません。

`docs` 名は、GitHub pages 設定の昔の名残りです。。。

### `wkwebview.py` を読み込む

`main.py` で、実行をしPythonista3 よりView を表示させます:

```main.py
import sys
from pathlib import Path

import ui

sys.path.append(str(Path.cwd()))
from wkwebview import WKWebView


class View(ui.View):
  def __init__(self, url, *args, **kwargs):
    ui.View.__init__(self, *args, **kwargs)
    self.wv = WKWebView()
    self.wv.load_url(str(url))
    self.wv.flex = 'WH'
    self.refresh_webview()
    self.add_subview(self.wv)
    self.set_reload_btn()

  def will_close(self):
    self.refresh_webview()

  def set_reload_btn(self):
    self.refresh_btn = self.create_btn('iob:ios7_refresh_outline_32')
    self.refresh_btn.action = (lambda sender: self.refresh_webview())
    self.right_button_items = [self.refresh_btn]

  def create_btn(self, icon):
    btn_icon = ui.Image.named(icon)
    return ui.ButtonItem(image=btn_icon)

  def refresh_webview(self):
    self.wv.clear_cache()
    self.wv.reload()


if __name__ == '__main__':
  uri_path = Path('./docs/index.html')
  view = View(uri_path)
  view.present(style='fullscreen', orientations=['portrait'])

```

`./docs/index.html` で、`index.html` をロードして実行をさせています。

`wkwebview.py` に関しては、決してお行儀がいい書き方とは言えませんが、同階層にファイルが存在しているので、無理矢理読み込ませています:

```py
sys.path.append(str(Path.cwd()))
from wkwebview import WKWebView
```

また、執拗に`reload` やら`refresh` 処理をしています。。。

WebAudio 関係の実装時に、以前実行した音が残っていたりと、謎挙動をしておりまして、それを潰すために執拗に繰り返しています。

### `.html`、`.js` 側のこと

`./docs` 内のファイルたちは、良きように設定いただければと思います。

例えばですが:

```index.html
<!doctype html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
  <script type="module" src="./js/main.js"></script>
  <title>Pythonista3 でWebView</title>
</head>

<body>
<h1>ほげ😇</h1>
</body>
</html>

```

`./docs/js/` 内に、`main.js` と、`module.js` を作成したとします。

```main.js
import { fnc1 } from './module.js';

const pTag = document.createElement('p');
pTag.textContent = 'js より☺️';

document.body.appendChild(pTag);

const pImport = fnc1();
document.body.appendChild(pImport);

console.log('main');
```

`main.js` から`module.js` をインポートするかたちとして:

```module.js
function fnc1() {
  const pModule = document.createElement('p');
  pModule.textContent = 'module.fnc1 より😎️';
  console.log('module');
  return pModule;
}

export { fnc1 };

```

これで`main.py` を実行すると、`module.js` もインポートされ処理されていることがわかります。

[エクスポートとインポート](https://ja.javascript.info/import-export)

https://ja.javascript.info/import-export

[ECMAScriptモジュール · JavaScript Primer #jsprimer](https://jsprimer.net/basic/module/)

https://jsprimer.net/basic/module/

ローカルサーバーをたてなくても、module 機能が使えるのです。

[ES Modules入門 - JavaScriptでモジュールを使う時代 - ICS MEDIA](https://ics.media/entry/16511/)

https://ics.media/entry/16511/

### JavaScript のconsole を確認したい

とはいえ、さっきのjs のコードにもあった`console.log` の確認の手立てがないですよね？

ほぼ完成したファイルをPythonista3 へ持ってきて実行するのであれば、問題はなさそうです。

しかし、Pythonista3（スマートフォン）でも開発したいんじゃ！って声は多数あると思います（私だけ？）。

#### Console for Mobile Browsers. その名はEruda

[Eruda: Console for Mobile Browsers](https://eruda.liriliri.io/)

https://eruda.liriliri.io/

[liriliri/eruda: Console for mobile browsers](https://github.com/liriliri/eruda)

https://github.com/liriliri/eruda

[1行のコードでモバイルブラウザに開発者ツールを搭載できるJavaScriptライブラリ「Eruda」を使ってみた！ - paiza開発日誌](https://paiza.hatenablog.com/entry/2018/04/26/1%E8%A1%8C%E3%81%AE%E3%82%B3%E3%83%BC%E3%83%89%E3%81%A7%E3%83%A2%E3%83%90%E3%82%A4%E3%83%AB%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6%E3%81%AB%E9%96%8B%E7%99%BA%E8%80%85%E3%83%84%E3%83%BC%E3%83%AB%E3%82%92)

https://paiza.hatenablog.com/entry/2018/04/26/1%E8%A1%8C%E3%81%AE%E3%82%B3%E3%83%BC%E3%83%89%E3%81%A7%E3%83%A2%E3%83%90%E3%82%A4%E3%83%AB%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6%E3%81%AB%E9%96%8B%E7%99%BA%E8%80%85%E3%83%84%E3%83%BC%E3%83%AB%E3%82%92

これです（直球）。

#### Eruda のインポート

気軽に試したい or 数回の確認でいい。のであれば、CDN 経由で実装が可能です。

今回は個人的な理由（ただ好きなだけ）で、Skypack から持ってくることにします。

[Skypack: search millions of open source JavaScript packages](https://www.skypack.dev/)

https://www.skypack.dev/

[Native ESM 時代のフロントエンドビルドツールの動向](https://zenn.dev/mizchi/articles/native-esm-age)

https://zenn.dev/mizchi/articles/native-esm-age

`.html` へ`import eruda from 'https://cdn.skypack.dev/eruda';` を追記し`eruda.init();` するだけで使えます。

```index.html
<!doctype html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
  <script type="module">
    import eruda from 'https://cdn.skypack.dev/eruda';
    eruda.init();
  </script>
  <script type="module" src="./js/main.js"></script>
  
  <title>Pythonista3 でWebView</title>
</head>

<body>
<h1>ほげ😇</h1>
</body>
</html>

```

[npm:eruda | Skypack](https://www.skypack.dev/view/eruda)

https://www.skypack.dev/view/eruda

#### ギガが気になるならローカルに入れる方法も

実行時に毎回、CDN へ取りにネットワーク（ギガ）を使うのも、色々と無駄感がありますね。

私の場合は、ローカルに格納し呼び出しをしています。

（バージョンの固定をすれば済む話ですが）うっかりバージョンが上がっており、自分で書いたコードのバグなのか、バージョンが原因なのか調査が煩雑化する可能性があります。

Pythonista3 で尚且つ`objc_util` で呼び出しているWKWebView を使った、特殊な中の特殊な環境での実行なので、疑うべき原因はなるべく排除した方が精神衛生上楽です。

### Node とかnpm とかwebpack とか

もちろんですが、今回実行しているJavaScript は、ブラウザ上で実行されているJavaScript です。

つまりNode.js 環境想定のコードは動きません（もちろんTypeScript も）。

[JavaScriptとは · JavaScript Primer #jsprimer](https://jsprimer.net/basic/introduction/)

https://jsprimer.net/basic/introduction/

[JavaScript 入門](https://ja.javascript.info/intro)

https://ja.javascript.info/intro

世の中のjs のコードには、node を経由したbuild 処理を前提としたコードがたくさんあります。

Pythonista3 で動かしたい場合、何がどのように処理をされているのか、実際にコードを見に行って確認し「これできないかも。。。」な場面もあるかもしれません。

node module で書かれたコードをES Modules へ、丹精込めて手作業で書き換えるのもなかなかオツなものですが。。。

## 次回は

まさか、Python 実行環境のPythonista3 でJavaScript 書くことになるとは思いも寄らぬですよね。

Web ブラウザという、すごいマルチプラットフォームではJavaScript 一択なのでJavaScript を楽しみましょう。

eruda があるので、モバイルブラウザでもやみくもに開発せずに済むのが最高な点ですね。

次回は、Pythonista3（WKWebView） とJavaScript の連携の例を、私の大好きなWebGL を実装しつつ紹介していきたいと思います。

ここまで、読んでいただきありがとうございました。

## せんでん

### Discord

Pythonista3 の日本語コミュニティーがあります。みなさん優しくて、わからないところも親身に教えてくれるのでこの機会に覗いてみてください。

https://t.co/0IOW2hn1VC

### 書籍

iPhone/iPad でプログラミングする最強の本。

- [Python や Jupyter で iPhone/iPad 先端機能を簡単･自由にプログラミング！「活用篇」：hirax](https://techbookfest.org/product/1WiyV4LEev3f4YrzesRKWV?productVariantID=axX32srsSgtLtZWUtjkj99)

https://techbookfest.org/product/1WiyV4LEev3f4YrzesRKWV?productVariantID=axX32srsSgtLtZWUtjkj99

- [Python や Jupyter で iPhone/iPad 先端機能を簡単･自由にプログラミング！「土台篇」：hirax](https://techbookfest.org/product/wTZTyeibm5GQ5XgdfMrEBV?productVariantID=kRDmN1udbEYZUWbETdwL8r)

https://techbookfest.org/product/wTZTyeibm5GQ5XgdfMrEBV?productVariantID=kRDmN1udbEYZUWbETdwL8r

### その他

- サンプルコード

[Pythonista3 Advent Calendar 2022](https://qiita.com/advent-calendar/2022/pythonista3) でのコードをまとめているリポジトリがあります。

コードのエラーや変なところや改善点など。ご指摘やPR お待ちしておりますー

https://github.com/pome-ta/Pythonista3AdventCalendar2022sampleCode

- Twitter

なんしかガチャガチャしていますが、お気兼ねなくお声がけくださいませー

https://twitter.com/pome_ta93

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">やれるか、やれないか。ではなく、やるんだけども、紹介説明することは尽きないと思うけど、締め切り守れるか？って話よ！（クズ）<br><br>Pythonista3 Advent Calendar 2022 <a href="https://t.co/JKUxA525Pt">https://t.co/JKUxA525Pt</a> <a href="https://twitter.com/hashtag/Qiita?src=hash&amp;ref_src=twsrc%5Etfw">#Qiita</a></p>&mdash; pome-ta (@pome_ta93) <a href="https://twitter.com/pome_ta93/status/1588570697777152006?ref_src=twsrc%5Etfw">November 4, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

- GitHub

基本的にGitHub にコードをあげているので、何にハマって何を実装しているのか観測できると思います。

https://github.com/pome-ta