constを付ける意味

2023年1月23日

開発環境
OS:Windows 10
SDK:VS Code + flutter 3.3.10

概要

constは極力、付けた方がいい。

青い波線に気付く

最近、flutterでWebサイトを作っているのですが、何も問題がなさそうな箇所に青い波線が出ているのに気付きました。

カーソルを青い波線に合わせてみると。

「定数コンストラクタにはconstを付与するのが好ましい。」

とのポップアップが。

constの役割

画面を構成するパーツ(flutterで言うところのwidget)には固定文言などの静的表示を行うパーツと入力データなどのその都度値が変わる動的表示を行うパーツが存在します。

constはこの静的表示を行うWidgetに対し、宣言するものとのこと。

そうすることによってコンパイル時(アプリ生成時)に値を確定させることでき、

画面表示時の処理負荷を軽減させる=画面描画速度を上げる

ことができるようです。

constの定義


Widget(Column)内のWidget(Text)がすべてconstとなるような場合、

上位のWidget側にconstを定義するようなパターンもあります。

常にconstを付与に対しケアしたほうがいいですが、ワーニングで知らせてくれるので助かります。

VS Code便利ですね。

Admob広告ユニットIDをデバッグとリリースで使い分ける

2023年1月23日

開発環境
OS:Windows 10
SDK:Android Studio Dolphin | 2021.3.1

概要

Admobから与えられたアプリID、広告ユニットIDをデバッグモードで使用してしまうとAdmobから警告を来て、収益に影響が出てしまいます。
そうならないためにもリリースとデバッグモードで広告ユニットIDを分けて定義しておくと便利です。

Admobを表示するときに必要となるID

Admobをアプリに表示させるにはアプリIDと広告ユニットID、少なくとも2つのIDを定義する必要があります。

アプリID、広告ユニットIDはAdmobで作成することができますが、

アプリのデバッグ時に使用することはできません。

代わりにデバッグ時にはサンプルIDを使用します。

【 サンプルアプリID 】

ca-app-pub-3940256099942544~3347511713

サンプル広告ユニットID

広告フォーマットサンプル広告ユニットID
アプリ起動ca-app-pub-3940256099942544/3419835294
バナーca-app-pub-3940256099942544/6300978111
インタースティシャルca-app-pub-3940256099942544/1033173712
インタースティシャル動画ca-app-pub-3940256099942544/8691691433
報酬型ca-app-pub-3940256099942544/5224354917
報酬型インタースティシャルca-app-pub-3940256099942544/5354046379
ネイティブアドバンスca-app-pub-3940256099942544/5354046379
ネイティブアドバンス動画ca-app-pub-3940256099942544/1044960115
サンプル広告ユニットID一覧

デバッグ用定義とリリース用定義

アプリIDと広告ユニットIDはstring.xmlに定義して管理します。

まずリリース用のアプリID、広告ユニットIDをstring.xmlに定義します。
(下3行がID定義となります。)

<resources>
    <string name="app_name">Look Look</string>
  ・・・
  ・・・
  ・・・
    <string name="ts_adview">Advertisement will be displayed from now on.</string>
    <string name="ad_attribution">Ad</string>
    <!-- admob -->
    <string name="admob_id_manifest">ca-app-pub-xxxxxxxxxxxxxxxx~xxxxxxxxxx</string>
    <string name="admob_id_bottom_main">ca-app-pub-xxxxxxxxxxxxxxxx/xxxxxxxxxx</string>
    <string name="admob_id_bottom_map">ca-app-pub-xxxxxxxxxxxxxxxx/xxxxxxxxxx</string>
</resources>

次にデバッグ用のアプリID、広告ユニットIDをstring.xmlに定義します。

デバッグ用のstring.xmlは最初は存在しないため、新たにデバッグ用フォルダ内に作成します。

【 デバッグ用フォルダの場所 】

(プロジェクトフォルダ)/app/src/debug/res/values/string.xml

※フォルダがない場合はフォルダも作成します。

※多言語化対応している場合は言語毎のvaluesフォルダ(例えば日本語の場合、values-ja
 フォルダ)にstring.xmlを格納します。
 string.xmlの定義内容は全て同じで問題ありません。

デバッグ用フォルダ
<resources>
    <!-- admob -->
    <string name="admob_id_manifest">ca-app-pub-3940256099942544~3347511713</string>
    <string name="admob_id_bottom_main">ca-app-pub-3940256099942544/6300978111</string>
    <string name="admob_id_bottom_map">ca-app-pub-3940256099942544/6300978111</string>
