くま's Tech系Blog

基本的には技術で学んだことを書き留めようと思います。雑談もやるかもね!

Route53でドメインを管理する

今回は、Route53でドメインを管理する方法をまとめます。

Route53とは?

Route53は、AWSが提供するDNSサービスです。

Route53を使用することで、AWSで開発したWebサービスを任意のURLで公開することができます。
例えば、EC2でWebサービスを提供するときに、ユーザーには「test.com」という名前でアクセスしてほしいときなどに、Route53を利用します。Rote53がEC2と「test.com」という名前を紐づけます。

ちなみに、Route53では、コンソール画面からドメイン名を登録(購入)することができます。
そして、お名前.comのような外部サービスで低価格で購入したドメイン名をRoute53で扱うことも可能です。

今回はお名前.comで購入したドメインを例にして進めます。

お名前.comで取得したドメインのホストゾーンをRoute53で管理する

では、実際にお名前.comで取得したドメインのホストゾーンをRoute53で管理します。 ドメインは既に取得済みであることを前提にしています。ご了承ください!

大まかには以下の2つを実施します。

  • jpサーバーにexample.jpへのアクセスは「自身の権威サーバーにアクセスしてください」と登録する
  • 取得したドメインIPアドレスの対応を自身の権威サーバーに登録する

※権威サーバーはレジストリによって管理されていて、お名前.comなどのDNSサービスを使ってドメインの登録をしているはずです。 このレジストリへの登録や更新を我々の代わりに行なってくれている組織をレジストラと呼びます。

なので、レジストラはお名前.com、自社の権威サーバーはRoute 53となります。

Route53の設定

AWSコンソールにログインをおこない、サービスからRoute53を選択します。 管理をするドメインのホストゾーンを追加するためホストゾーンの作成をクリックします。

ホストゾーン設定の画面が表示されるので、ドメイン名を入力してホストゾーンの作成ボタンで作成してください

作成が完了すると、下記のようにドメインに関連付けされたNSレコード(ネームサーバ)4つとSOAレコードが表示されます。 お名前.comでNSレコードの設定が必要になるため、4つのNSレコードをメモに残します。

お名前.comの設定

お名前.comでは、ドメインとNSレコードを紐づける作業を行います。

NSレコードが自身の権威サーバーの場所を示す情報なので、NSレコードをお名前.comに登録します。

まずは、お名前.comにログインしてご利用中のサービスからネームサーバーをクリックします。

ネームサーバーの変更の画面で他のネームサーバーを利用を選択して、NSレコードを赤枠の部分に追加します。 その時に紐づけたいドメインにチェックを入れてください

この時にNSレコードの末尾に「.」(ドット)がある場合にはドットを外して登録します。

これで登録できました。

登録できているか確認する場合にはnslookup -type=NS [対象ドメイン]のコマンドを実施して確認できます。 登録した4つのNSレコードが表示されていれば登録できています。

参照

aws.amazon.com

docs.aws.amazon.com

qiita.com

Vue.jsでのFormの送信について

今回はVue.jsでのFormの送信について記載します。

自分で実装する中で想定通りにはいかない部分もあったので備忘録としてまとめようと思います。

@submitとは?

まず、@submitとはなんでしょうか?

<template>
  <div>
    <form @submit="handleSubmit">
      <label for="username">ユーザー名:</label>
      <input type="text" id="username" v-model="username" />

      <label for="password">パスワード:</label>
      <input type="password" id="password" v-model="password" />

      <button>ログイン</button>
    </form>
  </div>
</template>

上記のようなtemplateを例にします。

ここでは、ログインボタンをタップすると、handleSubmitの処理が行われます。

buttonにクリックアクションをつけていないのにと思ったかもしれません。 buttonタグにはsubmitresetbuttonを指定することができますが、何も指定していないとsubmitがデフォルトで設定されていることになります。 submitはフォームのデータをサーバーへ送信します。

