2021年4月11日日曜日

Google AdMobで、Info.plistの入力項目が増えた

 AdMob SDK8.3.0をインストールしようと公式ガイドを見て進めていたら、先日8.0の時にはなかったInfo.plistに追加する項目がいっぱい増えていて驚いた。

SKAdNetworkIdentifierとしてcstr6suwn9.skadnetworkという値を追加することは以前と同じだが、それ以外にも現時点39個もの「SKAdNetworkItems キーの値を Google に提供したその他の購入者」なるものを選択して入力せよというのだ。もうなんのこっちゃいである。

AdMobのスタートガイドより

2021年2月16日火曜日

Appのプライバシーの入力が必須になる

 Apple Developerからメール(当然英語)でも来てたけど、2020/12/8からApp Storeでアプリを公開、アップデート時などに新たに「Appのプライバシー」という項目を入力する必要ができた。

アプリを使う上で、いろんな情報を収集してるか/してないか、しているならそれはどんな情報で、何のために収集してるか…ってことを入力するわけ。

2021年2月7日日曜日

Google AdMobの仕様がいつの間にか変更されてた

過去に‪Objective-C‬で書いたアプリを久しぶりにメンテしようと思い、AdMobのSDKを最新の8.0.0に上げた。(注意:7.69.0でも同じ問題出てほっといたもの)
ところが起動してタイトル画面が出てすぐにスレッドエラーだかが出て止まってしまった。
公式のインストールガイドどおりにやってるのだがダメ。

インストールするFrameworkも少し増えてるし、公式ガイドでGoogleMobileAds.frameworkとなってるのがダウンロードしたフォルダではGoogleMobileAds.xcframeworkになっていて戸惑ったが、後者をそのままインストールしていいようだ。

2020年10月2日金曜日

iPhoneバッテリー交換手順

 2018年に買ったiPhone8のバッテリーがヘタってしまったので交換する。

2020年5月5日火曜日

Xcode以外でAppStoreにアップロードする

Mac App Storeより
久しぶりにいじっててApp Store Connectにアクセスして気付いたんだけど、Xcode以外にもAppStoreにアプリをアップする方法があるんだな。

2019年11月29日金曜日

Blenderで3Dモデルを作ってXcodeで使う

無料だけど高機能な3DアプリのBlender。ARなんかで表示させるモデルを作りたくて勉強してる。

細かい使い方、作り方はともかく、Xcodeに取り込んで使う旨で引っかかった点、気づいた点を書く。

2019年11月27日水曜日

Xcodeアップデート時のトラブル(ファイルが消えない)

Xcode11.0がリリースされた時だったと思うんだけど、Xcode10.xからの自動アップデートが途中で止まってしまう現象に悩まされた。
Xcode10.xを削除してから新規で11.0をインストールするといいと聞き、ゴミ箱を空にしようとしたものの、Xcodeが使用中だから空にできないとつれないお返事。

Xcodeのパッケージを開き、中のファイルを少しずつ消していったところ、かなり深い改葬にあった
GameCenterUIService.app
というアプリが悪さしていることがわかり、これだけを取り出したところ、他のXcodeのファイルは全て空にでき、Xcode11.0は無事インストールできた。

しかし、このファイルだけがどうしても消せない。
このアプリのパッケージを開くと、
ca.lproj
というフォルダの中に
InfoPlist.strings
があるだけ。
中身は
bplist00— _ CFBundleDisplayNameo Servei d UI del Game
  Center
  ! Z
とだけ書かれてる。

ちなみにファイル名をコピーしようとするとなぜか勝手に「InfoPliót.strings」に名前が変わり、「名前を変更しようとしています。」というパスワードを求めるダイアログが出ることがある。これもわけがわからない。

ca.projフォルダからInfoPlist.stringsを取り出して捨てようとしても、
いくつかの必要な項目が見つからなかったため、操作を完了できません。
(エラーコード-43)
と出てゴミ箱にすら入れられない。
別のフォルダに移動させようとするとコピーされる。(コピーされたものは捨てられる)

macOS 10.15 Catalinaをセーフブートして試してもダメ。
今のところゴミ箱が空にできないだけでさしたる問題はないが、解消されてもいない。

結局クリーンインストールで解決