</resources>

string.xmlには、AdmobのサンプルIDだけ定義すればOKです。

アプリID、広告ユニットIDを参照する

あとはstring定義を参照するだけです。

(AdroidManifestの定義例)

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  ・・・
    <application
  ・・・
        <meta-data
            android:name="com.google.android.gms.ads.APPLICATION_ID"
            android:value="@string/admob_id_manifest" />
  ・・・
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:theme="@style/AppTheme.NoTitleMain">
  ・・・
       </activity>
  ・・・  
    </application>
</manifest>

(レイアウトXMLの定義例)

<?xml version="1.0" encoding="utf-8"?>
<ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:ads="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MapsActivity">


    <com.google.android.gms.ads.AdView
        android:id="@+id/adViewMap"
        ads:adSize="SMART_BANNER"
        ads:adUnitId="@string/admob_id_bottom_map"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        android:layout_marginStart="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        android:layout_marginBottom="0dp">
    </com.google.android.gms.ads.AdView>
</androidx.constraintlayout.widget.ConstraintLayoutt>

これでデバッグとリリースの使い分けができるようになりました。

いつも通りにデバッグを行えばサンプルIDが参照され、リリースアプリの作成を行えば、リリース用IDが参照されるようになります。

アプリのタイトルに☆を付けたらGoogle Playから削除されてしまった話

2023年1月15日

開発環境
OS:Windows 10
SDK:Android Studio Dolphin | 2021.3.1

一難去ってまた一難

ピートークという、Wi-Fi Directチャットアプリがポリシー違反と指摘され、

指摘から9日後の昨年12月30日に改修を行い、リリース。

改修方針は的を得ていたようでポリシー違反が解除されました!

と思ったのも束の間。

またしてもポリシー違反を指摘されてしまいました。。。

前回のポリシー違反、及び、対処方法は下記をご覧ください。

今回の指摘内容が以下。

メタデータポリシーに違反したとのこと。

そして、アプリステータスは、ストア削除。

なんということでしょう。。。

アプリタイトルに☆を付けてはいけない

メタデータポリシーとはなにか?

メール内のリンクを見るにユーザーに誤解を与えるようなテキストや画像に関する制限のようです。

そして、その制限の一つとして「絵文字、顔文字、特殊文字の使用」がありました。


前回のポリシー違反を修正する際にきまぐれにアプリタイトルを「ピートーク」から「ピー☆トーク」に変更したのがダメだったようです。

改修方針

アプリタイトル、及び、ストアのアプリ名から☆を削除します。

改修のその後

改修アプリをリリースした翌日にはポリシー違反は解除されていました。

今回のポリシー違反は明確でしたね。

今月のみてみて(2022年12月)

Firebase連携アプリ、みてみてエストーク伊勢125社巡り

このうち、みてみてにフォーカスを当て、Firebaseのアクセス動向やアプリの改善を考察する開発記事です。

Realtime Database+Storage構成の投稿アプリ

まずはアプリのユーザー数の変動とともにFirebaseのアクセス動向を確認します。

2022年12月18日~2023年1月14日のユーザー数の変動

(ユーザー数の変動)

1ヶ月の新規獲得ユーザー数

年末にメッセージ機能のUIを少し改良したもののそれほど効果はなかった模様。

いかにメッセージ機能を使ってもらうか、そこの焦点を当てて改善したほうがよさそう。

(インストールの上位国)

先月からの大きな変化は見られず。

2023年1月14日直近のFirebaseの変動

(Firebaseの変動)

直近のfirebaseの動き
1ヶ月のfirebase使用量

flutterでWeb版を作成してみました。

みてみてWeb

アプリの簡易版として利用予定でアプリと連携させてみたいと思っています。

まだまだ無料でやれそうです。

2022年12月15日~2023年1月14日のAdMob

今月はAdMob広告収入:179円、 Firebase使用料:0円となりました。

みてみての投稿を見てみて

(今月の投稿ピックアップ)

三方焼き。美味しいので食べて欲しい。

今後の改善案

・アプリUIブラッシュアップの継続

・フレンドグループ化と共有機能の追加

先々月からの継続と

・Web版UI改善

を行う予定。

,

Pトーク、アプリ内エクスペリエンスに反するってよ

ポリシー違反にざわつく日曜日

久々にポリシー違反メールを受け取ってしまいました。