そして、@submitは、formが送信されたときに呼び出されるカスタムイベントハンドラーです。 よって、ボタンをタップしたらhandleSubmitが実行されます。

例えば、buttonに@clickを追加することもできます。 ただし、メソッドが2回実行される可能性があるため、不要な処理が行われる恐れがあります。 そのため、通常はフォームの送信イベントだけを監視する@submitを使うのが適切だと思います。

@submitのイベント修飾子

@submitを使用すれば、formの内容を送信することができますが、1つ問題点がありました。

結果を含んだレスポンスが受け取れませんでした。

axiosを使用してバックエンドで処理を行った結果を受け取る予定でしたが、成功か失敗かわかりませんでした。

調べてみると画面がリロードされているよう...

さらに調べると、リロードされるのはImplicit submissionというHTMLの仕様のようでした。

submitボタンは無いがフォームのinput要素が1つだけの場合、もしくはフォームのinput要素が複数あるが、有効なsubmitボタンが存在する場合はImplicit submissionが発生すると記載があります。

なので、リロードしないようにする処理にする必要がありました。

ここでイベント修飾子を設定します。 イベントに修飾子をつけることでイベントハンドリングが簡単にできるようになります。 修飾子の書き方は@イベント.修飾子というように、イベントの後に「.」で繋いで記述します。また修飾子は複数繋げて記述することも可能です。

今回の場合には「@submit.prevent="handleSubmit」のようにprevent修飾子を使用することでリロードを防ぐことができます。

その他のイベント修飾子は参照に載せているVueのドキュメントに一覧が載っています。

最後に

HTMLの面とVueの面から確認するべき内容がありました。

当たり前ですが、Vue.jsで実装する上でHTMLの知識は前提として必要ですね(久々なので思い出さないと)

参照

macoblog.com

developer.mozilla.org

v2.ja.vuejs.org

html.spec.whatwg.org

iOSのWebViewについて

今回はiOSでのWebViewについてまとめます

AndroidのWebViewについては下記まとめていますので、気になる方はみてください!!

kumaskun.hatenablog.com

※今回はUIkitです。SwiftUIはこの記事に追加するか、別で記事にします🙇

Webページを表示させる

Webページを表示させるにはWKWebViewを使用します。

今回、レイアウトはUIViewにWKWebViewをaddSubViewします。

下記がWKWebViewの基本的な使い方です。

WKWebView を利用する際はUIKitとWebKitをimportします。 WKWebView のinitializeはCGRectとWKWebViewConfigurationを引数にとります。 該当するURLページを開くには、WKWebView の loadメソッドを実行します。

import UIKit
import WebKit

class WebViewController: UIViewController {

    var webView: WKWebView!

    override func viewDidLoad() {
        super.viewDidLoad()

        setupWebView()

        let url = URL(string:"https://www.apple.com")
        // URLRequestオブジェクトを生成
        let myRequest = URLRequest(url: url!)

        // URLをWebView にロード
        webView.load(myRequest)
    }

    private func setupWebView() {
        let userContentController = WKUserContentController()
        userContentController.add(self, name: "testCallBack")
        let webConfiguration = WKWebViewConfiguration()
        
        let customFrame = CGRect(
            origin: CGPoint.zero,
            size: webViewContainer.frame.size
        )
        
        webConfiguration.userContentController = userContentController
        
        webView = WKWebView(frame: customFrame, configuration: webConfiguration)
        webView.translatesAutoresizingMaskIntoConstraints = false
        webView.uiDelegate = self
        webView.navigationDelegate = self
        webView.scrollView.backgroundColor = .clear
        
        // webViewContainerは自前で用意したUIView(名前は自由に設定してください)
        webViewContainer.addSubview(webView)
        webView.topAnchor.constraint(equalTo: webViewContainer.topAnchor).isActive = true
        webView.rightAnchor.constraint(equalTo: webViewContainer.rightAnchor).isActive = true
        webView.leftAnchor.constraint(equalTo: webViewContainer.leftAnchor).isActive = true
        webView.bottomAnchor.constraint(equalTo: webViewContainer.bottomAnchor).isActive = true
        webView.heightAnchor.constraint(equalTo: webViewContainer.heightAnchor).isActive = true
    }
}


