2020年9月30日水曜日

[Visual Studio] Xamarinセットアップ

1.Android,Windows,iOSのクロスコンパイルができてしまう⁉


Visual Studio 2017/2019には、Microsoftが買収したXamarinが入っており、Android、Windows、iOSのクロスコンパイルができてしまうという、夢のような仕組みがあるらしい。
このうちiOSについては、そんなにうまい話ではないことが、露見した。
どうも、Visual Studio for Macを入れたMacが必要で、WindowsのVisual Studioと連携するらしい。
実行ファイルの生成、実行ファイルの実行はMac側で行われ、Windowsは単に窓口ということらしい。
なんかかえって面倒なことに巻き込まれそうなので、iOSはあきらめた。

今回の目的は、既にiOSで動いているアプリを、Androidに移植することであり、どうせならWindows版も同時に作れたらいいなぁぐらいの感じだったので、Android Studioではなく、このXamarinで開発することに決めた。


2.UWPというプロレス団体


さすがにMFCはもう使っていないが、いつもWindowsアプリは、.Net Framework+C#で作成し、Windows Formsアプリとしてリリースしている。
WPFとかStoreアプリという選択肢は見たことがあるが使ったことはなく、UWPという新しいプロレス団体のようなものについては、知る由もない。
とにかくXamarinを使うなら、この団体に加入しないといけないらしい。UIはXAMLで記述しないといけないらしい。


3.「Android SDKとツール」が真っ白


とりあえずMicrosoftのいう通りやってみた。

ビルドは通り、Androidのエミュレータも起動するが、サンプルが実行されない。
なんかよくわからないけど、バージョンが古いとか、Hyper-V使った方がいいとか言われ、「Android SDKとツール」ダイアログに誘導される。
が真っ白で何もでてこなくて困惑。

【処方箋】
歯車マークから、リポジトリをMicrosoft(推奨)→Google(サポート対象外)に変更すると治った。


4.Hyper-Vの有効化


Microsoft様の説明

結論からいうと、Windowsの機能で、Hyper-VをONにすればいい。
しかし、BIOSでVirtualization Technology (VT-X)が無効になっていると、Hyper-Vプラットフォームのチェックボックスがグレーアウトで触れない。


その場合は、BIOSでの設定変更が必要(いまどきの64bit Windows PCのBIOSなら選択肢があるはず)。


5.管理者権限でVisual Studioをつかう


「選択したエミュレーターを起動できませんでした。これはプロセスに十分なアクセス許可がないことが考えられます。VS を管理者として起動すると回避できる可能性があります。」
といわれる。常に管理者権限でないとダメなの?


6.UWPでビルドできない⁉


ソリューションエクスプローラにUWPが出てくるが、移行が必要とか言われる。
そもそも、新規プロジェクト>Cross-Platformで、UWPのチェックボックスが触れない。


Visual Studio InstallerでUWPを全部入れたら治った。


7.XAMLデザイナーって使えないんすか(゚Д゚)ハァ?


どうやって画面作ったらいいんでしょう? しばし茫然。
プレビューはあるらしいのだが、メニューに見当たらない。どこにあるのやら。

よ~く見ると右下に左右分割という小さなボタンがあり、これを押すとXAMLのプレビュー画面がでる。



2020年7月18日土曜日

[App Store Connect] App Storeに掲載する動画(Appプレビュー)をつくる

1. 実機がある場合


iOS12からいいものが実装された。録画ボタンを押すだけ!


あらかじめ15〜30秒以内に編集しておくと楽。

しかし最近の機種は、FPSが30以上になってしまう。
「設定>カメラ>ビデオ撮影」では、スクリーンキャプチャのFPSは制御できない模様。

FPSは30以下にしないとApp Storeにアップロードできないようなので、例えばffmpegで変更する。

コマンド例)ffmpeg -i in.mp4 -r 30 out.mp4


CUIが嫌な人は、下記のiMoveを使ってもいい。


2. 実機がないので、Simulatorを録画する場合


1. QuickTime Playerの「新規画面収録」を使う。


なんか知らないが、キャプチャーサイズは1/2スケールでいいようだ。
iPad用の1200×1600なら、600×800。
【ハマりPoint】収録終わりのボタンは、なぜかメニューバーにある。


2. QuickTime Playerが吐き出したmp4ファイルを、iMovieで編集。



「新規アプリケーションプレビュー」で編集する。

とにかく最終的に共有メニューから「アプリケーションプレビュー」(赤丸)を選びたいので、そうなるように頑張る。
なお、下段の編集タイムライン上をハイライトさせないと、赤丸は出現しない。


【ハマりPoint】このままだと無音なので、音を追加。