このためにやったわけじゃないんだけど、システムがかなり不安定だったんでクリーンインストールしたら、さすがにゴミ箱を空にできた。
お疲れ様でした。ちゃんちゃん。

2019年10月10日木曜日

ダークモードの回避

左が正常なのに、ダークモードだと右の表示になる
Labelの文字が白くなり、TextViewの背景が真っ黒に

iOS13とXcode11、macOS 10.15 Catalinaにした後にアプリを審査に出したら、表示が見づらいからってリジェクトされた。
なんじゃこりゃ、TextViewの背景色なんていじった覚えないぞ!? と思ったが、iOS13から付けられた(余計な)機能、ダークモードのせいだった。
どうやら新しく提出したアプリから影響するらしい。

UISegmentedControlの色もこのとおり

回避方法


Info.plistのUser Interface Style を Light にすればアプリ丸ごとこれまでのライトモードで動いてくれる。
親方様(アップル)としてはダークモードへの対応を推奨してるんだろうけど、また今度ね!
しかしどうしてこう、いちいちデベロッパーに無理な負担を強いるかなあ…。

2019年10月2日水曜日

ARAnchorの追加と削除

以下はARKit3とSpriteNodeで作る、XcodeのARのテンプレート(AR空間にインベーダー👾の絵文字スプライトを表示させるやつ)を基に直したもの。

ARAnchorの追加

Scene.swiftの方でARAnchorの位置を決めて
sceneView.session.add(anchor: )
すると、ViewControllerのARSKViewDelegateメソッド
view(_ view: ARSKView, nodeFor anchor: ARAnchor) -> SKNode?
が呼ばれるので、そこで貼りたいSpriteNodeをreturnしてやると、それがAnchorに貼られるという変則的なやり方になっている。
実際にどうやってanchorにspriteを貼ってるのかは知らない。

Scene.swift

//カメラ前方の位置にSpriteを表示するためのanchorを貼る
func addAlienAnchor(sceneView: ARSKView) -> Void {
if let currentFrame = sceneView.session.currentFrame {
var translation = matrix_identity_float4x4 //世界座標と向きを表す4*4の行列
//前方の多少ランダムな位置を決定
//行列の3列目のz値に-0.4なら前方0.4m
let x:Float = 0
let y:Float = 0
let z:Float = -4
translation.columns.3.x = x
translation.columns.3.y = y
translation.columns.3.z = z
//カメラの向いてる方向に対しての表示位置を計算
let transform = simd_mul(currentFrame.camera.transform, translation)
//セッションに新しいARアンカーを追加
let anchor = ARAnchor(transform: transform)
sceneView.session.add(anchor: anchor) //sceneViewのsessionにアンカー追加
//アンカー追加によってViewControllerのdelegateが呼ばれる
}
}

ViewController.swift

引数anchorがこれからspriteを貼るanchor。

func view(_ view: ARSKView, nodeFor anchor: ARAnchor) -> SKNode? {
//省略するが、ここにAnchorに貼りたいSpriteNodeを作り、returnで返す
return spriteNode
}

ARAnchorの削除

新たなARAnchorを貼るとき、または貼るSpriteNodeを作った時、anchorを管理用の変数に入れておき、削除が必要になったらそれを削除すると、それに貼り付いているSpriteNodeもろとも削除される。
sceneView.session.remove(anchor: anchor)

貼る時も削除する時も、sceneView.sessionに対してのメソッドを使ってるわけだ。

ARKitのカメラの露出を変えたい(今は無理っぽい)

以下は最新のiOS13、ARKit3でのお話。今後のことは知らん。

やりたいこと

夜の外とか暗いところでARKitで撮影すると、普通のカメラ撮影に比べてやたら暗いので、露出がコードから変えられないものか。

調べる

最初に、今のところ調べた結果ではカメラの撮影設定(オートフォーカスとかも)をいじれるAPIは公開されていないようだ。

明るい所ではシャッター速度を変えて露出を調節しているようだが(絞りまで変えているかは知らない)、暗い所ではスローシャッターの限界が1/60秒みたい。
ちなみにARカメラのシャッター速度は以下のコードでわかる。

if let currentFrame = sceneView.session.currentFrame {
let 露光時間 = 1 / currentFrame.camera.exposureDuration
print("露光時間 1/\(露光時間)")
}

