tomoyasuzuki / Memo

0 stars 0 forks source link

Notificationのまとめ #2

Open tomoyasuzuki opened 4 years ago

tomoyasuzuki commented 4 years ago

基本的には以下の記事で書かれていることで事足りる場面が多いと思う。 https://qiita.com/mshrwtnb/items/3135e931eedc97479bb5

Push通知付きの簡易TODOリストの要件を考えてみる

機能

・位置情報 or 時間、またはその両方を持たせたタスクを追加できる ・位置情報の場合は目的地に近づいた場合に通知を出す (具体的な距離はどこまで厳密にできるか要調査) ・スケジューリング機能

あったらいいな機能

・友達機能 登録した友達間でのみ、お互いのタスクを設定することができる。 これによってこのアプリを持ってさえいればLINEなどの通知をOFFにしていても緊急の連絡が可能になる。

詳細設計について考えてみる

① 採用するアーキテクチャ ・基本的にはもうStoaryboardは使いたくないのでオールコードで記述する前提 ・アーキテクチャはMVVMを採用する。なんか非同期っぽい処理が必要になりそうな気がする。

②必要になる独自クラス アカウント作成

・TopViewController

ユーザーの重複などがなければアプリとしては十分なので、ユーザーに持たせるのは名前、メール、パスワードだけ。画像などは基本的に持たせない。 用意するオブジェクト ・ログインボタン ・登録ボタン

・TopViewModel

必要になる機能 ・ViewControllerの通知を受けてModelへ処理を依頼する。 →具体的には引数に名前とメールアドレス、パスワードを持つ。その情報を元にFirebase系の処理を担当するModelに処理を依頼。ModelからはResult型のObservableをもらい、結果をViewに通知する。

・FirebaseModel

・Firebaseを使用したAuth機能 ・Firestoreを使用した独自User管理 →名前とIDと友達を持つ →友達はコレクションとして保持する(詳細は後ほど詰める)

・AllTasksViewController

概要

・tableViewで登録したTaskを保持する ・各Taskのセルをタップすると編集と削除が可能になる

機能

・セルのタップ通知をViewModelへ ・新しいタスクを追加するボタンのタップ通知をViewModelへ

・AllTasksViewModel

・セルタップ通知を元に特定のTaskViewControllerへ遷移 ・新しいタスクボタンタップ通知を元にCreateTaskViewControllerへ遷移

・TaskViewController

・タスクの名前(Require)・タスクのID(Require)・タスクの位置情報(Nullable)・タスクの時間設定(Require)を保持する ・もしかしたらMapKitを使って位置情報を表示するかもしれない

・TaskViewModel

・保存したタスクを取得する処理、FirebaseにしろRealmにしろ処理はModelに依頼する

・CreateTaskViewController

・タスクを追加するための名前と時間設定だけする ・処理はViewModelに依頼する

・CreateTaskViewModel

・データ永続化はModelへ依頼

・TaskManager(仮)

・ユーザーが保持しているタスクをローカルに永続化する(Firebaseのオブジェクトに持たせるかは要検討?) →ローカルに保存する場合はタスクはどこかしらデータを保存する場所を用意する(Realm?)

tomoyasuzuki commented 4 years ago

具体的なコードの設計について考えてみる

TaskModel

タスクモデルは下記の通り

title : String
description: String
createdAt: Date(秒切り捨て)
done: Bool
location: (type未定)
time: Date(秒切り捨て)

UILocationNotification

Task追加時にlocationの値がnilでは無いかつ、locationのtypeにラップできる時に位置情報を基にした通知をスケジューリングする。(タスク追加時に設定したスケジューリングはアプリ終了などをしても問題なく残るのか?) Taskを開くとMapViewで目的地を中心地とした地図を表示する。

ScheduledNotification

注意1 :バックグラウンドで通知をした時にも通知を表示する

かつ、通知をタップした時に該当のTaskに画面遷移する。

注意2 :フォアグラウンドで通知をした時にも通知を表示する

DatePickerを使用した日付の取得

datepicker.dateで日付取得が可能なので、それをformatterで整形してmodelに格納する。 task追加時にmodelを作成する。

tableのソースはTask型のオブジェクト