無音だとApp Storeにアップロードできない。
瞬間でもいいので、音声(紫丸)を追加すること。


3. iMovieの代わりにKeynote


以前Keynoteでもやったことがあるので、備忘録として記載する。

1. ドキュメントサイズを所望のサイズに設定
2. その上にmp4ファイルを載せる
3. ムービーで書き出す。元ファイルの前2秒、後5秒に静止画が追加される模様。



2020年6月6日土曜日

[iOS13, Xcode11, ShareExtension, 共有] 「〜で開く」ではなく「〜にコピー」にしたい。

1. 共有には2種類ある


自分で実装するまであまり考えたことがなかったのだが、共有には「Clipbox+」と「"Clipbox+"で開く」のように、「で開く」が付かない物と付く物の2種類がある。
そして動きが違う。
赤丸は編集モードのようなものが開くが、青丸はアプリ(ここではClipbox+)に遷移する。


2. Share Extensionを使った方法


赤丸はShare Extensionを実装すると出てくるようになる。
こちらのページが詳しい。

何をしないといけないかというと、Share Extension機能をもった別のBundle ID(例えばClipbox.shareとか)を持つ、新しいアプリを作らないといけない。

この新しく作ったアプリのinfo.plist内NSExtensionActivationRuleで、サポートしている機能を記述することにより、iOSに自分が持つ機能を宣言することになる。
結果、共有ボタンを押した時にiOSが、候補(Suggestions)に、新しく作ったアプリのCFBundleDisplayNameを表示できるようになる。

候補から新しく作ったアプリを押下すると、新しく作ったアプリがキックされ、標準で用意されているPostを押下すると、didSelectPost()でURL名、ファイル名、文字列等が獲得できる。

獲得したものを元のアプリに渡せば一件落着のはずなのだが、実は、新しく作ったアプリと、元のアプリは別のものなので、直接データのやりとりはできない。

この障壁を回避するために、App Groupsと言われる共有空間が用意されている。
新しく作ったアプリと、元のアプリで同じApp Groupsを定義して使う。
Xcode上で実施すればうまくいくはずなのだが、うまく自動生成できない場合は、Apple Developper Program「Certificates, Identifiers & Profiles」のページで手動登録するしかない。

作成した共有空間にデータを書き出す方法としては、ググると、plistでの方法が、

let userDefaults = UserDefaults(suiteName: "group.XXX.XXX")
userDefaults.set(item, forKey: "text")
userDefaults.synchronize()


紹介されているが、後々のことを考えると、ファイルやりとりの方が便利と思う。

let dst = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.XXX.XXX")?.appendingPathComponent("test.txt")
item.write(to: dst, atomically: true, encoding: .utf8)

3. Share Extension :アプリをキックできない


ここまで頑張って実装しても、コード上から元アプリを呼び出すことができないので、元アプリをユーザーが立ち上げなくてはならず格好が悪い。
iOSの仕様変更で今はできない。昔はできていただけに情けない(こちらに詳しい)。

でもAdobeはできてるんだよね。何か方法はあるのだろう。


4. Document Content Type UTIを使った方法


表題の「〜で開く」「〜にコピー」の話だが、Windowsで言うところの拡張子の関連付けみたいなものだろうか、UTIで設定すればいいだけ。
Share Extensionに比べると相当楽。

ただ以下の点でつまづいた。
・public.textを指定しても、標準アプリの「メモ」からは呼び出せない。
・「〜にコピー」にしたいのに、「〜で開く」になってしまう。

それぞれの処方箋は以下のとおり。
【処方箋】標準アプリの「メモ」はそもそもUTIが謎。あきらめてShare Extensionの方法で対応した。