extension ViewController: WKUIDelegate {
}


extension ViewController: WKNavigationDelegate {
}

この設定で表示させられます。ここからは細かい設定について説明します。

WebViewの生成の補足

WKWebViewConfiguration

WKWebViewConfigurationはWKWebViewの初期化時に参照される設定プロパティが含まれるクラスです。

WKWebViewConfigurationを使用すると、Webページがレンダリングされるまでの時間、メディア再生の処理方法、ユーザーが選択できるアイテムの細分などのオプションを決定できます。

WKWebViewConfigurationは、WKWebViewが最初に初期化されたときにのみ使用されます。 このクラスを使用して、WKWebViewの作成後にその設定を変更することはできません。

WKUserContentController

WKUserContentControllerJavaScriptからのコールバック受信や、スクリプトをWebビューに挿入する方法を提供します。 必要であれば、WKWebViewConfiguration に生成したWKUserContentControllerを登録する処理を追加してください。

基本的な使い方の箇所で、userContentController.add(self, name: "testCallBack")のようにコールバックを登録しています。 具体的なコールバックの処理は下記のように追加します。

extension WebViewController: WKScriptMessageHandler {
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        if(message.name == "testCallBack") {
             print(" \(message.body)") 
        }
    }
} 

これでJavascriptからメッセージが送られてきたらアプリで受け取って処理を行うことができます。

WKUIDelegateとWKNavigationDelegate

基本的な使い方の箇所でWKUIDelegateWKNavigationDelegateの設定を行いました。

ここでは、WKUIDelegateとWKNavigationDelegateについて補足します。

まず、WKUIDelegateはWebビューのUIを制御するデリゲートです。 主にウィンドウ、Javascriptのダイアログ(alert, confirm, prompt)、コンテキストメニューを制御します。

下記に例を記載します。

extension WebViewController: WKUIDelegate {

    // Webビューを作成
    func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
        guard let url = navigationAction.request.url else {
            return nil
        }
        
        guard let targetFrame = navigationAction.targetFrame,
              targetFrame.isMainFrame else {
            // 同一ページでWebを読み込む
            webView.load(URLRequest(url: url))
            return nil
        }
        
        return nil
    }

    // Javascriptのalertダイアログを表示する
    func webView(
        _ webView: WKWebView,
        runJavaScriptAlertPanelWithMessage message: String,
        initiatedByFrame frame: WKFrameInfo,
        completionHandler: @escaping () -> Void
    ) {
        let alertController = UIAlertController(
            title: "",
            message: message,
            preferredStyle: .alert
        )
        alertController.addAction(UIAlertAction(title: "OK", style: .default) {action in
            completionHandler()
        })
        present(alertController, animated: true, completion: nil)
    }
}

他にもデリゲート処理はありますが、公式ドキュメント等を参考にしてみてください。

WKNavigationDelegateはwebビューのナビゲーションのデリゲートです。 遷移開始時・開始時・ページの読み込み完了時・読み込みエラー発生時リダイレクト時などのイベントを受け取りたい場合はWKNavigationDelegateを継承します。

下記に例を記載します。

extension WebViewController: WKNavigationDelegate {
    // リクエストを許可するか判定
    func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
        decisionHandler(.allow)
    }
    
    // コンテンツを読み込んだ時に呼ばれるメソッド
    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
    }
}

この例では、特定のURLのみ許可するのに使用したり、コンテンツを読み込んだ後に何か処理を行う場合に使用します。

HTMLを表示させる