たとえば晴れた昼間カーテンをした部屋の中だと1/60や1/90、青空だと1/4484、太陽に向けると1/10000。暗い押入れに向けた場合1/60だった。
あんまりスローシャッターにすると手ぶれで物体の検出に影響するから制限してるっぽい? ISO感度も変更しない?

結論

今は無理っぽい。
あんまり暗い場所での使用は推奨されないんだろうな。ARanchorにSprite貼って表示させても、距離感が狂うせいか勝手に大きさが変わったり、消えちゃうことも。
暗闇に浮かぶ宇宙人や幽霊を表現したいんだが、別のやり方考えなきゃダメか?

2019年9月28日土曜日

バージョン表記にご注意

以前リリースしたものが些細なバグを治したものだったのでVersion 1.01だったのね。
んで、今度リリースするのを1.1にしてXcodeからvalidateかけたらエラーが出やがった。Invalid Bundleだって。前のよりも高い数字を入れろっていう。

前のが1.0.1のつもりだったんで、1.1ならいいだろうと思ったんだけど、よく考えたら1.01と1.1じゃ数学的には同じだな。AppStoreConnectでは単なる文字列としてじゃなく数字として見てるみたい。
一応公式にはメジャーバージョン・マイナーバージョン・些細な修正みたいな感じで数字を作れって推奨してるようだ。1.2.10みたいに。

AppStoreConnectじゃもうversion1.1として新バージョンを作り始めちゃってて、それを消したりバージョンを変えたり変更ができない。
しょうがないからXcodeのプロジェクトのVersionを1.1.1にしたらvalidateもuploadも通って、一応審査には出せた。
最終的に審査でリジェクトされるかもしれないけど、様子見。

もし1.01と1.1を本当に同じものとして見てるなら、1.1でBuildナンバーを1.01の時より増やせば行けるかもしれない。もしリジェクトされたら試すー。

って言うか、誤った情報の取り消しや編集をさせろよ…💢

2019年9月26日木曜日

iOS13はモーダル画面表示が変更された


iOS13はモーダル画面を表示する際、全面を覆うのではなく上の一部に隙間を残し、指で下にスライドすると画面が閉じるような仕様になった。
これは強化されたマルチタスクを扱う際の変更じゃないかと思うんだが、デフォルトがこれなので、iOS13 & Xcode11であらためてビルドすると今までのアプリもそうなってしまうようで困る。

直し方は簡単で、vcに遷移先のViewControllerが入っているとすると、以下のようにプロパティ modalPresentationStyle.fullScreen を指定するだけでいい。

vc.modalPresentationStyle = .fullScreen
self.present(vc, animated: true, completion: nil)

簡単はいいけど、これをデフォルトにしなくてもいいじゃねえかって思うんだけどね。よけいな作業増やしやがって。

iOS13でAVAudioPlayerが落ちるようになった

以下のようなコードでBGMやSEを鳴らすアプリをリリースしてたのだが、iOS13にアップデートしたら音を鳴らす所で落ちるようになってしまった。
もっと詳細には try player = AVAudioPlayer(contentsOf: url) 
Thread 1: EXC_BAD_ACCESS (code=1, address=0x48) - broken with 
というエラーコードで落ちた。リリースしていたアプリが落ちるのだから、iOS13のせいだ。

import UIKit
import AVFoundation

class ViewController: UIViewController {
var player = AVAudioPlayer()
override func viewDidLoad() {
super.viewDidLoad()
do {
if let audioPath = Bundle.main.path(forResource: "BGM", ofType: "mp3") {
let url = URL(fileURLWithPath: audioPath)
try player = AVAudioPlayer(contentsOf: url)
}
} catch {
print("ERROR")
}
self.player.play()
}
}

調べたところ、頭でインスタンス変数として宣言している
var player = AVAudioPlayer()
var player:AVAudioPlayer!
とoptionalにしたところ、落ちなくなった。

変数作る際に初期化コードを書いても初期化されなくなる仕様変更があったんだろうか? そうなるとAVAudioPlayer以外でも同じ問題出てくる?

参考サイト


2019年9月22日日曜日

Xcode11リリース

