UIWebView を使用しているライブラリを見つける


AppStoreConnect にバイナリをアップロードすると Apple からこんなメールが届きました。

>Dear Developer,
>
>We identified one or more issues with a recent delivery for your app, "XXXXX" X.X.X (XXXX). Your delivery was successful, but you may wish to correct the following issues in your next delivery:
>
>ITMS-90809: Deprecated API Usage - Apple will stop accepting submissions of apps that use UIWebView APIs . See https://developer.apple.com/documentation/uikit/uiwebview for more information.
>
>After you've corrected the issues, you can use Xcode or Application Loader to upload a new binary to App Store Connect.
>
>Best regards,
>
>The App Store Team

UIWebView がプロダクトに含まれているアプリは 2020年12月から**アップロードできなくなります**。つまり、UIWebView を WKWebView に置き換えてねということです。
これは、プロダクトに含まれるライブラリ・フレームワークが UIWebView を使用している場合も同様にアップロードできなくなるので注意が必要です。今回はそんなライブラリ・フレームワークに含まれている UIWebView を探す方法を簡単にまとめます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/292456/38958a1d-db78-1042-59d3-252c227243ed.png)


 **公式記事**

- https://developer.apple.com/news/?id=12232019b


## どうやってやるのか？

シェルスクリプトを作って全ての Framework に対して UIWebView を含んでいるかどうかを調べます。

### UIWebView を含む Framework を探す

まずは UIWebView を作成するためにスクリプトを作っていきます。ファイルの中身は下記のような感じです。

```sh
FRAMEWORK_DIRS=$(find . -name '*.framework')
for framework in $FRAMEWORK_DIRS; do
    fname=$(basename $framework .framework)
    echo $framework/$fname
    nm $framework/$fname | grep UIWeb
done
```

nmコマンドはオブジェクトファイルや実行ファイルを指定することでそのオブジェクトファイルのシンボル情報を確認できます
grep コマンドはファイルの中にある文字列を検索するコマンドです。

nm コマンドで Framework を指定してファイルのシンボル情報を取得します。その情報の中にある `UIWeb` キーワードを grep コマンドで表示します。


シェルスクリプトの実行にはアクセス権限が必要で、このまま実行してしまうと下記のような権限 Error を吐いて
しまうのでアクセス権限を修正します。

```
$ ./script/uiwebview-search.sh
-bash: ./script/uiwebview-search.sh: Permission denied
```

とりあえずは今回は自分だけ書き込みができればいいので `755` とかにしておきます。
シェルスクリプトの基本的なことについては下記のリンクの記事に目を通すとわかった気になれます🏃🏻‍♂️

   - https://qiita.com/zayarwinttun/items/0dae4cb66d8f4bd2a337
   - https://qiita.com/shisama/items/5f4c4fa768642aad9e06

```
chmod 755 script/uiwebview-search.sh
```


アクセス権限が修正できたのでスクリプトを実行します。
これで、UIWebView を使ってる可能性のあるライブラリはライブラリ名の後に結果が出力されます。出力は省略していますが
下記のようになります。