次にHTMLを表示させる方法です

HTMLファイルを置いたら、Webページと同じように、ロードするだけで表示できます。

func loadLocalHTML() {
    guard let path: String = Bundle.main.path(forResource: "index", ofType: "html") else { return }
    let localHTMLUrl = URL(fileURLWithPath: path, isDirectory: false)
    webView.loadFileURL(localHTMLUrl, allowingReadAccessTo: localHTMLUrl)
}

Bundle.main.pathはプロジェクトのBuild PhasesのCopy Bundle Resourcesにあるファイルを取得するはずです。

また、HTMLのファイルではなく、文字列を読み込む場合にはloadHTMLStringメソッドを使用します。

func loadLocalHTML() {
    let webView = WKWebView()
    webView.loadHTMLString("<html><body><p>Hello!</p></body></html>", baseURL: nil)
}

CSSの適応

CSSを適用させるにはページ読み込み完了したらCSSファイルを読み込む処理を行います

まずはHTML読み込み時にCSSも読み込むパターンです。

private func loadHTML() {
        guard let html = Bundle.main.url(forResource: "index"(ファイル名), withExtension: "html")  else { return }
        guard let htmlString = try? String(contentsOf: html) else { return }

        /// CSSのファイルURLを取得する
        guard let css = Bundle.main.url(forResource: "style"(ファイル名), withExtension: "css")  else { return }

        webView.loadHTMLString(htmlString, baseURL: css)
 }

ただ、CSSファイルを読み込むパターンはうまく適用されない場合もあります。

そこで、HTML表示後にCSSを適用させるパターンも紹介します。

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        let css = "img {max-width: 100%; width: 100%; height: 75%; vertical-align: middle;}"
        
        let js = "var style = document.createElement('style'); style.innerHTML = '\(css)'; document.head.appendChild(style);"
        
        cssWebView.evaluateJavaScript(js, completionHandler: nil)
}

WKNavigationDelegateのdidFinish(表示完了時)にCSSを含めたJavascriptを実行する方法です。 evaluateJavaScriptメソッドはJavascriptを実行するメソッドです。

また、下記のようにスクリプトを追加することで表示前にCSSを適用するパターンもあります。

let script = WKUserScript(source: "JavaScriptコード", injectionTime: .atDocumentEnd, forMainFrameOnly: true)

webView.configuration.userContentController.addUserScript(script)

let controller = WKUserContentController()
controller.addUserScript(script)
let configuration = WKWebViewConfiguration()
configuration.userContentController = controller
let webView = WKWebView(frame: view.bounds, configuration: configuration)

CSSの適用は場合によってはできない場合にもあるので注意が必要です。

Javascriptとの連携

Javascriptからアプリの処理を実行したり、アプリからJavaScriptのメソッドを実行したい場合があると思います。 それぞれの方法を紹介します。

Javascriptからアプリの処理を実行する

Javascriptでボタンのタップアクションをアプリ側で受け取りアプリで処理を行う方法について説明します。 WKUserContentControllerの説明と多少、重複します。

HTMLは次の例を使用します。

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
    </head>
    <body>
        <script type="text/javascript">
            function showMessage() {
                webkit.messageHandlers.showMessage.postMessage("");
            }
            function setMessage(message) {
                document.getElementById("text").innerHTML = message;
            }
        </script>
        <input type="button" value="Button"
               onClick="showMessage();" />
        <p id="text"></p>
    </body>
</html>

基本的な使い方の箇所でコールバックの設定を行いましたが、同様にハンドラを追加します。

let config: WKWebViewConfiguration = WKWebViewConfiguration()
let controller: WKUserContentController = WKUserContentController()

// JavaScriptから呼び出せるメッセージハンドラを設定する
controller.add(self, name: "showMessage")

config.userContentController = controller
webView = WKWebView(frame: self.view.bounds, configuration: config)

