頭と尻尾はくれてやる!

パソコンおやじのiPhoneアプリ・サイト作成・運営日記


macOSでバインドを使いテーブルを表示する

macOSでテーブルを表示させようとした。
NSTableViewDelegate、NSTableViewDataSourceを使わずにバインドを使う。
NSArrayControllerで検索するといろいろと参考ページが見つかると思う。

この方法は以前Objective-Cでやったことがあったのだが、今回はSwiftでやろうとしたらえらくはまってしまった。はまりすぎて、俺の理解がおかしいのか?とObjective-Cに戻ってまで確認したりもした。

YamaguchiTatsuya/NSTableViewWithBindings: Simple sample code for macOS, to show table view with bindings
↑とりあえず最小限のサンプルコードをGitHubで公開してみた。


NSArrayControllerをドラッグ&ドロップする

↑Interface builderでViewControllerにArrayControllerを追加(矢印のようにドラッグ&ドロップ)。

NSArrayController追加後の画面

↑ここに見えるので、これを選択して
1. outletを接続
2. Content ArrayをViewControllerのpropsArrayに設定

すると、ViewController内の以下のコードのように
        let tp1 = TableProps(name: "apple")
let tp2 = TableProps(name: "banana")
let tp3 = TableProps(name: "chocolate")

assert(arrayController != nil, "arrayController is nil")

arrayController?.addObject(tp1)
arrayController?.addObject(tp2)
arrayController?.addObject(tp3)

for tp in propsArray {
print(tp.name)
}

↑arrayControllerにデータを突っ込んだ直後にpropsArrayの中身を確認すると、ちゃんと表示されている!バインディングすげー!って感心しました。


テーブルで表示するには他にもテーブル周りでバインドを設定する必要がありますがサンプルコードでご確認ください。


なお、結局Objective-Cで動いたのにSwiftで動かず、
this class is not key value coding-compliant for the key name.
といったエラーばかり出てたのは、TablePropsクラスで@objc dynamic というのが必要だったためのようでした。

//var name: String = “” 実行時エラー
@objc dynamic var name: String = “”


Version
Swift 5.1.3
スポンサーサイト






独自ファイル形式のDocument Based Applicationのサンプルコード

macOSのDocument Based Applicationの公式サンプルコードの謎
↑この続き。

AppleのサンプルコードやXcodeのデフォルトのプロジェクトではファイルを.txtファイルとして保存・読み込みする。
実際にmacOSのdocument based applicationを作成する場合にStringオブジェクトだけ扱う、なんてことはなく、様々な型のデータを扱うことになる。
そういう場合のコードを作成し、サンプルコードをGitHubで公開してみた。

YamaguchiTatsuya/DocumentBasedAppWithOwnExtension: macOS sample code for document-based application for own extension

↑こちらで適当なところから git clone https… とかで見ることができるはず。

textの場合と異なるのはContextクラスをNSSecureCodingプロトコルに準拠するようにした、というくらいか。


macOSのDocument Based Applicationの公式サンプルコードの謎

macOSアプリの話です。iOSではありません。

XcodeでmacOSアプリのプロジェクト作成時の画面で、”Create Document-Based Application”のところにチェックするとそれ用のテンプレで新しいプロジェクトが作成される。
これをベースに自分が作りたいようにごにょごにょしていくものの、クラス間の参照関係だとかがイマイチよくわからない。実際作ってはみたものの、何だか動きがおかしい、、、
ということがあったので、再度調べてみたら、公式のサンプルコードがあることに気付いた。

Developing a Document-Based App | Apple Developer Documentation

↑これを動かしてみたんだが、、、確かに動く。でも、コードがなんか不思議だ。

ViewController内に下のようなコードがある。
    weak var document: Document? {
if let docRepresentedObject = representedObject as? Document {
return docRepresentedObject
}
return nil
}
Documentクラスのオブジェクトを得ているように見える。
ViewControllerのrepresentedObjectから参照を得ているけどそもそもDocumentクラス内でViewControllerのrepresentedObjectにはContentクラスのオブジェクトを入れてる。
それじゃあこのdocumentには値は入らないんじゃないのと思って調べるとやはりdocumentは常にnil、、、
document?.objectDidBeginEditing(self)
とかやってるけど意味ないのでは、、、?
何か見落としているのだろうか?んー、謎だ。


TensorFlow2.0の学習結果を.h5→.pb→.mlmodelと変換してiOSで使う

やりたいことは、TensorFlow 2.0 + Kerasで学習させた結果をiOSで使いたい。
iOSで係数だけをもらうことも可能(※1)だが、mlmodel形式だと一番使いやすいのでベスト。