2019/9/20にiPhone用にiOS13が、1日遅れの顰蹙でXcode11がそれぞれリリースされた。
iPadの方は新しくiPadOSになるということで、リリースはもうちょっと後らしい。
iOS13はダークモードがある以外は見た目にはあまり変化がない。

以下、とりあえず気づいた点だけ。

デバイス欄にiPhone、iPadに加え、Macが!


iPadOSとmacOSで共通のアプリを作れるような話もあるが、まさかmacOS 10.15 Catalinaにしたら一本のコードでiPhone、iPad、Macで共通のアプリなんて作れたりする?
それとも表示されてるだけで、iPadOSかmacOS用のテンプレートじゃなきゃここがenableにならんか?

SwiftUI

これまでのAutoLayoutのStoryboardとプロジェクトを作る段階で選択になる。
作成時、User Interfaceのメニューで選択

ひな形からプロジェクトを作成するとこれまでのViewControllerの代わりにSceneDelegate.swift、ContentView.swiftというファイルが作られている。

SceneDelegateはUIWindowSceneの状態が変化した時のdelegateメソッドが並んでる。これがUIViewControllerのviewDidLoadとかの代わりになるのかはよくわからん。

ContentViewの方が実際に画面デザインのコードを記述するファイルとなるようだ。
中に書かれたContentView構造体はSwiftUI用のviewプロトコルを継承した構造体の定義になっている。まだよくわかんないけど、ここに書くってことさえ覚えときゃいいか。

SwiftUIを勉強しようとしたのだが、コードと1対1のプレビューが出てこない。これMacもCatalinaに上げないといけないんだって(今はβだけ)。リリースまでおあずけ。コードを試すだけしかない。

単にAutoLayoutのconstraints設定を新しくするようなものでなく、UI部品の配置までコードとともに見えるようになるわけだな? ActionやOutletの接続はどうなるのやら。プレビューが見られるようになった場合、segueで繋がれたViewController同士の関係性とか見られるのかね?
Storyboardと併用できないようなので、早めに覚えてしまいたい。
詳しいことがわかったら勉強がてら別にまとめる。

ボタンアイコンを大量に搭載


Storyboardで右上の「+」ボタン(UI部品を選ぶ所)を押すとボタンなどのアイコンデザインが大量に出てくる。今までほんの少ししか搭載されておらず、あとは自作するしかなかったシステムアイコンだが、ようやくこれで統一が図れる。
StoryboardにドラッグするとImageViewとして貼られる。名前を指定すればコード中からも使える。

Swift UIを選択した場合は「+」ボタンの中にこの一覧がないんだが、別に見られるのかしら? まあ、UIButtonとかのImage名を入れるとこのメニューに名前が出てくるけどね。
アイコン画像サイズは最小しかない?

RealityKit

ARのテンプレートにRealityKitというContent Technologyが選択できるようになった。
RealityComposerというXcode内のアプリを使ってARに表示する物を編集できるようなのだが、まだよくわからない。
テンプレートをそのままRunするとタッチした正面に3Dの立方体を1個表示するアプリができる。
Composerでその質感とかを変えられるようなのだが、変えるとエラーが出るのでよくわからん。

2019年9月17日火曜日

LaunchImage(スプラッシュ画面)の大きさは適切に

様々なサイズの画面に対応させるべく、StoryboardのAutoLayoutを設定していても、シミュレータや実機で画面上下が黒帯になってしまうことがある。
iPhone8とかじゃきれいに表示できたのに、iPhoneXじゃこのとおり
これはLaunchImage(スプラッシュ画面とも。要するに起動時に表示される画像)の設定が未対応のため。
これをLaunchScreen.storyboardできちんとAutoLayoutのConstraintsを設定してやればいい。
AutoLayout未対応の頃のアプリだとLaunchScreen.storyboardがないが、New FileのUser InterfaceにLaunch Screenがあるから、それを追加すればいい。
あとはTARGETS / General / App Icons and Launch Imagesの Launch Screen Fileを今作ったLaunchScreenにしてやる。

2019年8月20日火曜日

ギャンブルアプリのレーティングと、韓国でのアプリ販売について

2019/8/20にアップルからメールが来たので、日本語に要約しておく。(自分用なので、内容が正確とは限りません)