```
$ ./script/uiwebview-search.sh
./TwitterKit.framework/TwitterKit
                 U _OBJC_CLASS_$_UIWebView
0000000000001730 s l_OBJC_$_PROTOCOL_INSTANCE_METHODS_OPT_UIWebViewDelegate
0000000000001798 s l_OBJC_$_PROTOCOL_METHOD_TYPES_UIWebViewDelegate
0000000000001718 s l_OBJC_$_PROTOCOL_REFS_UIWebViewDelegate
0000000000001430 S l_OBJC_LABEL_PROTOCOL_$_UIWebViewDelegate
0000000000001e10 D l_OBJC_PROTOCOL_$_UIWebViewDelegate
./Carthage/Build/watchOS/SwiftyJSON.framework/SwiftyJSON
./Carthage/Build/watchOS/Kingfisher.framework/Kingfisher
./Carthage/Build/watchOS/KeychainAccess.framework/KeychainAccess
./Carthage/Build/iOS/RxAtomic.framework/RxAtomic
./Carthage/Build/iOS/Alamofire.framework/Alamofire
./Carthage/Build/iOS/RxCocoa.framework/RxCocoa
0000000000069500 T _$s7RxCocoa0A20WebViewDelegateProxyC03webD0ACSo05UIWebD0C_tcfC
00000000000843e0 S _$s7RxCocoa0A20WebViewDelegateProxyC03webD0ACSo05UIWebD0C_tcfCTq
0000000000069590 T _$s7RxCocoa0A20WebViewDelegateProxyC03webD0ACSo05UIWebD0C_tcfc
0000000000069760 t _$s7RxCocoa0A20WebViewDelegateProxyC03webD0ACSo05UIWebD0C_tcfcTf4gn_n
0000000000013b70 T _$s7RxCocoa0A20WebViewDelegateProxyC03webD0So05UIWebD0CSgvM
00000000000694c0 T _$s7RxCocoa0A20WebViewDelegateProxyC03webD0So05UIWebD0CSgvg
00000000000843c8 S _$s7RxCocoa0A20WebViewDelegateProxyC03webD0So05UIWebD0CSgvgTq
0000000000069430 t _$s7RxCocoa0A20WebViewDelegateProxyC03webD0So05UIWebD0CSgvpACTK
0000000000069480 t _$s7RxCocoa0A20WebViewDelegateProxyC03webD0So05UIWebD0CSgvpACTk
000000000008fcd8 S _$s7RxCocoa0A20WebViewDelegateProxyC03webD0So05UIWebD0CSgvpMV
00000000000ae110 D _$s7RxCocoa0A20WebViewDelegateProxyC03webD0So05UIWebD0CSgvpWvd
0000000000013320 T _$s7RxCocoa0A20WebViewDelegateProxyC03webD0So05UIWebD0CSgvpfi
0000000000013b70 T _$s7RxCocoa0A20WebViewDelegateProxyC03webD0So05UIWebD0CSgvs
0000000000069650 T _$s7RxCocoa0A20WebViewDelegateProxyC12parentObject08delegateF0ACSo05UIWebD0C_xmtcAA0eF0CyAGSo0jdE0_pGRbzAA0eF4TypeRzSoAJ_p0E0RtzAG06ParentH0RtzlufC
0000000000069680 T _$s7RxCocoa0A20WebViewDelegateProxyC12parentObject08delegateF0ACSo05UIWebD0C_xmtcAA0eF0CyAGSo0jdE0_pGRbzAA0eF4TypeRzSoAJ_p0E0RtzAG06ParentH0Rtzlufc
0000000000057fd0 t _$s7RxCocoa13DelegateProxyC012setForwardToC0_06retainC0yq_Sg_SbtFSo9UIWebViewC_So0ijC0_pTg5
00000000000aa888 d _$s7RxCocoa13DelegateProxyCySo9UIWebViewCSo0efC0_pGML
0000000000018ca0 t _$s7RxCocoa13DelegateProxyCySo9UIWebViewCSo0efC0_pGMa
000000000003f0b0 t _$s7RxCocoa16castOrFatalErroryxypSglFSo17UIWebViewDelegate_p_Tg5
000000000003e990 t _$s7RxCocoa16castOrFatalErroryxypSglFSo9UIWebViewC_Tg5
000000000005c980 t _$s7RxCocoa17MessageDispatcher33_C4A5AA2E6C0BB4C2CAE530F540A96C33LLC8selector13delegateProxyAD10ObjectiveC8SelectorV_AA08DelegateR0Cyxq_GtcRlzCr0_lufcTf4ngn_nSo9UIWebViewC_So0vwU0_pTg5
000000000005da00 t _$s7RxCocoa17MessageDispatcher33_C4A5AA2E6C0BB4C2CAE530F540A96C33LLC8selector13delegateProxyAD10ObjectiveC8SelectorV_AA08DelegateR0Cyxq_GtcRlzCr0_lufcyycfU0_So9UIWebViewC_So0vwU0_pTg5TA
000000000005d9d0 t _$s7RxCocoa17MessageDispatcher33_C4A5AA2E6C0BB4C2CAE530F540A96C33LLC8selector13delegateProxyAD10ObjectiveC8SelectorV_AA08DelegateR0Cyxq_GtcRlzCr0_lufcyycfU_So9UIWebViewC_So0vwU0_pTg5TA
0000000000020ec0 t _$s7RxCocoa20DelegateProxyFactory33_10C538EC0CCB612AAE329C1334BA0687LLC6extend4makeyxq_c_tAA0cD4TypeRzr0_lFAA0a7WebViewcD0C_So05UIWebP0CTg5
00000000000257a0 t _$s7RxCocoa20DelegateProxyFactory33_10C538EC0CCB612AAE329C1334BA0687LLC6extend4makeyxq_c_tAA0cD4TypeRzr0_lFyXlyXlcfU_AA0a7WebViewcD0C_So05UIWebP0CTg5TA
00000000000259f0 t _$s7RxCocoa20DelegateProxyFactory33_10C538EC0CCB612AAE329C1334BA0687LLC6extend4makeyxq_c_tAA0cD4TypeRzr0_lFyXlyXlcfU_AA0a7WebViewcD0C_So05UIWebP0CTg5TATm
00000000000190f0 T _$s7RxSwift8ReactiveV0A5CocoaSo9UIWebViewCRbzlE11didFailLoadAA10ObservableCys5Error_pGvg
00000000000813c4 S _$s7RxSwift8ReactiveV0A5CocoaSo9UIWebViewCRbzlE11didFailLoadAA10ObservableCys5Error_pGvpMV
0000000000018c80 T _$s7RxSwift8ReactiveV0A5CocoaSo9UIWebViewCRbzlE12didStartLoadAA10ObservableCyytGvg
0000000000018d70 t _$s7RxSwift8ReactiveV0A5CocoaSo9UIWebViewCRbzlE12didStartLoadAA10ObservableCyytGvgTm
00000000000813c4 S _$s7RxSwift8ReactiveV0A5CocoaSo9UIWebViewCRbzlE12didStartLoadAA10ObservableCyytGvpMV
0000000000018d50 T _$s7RxSwift8ReactiveV0A5CocoaSo9UIWebViewCRbzlE13didFinishLoadAA10ObservableCyytGvg
00000000000813c4 S _$s7RxSwift8ReactiveV0A5CocoaSo9UIWebViewCRbzlE13didFinishLoadAA10ObservableCyytGvpMV
0000000000018c30 T _$s7RxSwift8ReactiveV0A5CocoaSo9UIWebViewCRbzlE8delegateAD13DelegateProxyCyAFSo0efH0_pGvg
00000000000813c4 S _$s7RxSwift8ReactiveV0A5CocoaSo9UIWebViewCRbzlE8delegateAD13DelegateProxyCyAFSo0efH0_pGvpMV
0000000000018c10 t _$sSayypGs5Error_pIeggzo_AAytsAB_pIegnrzo_TR92$s7RxSwift8ReactiveV0A5CocoaSo9UIWebViewCRbzlE12didStartLoadAA10ObservableCyytGvgySayypGcfU_Tf3nnpf_n
0000000000018c20 t _$sSayypGs5Error_pIeggzo_AAytsAB_pIegnrzo_TR93$s7RxSwift8ReactiveV0A5CocoaSo9UIWebViewCRbzlE13didFinishLoadAA10ObservableCyytGvgySayypGcfU_Tf3nnpf_n
00000000000194a0 t _$sSayypGs5Error_psAB_pIeggozo_AAsAB_psAB_pIegnrzo_TR077$s7RxSwift8ReactiveV0A5CocoaSo9UIWebViewCRbzlE11didFailLoadAA10ObservableCys5A21_pGvgsAJ_pSayypGKcfU_Tf3nnpf_n
00000000000aa898 d _$sSo17UIWebViewDelegate_pML
00000000000ac838 d _$sSo17UIWebViewDelegate_pmML
00000000000695f0 t _$sSo9UIWebViewC7RxCocoa0c3WebB13DelegateProxyCIeggo_AbEIegno_TR03$s7c9Cocoa0A20ebfg41C28registerKnownImplementationsyyFZACSo05A7D0CcfU_Tf3npf_n
00000000000693c0 t _$sSo9UIWebViewC7RxCocoa11HasDelegateA2cDP8delegate0F0QzSgvMTW
0000000000069400 t _$sSo9UIWebViewC7RxCocoa11HasDelegateA2cDP8delegate0F0QzSgvMTW.resume.0
0000000000069360 t _$sSo9UIWebViewC7RxCocoa11HasDelegateA2cDP8delegate0F0QzSgvgTW
0000000000069390 t _$sSo9UIWebViewC7RxCocoa11HasDelegateA2cDP8delegate0F0QzSgvsTW
000000000008622c s _$sSo9UIWebViewC7RxCocoa11HasDelegateACMA
0000000000084350 S _$sSo9UIWebViewC7RxCocoa11HasDelegateACMc
00000000000ae118 D _$sSo9UIWebViewC7RxCocoa11HasDelegateACWP
0000000000049d50 T _$sSo9UIWebViewC7RxCocoaE06createC13DelegateProxyAC0c3WebbfG0CyF
00000000000aa890 d _$sSo9UIWebViewCML
00000000000aa9f0 d _$sSo9UIWebViewCmML
                 U _OBJC_CLASS_$_UIWebView
000000000007ef72 s _symbolic 7RxCocoa13DelegateProxyCySo9UIWebViewCSo0efC0_pG
000000000007e96e s _symbolic 7RxCocoa13DelegateProxyCySo9UIWebViewCSo0efC0_pGSgXw
000000000007e9a4 s _symbolic 7RxCocoa13DelegateProxyCyxq_GSgXwz_So9UIWebViewC_So0efC0_pRlzCr0_lXX
000000000007ef4c s _symbolic So17UIWebViewDelegate_p
000000000007ef64 s _symbolic So9UIWebViewC
000000000007c232 s _symbolic So9UIWebViewC7RxCocoa0c3WebB13DelegateProxyCIegno_
000000000007efaa s _symbolic So9UIWebViewCSgXw

....省略
```

これで `TwitterKit`・`RxCocoa` あたりが怪しいのが分かるようになりました 👏