【Flutter】AWS SNSからFlutterで作成したAndroidアプリにPush通知を送った際に「Unable to handle incoming background message.」が発生した際の解決方法

FlutterでPush通知を実装する場合、firebase_messagingライブラリを使用するかと思います。 私も使用していたのですが、AWS SNSと組み合わせたところ、バックグラウンドでPush通知を受け取ることができませんでした。

AndroidのPush通知に不慣れなこともあり、解決に時間を要したため備忘もかねて記録します。

発生した事象

バックグラウンドの状態でPush通知を送ると、下記のエラーが発生しPush通知を正常に受け取ることができませんでした。 なお、Firebase Cloud Messagingから送信した場合は問題なくPush通知を受け取れています。

I/flutter ( 7845): Unable to handle incoming background message.
I/flutter ( 7845): NoSuchMethodError: The method 'call' was called on null.
I/flutter ( 7845): Receiver: null
I/flutter ( 7845): Tried calling: call(_LinkedHashMap len:1)

本エラーメッセージで検索をかけたところ、Firebase Messagingのライブラリ内でエラーが起きていることがわかりました。 github.com

なおAWS SNSからは下記のペイロードで送っています。 こちらは、 AWS SNSからFirebase Cloud Messaging (FCM)のApplication Platformを作成して、GUI上からメッセージを送ろうとし、 「配信プロトコルごとにカスタムペイロード。」を選択した際にデフォルトで表示される内容です。

f:id:Iganin:20200909143318p:plain

解決方法

送信するペイロードを下記のような形式に変更することで解消しました。

{ 
"GCM": "{ \"notification\": { \"body\": \"body\", \"title\": \"title \" } , \"data\" : {\"key1\" : \"value1\", \"key2\" : \"value2\" } }""
}

dataのpayloadが無視されてしまっていることが原因のようです。 notificationのpayloadに変更し、カスタムのdataはnotification payload内に含ませることでバックグランド状態でもPush通知を受け取れるようになります。

まとめ

AWS SNSで自動生成されたpayloadではうまく動作しないというところがはまりポイントかなと思います。 どなたかのお役に立ちましたら幸いです。

参考

【Server】APIにおけるHTTPメソッドの分類と意味

APIにおいて、URIとともにGETやDELETEといったHTTPメソッドを使用します。 GETやDELETEはそれぞれ意味がすぐにわかりますが、POST、PUTやPATCHおよびそれらの差異となるとたまに思い出すために時間がかかるため、備忘を兼ねメモします。

分類

  • GET
    • リソースの取得
  • POST
    • リソースの新規生成
  • PUT
    • 作成済みリソースの置き換え
  • PATCH
    • 作成済みリソースの修正
  • DELETE
    • 作成済みリソースの削除
  • HEAD

各論

以下で https://sample.com/api/v1/items/ リクエストを例に各論を見ていきます。

GET

リソースを取得します。 https://sample.com/api/v1/items/${item_id}の形でidを用いて一意に限定して取得するか、https://sample.com/api/v1/itemsでリストを取得することが多いかと思います。 GETはあくまでリソースの取得を行い、リソース自体への修正や削除は実施しません。(ただし、既読などの情報取得に応じた状態の変更はこの限りではなさそうです)

POST

リソースの新規生成を行います。 https://sample.com/api/v1/itemsの形式でリクエストを行い、対応するリソースの生成を行います。 RFC7231に記述されているように、リソース生成によって作成されたidを返却する場合が多いように思います。 つまり、 https://sample.com/api/v1/itemsによってitemが生成され 12345というidが採番された場合は 12345を返却します。

POST 要請が成功裡に処理された結果,生成元サーバ上にて一つ以上のリソースが作成された場合、生成元サーバは,次を包含する 201 (Created) 応答を送信するべきである:[ 作成された主たるリソース用の識別子 ]を供する Location ヘッダ,新たなリソース(たち)を指しつつ, 要請の状態°も述べるような,表現。

PUT

指定したURIにおける情報を更新します。 この際に、部分的に更新するのではなく、新しいリクエストに含まれる値で置換します。すなわち、下記リソースに対し、 https://sample.com/api/v1/items/12345 のリクエストで name = "sample" category = "sample"のようにリクエストし、12345で表されるリソースを上書きし200(OK)か204(No Content)を返却します。 また、該当URIのリソースが存在しない場合は新規リソースを生成し、201(Created)を返却します。

{
  "id" : "12345",
  "name": "hoge",
  "category": "fuga"
}

PATCH

指定したURIにおける情報の部分更新を行います。 先ほどのPUTではURIに存在するリソースを新しいリソースで置換していましたが、本メソッドではリソースの一部分を上書きします。 例えば、PUTでのリソースの場合に https://sample.com/api/v1/items/12345 name = "sample"のようにリクエストし、下記のように該当リソースを修正します。

