加载和刷新时间敏感数据的最佳做法

时间:2019-03-23 18:26:42

标签: ios swift uitableview core-data

问题总结

我正在编写一个iOS应用,就像待办事项列表和日历之间的混合体。与待办事项列表不同,您不必将所有内容都检查完整。而是将待办事项放置在UITableView中用于活动和非活动的部分中。问题在于,该部分和排序可能会在相对于当前日期和时间的任何时间进行更改。

这是我的第一个iOS应用程序,我真的想确保自己正在学习最佳实践。我真的很想在应用程序首次启动时,何时从后台返回,何时触发通知以及什至在前台刷新计时器时何时何地刷新获取的数据。

当前,该应用程序可以很好地加载核心数据,并且在添加,更改或删除记录时,视图控制器会适当地调整行的节和排序顺序。当我的应用程序位于前台时,当通知被触发时,我还可以刷新单个记录。即,由于触发了通知,非定期提醒从“活动”更改为“非活动”,并且定期提醒也设置了其栏目和下一个提醒日期。

所以我目前在以下方面挣扎最多:

  1. 初始加载。在获取数据之后但视图控制器开始显示它之前,如何以及何时刷新首次应用程序加载时的section / nextAlert日期。

  2. 应用进入前景。当应用程序移至前台时,如何以及何时刷新数据。这是必需的,因为如果在后台运行应用程序时发送了通知,则直到用户与通知进行交互时,应用程序才会被触发。一旦用户与通知交互(包括简单地将其关闭),我就会更新刷新节和日期,但是如果用户忽略通知并加载应用程序,则数据将过时。

  3. 时间已经过去。当用户在应用程序中时,如何以及何时刷新数据,时间已经过去,应该刷新数据。这实际上仅是解决上述问题2的必要条件。

  4. [已添加] 无法从通知启动x-callback-url 。 [我从原始帖子开始就添加了此内容,因为它可能会对我的应用程序的操作方式/时间/位置产生影响。]我的应用程序提醒的全部要点是,用户可以使用x-callback-url。我注意到,如果我的应用程序已经关闭,通知仍会传递,但是当用户对通知执行操作时,它只是将我的应用程序打开到主表视图中,而实际上没有执行启动其他应用程序的操作它应该。我真的不知道为什么,当然,当我关闭该应用程序时,我当然不能调试它;-)所以我也在寻求帮助。

如果很重要,我也希望将x-callback-url功能也添加到我自己的应用程序中。我希望其他应用能够创建,更新或删除提醒记录。与我的通知处理相关的代码如下。

  1. 常规最佳做法。我遵循了Apple和许多其他公司的教程,并查看了各种代码,我觉得我正在按照应做的方式来做事情。我将核心数据“绑定”到表视图控制器,并在viewDidLoad中获取数据。但是,它使视图控制器变得非常胖。我对也许使用这种将所有核心数据代码分解为一个“模型控制器”的方法感兴趣。 https://medium.com/@maddy.lucky4u/swift-4-core-data-part-3-creating-a-singleton-core-data-refactoring-insert-update-delete-9811af2fcf75这似乎很棒!视图控制器更干净/更苗条,我想我什至可以“隐藏”所有可选的展开式丑陋功能。而且,如果所有人最终都告诉我要解决我的问题,我需要将获取和刷新操作移到其他地方,例如在AppDelegate中,那会更加容易。

关于我尝试过的内容的背景

我已经研究并尝试了许多不同的方法来解决这些问题。

  1. 提取后刷新viewDidLoad 。我试图在viewDidLoad期间在表视图控制器中获取数据后立即调用refreshReminders函数。只要我实际上没有在该函数中保存到核心数据,这似乎就可以正常工作。只需在一个或多个记录上设置字段即可触发控制器,并且行将按其应有的方式移动和更新。尽管看起来加载顺序可能为时已晚。我们是否真的希望表视图在应用程序首次启动时必须正确地移动记录?这听起来像是不好的做法。