とは言うものの、

ポリシー違反を指摘をされたアプリの最終更新日は、2022年6月3日。

しばらく違反状態だったようです。。。

それはさておき、指摘内容は何かと言うとこちら。

ポリシー違反メール

そして、これが証拠画像だと添付されていた画像がこちらです。

添付画像を見ても違反らしいものは見えないのですが・・・

アプリ内エクスペリエンス

アプリ内エクスペリエンス、初めての指摘内容です。
(過去に指摘されたものは水平展開しているので同じ指摘をされると困るわけですが。。。

今回指摘されたアプリ内エクスペリエンスを解釈してみるに、

「ユーザー作成コンテンツ」の表示やアクセスができるアプリなのに

ユーザー作成コンテンツに関するポリシーが適用されていないよね?

ってことのようです。

Pトークとはざっくりと言えば、Wi-Fi Directを使ったチャットアプリです。

「アプリ内エクスペリエンス」を考慮しつつ、アプリを動かしてみると

確かに「データを相互に交換できること」に対する考慮が足りてないようです。

改修方針

ユーザー作成コンテンツに関するポリシーの遵守のため、

下記の対策をとります。

・アプリの利用規約の定義

・利用規約の同意画面を表示し、同意しない場合はアプリの使用を遮断する(←添付画像は今、これがないよねという指摘と捉えたが果たして。。。

それでは改修を行い、申請は通るのか。

結果が出たらまた記事にしようかと思います。

追記

ポリシー違反メール受信から5日後、Admob停止の通知がきました。。。

今月のみてみて(2022年11月)

Firebase連携アプリ、みてみてエストーク伊勢125社巡り

このうち、みてみてにフォーカスを当て、Firebaseのアクセス動向やアプリの改善を考察する開発記事です。

Realtime Database+Storage構成の投稿アプリ

はっ、としていたら、12月もなかばを過ぎてしまいました。。。

まずはアプリのユーザー数の変動とともにFirebaseのアクセス動向を確認します。

2022年11月16日~12月17日のユーザー数の変動

(ユーザー数の変動)

1ヶ月の新規獲得ユーザー数

先月少しインストール数が上向いたのもつかのま。

今月は手付かずです。

(インストールの上位国)

今月もフィリピンに支えられた形です。

残念ながら、トルコ、インドでは飛躍できず。

ここにきてサードパーティからの参照に伸びが。

12月17日直近のFirebaseの変動

(Firebaseの変動)

直近のfirebaseの動き
1ヶ月のfirebase使用量

あれ?1ヶ月前のデータを見ているのか?

いやいやそんなことはありません。

ダウンロードが10倍になっています。

2022年11月16日~12月17日のAdMob

今月はAdMob広告収入:186円、 Firebase使用料:0円となりました。

みてみての投稿を見てみて

(今月の投稿ピックアップ)

鶴橋での一食。美味しかったです。

今後の改善案

・UIブラッシュアップの継続

・フレンドグループ化と共有機能の追加

先月から継続です。

,

DQWリアルイベントに行ってきました。

2022年12月4日

ドラクエウォーク初のリアルイベント開催

ドラクエウォーク初のリアルイベントが12月3日(土)、4日(日)に開催されました。

会場は大阪、万博記念公園。

三重から頑張れば行ける距離。

ならば行くしかない!ということでリアルイベントに行ってきました!

12月4日、普段より早く起きて久しぶりの電車に乗り、会場に向かいます。

近鉄~地下鉄~阪急~モノレールと乗り継ぎ、到着したのは12時過ぎ。

先着配布のグッズは厳しそうだが頑張るしかない。

太陽の塔が出迎えてくれます。

大学時代、すごく近くに住んでいたのに万博記念公園に行ったことがなかったんですよね。

まさか、あれから十何年後に訪れようとは。。。

入園券を購入。

混雑対策で臨時発売所が出ていて買いやすかったんです。

それにしても入園券安い。

入園してすぐ右手を見ると今度はスラミチがお出迎え。

ゲーム画面でマップを確認。

F⇒C⇒A⇒D⇒B⇒Eの順で周ることに。

もちろんCoke ONコラボ大王も討伐します。

公園内は見どころいっぱいですがそんな余裕はない。。。

そして、4人目のカンダタさんに遭遇したところで先着配布のグッズが品切れになったとの放送が。。。

ああ、無情。

それでもなんとか制覇。

称号をもらえたのでヨシ!

行く先々でシャッターチャンスをくれるカンダタさん。

素敵やん。

スランプラリーを周った後にグッズを見に行くもほぼソールドアウト。

ああ、無情。。。

万博記念公園に別れを告げます。

あばよ!

万博記念公園を満喫した後に向かったのは・・・

そして、訪れたのは。

大阪城。

日本100名城キャンペーン。

まだ1つも訪れてなかったので大阪城でおしろつむりを狩ります。

アクセサリー、ゲット。

お疲れさまでした。

最後は鶴橋で締め括りです。

お疲れさまでした!

今月のみてみて(2022年10月)

Firebase連携アプリ、みてみてエストーク伊勢125社巡り

このうち、みてみてにフォーカスを当て、Firebaseのアクセス動向やアプリの改善を考察する開発記事です。

Realtime Database+Storage構成の投稿アプリ

アプリのユーザー数の変動とともにFirebaseのアクセス動向を確認します。

2022年10月1日~11月02日のユーザー数の変動

(ユーザー数の変動)

1ヶ月の新規獲得ユーザー数

先月のUI改善が功を奏したのか少し上向いてきました。

まだまだUI改善の余地はあるので早めに対処していきたい。

(インストールの上位国)

またわずかながらトルコで動きがありました。

そして、インドでもインストールされたようです。

インドはなんとかできないかと注目している国です。

11月3日直近のFirebaseの変動

(Firebaseの変動)

直近のfirebaseの動き
1ヶ月のfirebase使用量

よくよく見てください。

わずかながら使用されていますよ。

2022年10月1日~11月02日のAdMob

今月は、AdMob広告収入:167円、 Firebase使用料:0円となりました。

また少し盛り返したのでまだこれから。

みてみての投稿を見てみて

(今月の投稿ピックアップ)

最新の投稿は。。。テスト投稿ですね、これは。

今後の改善案

・UIブラッシュアップの継続

・フレンドグループ化と共有機能の追加

開発が滞ってしまっているが、せっかく盛り返しているので注力したい。

,

APNsは正常に動作しているのか?

2022年10月17日

開発環境
OS:macOS Monterey
SDK:Xcode14.1

概要

プッシュ通知が実装されたandroidアプリとiosアプリ。

プッシュ通知の動作確認のため、androidとiphoneのデバイスでデバッグをするものの、

Firebase経由で発行したプッシュ通知がandroidには届くが、iosには届かない。

なぜiosに届かないのか調査します。

APNs

APNs(Apple Push Notification service)とは、アップルプッシュ通知サービスのこと。

プッシュ通知におけるandroidとiphoneの大きな違いはAPNsの存在。

androidの場合、Firebase → androidデバイスとプッシュ通知が配信されるのに対し、

iosの場合、Firebase → APNs → iPhoneデバイスとプッシュ通知が配信されます。

iPhoneにプッシュ通知が届かない。

しかし、Firebaseではプッシュ通知が配信されている。(androidは受信できているので)

それが確かであるなら、iphoneに届かない理由は、「APNsが正常に動作していない」からに絞られます。

そこでAPNsが正常に動作しているかどうかを確認します。

確認手段は、cURLコマンド。

curlコマンドでAPNsに直接、プッシュ通知依頼をかけ、iPhoneにプッシュ通知を配信できているかを確認します。

curlコマンド

まずはcurlコマンドを実行するためのバッチファイルを作成します。

#!/bin/bash

set -x

AUTH_KEY_FILE_PATH="xxxxxxxx.p8"
AUTH_KEY_ID="xxxxxxxx"
TEAM_ID="xxxxxxxxx"

APP_BUNDLE_INDENTIFER="com.sample.test"
DEVICE_TOKEN="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
API_ENDPOINT="https://api.development.push.apple.com/3/device/"

base64() {
   openssl base64 -e -A | tr -- '+/' '-_' | tr -d =
}

sign() {
   printf "$1"| openssl dgst -binary -sha256 -sign "${AUTH_KEY_FILE_PATH}" | base64
}

TIME=$(date +%s)
HEADER=$(printf '{ "alg": "ES256", "kid": "%s" }' "${AUTH_KEY_ID}" | base64)
CLAIMS=$(printf '{ "iss": "%s", "iat": %d }' "${TEAM_ID}" "${TIME}" | base64)
JWT="${HEADER}.${CLAIMS}.$(sign ${HEADER}.${CLAIMS})"

curl -v \
   -d '{"aps":{"alert":{"title":"Test","body":"Hello from request with P8 certificates"}}}' \
   -H "Content-Type: application/json" \
   -H "Authorization: Bearer ${JWT}" \
   -H "apns-topic: ${APP_BUNDLE_INDENTIFER}" \
   --http2 \
   ${API_ENDPOINT}${DEVICE_TOKEN}

上記内容を定義したcommandファイルを作成し、認証キー(p8ファイル)と同階層に配置します。

ちなみにバッチファイル内の可変項目は以下の5つ。

1)AUTH_KEY_FILE_PATH → Apple Developerで取得した認証キー(p8ファイル)名

2)AUTH_KEY_ID → Apple Developerで定義されたキーID