画面遷移の際にはそのままタスク型のオブジェクトを渡してしまう。 selectedAtの時に該当の配列のindexpath.rowのtaskを取得して画面遷移する際に渡す

taskはfirestoreで管理する

firestoreのsnapshotで管理することにより、リモート通知の実装をせずにローカル通知の実装だけですむはず。

tomoyasuzuki commented 4 years ago

Firestoreのデータ構成

-- users
------documentID(=USER ID)
-----------id(documentIDと同じ=UserID)
-----------name
-----------tasks
-------------task1
-------------task2
-------------task3
----------friends
-------------friend1Ref(users/user1)
-------------friend2Ref
-------------friend3Ref

--tasks
------documentID1
--------id
--------createUserRef
-----------name
--------assignedUserRef(users/user1)
-----------name
--------title
------documentID2
--------id
--------createUser
-----------name
--------assignedUser
-----------name
--------title

実現したい処理 ・友達の追加・削除 ・自分のタスクを作成する ・自分のタスクを変更する ・自分のタスクを削除する ・友達になったユーザーのタスクを作成する ・友達になったユーザーのタスクを変更する ・友達になったユーザーのタスクを削除する ・自分のタスクを一覧取得する ・友達になったユーザーのタスクを一覧取得する

// ユーザーの追加処理
func createUser() {
let user = Auth.auth.currentUser
let ref = Firestore.firestore.collection(users).document(user.uid).set(名前などdataをセットする)
}
// 自分のタスクを追加する
func saveTask(title: String) {
  let ref = Firestore.firestore.reference.collection("users").document(user.uid).collection(tasks)
        ref.addDocument(data[icreateUser: "USERID", assignedUser: "USERID"title: title])
// document名を指定するときはaddDocumentではなくsetを使う
}

// 自分のタスク一覧を取得する
func updateTasks() {
    let ref = Firestore.firestore.collection("users").document(user.uid).collection(tasks)
         ref.addsnapshotListenter { snapshot, error in 
                snapshot.documentChanges.forEach {
            // 各ドキュメント(タスク)にアクセス
}
}

// 友達の追加処理
func registerFriend(id: String) {
  let ref = Firestore.firestore.collection("users").document(user.uid).collection(friends)
  ref.document(id).set(ref: /users/id)
}

// 友達のタスク追加
// registerFriendTask(id: FRIEND USER ID) {
   let ref = Firestore.firestore.collection("tasks").addDocument(data[createUserRef: /users/uid ,assignedUserRef: /users/id])

let userRef = Firestore.firestore.collection("users").document(id).collection(tasks).addDocument(全く同じ内容をUser配下にも登録する)
}

// 友達に依頼したタスク取得

func getAssignedTasks(id) {
   let ref = Firestore.firestore.collection("tasks")
     .whereField(createuserRef, isEqualTO: /users/user.uid)

// !=  のクエリをFirebaseでは使えないので、この条件で抽出したタスクから
// アサインドユーザーが自分でないものをフィルタする
// かつアサインドユーザーが自分のフレンドリストのどれかに一致することを確認する
}
tomoyasuzuki commented 4 years ago

Firebaseのクエリは基本的にはこれでいけるはず。 ・友達に依頼したタスク一覧を取得する際に、全友達情報が必要になる

tomoyasuzuki commented 4 years ago

locationデータの持たせ方

String型で持たせる。これをMapKit表示させる時にジオコーディングさせる。

tomoyasuzuki commented 4 years ago

まずは必要となる処理が実現可能かテストしたい

最低限のViewを作成し、FirebaseUesrsModel,FirebaseTasksModelを作成する。 とにかく値が取得できているかどうかだけLOGで確認する。

tomoyasuzuki commented 4 years ago

これをやった上で全てのクエリ処理が問題なく実行できることが判明した時点で ・Viewを作成する 今回は画面ごとに必要となる処理がほぼ対応するはずなので。 具体的に画面は

これと同じ感じで編集と削除をセルごとに判断する。

tomoyasuzuki commented 4 years ago

バックグラウンドでPush通知を受信して起動した場合の処理

どの場合でもTopViewControllerに画面遷移させるが、その場合にどのタブを開いた状態にするかを制御する。これはTopViewController側で処理が必要ではあるが、appdelegateでは処理は必要ない?