现在,如果我尝试在刷新后将更改保存到核心数据中,这将变得很奇怪。该表显然是根据当前上下文中记录的更改来更新的,但是当上下文被保存时,它将再次触发更改!由于已经进行了更改,因此在尝试将行从旧位置移动到新位置后,该应用程序崩溃。参见下面的代码。

  1. 刷新viewWillAppear 。我已经尝试从viewWillAppear调用refreshReminders,因为我认为这会在初始加载以及应用程序进入前台后触发。它的工作原理与viewDidLoad中的工作原理相同,因为如果不保存上下文,它将正确移动记录。如果确实保存上下文,则会得到与viewDidLoad中相同的错误。

但是,当应用重新回到前台时,它实际上并没有触发,这让我感到困惑。

当我们从详细信息视图/编辑屏幕中选择返回时,它还具有触发的负面影响。我们不需要,因为segue已经可以创建新记录或更新现有记录并保存上下文。

  1. 在唤醒之前刷新。我使用了对核心数据对象的扩展(提醒)来设置节和nextAlert日期。但是,这似乎没有任何作用。即,该表仍然以错误的部分和/或排序顺序加载了提醒。

刷新此处的字段也不会将数据标记为“脏”(已更改),因此表视图控制器不会移动它,并且仅在数据变脏时才触发的上下文保存不会触发当然。我什至尝试在获取后保存上下文,而不检查其是否已更改,但这也不起作用。也就是说,核心数据即使更改了,也确实没有任何改变。

  1. 刷新计时器。我还没有尝试过基于计时器的refreshReminders。我认为,如果我什至无法在初始加载期间恢复正常工作并返回到前台,那么本质上“随机”调用基于计时器的刷新肯定会导致崩溃。不过,有趣的是,我暂时启用了“拉动刷新”功能,看来我可以随时执行此操作,而且效果很好。

代码在这里

  1. 提取后刷新和2。刷新viewWillAppear

这是tableViewController控制器功能中的.move情况

case .move:
            if let oldPath = indexPath, let newPath = newIndexPath {
                os_log("RemindersViewController: Move was triggered, now updating row in table. Old path was %{public}@ and new path is %{public}@", log: OSLog.default, type: .info, oldPath as CVarArg, newPath as CVarArg)
RemindersCell, withReminder: anObject as! Reminders)
                configureCell(tableView.cellForRow(at: oldPath) as! RemindersCell, withReminder: anObject as! Reminders)
                os_log("RemindersViewController: updated moved cell.", log: OSLog.default, type: .info)


                // Don't actually try to move it if the old and new path are the same
                if (newPath != oldPath) {
                    os_log("RemindersViewController: Moving row in table.", log: OSLog.default, type: .info)
                    tableView.moveRow(at: oldPath, to: newPath)
                    os_log("RemindersViewController: row moved.", log: OSLog.default, type: .info)
                }
            }

这是我的configureCell函数的简化版本。

    func configureCell(_ cell: RemindersCell, withReminder reminder: Reminders) {
        cell.labelTitleField!.text = reminder.title ?? "New Reminder"
        cell.labelAlertField!.text = reminder.nextAlert!.description
    }

这是控制台中与该代码相关的内容:

2019-03-23 12:31:09.307801-0500 Scheduler[5711:2218287] RemindersViewContoller in viewDidLoad: Fetched records successfully.
Refreshing reminders!
2019-03-23 12:31:09.311755-0500 Scheduler[5711:2218287] Reminder 'Test Non-recurring Reminder' section updated to Inactive.
2019-03-23 12:31:09.313254-0500 Scheduler[5711:2218287] RemindersViewController: Move was triggered, now updating row in table. Old path was <NSIndexPath: 0x28078e480> {length = 2, path = 0 - 0} and new path is <NSIndexPath: 0x28078f140> {length = 2, path = 1 - 2}
Scheduler was compiled with optimization - stepping may behave oddly; variables may not be available.
(lldb) 