韓国で販売するアプリについて、より多くのアプリを利用できるようにした。
また子供への安全対策として、頻繁に強烈なギャンブルを模した内容のものは、2019年8月20日以降に全ての国と地域でR17レーティングになる。
以下の条件を一つ以上満たす場合、韓国のApp Storeでは19歳以上への販売となる。
8月20日以降、App Store Connectのゲーム格付けおよび管理委員会から格付け番号を入力する必要があり、アプリは今週後半に公開される。格付け番号を持ってない場合は、今すぐ申し込みむこと。→韓国の格付け番号取得のためのサイトへのリンクらしい
  • 年齢が17歳以上のカジノサブカテゴリのアプリ
  • 次のコンテンツの説明の少なくとも1つで頻繁/強烈が選択されたゲームまたはエンターテイメントカテゴリのアプリ:
  • 模擬ギャンブル
  • 性的なコンテンツまたはヌード
  • アルコール、タバコ、または薬物の使用または参照
  • リアルな暴力
評価分類番号を入力するには:
  1. App Store Connectにサインインします。
  2. 大韓民国の可用性の下にあるアプリ情報セクションで、評価分類番号を入力する。
  3. レビューのためにアプリを送信する。
よくわからんのだが、ギャンブルとかが世界的に17歳以上対象になり、韓国はさらに厳しく19歳以上になったということか?
韓国はゲームを販売するのにも許可(管理番号を取る?)が必要みたいな話もあるみたいだし、よくわからんのよね。

2019年8月8日木曜日

App-ads.txtってなんじゃ?(まだよくわからねえ)

GoogleのAdMobを広告サービスとしてアプリに組み込んでいるのだが、App-ads.txtのサポートを開始するというメールが来た。なんだそれは?

調べたところ、よくわからないのだが、アプリデベロッパー(自分のこと)のサイト(iOSのアプリなら、AppStoreにサポートサイトとして登録してるサイトのことのようだ)にアップするテキストファイルで、これによって広告が不正使用されることを防ぐらしい。
詐欺対策。表示された広告料がアプリデベロッパー以外に不正に横取りされてしまうのを防ぐ?
偽の広告枠が表示されることを防ぐ。
広告枠が広告事業者より認定されたものかどうか

DSPがアプリ内の広告枠に入札する際、デベロッパーのサイトを確認し、正式に認証されている広告事業者のみの入札を受け付ける…そうだ。わかんねえ。

アプリの広告の仕組みがよくわかってないんでさっぱりわからんのだが、要は怪しげで詐欺まがいな広告が表示されないよう、承認されてる広告事業者の情報を持ってるファイルっぽい。
広告料が横取りされないようにということだけど、たとえば「アプリがウィルスに感染してるよ! 駆除するならこの(怪しげな)アプリをダウンロードなう!」とかいうよくあるやつなんかも排除してくれるんだろうか?

Googleの説明じゃ、まず「IABが提供するAuthorized Sellers for Apps 仕様書にそってapp-ads.txtファイルを作れ」って書いてあるんだけど、仕様書が英語で、しかもなんだかわかんねえんだよ。

DSP(デマンドサイドプラットフォーム):よくわからんが、Googleとかが提供してるネット広告サービスのプラットフォームことか?
IAB(インタラクティブ・アドバタイジング・ブリュー/インタラクティブ広告協会):ネット広告とかの協会だろうな



とりあえず、今の所は絶対実装しなければ広告表示されないといった類のものではなく、「変な広告が出てせっかくの広告料を受け取れない」ことがあってもいいなら設定不要かもしれない。
ちょっと様子見る。

2019年6月26日水曜日

デバイスがロックされてると出て実機確認できない

Xcode cannot launch アプリ名 on 〇〇のPhone because the device is locked.

Xcode9になったあたりから、上記のようなメッセージが表示されて実機確認できないことが増えた。
iOSの設定 / 一般 / リセット / 位置情報とプライバシーをリセット してやり、デバイスを繋ぎ直すと治る。
しかし、MacのiTunesが起動して「このコンピュータを信頼しますか?」みたいなやり取りをすることになるし、その後もリセットされた内容についてアプリごとにいちいち「フォトライブラリにアクセスしようとしています。許可しますか?」「位置情報を利用しようとしています。許可しますか?」と再回答しなければならず、面倒くさい。