3)TEAM_ID → Apple Developerで定義されたチームID

4)APP_BUNDLE_INDENTIFER → アプリに定義したバンドルID

5)DEVICE_TOKEN → アプリから取得したAPNsデバイストークン

APNsデバイストークンはAPNsとの通信で取得するため、アプリでログ出力して取得します。

func application(_ application: UIApplication,
    didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    Messaging.messaging().apnsToken = deviceToken

    let token = deviceToken.map { String(format: "02.2hhx", $0)}.joined()
    printf("apnsToken:%s\n",token)
}

いざ、実行

バッチファイルの準備が整ったら、実行します。

bash apns.command

そして、実行した結果が、

HTTP/2 400 BadDeviceToken

異常です。

デバイストークンが無効とのこと。

やはりAPNsが正常に動作していなかったようです。

Apple Developer再定義

Apple Developerの定義を見直します。

ここでApple Developerに詳しい方にヘルプ。

定義を確認したもらった結果、2点の間違いが発覚。

①デバッグ用デバイスの定義漏れ

②開発用定義になっていない。(Distoribution → Development)

改めて定義をしなおし、Provisioning Profileを読み直してリビルドします。

そして、再度、apns.commandを実行してみると、

結果は、HTTP/2 200となり、プッシュ通知が届きました。