因此,似乎操作系统本身已在某种程度上不使用我的控制器功能而将提醒移动了。刷新各个提醒字段时必须执行此操作,但是当我将上下文保存到核心数据时,它将再次调用我的控制器函数来执行已经完成的移动。

  1. [已添加] 无法从通知启动x-callback-url
    // Handle notifications when our app is in the background
    // Note that this isn't triggered when the notification is delivered, but rather when the user interacts with the notification
    //
    // TO-DO: THIS DOESN'T RUN THE SHORTCUT IF THE APP WAS CLOSED WHEN THE NOTIFICATION WAS RESPONDED TO!!!
    //
    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: () -> Void) {

        // Get the user info from the notification
        let userInfo = response.notification.request.content.userInfo

        // Get the specific record id that triggered this notification
        let itemID = userInfo["ITEM_ID"] as? String
        let itemTitle = userInfo["ITEM_TITLE"] as? String
        let itemShortcut = userInfo["ITEM_SHORTCUT"] as? String
        let itemURL = userInfo["ITEM_URL"] as? String

        // Handle the notification action
        print("RemindersViewController: Received notification. actionIdentifier:", response.actionIdentifier)
        switch response.actionIdentifier {

        // If user selected the Run Shortcut option or simply tapped the notification, run the associated shortcut
        case "RUN_SHORTCUT", "com.apple.UNNotificationDefaultActionIdentifier":
            os_log("RemindersViewController: Notification Action Received: RUN_SHORTCUT: %{public}@ for shortcut %{public}@", log: .default, type: .info, String(describing: itemTitle!), String(describing: itemShortcut!))
            if (itemShortcut != nil && itemShortcut != "") {
                if (itemURL != nil) {
                    print("RemindersViewController: Shortcut URL=", itemURL!)
                    let launchURL = URL(string: itemURL!)
                    if UIApplication.shared.canOpenURL(launchURL!) {
                        UIApplication.shared.open(launchURL!, options: [:], completionHandler: { (success) in
                            print("RemindersViewController: Notification Action: Run shortcut: Open url : \(success)")
                        })
                    } else {
                        let alert = UIAlertController(title: "You don't have the Shortcuts app installed", message: "Please download from the Apple App Store", preferredStyle: .alert)
                        let action = UIAlertAction(title: "Ok", style: .default, handler: nil)
                        alert.addAction(action)
                        self.present(alert, animated: true, completion: nil)
                        print("RemindersViewController: Notification Action: User doesn't have the Shortcuts app.")
                    }
                } else {
                    let alert = UIAlertController(title: "You don't have a shortcut name filled in", message: "Please fill in a shortcut name on your reminder", preferredStyle: .alert)
                    let action = UIAlertAction(title: "Ok", style: .default, handler: nil)
                    alert.addAction(action)
                    present(alert, animated: true, completion: nil)
                    print("RemindersViewController: Notification Action: No shortcut name filled in!")
                }
            }
            break

        default:
            os_log("RemindersViewController: Default action selected, which is: %{public}@. Doing nothing.", log: .default, type: .info, response.actionIdentifier)
            break
        }

        // Regardless of what the response was, update the reminder with section and nextAlert date
        // REMOVE THIS once we auto-refresh when the app enters the foreground again as that will take care of it
        if (itemID != nil) {
            print("Refreshing reminder from the notification.")
            refreshReminder(stringURI: itemID!)
        }

        // Call the completion handler to close out the notification
        completionHandler()
    }

预期和实际结果

我认为我已经在上面进行了介绍,但是总的来说,我想了解有关如何以及何时在初始加载时刷新时间相关数据,返回到前台以及随着时间流逝的最佳实践。 [添加]我还希望当用户请求关闭我的应用程序时,通知操作仍将执行该功能(通过x-callback-url调用另一个应用程序)。

0 个答案:

没有答案