{
  "id" : "12345",
  "name" : "sample",
  "category" : "fuga"
}

DELETE

指定したURIのリソースを削除します。 レスポンスのステータスコードは下記のように定義されています。(RFC7231より)

動作は成功する見込みが高いが、まだ実行済みでない場合 : 202 (Accepted)
動作は実行済みで、更なる情報は給されない場合 : 204 (No Content)
動作は実行済みで、応答メッセージが[ その状態°を述べる表現 ]を内包する場合 : 200 (OK)

HEAD

GETリクエストとほぼ同じですが、ヘッダーのみ返却されます。 また、ヘッダーの内容のうち、ペイロードヘッダーは省略されえます。

まとめ

HTTPメソッドに関してそれぞれの役割について簡単にまとめました。 POSTは実際はもっと多様に扱われますが、一つのリソースに対する扱い方という観点から整理しています。 調べる中でRFCが参考になりましたので、一度ご確認いただけますと幸いです。

参考

2020年5-7月振り返り

5-7月の振り返り

5-7月の振り返りです。直近私生活で色々あり、ブログの更新が滞っしまいました。。。 5-7月は新しい会社に移って以降、下記のようなことをしていました。

  • iOSアプリ開発
  • DB論理設計
  • API Server実装
  • Pythonの実装修正
  • サービスの全体設計
  • 全体のスケジュール作成・チケット作成
  • セキュリティ

経験が薄いことや初めてのチャレンジが多く、とても大変でしたが自身の知見やスキルの向上が実感できた有意義な3ヶ月だったと感じています。

話は変わりまして、この振り返りですが目標の数を削減し、「読書」とブログ投稿のみ残そうかと思います。 日々の中でやるべきこと、やりたいことが増え、目標の達成に避ける時間が減ってきたことが主な理由です。 以下、目標の対象を限定した上での振り返りです。

プロダクト作成の基礎力向上

読書

「達人に学ぶDB設計 徹底指南書」を読み終えました。RDBに関するバッドプラクティスや論理的な方針、 実際に運用を前提とした上でのグレーノウハウの紹介など実務に応用できる内容が多かった印象です。 正規化周りの話は情報処理試験で知ってはいましたが、詳しい解説の中で微妙に理解が曖昧だったところもあり、 勉強になりました。

「Web API The good parts」を読みはじめました。Web APIを作成する上での実践的なプラクティスを学べたらと思います。

アウトプット

ブログ投稿

  • 年次目標 50(月次目標 約4)
  • 実績:月次(3ヶ月合計) 4記事 / 累計 14記事

本記事と合わせて4記事でした。 3ヶ月の合計なので1記事/月ですね。。。 もう少し書けるよう時間の使い方を考えます。

その他

活動

  • Coursera の Machine-Learningコースを完了しました。予想通り業務の合間に行うのは大変でしたが、なんとか終わることができほっとしています。
  • Flutterでのアプリ開発を本格的にはじめました。

読書

読書開始

読書中

読書完了

読書中止

【GitLab】GitLab内のProjectを複製する方法

はじめに

GitLab内で既存Projectを複製し新規Projectを作成する方法を記載します。

環境設定

以下の環境を使用しています。

  • GitLab

内容

複製元のProjectの操作

  • export projectを押下する f:id:Iganin:20200726145713p:plain

  • Project export started. ~~ のバナーが表示される f:id:Iganin:20200726145726p:plain

  • exportに成功するとdownload exportが選択できるようになる f:id:Iganin:20200726145814p:plain

複製先のProjectの操作

  • new projectを押下 f:id:Iganin:20200726150040p:plain

  • create project画面でimport projectを選択 f:id:Iganin:20200726150059p:plain

  • 作成するproject名称入力

  • exportした gzファイルを GitLab project exportの「ファイルを選択」から選択
  • import projectを押下 f:id:Iganin:20200726150212p:plain

  • projectのimport成功のバナー表示 f:id:Iganin:20200726150235p:plain

まとめ

projectのimport・exportはなかなか行う機会がないので、一度流れをおっておくといざ対応するタイミングで混乱しない気がします。

【iOS】PlaygroundでSwiftUIのViewを描画する

はじめに

SwiftUIで簡単なView構成を試したいときにわざわざProjectを作るのもめんどくさいなという時がありました。 Playgroundを使用してViewの画面を作成し、表示や動作を確認する方法がわかりましたのでメモがてら記載します。

環境設定

以下の環境を使用しています。

内容

Playground Supportをimportし PlaygroundPageを使用することでPlayground上で画面を描画することができます。 PlaygroundPage.current.setLiveView(ContentView())としてるのが設定箇所です。 UIHostingViewControllerを使用して、PlaygroundPage.current.liveView = UIHostingController(rootView: ContentView())とすることでも設定可能です。