【処方箋】「〜で開く」は、LSSupportsOpeningDocumentsInPlaceを削除すると「〜にコピー」にできる。(*1
iOSは、LSSupportsOpeningDocumentsInPlace=NOが必要。(*2
MacOSは、LSSupportsOpeningDocumentsInPlaceは削除。(*3

*1 以前は、ApplicationDelegateで受け取るために、LSSupportsOpeningDocumentsInPlaceが必要だったが、今は常にSceneDelegateが呼ばれる仕様に変わったようなので、削除することができる。

*2 iOSアプリをAppStoreにあげる時、LSSupportsOpeningDocumentsInPlaceがないと、Document Typeを指定してるくせにと怒られる。

*3 一方でMacOSアプリでは、LSSupportsOpeningDocumentsInPlace=NOは許されないので、削除するしかない。


5. ShareExtention:public.textだけどpublic-file-urlもサポートすべき例


public.text系のアプリから、青丸の「〜でコピー」を選択すればうまくいくのだが、試しに赤丸のShare Extensionも試してみると、UTTypeText(public-text)が空っぽだった。

しかし、kUTTypeFileURL(public-file-url)には、ファイルパスが入っていたので、どうやらこれを使えということらしい。
標準の「メッセージ」や「メール」は、当然のことながら、うまく実装できていた(下図)。



2020年6月2日火曜日

[Swift5, Xcode11] Mac Catalyst で悪戦苦闘

1. archiveできない!?


古いXcodeを使っていた時のくせで、archiveの時は[Generic iOS Device]を選ぶものと思い込んでいた。
でもそれだと、ipaファイルしか生成されない。

【処方箋】正解は、[My Mac]を選んでarchive


2. スワイプできなくねぇ?


SceneKit使っていて、2本指によるスワイプ(位置移動)、ピンチイン・アウト(拡大縮小)ができなかった。ドラッグ(回転)は1本指なのでできる。
ググって見ると、ベータ版の頃から話題になっているよう。重大な不具合に思えるが、現状、直されていないし、制限事項にも上がっていない⁉️

option押しながらのマウス操作で、スワイプ ・ドラッグができるのかと思いきや、なんと用意されていない。
Mac miniとかMac Proでは、入力デバイスとしてタブレットが必要になってしまう。

SceneKit、ARKitを使ったiOSアプリでは、Mac Catalyst対応しました!って能天気にリリースできないという厳しい現実。

【処方箋】別途UIを実装する逃げしかない模様。


3. TableViewも変じゃねぇ?


複数選択(multiple selection)が働かない。

【処方箋】willSelectRowAtとwillDeselectRowAtで、むりやり実装するしか無さそう。

mizuBuro_cureさんの記事が参考になった。

このほかハイライト色(GrayとBlueの差別化)にも問題がありそうなので、複数選択をむりやり実装すると、なんか変な感じになる。
がんばったとしても、開発者のバグをカバーする努力に対して、App Store「アプリ審査」が冷や水を浴びせる可能性が高い。


4. SplitViewの幅おかしくねぇ?


起動時、primary側の幅がせまい。
minimum値を設定しても、それを下回る幅で起動する。

【処方箋】maxとminに同じ値を設定して逃げた。
splitViewController.preferredDisplayMode = .allVisible
splitViewController.minimumPrimaryColumnWidth = 300
splitViewController.maximumPrimaryColumnWidth = 300


5. アプリをクリーンインストールできなくねぇ?


~/Library/Containers/(bundle ID)/Data/Document
~/Library/Containers/(bundle ID)/Data/Library/Application Support/(app)
を参照してるので、アプリケーションフォルダから「通常通り」に削除しても、設定(plistやCoreDataなどなど)は残存してしまう。

【処方箋】Terminalで、defaults delete (Bunndle ID)を実行。


※App StoreでReady for Saleになってから確認したが、正式リリース版では「通常通り」の削除で残存物はなく、クリーンインストールは実現できた。


6. アプリ内課金ができない?


request = SKProductsRequest(productIdentifiers: Set([”XXX.XXX.XXX”]))
request.delegate = self
request.start()

のrequestの永続性が、iOSとは違うみたい。

【処方箋】requestは、SKProductsRequestDelegateを継承しているControllerで保持する。



7. ShareExtensionはむずかしい


私のアプリの作りが悪いのか、NSExtensionPrincipalClassを指定しないと、App Storeに上がらなかった。結果、NSExtensionMainStoryboardは排他の関係となるので、削除した。

MainStoryboardがなくなると、@Objcでクラス名を宣言しないと、OSは呼んでくれなくなる。
例:
@objc(ShareViewController)
class ShareViewController: SLComposeServiceViewController {
...
}


8. 勝手にメニューが作成されてしまう


勝手に作ってくれるのはいいのだけれど、問題だらけ。

1. Bundle display nameが使われない。XXX.appのXXXの部分が使われてしまう。


【処方箋】PRODUCT_NAMEを変えるしかなさそう。


2. File > Preferenceで、Version、Build、Acknowledgementが出てくるが空っぽ。


RunScriptで以下のようなスクリプトを走らせてるんだけど、これじゃぜんぜんダメ。

version=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "$PROJECT_DIR/$INFOPLIST_FILE")

build=$CURRENT_PROJECT_VERSION

root=${CODESIGNING_FOLDER_PATH}/Settings.bundle/Root.plist

if test -f $root; then

/usr/libexec/PlistBuddy -c "Set PreferenceSpecifiers:1:DefaultValue $version" "$root"

/usr/libexec/PlistBuddy -c "Set PreferenceSpecifiers:2:DefaultValue $build" "$root"

fi



【処方箋】以下のようにしてほぼ削除。

extension AppDelegate {

    override func buildMenu(with builder: UIMenuBuilder) {

        super.buildMenu(with: builder)

        guard builder.system == .main else { return }

        builder.remove(menu: .preferences)

        builder.remove(menu: .services)

        builder.remove(menu: .file)

        builder.remove(menu: .edit)

        builder.remove(menu: .format)

        builder.remove(menu: .view)

        builder.remove(menu: .window)

        builder.remove(menu: .help)

    }

}


「Helpがないと審査に通らない」という書き込みも見たような気がするが、とりあえずこれで挑戦してみる。

追記:
Aboutのコピーライトだけは、NSHumanReadableCopyrightで簡単にできた。

2020年5月23日土曜日

[Swift 5, Xcode11, CoreData] UITableViewでSectionタイトルを出す

1. モデルエディタでCoreDataを編集


Sectionタイトルに対応する項目を追加する。
ここでは、sectionName:Stringとし、Default値を「Data」とした。
Transientは任意だが、ONとした。
TransientをONにすると、データベース(sqlite)にカラムは生成されない。


2. コーディング


NSFetchedResultsController作成時に、引数sectionNameKeyPath:で、作成したCoreDataの項目名"sectionName"を渡す。


Sectionタイトル用のtableViewのデリゲートメソッドを使い、.nameを返す。


3. 動作例


Sectionタイトルが現れ、Default値「Data」で分類できた。



2020年3月24日火曜日

[iOS App] フランス語 中国語 ローカライズの効果

1. フランス語のローカライズ


続報です。
一過性ではなく、シェアはそのまま維持されているようです。
さらなる伸びはないですが...


2. 中国語のローカライズ


gengoに700円くらい払って、繁体と簡体のローカライズしてみました。
フランス語よりは効果あるみたいです。
世界シェア的にはもっとあってもいいような気もしますが、中国本土でAppleのアカウント持ってる人(海外に出ることができる限られた富裕層)ってこのぐらいしかいないんですかね。


2020年2月22日土曜日

[iOS App] フランス語 ローカライズの効果

私が配信しているアプリで、唯一のヒット作が Midnight Video Camera


ですが、これを使って多言語化がどれだけ効果があるのか検証してみました。

1. まずは実績


英語をメイン言語にし、日本語を加えて、2年くらい配信しているのですが、アメリカ3:日本7が実体です。イギリス、オーストラリアもあるけど、ほぼアメリカと考えていいようです。


当方としては初めから海外リリースを念頭に取り組んできたので、シェア的には5:5になるべきものが3:7であるという結果は予想外で、原因があれば取り除きたいと考えています。
が、思い当たる節はないんですよね。そもそもアプリ名は英語名しか用意していないし。
App Storeの仕様としか思えないんですがね。

2. フランス語化


買ってくれそうでパイがでかい国となると、ヨーロッパ言語かと思い、手始めにフランス語化してみました。


配信開始月(1月)に明らかに伸びはありましたが、全体(アメリカ+日本)からみると1/50くらいですか。劇的な効果はでませんね。
あとヨーロッパならやってドイツ語か(ブラジルが買ってくれるなら)ポルトガル語もありかと思いますが、やはり中国語ですかね。
App Storeのシェアは、アメリカ:日本:中国=1:1:1らしいんで。

2020年2月11日火曜日

[Xcode11] libstdc++をまだ使いたい

Podで引き込んだライブラリが要求するんで、libstdc++を今でも使う場面があるかと思います。
最終リリースはXcode9なので、そこから抽出する必要があります。

ビルドするための処方箋


1. ヘッダー


/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/include
のc++フォルダをまるごと、Xcode11の同ディレクトリにコピー。

2. ライブラリ


/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/lib
の3つ
libstdc++.6.0.9.tbd (実体)
libstdc++.6.tbd (シンボリックリンク)
libstdc++.tbd (シンボリックリンク)
をXcode11の同ディレクトリにコピー。


iOS Simulatorでデバッグするための処方箋


1. ヘッダー


/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/include
のc++フォルダをまるごと、Xcode11の同ディレクトリにコピー。

2. ライブラリ


/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/lib
の3つ
libstdc++.6.0.9.tbd (実体)
libstdc++.6.tbd (シンボリックリンク)
libstdc++.tbd (シンボリックリンク)
をXcode11の同ディレクトリにコピー。

3. ライブラリ


/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib
の3つ
libstdc++.6.0.9.dylib (実体)
libstdc++.6.dylib (シンボリックリンク)
libstdc++.dylib (シンボリックリンク)
をXcode11の
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib
にコピー。