第10回コベチケの会

コベリンの最近の取り組みとして、業務などで役立ちそうな知見を共有する会を開催することになりました。 そのついでに発表に使ったアジェンダもそのまま公開してしまおうという豪快な企画です。

※ アジェンダをそのままコピペして公開したものなので若干見にくい箇所もあるかもしれませんが、ご了承ください。

Apple Silicon(M1) の mac だと Share Extension がうまく動かなかったお話 @mironal

概要

Share Extension のときに使う NSItemProviderhasItemConformingToTypeIdentifier(_ typeIdentifier: String) -> Bool がどうやら正しい値を返さない

M1 mac の iOS シミュレーター(Xcode 12.4, Xcode 13の両方)でのみ現象を確認しており、 Intel mac でのシミュレーターや実機ではこの現象は発生しなかった。

不具合の調査に何時間もハマって悲しかった。

詳細

Apple 製品はファイルの種類の識別に Uniform Type Identifier というものを使います。

Share Extension は様々なアプリから様々な形式でデータが渡されるため、データ形式から処理を分岐することがある。 例えば動画・画像、画像と言っても jpeg なのか? png なのか?など。

そしてそのファイルの種類の検出に使えるメソッドの一つに NSItemProviderhasItemConformingToTypeIdentifier(_ typeIdentifier: String) -> Bool がある。

https://developer.apple.com/documentation/foundation/nsitemprovider/1403921-hasitemconformingtotypeidentifie

NSItemProvider とは Share Extension に渡されたデータの情報が入っているクラスである。例えば一つの URL を共有した場合には、その URL の情報が入った NSItemProvider が一つだけ入手できる。複数の画像を選んで共有した場合にはその数だけ NSItemProvider が渡されるという感じである。

例えば Share Extension の中で渡されたデータが画像だった場合に、何かの処理をしたいときは以下のように書く。

guard let inputItem = extensionContext?.inputItems.first as? NSExtensionItem else { fatalError() }

// attachment が NSItemProvider である
guard let attachment =  inputItem.attachments?.first else { fatalError() }

if attachment.hasItemConformingToTypeIdentifier(UTType.image.identifier) {
  print("画像だった場合の処理")
} else {
  print("画像じゃない場合の処理")
}

このコードを含む Share Extension を実行して、Photosアプリ(写真アプリ)から適当に画像を一つ選んで 開くと、常に 画像じゃない場合の処理 のログが出てしまいます。 つまり attachment.hasItemConformingToTypeIdentifier(UTType.image.identifier) が false を返してしまいます。

細かい動作確認

デバッグのために以下のようなコードを書いて走らせました。

print(attachmentItem.registeredTypeIdentifiers)
print("Is jpeg", attachmentItem.hasItemConformingToTypeIdentifier(UTType.jpeg.identifier))
print("Is image", attachmentItem.hasItemConformingToTypeIdentifier(UTType.image.identifier))

jpeg の画像を開いた場合は 以下のようなログが出ることを期待しています.

jpeg は image のサブタイプ

  • ["public.jpeg"]
  • Is jpeg true
  • Is image true

実機で実行

実機で実行した結果が以下になります。

 ["public.jpeg"]
Is jpeg true
Is image true

納得です。

M1 mac のシミュレーターで実行

以下のようになりました。

["public.jpeg"]
Is jpeg true
Is image false

jpeg なのに image じゃないとはどういうことなのか...?

さらなる調査

単純に UTType の処理が壊れているのかな?と推測して以下の処理を実行してみました。 jpeg は image を conform しているので true が得られるはずです。

print(UTType.jpeg.conforms(to: .image))

実機

true が得られました。

M1 mac のシミュレーター

false が得られました。

また以下のようなログが出力されていました。

Failed to realize static UTType instance 0x10e1c60b0 for identifier public.jpeg. Please file a bug. The type should be present in Core Types.

なにか特有の問題があるようですね...

まとめ

めちゃくちゃわかりにくい不具合なので注意してください。

Run ScriptのPATHにhomebrewのパスが入って来なくなった @takkumattsu

背景

ちょっと特殊なプロジェクトでiOSとAndroidの共通処理をC++で書いているプロジェクトがあり、C++部分の開発はXcodeやることが多かったので Run Script に clang-format を走らせるスクリプトを入れていた

現象

Xcode13 or m1 Mac にしてからhomebrewで入れていたclang-formatがスクリプトから見えないという現象が起きいてた

notfound.png (38.8 kB)

実際にRun Scriptに設定していたのはこんな感じ

一部プロジェクト名の部分はぼかしています

image.png (32.2 kB)

#!/bin/sh

SCRIPT_DIR=$(cd $(dirname $0); pwd)
cd $SCRIPT_DIR/..

COMMON_DIR="${SCRIPT_DIR}/../common"

COMMONS=$(find $COMMON_DIR -follow -type f | egrep ".h$|.hpp$|.cpp$")
for f in $COMMONS
do
  clang-format -i $f
done

OBJC_DIR="${SCRIPT_DIR}/../プロジェクト名"

OBJS=$(find プロジェクト名 -type f | egrep ".m$|.mm$|.h$")
for f in $OBJS
do
  clang-format -i $f
done

解決策

解決策は単純にパスを通しただけ

diff --git a/ios/tools/wrapper_format.sh b/ios/tools/wrapper_format.sh
index a305b309..c68f2754 100755
--- a/ios/tools/wrapper_format.sh
+++ b/ios/tools/wrapper_format.sh
@@ -1,5 +1,9 @@
 #!/bin/sh

+if [ -d /opt/homebrew/bin/ ]; then
+  export PATH=/opt/homebrew/bin:$PATH
+fi
+
 SCRIPT_DIR=$(cd $(dirname $0); pwd)
 cd $SCRIPT_DIR/..

原因はちょっとわかってない

m1 MacにしたタイミングなのかXcode13にしたタイミングなのか分かってないですが環境を変えたタイミングで起きました。 普通に考えるとパス通してないので以前の環境でも起きておかしくなさそうな気もする?

完全に関係ない話

Uber Eats で天下一品があったので注文してみた。 自分で麺を茹でることもできこういうタイプはかなりお店の味に近づけるので期待度が高かったが、食べてみた感想はそこまでだった。 こってりを頼んだのだがお店のあのこってり感は再現できなかった。 今のところラーメンだと町田家のUberEatsが店の味に近かった気がしました(おわり)

image.png (641.9 kB)