// 既にwebViewを初期化している場合は、こちらでもよい
webView.configuration.userContentController.add(self, name: "showMessage")

WKScriptMessageHandlerに準拠して、上で定義したメッセージハンドラをJavaScriptから呼び出すと、userContentController(_:didReceive:)が実行され、メッセージに応じた処理ができます。

extension WebViewController: WKScriptMessageHandler {
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        if message.name == "showMessage" {
            print("\\\\(message.body)")
        }
    }
}

message.bodyはHTMLでwebkit.messageHandlers.showMessage.postMessage("");の箇所のpostMessageで設定した文字列になります。

アプリからJavaScriptのメソッドを実行する

今度は先程と逆でアプリからJavascriptを実行する方法です。

次のようにevaluateJavascriptメソッドでJavascriptの関数を呼び出します

let message = "Message"

let jsFunc = "test(\\\\"\\\\(message)\\\\")"

// JavaScriptメソッドを実行する
webView.evaluateJavaScript(jsFunc, completionHandler: { (object, error) -> Void in
    // JavaScriptメソッドの実行結果を受け取れる
})

参照

developer.apple.com

sogablog.net

gekkado.com

qiita.com

rbenvでうまくバージョンアップできない場合の確認(あまりないパターン)

今回は小ネタです。しかも、iOSの開発している人しか発生しないかもしれないです。

rbenvを使って別のバージョンのRubyをインストールしようと思ったら下記のエラーが発生しました。

$ rbenv install 3.1.2
Downloading openssl-3.0.5.tar.gz...
-> https://dqw8nmjcqpjn7.cloudfront.net/aa7d8d9bef71ad6525c55ba11e5f4397889ce49c2c9349dcea6d3e4f0b024a7a
Installing openssl-3.0.5...

BUILD FAILED (macOS 12.6.4 using ruby-build 20220721)

Inspect or clean up the working tree at /var/folders/14/zqq9_0gx5s5f78ks8s_p4k240000gn/T/ruby-build.20231209082717.65642.nfBnoE
Results logged to /var/folders/14/zqq9_0gx5s5f78ks8s_p4k240000gn/T/ruby-build.20231209082717.65642.log

Last 10 log lines:
***                                                                ***
***       perl configdata.pm --dump                                ***
***                                                                ***
***   (If you are new to OpenSSL, you might want to consult the    ***
***   'Troubleshooting' section in the INSTALL.md file first)      ***
***                                                                ***
**********************************************************************
xcrun: error: active developer path ("/Applications/Xcode.app/Contents/Developer") does not exist
Use `sudo xcode-select --switch path/to/Xcode.app` to specify the Xcode that you wish to use for command line developer tools, or use `xcode-select --install` to install the standalone command line developer tools.
See `man xcode-select` for more details.

対応方法はログに記載のあるようにXCodeのxcrunコマンドに参照問題を解消すれば大丈夫でした。 自分がインストールしていたXCodeのパスを選択することでインストールできるようになりました。

sudo xcode-select --switch path/to/Xcode.app(Xcodeのパスを指定)

まとめ

Xcodeを入れていると結構ややこしいことが起こる可能性があるので注意が必要です。

Rubyの設定がバッティングしてエラーになることもありそうです。

参照

qiita.com

zenn.dev

qiita.com

MySQLのアクセス権限エラーの解決方法

今回はMySQLで「Access denied for user ‘root’@’localhost’」というエラーが発生した場合の対処法についてです。

$ mysql -u rootを実行すると「Access denied for user ‘root’@’localhost’ (using password: NO)」というエラーが表示されました。

rootユーザーなのでコマンドが間違っていることもなく、別に原因がありそうです。

「(using password: NO)」という部分について調べてみると、以下2つの可能性がありそうです。(「using password: YES」でも下記の原因があるかもしれないので疑ってみてください)

①rootユーザーにパスワードを設定していない
②そのそもrootユーザーを作成していない

