SwiftUI·后台计时器适用于模拟器,但不适用于真实设备

时间:2020-07-10 22:32:49

标签: ios swiftui

我正在尝试构建一个计时器,当应用程序在后台甚至屏幕被锁定时,该计时器将继续递减计数。计时器达到0后,应该发送通知。

到目前为止,它可以在模拟器上运行,但不能在真实设备(运行iOS 13.5.1的iPhone X)上运行。进入后台后,任务只会暂停。

如何使倒数计时在真实设备上运行?

import SwiftUI
import UserNotifications

struct ContentView: View {
    @State var start = false
    @State var count = 10 // 10 sec timer
    @State var time = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
    
    var body: some View{
        VStack{
            Text("\(self.count)")
            
            Button(action: {
                self.start.toggle()
            }) {
                Text("Start")
            }
        }
        .onAppear(perform: {
            UNUserNotificationCenter.current().requestAuthorization(options: [.badge,.sound,.alert]) { (_, _) in
            }
        })
            .onReceive(self.time) { (_) in
                if self.start{
                    if self.count != 0{
                        self.count -= 1
                    }
                    else{
                        self.sendNotification()
                    }
                }
        }
    }
    
    func sendNotification(){
        
        let content = UNMutableNotificationContent()
        content.title = "Timer"
        content.body = "Time is up!"
        
        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
        let req = UNNotificationRequest(identifier: "MSG", content: content, trigger: trigger)
        
        UNUserNotificationCenter.current().add(req, withCompletionHandler: nil)
    }
}

Selected background Modes (Screenshot)

1 个答案:

答案 0 :(得分:3)

Apple不允许计时器在后台运行。

有关更多信息,请参见以下两个问题:

在您的应用程序后台运行后,Apple不允许您长时间运行进程。从文档中:

执行长期运行的任务

对于需要更多执行时间才能实现的任务,您必须请求特定权限才能在后台运行它们而不暂停它们。在iOS中,只允许特定的应用类型在后台运行: ]

  • 在后台播放用户可听内容的应用,例如音乐播放器应用
  • 在后台录制音频内容的应用
  • 始终让用户了解其位置的应用程序,例如导航应用程序
  • 支持互联网协议语音(VoIP)的应用
  • 需要定期下载和处理新内容的应用
  • 从外部附件接收定期更新的应用
  • 实现这些服务的应用必须声明其支持的服务,并使用系统框架来实现这些服务的相关方面。

声明服务可以使系统知道您使用的服务,但是在某些情况下,实际上是系统框架阻止了您的应用程序被挂起。

但是在这种情况下,您想发送通知,所以我建议您做类似的事情,但要有所不同。

当用户离开应用程序时,您想要获取当前时间。然后,您想要将剩余计时器时间添加到当前时间,并在应用中运行代码以在当前时间+计时器剩余时间发送通知。

可以在特定时间发送通知的代码here

let content = UNMutableNotificationContent()
                content.title = "Title"
                content.body = "Body"
                content.sound = UNNotificationSound.default()

                let gregorian = Calendar(identifier: .gregorian)
                let now = Date()
                var components = gregorian.dateComponents([.year, .month, .day, .hour, .minute, .second], from: now)

                // Change the time to 7:00:00 in your locale
                components.hour = 7
                components.minute = 0
                components.second = 0

                let date = gregorian.date(from: components)!

                let triggerDaily = Calendar.current.dateComponents([.hour,.minute,.second,], from: date)
                let trigger = UNCalendarNotificationTrigger(dateMatching: triggerDaily, repeats: true)


                let request = UNNotificationRequest(identifier: CommonViewController.Identifier, content: content, trigger: trigger)
                print("INSIDE NOTIFICATION")

                UNUserNotificationCenter.current().add(request, withCompletionHandler: {(error) in
                    if let error = error {
                        print("SOMETHING WENT WRONG")
                    }
                })