度重なる試行錯誤の結果、自分の場合はこれでいけた、というのをシェア。


TensorFlow 2.0で学習し、その結果を.h5ファイルで保存する。

virtualenvでTensorFlow 1.14.0の環境を作成、そこで
(1).h5ファイルを.pbファイルに変換
(2)その.pbファイルを.mlmodelに変換
これでiOSでVision/CoreMLを使いすんなりと予測ができた。


(1).h5→.pbの変換
Deep Learningアプリケーション開発 (2) TensorFlow with Python - Qiita
.h5→.pbの変換は↑こちらのページにある通り。ただ感謝、感謝!

(2).pb→.mlmodelの変換
上記リンク内の通りに動かすと.pbファイルができる。同じフォルダに.jsonファイルも生成されて、そこを見て入出力の名前を確認しておく必要がある。確かにKerasで自分で付けた名前と違っていた。
後はtfcoremlを使って以下のようにして変換。
import tfcoreml as tf_converter

classLabels = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15']

tf_converter.convert(tf_model_path = PbFilePath,
mlmodel_path = MlmodelFilePath,
output_feature_names = ['y_out/Softmax:0'],
input_name_shape_dict = {'x_input:0':[1, height, width, 3]},
image_input_names = ['x_input:0'],
class_labels = classLabels,
image_scale = 1.0/255.0
)
なお、この2回の変換はTensorFlow 2.0だとすんなり動かず、、、私には書き換えられそうにないので諦めました。この変換だけ1.14.0で実行することにします。





以下は背景とか。

当初は、
Kerasでの学習結果をmlmodelにしてiOSで使う

ここに書いた方法の.h5ファイルを.mlmodelに変換、updateする、でなんとかなると思っていた。
MNIST(白黒、畳み込みなし)で検証していけることを確認したのだが、、、

いざ本番(※2)の学習結果を同様にやろうとしたらupdateがうまくいかない。一応保存まではするんだが、
RuntimeWarning: You will not be able to run predict() on this Core ML model. Underlying exception message was: Error compiling model: "compiler error: Espresso exception: "Invalid argument": generic_reshape_kernel: Invalid bottom shape (32 8 2 1 1) for reshape to (20480 -1 1 1 1)".
RuntimeWarning)

なんて警告が出る。
途中での計算がうまくいってないんだろうか?MNISTは畳み込みを使っていないモデル、白黒画像を入力だったが、今回は畳み込みあり、カラー画像入力というように多少異なる。そのあたりに起因するcoremltoolsのバグなのかなんなのか、、、?
試しにXcodeで使おうとしても、

Xcodeでのエラーメッセージ

↑同じエラーが表示されビルドできなかった。






※1
係数だけ出力してiOSでそれを読み込む、というのもやったことあるが、iOS内でニューラルネットを構成して係数を読み込んで、、、とか記述が膨大。一度mlmodelを味わうともうやってられない。

※2
個体値カメラ
↑先日リリースしたこのポケモンGO用の個体値チェックアプリではバーの部分の画像(カラー)を入力画像として予測している。リリース版ではTensorFlow 1.14を使っていたが、macOSの破損→クリーンインストールを機に、TensorFlow 2.0 + Kerasで学習させ、その結果をiOSで使えるようにしたいのだ。


iOSアプリ「個体値カメラ」のリジェクト対応

iOSアプリ「個体値カメラ」をリリースしました(ポケモンGO用)
↑今回リリースした「個体値カメラ」というiOSアプリは4回リジェクトされたのでその辺りの対応を情報共有します。

上記リンクにあるデモ動画のように、ポケモンGOアプリを表示してるiOSデバイスを別なiOSデバイスのカメラでポケモンの個体値を表示するアプリ、です。
審査する方も勘違いするかもなあ?ってややこしさかもしれません。



(その1)
Guideline 2.3.4 - Performance - Accurate Metadata

Guideline 4.1 - Design - Copycats

Guideline 4.2.3 - Design - Minimum Functionality

↑最初のリジェクトは3項目ありました。
詳細は省きますが、どれもreviewerの勘違いです。App preview内に手が写っているとかアプリがポケモンGOに似た画像を使っている、とか。
なので再度申請ではなく、App Store Connect内で説明を書いて返信。
このやりとりにはファイルを添付できるので、ポケモンGOのスクショを2枚添付して、添付画像に対してこのアプリを使ってみて!とアピール。