②のrootユーザーを作成していない場合にはユーザーを作成しましよう。

今回メインで説明するのは①のrootユーザーにパスワードを設定していない場合です。 パスワードを設定するためにはrootユーザーで接続する必要があります。

まず、MySQLを起動している場合には、停止します。

$ mysql.server stop

そして、権限を回避してアクセスできるように下記コマンドを実行します。

$ mysqld_safe --skip-grant-tables

上記コマンドを入力したら、新しくターミナルタブを開きます。 その状態で、rootユーザーでの接続を行うと、接続できるはずです。

$ mysql -u root

mysql>

rootに接続してパスワードを設定していなければ、下記コマンドのようにしてパスワードを変更しましょう。 パスワードはMySQLが定めるパスワードポリシーに準拠する必要があるので注意してください。

alter user root identified by 'password';

そして、権限を変更します。 ユーザーが使用できるその他の一般的なアクセス権限一覧を変更します。 具体的には次のような権限に変更できます。

権限 内容
ALL PRIVILEGES ユーザーは指定されたデータベースへフルアクセスができます。データベースが選択されていない場合は、システム全体のグローバルアクセスができます
CREATE 新しいテーブル、データベースを作成できます
DROP テーブル、データベースを削除できます
DELETE テーブルから行を削除できます
INSERT テーブルに行を挿入できます
SELECT データベースを読み取ることができます
UPDATE テーブルの行を更新できます
GRANT OPTION 他のユーザーの権限の付与または削除ができます

今回は「ALL PRIVILEGES」で変更してみます。

mysql> grant all privileges on *.* to 'root';
Query OK, 0 rows affected (0.01 sec)

成功したら、下記コマンドを実行しましょう。 直接userテーブルをメンテナンスするオペレーションする場合はFLUSH PRIVILEGESを実行して、有効化します。

mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)

もしかしたら、権限を変更する際に失敗する可能性があります。コマンドが間違っていない場合でも失敗する場合にはflush privileges;を実行してから権限変更のコマンドを実行すると成功することがあります。

ここで、exitコマンドからMySQLから抜けて、MySQL再起動に再アクセスを行うとエラーは発生しないはずです。

$ mysql.server start
Starting MySQL
.. SUCCESS! 

$ mysql -u root -p
Enter password: 

参照

qiita.com

qiita.com

qiita.com

ssabcire.hatenablog.com

qiita.com

qiita.com

Cookie/Session/キャッシュとは?Webブラウザの重要な要素を解説

今回はWebブラウザを使うときによく耳にするCookie/Session/キャッシュについて記載します。

CookieSessionキャッシュってWebでよく聞く、似たようなものじゃないの?と思うかもしれませんが、違うものなのでそれぞれ紹介していきます。

ちなみに、今回タイトルはAIに決めてもらいました。今までの自分のブログと毛色が違うタイトルになりましたが、いいタイトルになったと思います。

Session

まず、Sessionとは一体なんでしょうか?

Sessionは複数のリクエストを同一ユーザーと認識すること(機能)です。

HTTPは状態を持たないため同じブラウザでリクエストしても同じユーザーとは認識できないという特徴があります。

同じユーザーとは認識できるようにするのがSessionの役割です。

セッションとは、ウェブサーバー上で動作しているアプリケーションがユーザーの情報を一時的に保存する仕組みです。一般的に、この情報はサーバーのメモリやデータベースに保存されます。

例えば、ユーザーがウェブサイトを訪れると、セッションが開始され、セッションIDが生成されます。 IDはこの後説明するCookieとしてブラウザに保存されます。 ユーザーが再びサイトを訪れると、IDに基づいてサーバー側の情報が呼び出されます。

例えば、ChromeCookieを確認するには、以下にアクセスすることで確認できます。

Sessionで「複数のリクエストを同一ユーザーと認識する」と説明しました。