実際にiPhoneによるデバッグでもプッシュ通知が届くことを確認。

なかなかにAppleの管理は厳密ですね。

ジェスチャーナビゲーションを一時的に無効化する

2022年10月14日

開発環境
OS:Windows 10
SDK:Android Studio Dolphin | 2021.3.1

概要

Android 10から実装されたジェスチャーナビゲーション。

この機能とDrawerLayoutとの相性が悪いので一時的に無効化します。

ジェスチャーナビゲーションを初めて知る

Android 13のサポート対応のため、伊勢125社巡りをデバッグしていたときのこと。

Android 13の仮想デバイスを用意し、マップ画面を確認していた際に違和感が。

マップ画面は、DrawerLayoutで構成しており、画面左右にNagivationViewをセット。

画面左端から右へスワイプ、または、画面右端から左へスワイプするとメニューが表示される作りになっています。

その想定で画面左端から右へスワイプした結果、画面左端に下記のような矢印(<)が表示されました。

そして、次の瞬間、マップ画面が閉じて、ホーム画面が表示されました。

えっ。。。

戸惑います。

もう1度、同じ操作をしてもやはりホーム画面に戻されます。

そこでネットで調べたところ、ジェスチャーナビゲーションが機能していたことが判明しました。

このとき、初めてジェスチャーナビゲーションの存在を知りました。。。

ジェスチャーナビゲーションを無効化する

このジェスチャーナビゲーション。

有効な状態だとマップ画面でスワイプ操作によるメニュー表示ができません。

そこで、マップ画面表示中だけ無効化するようにします。

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_maps)


    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        val layoutMap = findViewById<DrawerLayout>(R.id.layout_map)
        val rect = Rect(0 , 100, 100, 200)
        layoutMap.systemGestureExclusionRects = listOf(rect)
    }

}

ロジックはいたって簡単。

マップ画面のDrawerLayoutでsystemGestureExclusionRectsをコールし、ジェスチャーを無効化するエリアを指定すればいいだけです。

(念のため、Android 10(APIレベル29)以降でのみ動作するようにしています。

そして、再度、デバッグしてみると。

画面左端から右スワイプするとメニューが表示されるようになりました。

ジェスチャーナビゲーション

設定画面の「システム」ー「ジェスチャー」ー「システムナビゲーション」から設定できます。

機能を有効化すると画面端あたりをスワイプすると戻る操作となるようです。