コード例を下記に記載します。

import Combine
import SwiftUI
import PlaygroundSupport

struct ContentView: View {
    var body: some View {
        NavigationView {
            List {
                Text("Sample1")
                Text("Sample2")
                Text("Sample3")
            }
        }.navigationBarTitle("Sample Page")
    }
}

// ここで画面描画のための設定を行なっている
PlaygroundPage.current.setLiveView(ContentView())

画面表示は下記となります。コードを実行することで動作確認等も行うことができます。 f:id:Iganin:20200518065527p:plain

まとめ

Playgroundは主に簡単なロジックの挙動確認やSwiftの仕様確認に使用していましたが、簡単な画面の作成に使用するのも良さそうです。

参考

【iOS】UIViewをUIImageに変換する

はじめに

UIViewをUIImageに変換するというよくあるやつです。 今だとこのやり方が良いのではないかというのが見つかったのでメモがてら記載します。

環境設定

以下の環境を使用しています。

内容

いかに作成したextensionメソッドを記載します。 UIGraphicsImageRendererを使用することでシンプルに書くことができます。

public extension UIView {
    func convertToImage() -> UIImage {
       let imageRenderer = UIGraphicsImageRenderer.init(size: bounds.size)
        return imageRenderer.image { context in
            layer.render(in: context.cgContext)
        }
    }
}

従来通りのよくあるやり方は下記です。 UIGraphicsBeginImageContextWithOptionsを使用することでUIImageを作成しています。 UIGraphicsBeginImageContextWithOptionsscaleに0.0以外を入力したり、 UIGraphicsBeginImageContextを使用したりすると画像がぼやけたりするので注意が必要です。 また、 UIGraphicsGetImageFromCurrentImageContext()の返却値がOptionalのため安全に書こうとするとメソッドの返却値が UIImage?となります。

public extension UIView {
    func convertToImage() -> UIImage? {
        UIGraphicsBeginImageContextWithOptions(bounds.size, false, 0.0)
        guard let context = UIGraphicsGetCurrentContext() { return nil }
        layer.render(in: context)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return image
}

まとめ

UIGraphicsImageRendererはiOS10で導入されたclassです。contextの制御やscaleの管理などをせずに描画を行うことができます。 OSのサポートバージョンが10.0以上でしたら是非使用してみてください。

参考

2020年4月振り返り

4月の振り返り

目標の進捗状況の定期確認です。5月に入って久しいですが。。。

4月はPostgresの取り扱い、サーバーサイド開発、 Docker + Docker Composeの習得など新しい分野にいろいろ挑戦しました。 新しいことを学ぶのはとても楽しいですが、反面締め切りがある中での新しいことへの挑戦はなかなか負荷がたかいなというのも正直感じているところです。

そうはいってもやるべきことはやるしかないので、引き続き気合をいれて頑張ります。まずは生き残らなければ。

データ構造とアルゴリズム

プログラミング問題

  • 変更後目標: 年間150問(Easy: 75問、 Medium 60問、 Hard 15問) - 月次 12.5問
  • 実績: 月次 5問 / 累計 41問

スタックの問題を中心に進めています。 なかなか時間をさけていないのが実情です。

世界で闘うプログラミング力を鍛える本 通読

  • 年次目標 通読・問題全問正解
  • 実績: Chapter3 - 2/6

Chapter3 のスタックとキューの問題を解いています。

プロダクト作成の基礎力向上

読書

進捗なしです。。。カフェに行かないとなかなか厳しい。。。

アウトプット

ブログ投稿

  • 年次目標 50(月次目標 約4)
  • 実績:月次 3記事 / 累計 10記事

本記事と合わせて3記事でした。Ktor + Exposeの話を書いています。

身体能力の強化

ウェイトトレーニング

Covid-19の感染防止のためジムを休会しました。 状況が落ち着くまで本目標の更新は停止とします。

体脂肪率 10%

  • 年次目標:体脂肪率を約 10% 以下
  • 実績: --%

上述のようにジムに通うことが困難になったことに伴い、本目標の月次更新も状況が落ち着くまで休止とします。

その他

活動

  • Weekly で購読している Kotlin Weekly, iOS Weekly, Swift Weekly, Android Weekly, Flutter Weeklyのメルマガから各3記事程度は目を通すことにしました
    • Pocketを使用してStock -> その週のうちにPick Upしたものを読み通す、でしばらく運用してみようと思います。
  • Point-FreeのSubscriptionをはじめました。Composable Architectureを理解したいです。
  • Coursera の Machine-Learningのコースをはじめました。
    • 思ったよりきつそうで挫けそうです。頑張ります。 www.coursera.org

読書

読書開始

読書中

読書完了

読書中止