Cookieはユーザー識別を実現させるための手段なので、Sessionとは異なります。(Sessionは目的や機能を表しています)

Cookieはユーザーのブラウザに直接保存される小さなテキストファイルです。 サーバーがテキストファイルをブラウザに送信し、ブラウザは保存して次回のサーバーへのリクエスト時に同じCookieを送り返します。
Cookieはクライアント側のマシン(主にブラウザ)にのみ保存され、セッションはブラウザだけでなく、サーバーにも情報が保存されます。

キャッシュ

キャッシュについては「キャッシュを削除してください」という言葉を聞いたことがある方もいるかもしれません。

キャッシュは、よく使うデータへのアクセスを速くするために、より高速な記憶装置に一時的に保存する仕組みのことを指します。 Webサイトのキャッシュは、頻繁にアクセスされるコンテンツを保存し、その情報を呼び出すことで表示速度を高速化します。

例えば、一度アクセスしたウェブページの画像やJSやCSS などのデータを保存して、次回以降同じページにアクセスしたときに再度読み込む必要がなくなり、ページの表示速度が速くなります。

JSやCSS などのデータを更新した場合に変更が反映されない場合は、コンテンツを保存している可能性があるため、「キャッシュを削除してください」といわれることがあります。

最後に

似たような言葉ですが、明確に意味が分かれていると思います。

主に、セキュリティ面で使用すると思うので、気になる方はさらに深掘りしてみてください!

参照

ipeinc.jp

ssaits.jp

Android13のプッシュ通知について

今回はAndroid13のプッシュ通知について変更点があるので紹介していきたいと思います。

Android 14 ベータ版がリリースされる時期ですが.....見ていってください!

Android 13(targetSdk 33)から、アプリ通知を送るのにユーザーの事前許可が必要になりました。

実装方法も多少の変更がありました。

通知の権限

まずは、POST_NOTIFICATIONSの権限を追加します。 この権限を追加しないとAndroid13ではプッシュ通知の権限を確認するダイアログは表示されません。

<manifest ...>
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
    <application ...>
        ...
    </application>
</manifest>

権限確認ダイアログでは①「許可」を選択する、②「許可しない」を選択する、③どちらのボタンも押さずにダイアログをスワイプして閉じるという3パターンのアクションを取ることができます。

③の場合には権限を未選択状態になるので注意が必要です。

また、Android12以下ではダイアログは表示されないので、今まで通りチャンネルを作成して通知の設定を行なってください。

通知のタイミング

通知のタイミングについては開発者で決めることができます。

ActivityResultLauncherを起動して結果を受け取れるようにします。

private val requestPermissionLauncher =
    registerForActivityResult(ActivityResultContract.RequestPermission()) { isGrant: Boolean ->
    }

下記のようにしてダイアログを表示させます。(どこかのActivityで表示させてください)

if (ContextCompat.checkSelfPermission(
            this,
            Manifest.permission.POST_NOTIFICATIONS,
        ) != PackageManager.PERMISSION_GRANTED
    ) {
        requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
    }

ここまでの実装でプッシュ通知確認のダイアログは表示できるようになり、権限の設定は行えます。 しかし、上記の呼出処理だと、拒否し続けると画面表示のたびに確認ダイアログが表示されることになってしまいます。

PackageManager.PERMISSION_DENIEDで判定すればいいんじゃないのと思ったかもしれないですが、最初の1回目が表示されなくなる可能性が高いため別の方法が良さそうです。

そこで、shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)を使用します。

shouldShowRequestPermissionRationaleは、以前ユーザーがリクエストを許可しなかった場合trueを返します。

しかし、ここでも注意が必要なのですが、何回拒否したのかで結果が変わります。

  • 1回も拒否していない→shouldShowRequestPermissionRationale = false
  • 1回拒否→shouldShowRequestPermissionRationale = true
  • 2回以降拒否→shouldShowRequestPermissionRationale = false

