Hacking with iOS:SwitUI Edition-潜在的な顧客リストプロジェクト(3)
7777 ワード
UserDefaultsを使用したデータの保存とロード
このアプリケーションはほとんど実行できますが、アプリケーションを再起動すると、追加したすべてのデータが消去され、知っている人を覚えるのにあまり役に立たないという致命的な欠陥があります.この問題は、Prospects初期化プログラムが
UserDefaults
からデータをロードし、データ変更時に書き戻すことで解決できます.今回、私たちのデータは、
Prospects
クラスが@Published
プロパティパッケージを使用しているにもかかわらず、people
配列は非常に簡単で、プロトコルを追加するだけでCodable
に合致しています.そのため、3つの小さな変更を行うことで、目標の大部分の方法を実現することができます.Prospects
初期化プログラムを更新し、可能な場合にはUserDefaults
からデータをロードする.save()
メソッドを同じクラスに追加し、UserDefaults
に現在のデータを書き込む.isContacted
の属性を切り替えたりするときに、save()
を呼び出す.コードがこれらの作業をすべて完了できるのを見たことがありますので、始めましょう.
Prospects
に簡単な初期化プログラムを提供したので、以下に示すようにUserDefaults
を使用するように更新することができます.init() {
if let data = UserDefaults.standard.data(forKey: "SavedData") {
if let decoded = try? JSONDecoder().decode([Prospect].self, from: data) {
self.people = decoded
return
}
}
self.people = []
}
save()
メソッドについては、次の内容を追加します.func save() {
if let encoded = try? JSONEncoder().encode(people) {
UserDefaults.standard.set(encoded, forKey: "SavedData")
}
}
私たちのデータは2つの場所で変更されているので、
save()
を呼び出して、常にデータを保存する必要があります.1つ目は、
Prospects
のtoggle()
メソッドであるため、以下のように修正される.func toggle(_ prospect: Prospect) {
objectWillChange.send()
prospect.isContacted.toggle()
save()
}
2つ目は、
ProspectsView
のhandleScan(result:)
メソッドで、リストに新しい潜在顧客を追加します.この行を見つけます.self.prospects.people.append(person)
以下に直接追加します.
self.prospects.save()
アプリケーションを実行すると、アプリケーションを再起動しても追加した連絡先がそこに残っているので、簡単にここで停止できます.しかし、今回はさらに、他の2つの問題を解決したいと思います.
ProspectsView
で呼び出さなければならないのは、良い設計ではありません.一部の理由は、私たちのビューがモデルの内部動作原理を知るべきではないからです.また、他のビューがデータを処理している場合、save()
を呼び出すのを忘れる可能性があります.最初の問題を解決するために、
save()
に静的プロパティを作成して保存キーを含める必要があります.したがって、文字列ではなくProspects
に対してこのプロパティを使用します.これを
UserDefaults
クラスに追加します.static let saveKey = "SavedData"
次に、ハードコーディングされた文字列ではなく、初期化プログラムを変更することで、次のようにすることができます.
if let data = UserDefaults.standard.data(forKey: Self.saveKey) {
保存方法は上の修正と同じで、
Prospects
はここでSelf
なので、Prospects
と書くのと同じ意味です長期的に見ると、この方法はもっと安全です.偶然「SaveKey」や「savedKey」を書くのは簡単すぎて、いろいろな間違いを導入します.
Prospects.saveKey
を呼び出す問題については、save()
のようなコードを記述すると、パッケージと呼ばれるソフトウェアエンジニアリングの原理を破っているというより深い問題です.これは、クラスまたは構造体で値の読み取りと書き込みが可能な外部オブジェクトの数を制限し、データの読み取り(取得)と書き込み(設定)の方法を提供することです.実際には、
self.prospects.people.append(person)
を記述する必要がなく、self.prospects.people.append(person)
クラスにProspects
メソッドを作成することを意味し、add()
というコードを記述することができます.結果は同じです.私たちのコードは、1人のユーザをユーザ配列に追加しますが、実装は非表示になります.これは、配列を他の位置に切り替えることができ、self.prospects.add(person)
は中断しないことを意味しますが、ProspectsView
メソッドに追加の機能を追加できることを意味します.したがって、第2の問題を解決するために、
add()
にProspects
メソッドを作成し、add()
を内部でトリガすることができるようにします.今すぐ追加:func add(_ prospect: Prospect) {
people.append(prospect)
save()
}
より良いことに、アクセス制御を使用して
save()
配列の外部書き込みを停止することができます.これは、ビューがpeople
メソッドを使用して前景を追加する必要があることを意味します.これは、add()
プロパティの定義を次のように変更することによって行われます.@Published private(set) var people: [Prospect]
people
の内部コードのみがProspects
メソッドを呼び出し、プライベートとしてマークすることもできます.private func save() {
これは、偶然にエラーを犯さないようにコードをロックするのに役立ちます.コンパイラでは許可されていません.実際には、コードを構築しようとすると、
save()
がProspectsView
配列に追加され、people
が呼び出されます.これは許可されません.このエラーを解決し、コードを再度きれいにコンパイルするには、次の2行を次のコードで置き換えます.
self.prospects.add(person)
文字列を放棄し、カプセル化とアクセス制御を使用することは、コードをより安全にするための簡単な方法であり、より良いソフトウェアを構築するための重要なステップです.
ロック画面にローカル通知を送信
アプリケーションの最後のセクションでは、コンテキストメニューに別のボタンを追加し、ユーザーに特定のユーザーに連絡するように注意します.これにより、iOSの
save()
フレームワークを使用してローカル通知が作成され、簡単なUserNotifications
選択条件でコンテキストメニューに含めることができます.SwitUIは十分頭が良く、テストに合格した場合はコンテキストメニューボタンを追加できます.もっと面白いのは、ローカル通知をどのように手配するかです.最初の試みでは、
if
を使用して、ロック画面に通知を表示する権限を明示的に要求する必要がありますが、ユーザーがいつでも考えを変更し、通知を無効にすることができるので、その後も注意してください.1つの選択肢は、通知を発行するたびに
requestAuthorization()
が呼び出されることです.これは確かに有効です.最初にアラートが表示されますが、他のすべての場合、以前の応答に基づいてすぐに成功または失敗に戻ります.しかし、完了の目的で、現在のライセンス設定を要求し、通知またはライセンスを要求するかどうかを決定するために、より強力な代替案を示したいと思います.この方法を使用するのは、権限を繰り返し要求するのではなく、私たちに返された設定オブジェクトに
requestAuthorization()
などの属性が含まれているためです.アラートを表示できるかどうかを確認します.ユーザーはこれに制限されている可能性があります.そのため、私たちのアイコンに角記号を表示することができます.したがって、
alertSetting
を呼び出して、通知が現在許可されているかどうかを確認します.もしそうであれば、通知を表示します.ない場合は権限を要求し、正常に戻った場合は通知も表示します.通知をスケジュールするためにコードを繰り返す必要はありません.両方の場合に呼び出すことができる閉パッケージに配置します.まず、このインポートをProspectsView.swiftの上部付近に追加します.
import UserNotifications
次に、この方法を
getNotificationSettings()
構造体に追加します.func addNotification(for prospect: Prospect) {
let center = UNUserNotificationCenter.current()
let addRequest = {
let content = UNMutableNotificationContent()
content.title = "Contact \(prospect.name)"
content.subtitle = prospect.emailAddress
content.sound = UNNotificationSound.default
var dateComponents = DateComponents()
dateComponents.hour = 9
let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: false)
// identifier , ,
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
center.add(request)
}
// more code to come
}
これにより、現在の潜在的な顧客に通知を作成するためのすべてのコードが閉パッケージに配置され、必要に応じて呼び出すことができます.
ProspectsView
をトリガに使用しました.このトリガにより、カスタムUNCalendarNotificationTrigger
インスタンスを指定できます.時間部分を9に設定します.これは、次回の午前9時にトリガーされることを意味します.ヒント:テストの目的で、トリガーコードを注釈して、次のコードに置き換えることをお勧めします.このコードは、これから5秒でアラートを表示します.
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
この方法の第2の部分については、
DateComponents
およびgetNotificationSettings()
を併用して、通知が許可されたときにのみスケジュールされることを保証する.これは、上記で定義したrequestAuthorization()
閉パッケージを使用します.なぜなら、私たちがすでに権限を持っている場合、または私たちが問い合わせて権限を付与されている場合、同じコードを使用することができるからです.置換
addRequest
:center.getNotificationSettings { settings in
if settings.authorizationStatus == .authorized {
addRequest()
} else {
center.requestAuthorization(options: [.alert, .badge, .sound]) { success, error in
if success {
addRequest()
} else {
print("Can't send")
}
}
}
}
これは、特定の潜在的なお客様に通知をスケジュールするために必要なすべてのコードです.残りは、コンテキストメニューに追加のボタンを追加します.前のボタンの下に追加します.
if !prospect.isContacted {
Button("Remind Me") {
self.addNotification(for: prospect)
}
}
これにより、現在の手順が完了し、プロジェクトも完了しました.すぐに実行しようとすると、新しい潜在的な顧客を追加し、押さえて連絡先としてマークしたり、連絡先の注意を手配したりすることができます.
Saving and loading data with UserDefaults Posting notifications to the lock screen