他のやり方として、Xcodeの Window / Devices and Simulators でConnectedにあるデバイスを右クリックし、Unpair Deviceし、再度つなぎ直すと、デバイスの方で「信頼しますか?」メッセージとかは出るけど、Macの方はこれ以上作業が必要ないので少し便利。

根本的な原因はXcodeのバグだろうから、アップルが治してくれないとどうしようもない。

2019年6月24日月曜日

Xcode10になったらFrameworkがnot foundになった

Swift3の時代に作ったソースを久しぶりにXcodeで開いたら、Bridging-Header.hで#importしてるTwitterKit3.4.0とOpenCVがnot foundと表示されるようになってしまった。
リリースしてるアプリだから当然動いてたし、この間いじった覚えもないため、原因としてはXcode10になったくらいしか心当たりがない。

かなり試行錯誤し、結局は プロジェクトの
TARGETS / Build Settings / Search Paths / Framework Search Paths
がおかしかったことがわかった。
読んで字のごとくFrameworkを検索するPathを書くところだが、

$(inherited)
$(PROJECT_DIR)
$(PROJECT_DIR)/Twitter-Kit-iOS

などとなっていたのを

$(inherited)
$(PROJECT_DIR)/Twitter-Kit-iOS
$(PROJECT_DIR)

と順番を入れ替えたところ治ってくれた。
外部Frameworkをプロジェクトにaddすると勝手に新しい pathを追加してくれるみたいなんだが、関係ないpathも消した。
そのディレクトリになければ順番に下に書かれたディレクトリまで検索してくれそうなもんだけど、うまくいかないようで。

なお、実際のフォルダの階層は以上のようになってる。

プロジェクトへのaddは以下のようにした。
アップル純正のが入ったFrameworksフォルダとかもあるけど、直下に入れろみたいな情報も見たのでその通りにしてみてる。

ところでビルドできない致命的エラーは消えたけど、以下のようなWarningが一個出て消えない。
/Users/ユーザ名/Dropbox/Develop/Swift開発/tukkomi/Twitter-Kit-iOS/TwitterCore.framework/Modules/module.private.modulemap:1:29: Private submodule 'TwitterCore.Private' in private module map, expected top-level module
エラー個所は
explicit module TwitterCore.Private {
}
とだけ書かれたところ。とりあえず動きゃいいか。

TwitterKitはもうサポートが終了されてしまったのでこれからはSwifterというのに乗り換えた方が良さそう。

2019年6月20日木曜日

Swift3のDispatch semaphore

同期処理や非同期処理などを制御する仕組み。
Objective-Cと書き方がだいぶ変わったみたいなんでまとめる。
まだよくわかってないんで、書きかけとする。

言葉の意味

dispatch = 使者や手紙などを送る
semaphore = 手旗信号

要するに処理待ちのカウンターみたいなもので、値がマイナスの間はスレッドが停止する。
希望の処理が完了したら値をプラスしてやり、それが0になったらスレッド再開。

書式

詳しくは公式サイトで。

semaphoreを作る

let semaphore = DispatchSemaphore(value: 0)

semaphoreを通知、または増やす


semaphore.signal()

semaphoreを待つ、または減らす


semaphore.wait()
_ = semaphore.wait(timeout: DispatchTime.distantFuture
semaphore.wait(wallTimeout: DispatchWallTime)

同期処理させたいコードを書くところ

DispatchQueue.global(qos: .background).async { ここ }

具体的書き方

  1. semaphoreを作る
  2. DispatchQueue.global(qos: ここ).async { }の引数に優先順位を書く。
  3. DispatchQueue.global(qos: .xxxxx).async { ここ }のクロージャ内に同期処理させたいコードを書く
  4. 処理が一件終了したらsemaphore.signal( )を実行して待ち行列を解消して次の処理に移る。
  5. クロージャの最後にどれくらい待つのかを書く(たとえば処理が終わるまでずっと(以前はFOREVERとかなんとか書いてたけど、今はdistantFuture(遠い未来)だそうだ)とか)

let semaphore = DispatchSemaphore(value: 0)

_ = semaphore.wait(timeout: DispatchTime.distantFuture)