このように1回拒否した時のみtrueを返します。なので、これだけだと判断できない場合には端末情報として許諾したかどうかを保持するなどの追加で必要になるかもしれません。

最後に

開発者側としては微調整が必要になりましたが、使いやすくなったと思います。

プッシュ通知はiOSのフローの方がふさわしいと思っているので近づいてよかったと思いました。

参照

developer.android.com

www.fuwamaki.com

kubiakdev.medium.com

staticについて

今回はstaticについて記載しようと思います。特に、static変数とstaticメソッドについて解説します。

static変数

static変数とは、クラスをインスタンス化せずにその変数にアクセスすることができる変数です。

static変数はクラス名.static変数のように記述しますのでクラス変数、非static変数はインスタンス名.変数のように記述しますのでインスタンス変数とも呼ばれます。

クラス変数はそのクラスから作られたインスタンスの全てで共有される変数、インスタンス変数はそのインスタンス1個内だけで使われる変数となります。

例えば、Javaでの例を下記に記載します。 まず、下記のようなクラスがあるとします。messageはstatic変数です。

public class MessageText {
    public static String message;  // static変数

    public void echoMessage () {
        System.out.println(message);
    }
}

message変数は、各インスタンスではなくMessageTextクラス上に定義されるため、jvmプロセス上にたった1つしか存在しない。

以下のmain文で実行してみると、2つのインスタンスが同じメモリ上の値を参照しているのがわかると思います。

public static void main (String[] args) {
    MessageText.message = "メッセージ1";
    MessageText message1 = new MessageText();
    MessageText message2 = new MessageText();

    message1.echoMessage(); // メッセージ1が出力される
    message2.echoMessage(); // メッセージ1が出力される

    MessageText.message = "メッセージ2";
    message1.echoMessage(); // メッセージ2が出力される
    message2.echoMessage(); // メッセージ2が出力される
}

static変数の使い所は定数クラス内でのみ使用される共用変数が考えられます。定数の場合にはfinal修飾子をつけて上書きできないようにします。 クラス内でのみ使用される共用変数はアクセス修飾子をprivateにして、そのクラス内からのみ参照・更新できないようにアクセスレベルを制限します。

今回はJavaで補足しましたが、他のプログラミング言語でもstaticの概念があればあまり差はないと思います。

staticメソッド

次はstaticメソッドについてです。

staticメソッドはクラスに直接属し、クラスが実行するメソッドです。 インスタンスに属し、インスタンスが実行するインスタンスメソッドとは異なります。

staticメソッドを呼び出すには、原則としてクラス名.staticメソッド名と指定します。

staticメソッドの特徴は下記3点です。

インスタンスを生成せずに呼び出せます。頻繁に呼び出されるユーティリティメソッドはよくstaticメソッドとして実装されます。

インスタンスを複数生成しても、そのインスタンスが持つstaticメソッドの処理はどのインスタンスも同じ動作になります。 なので、SwiftでのExtensionに使われたりします。

インスタンス化しないと状態を持てないので、状態に依存しないことを保証できます。 具体的には、 与えられた引数のみでメソッドの挙動が決まることが保証できます。

staticメソッドにする場合には副作用のない、純数な関数であること。 つまり、関数の出力値が入力値によってのみ決まる関数にした方がよさそうです。

注意点としてはstaticメソッドではstatic変数しか扱えないです。

public class Test {
    String name;
    Static int age;

    public static void setAge() {
        Test.age = 20;
        System.out.println(this.name + "です");
    }
}

上記の例では、this.nameが参照できないのでエラーになります。

終わりに

static変数とstaticメソッドについて解説しました。

static変数は定数として使用できるかと思いますが、staticメソッドはオブジェクト指向から外れる処理になる気がするので、 共通処理で使用するくらいかなと思います。

参照

www.sejuku.net

www.sejuku.net

www.bold.ne.jp