すると返信してから数時間後に
「よお!審査に少し時間がかかるで」
みたいな返事と共に、アプリのstatusは In Review(審査中)に変わりました。



(その2)

Guidelines 2.3.7 - Performance - Accurate Metadata
それからおよそ8日後に2度目のリジェクト。
「タイトル、サブタイトルに”Pokémon GO”なんて使ったらあかんやろ?!」
とのこと。

2.3.7の一部を引用すると
2.3.7 独自性のあるApp名を選び、Appを正確に説明するキーワードを割り当ててください。App Storeのシステムを悪用する目的で、メタデータに商標登録用語、人気のApp名、その他無関係のフレーズを含めることはできません。

当初のアプリ名称は
IVs Camera for Pokémon GO
個体値カメラ for ポケモンGO(日本語版)
でした。
こういう特定のアプリに関連するアプリだとアプリ名にfor ポケモンGOとか付けたくなるし、実際そういうアプリがストアにたくさん並んでいます。
それらがこのガイドラインが無い頃に審査が通過したのか、reviewerが見逃したのか、もしくは"悪用ではない"と判断したのか分かりませんが文句を言っても仕方ないので、泣く泣くタイトルを
IVs Camera
個体値カメラ(日本語版)
↑このように変更しました。なんのアプリかよくわからないですね。
アプリ名をアプリ内で表示していたのでアプリのバージョンを1.0.1とし再度アーカイブ、再申請しました。



(その3)
5.1.1 Legal: Privacy - Data Collection and Storage
3度目のリジェクトの指摘は
「カメラの使用目的をはっきり記述せえや!」
とのこと。
プライバシー関連で、カメラなどを使うアプリだとInfo.plistに記述する必要があるわけだが、、、それが
This app uses the camera
このアプリはカメラを使います(日本語版)
↑みたいな記述でした、多分。

5.1.1 (ii)許可
必ず、「目的」でデータの用途を明確かつ十分に説明してください。
↑一部だけどそういうことなんだろう。確かに不十分だったな、ということで、、、

This app uses the camera to see the image of Pokémon GO app.
このアプリはポケモンGOアプリの画像を得るためにカメラを使用します。(日本語版)
に変更しました。

また、5.1.1を読んでて気付いたのですが、

5.1.1 データの収集および保存
(i)プライバシーポリシー:すべてのAppには、App Store Connectのメタデータフィールドと各App内にアクセスしやすい形で、プライバシーポリシーへのリンクを必ず含める必要があります。プライバシーポリシーはわかりやすく明確なものである必要があります。
↑App内にプライバシーポリシーへのリンクが必須、とある。知らなかった、、、
ずっと自分のアプリ紹介サイトにプライバシーポリシーを記述し、申請時にそのリンクを入力することで済まして他のだが。
こりゃまずいかも、と思いアプリ内にプライバシーポリシーを記述しました。
これを1.0.2として再提出。



(その4)
Guideline 2.3.3 - Performance - Accurate Metadata
2.3.3 スクリーンショットは、単なるタイトル画面、ログインページ、スプラッシュ画面ではなく、Appにおける実際の画面を写したものである必要があります。(以下略)

「スクショが使用状況を反映してへんやろ!」という指摘、、、
そう、最初のApp previewと同じ勘違い。

reviewerの指摘内容がアレなので面白いのだが書くのも悪いのでやめとこう。
最初と同じようにテキストでやりとり。
私「スクショは使用時に撮影したものです」

reviewer「情報おおきにな!再提出したアプリの審査をするの、楽しみにしてるで!」

私「ほんとに使ってる時に撮影したものです」

みたいなやりとり後、、、

突然アプリのstatusは配信準備完了(Ready for Sale)に変わったのでした!


そんなわけで案外reviewerの勘違いもある、とか何か参考になれば幸いです。



おまけ
今回、App preview内およびスクリーンショットでポケモンGOの画面をぼかしています。

Appプレビュー - App Store - Apple Developer
掲載する権利がある素材だけを使用してください。
↑このような記載があったから。
今回のアプリの場合、そのままポケモンGO画面を使用しても著作権的には”引用”ってことで問題はないような気もしますが、Appleのサイトに上記のように書いてるし、引用ならOKってのが世界共通なのか知らないし他の法律に引っかかるかもしれないし、、、ってことで安全側にしてぼかしときました。ポケモンGOユーザーならどの画面かわかるだろうし。



参考
App Store Reviewガイドライン - Apple Developer





Copyright ©頭と尻尾はくれてやる!. Powered by FC2 Blog. Template by eriraha.