美文网首页Hacking with iOS: SwiftUI Edition
Hacking with iOS: SwiftUI Editio

Hacking with iOS: SwiftUI Editio

作者: 韦弦Zhy | 来源:发表于2020-09-23 20:52 被阅读0次

    \color{red}{\Large \mathbf{Hacking \quad with \quad iOS: SwiftUI \quad Edition}}

    {\Large \mathbf{潜力客户名单}}

    {\Large \mathbf{Hot \ Prospects}}

    • 放弃字符串,然后使用封装和访问控制是使我们的代码更安全的简单方法,并且是构建更好软件的重要步骤。

    使用 UserDefaults 保存和加载数据

    该应用程序大多数情况下都可以运行,但是有一个致命缺陷:重新启动该应用程序时,我们添加的所有数据都会被清除掉,这在记住我们认识的人方面没有多大用处。我们可以通过使 Prospects 初始化程序能够从UserDefaults加载数据,然后在数据更改时将其写回来解决此问题。

    这次,我们的数据以稍微容易些的格式存储:尽管Prospects类使用@Published属性包装器,但其中的people数组非常简单,仅通过添加协议就已经符合Codable。因此,我们可以通过进行三个小更改来实现目标的大部分方法:

    1. 更新Prospects初始化程序,以便在可能的情况下从UserDefaults加载其数据。
    2. save()方法添加到同一类中,然后将当前数据写入UserDefaults
    3. 在添加潜在客户或切换其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")
        }
    }
    

    我们的数据在两个地方进行了更改,因此我们都需要调用save()来确保始终将数据保存。

    第一个是在Prospectstoggle()方法中,因此将其修改为:

    func toggle(_ prospect: Prospect) {
        objectWillChange.send()
        prospect.isContacted.toggle()
        save()
    }
    

    第二个是在ProspectsViewhandleScan(result:)方法中,我们在该方法中向列表添加新的潜在客户。找到这一行:

    self.prospects.people.append(person)
    

    并直接在下面添加:

    self.prospects.save()
    

    如果您现在运行该应用程序,您会发现即使重新启动该应用程序后,添加的任何联系人仍将保留在那里,因此我们可以轻松地在此处停止。但是,这次我想更进一步,解决其他两个问题:

    1. 我们必须在两个地方对键名“SavedData”进行硬编码,如果名称更改或需要在更多地方使用,将来可能再次引起问题。
    2. 必须在ProspectsView中调用save()并不是一个好的设计,部分原因是我们的视图确实不应该知道其模型的内部工作原理,而且还因为如果我们有其他视图在处理数据,那么我们可能会忘记调用save()那里。

    为了解决第一个问题,我们应该在Prospects上创建一个静态属性以包含我们的保存键,因此我们对UserDefaults使用该属性而不是字符串。

    将此添加到Prospects类中:

    static let saveKey = "SavedData"
    

    然后,我们可以使用它而不是硬编码的字符串,首先通过修改初始化程序,如下所示:

    if let data = UserDefaults.standard.data(forKey: Self.saveKey) {
    

    保存方法同上修改,Self在此为 Prospects 所以和写 Prospects.saveKey 是一样的意思

    从长远来看,这种方法更安全——偶然编写“SaveKey”或“savedKey”太容易了,这样做会引入各种错误。

    至于调用save()的问题,这实际上是一个更深层次的问题:当我们编写诸如self.prospects.people.append(person)之类的代码时,我们正在打破一种称为 封装 的软件工程原理。这是一个想法,我们应该限制一个类或结构体中可以读取和写入值的外部对象数量,并且提供读取(获取)和写入(设定)数据的方法。

    实际上,这意味着我们无需编写self.prospects.people.append(person)而是在Prospects类上创建add()方法,因此我们可以编写如下代码:self.prospects.add(person)。结果将是相同的——我们的代码将一个人员添加到人员数组中——但是现在隐藏了实现。这意味着我们可以将数组切换到其他位置,而ProspectsView不会中断,但这也意味着我们可以向add()方法添加额外的功能。

    因此,为了解决第二个问题,我们将在Prospects中创建一个add()方法,以便我们可以在内部触发save()。立即添加:

    func add(_ prospect: Prospect) {
        people.append(prospect)
        save()
    }
    

    更好的是,我们可以使用访问控制来停止对people数组的外部写入,这意味着我们的视图必须使用add()方法添加前景。这是通过将people属性的定义更改为以下内容来完成的:

    @Published private(set) var people: [Prospect]
    

    现在,只有Prospects内部的代码才调用save()方法,我们也可以将其标记为私有的:

    private func save() {
    

    这有助于锁定我们的代码,以便我们不会因偶然而犯错误——编译器根本不允许这样做。实际上,如果您现在尝试构建代码,您将确切理解我的意思:ProspectsView尝试追加到people数组并调用save(),这不再被允许。

    要解决该错误并让我们的代码再次干净地编译,请用以下代码替换这两行:

    self.prospects.add(person)
    

    放弃字符串,然后使用封装和访问控制是使我们的代码更安全的简单方法,并且是构建更好软件的重要步骤。

    发送本地通知到锁屏界面

    对于应用程序的最后一部分,我们将在上下文菜单中添加另一个按钮,以提醒用户选择联系特定人员。这将使用iOS的UserNotifications框架创建本地通知,我们将通过简单的if选中条件将其包含在上下文菜单中——SwiftUI足够聪明,如果测试通过,则可以添加上下文菜单按钮。

    更有趣的是我们如何安排本地通知。请记住,第一次尝试时,我们需要使用requestAuthorization()显式请求在锁定屏幕上显示通知的权限,但随后的时间我们也要小心,因为用户可以随时改变主意并禁用通知。

    一种选择是,每当我们要发布通知时,都调用requestAuthorization(),这确实很有效:第一次显示警报,而在所有其他情况下,它将根据先前的响应立即返回成功或失败。

    但是,出于完成的目的,我想向您展示一个更强大的替代方案:我们可以请求当前的授权设置,并使用该设置来确定是否应该安排通知或请求许可。使用此方法而不是重复请求权限的帮助,是因为交还给我们的设置对象包含诸如alertSetting之类的属性,用于检查我们是否可以显示警报——用户可能已对此进行了限制,因此我们可以要做的是在我们的图标上显示一个角标。

    因此,我们将调用getNotificationSettings()来了解当前是否允许通知。如果是,我们将显示一条通知。如果没有,我们将请求权限,如果成功返回,我们还将显示一条通知。我们无需重复代码来安排通知,而是将其放在可以在两种情况下均可调用的闭包中。

    首先在 ProspectsView.swift 顶部附近添加此导入:

    import UserNotifications
    

    现在,将此方法添加到ProspectsView结构体中:

    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
    }
    

    这会将所有用于为当前潜在客户创建通知的代码置于闭包中,我们可以在需要时调用它。请注意,我已将UNCalendarNotificationTrigger用于触发器,该触发器使我们可以指定自定义DateComponents实例。我将其小时部分设置为9,这意味着它将在下次上午9点触发。

    提示:出于测试目的,建议您注释掉该触发代码,然后将其替换为以下代码,该代码从现在起五秒钟显示警报:

    let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
    

    对于该方法的第二部分,我们将一起使用getNotificationSettings()requestAuthorization(),以确保仅在允许时安排通知。这将使用我们上面定义的addRequest闭包,因为如果我们已经拥有权限,或者如果我们询问并已被授予权限,则可以使用相同的代码。

    替换 // more code to come

    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

    相关文章

      网友评论

        本文标题:Hacking with iOS: SwiftUI Editio

        本文链接:https://www.haomeiwen.com/subject/ygbhyktx.html