How to do timeIntervalSinceNow in Swift3? - ios

I was making a notification and watching a tutorial on it and when I typed in:
notification.fireDate = NSDate(timeIntervalSinceNow: 0)
it says
Argument labels '(timeIntervalSinceNow:)' do not match any available
overloads
How do I fix this? Here is my code:
import UIKit
import UserNotifications
class ViewController: UIViewController {
var timer = Timer()
var time = 10
override func viewDidLoad() {
super.viewDidLoad()
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: Selector(("Notification")), userInfo: nil, repeats: true)
// Do any additional setup after loading the view, typically from a nib.
}
func notification() {
time -= 1
if time <= 0 {
let notification = UILocalNotification()
notification.alertAction = "Call"
notification.alertBody = "You have a call right now"
notification.fireDate = NSDate(timeIntervalSinceNow: 0)
UIApplication.shared.scheduleLocalNotification(notification)
timer.invalidate()
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func pushNotification(_ sender: AnyObject) {
let AlertView = UIAlertController(title: "Time for your call!", message: "Press go to continue", preferredStyle: .alert)
AlertView.addAction(UIAlertAction(title: "Go", style: .default, handler: nil))
self.present(AlertView, animated: true, completion: nil)
}
}

Swift 3
let minute:TimeInterval = 11.0 * 60.0;
Date(timeIntervalSinceNow: minute);
for +11 minutes to now.

If you want the fire date to be now, it's just:
notification.fireDate = Date()
As Leo noted elsewhere, the selector is incorrect. It should be:
weak var timer: Timer?
var time = 10
override func viewDidLoad() {
super.viewDidLoad()
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(handleTimer(_:)), userInfo: nil, repeats: true)
}
func handleTimer(_ timer: Timer) {
time -= 1
if time <= 0 {
let notification = UILocalNotification()
notification.alertAction = "Call"
notification.alertBody = "You have a call right now"
notification.fireDate = Date()
UIApplication.shared.scheduleLocalNotification(notification)
timer.invalidate()
}
}
You then asked:
that definitely works but now I don't get a notification when I leave the app
You don't get the notification when you leave your app, because the Timer doesn't continue to fire when the app isn't running. It's better to eliminate the timer altogether and just immediately schedule the local notification for the desired time. For example, to schedule a local notification to fire in 10 seconds:
override func viewDidLoad() {
super.viewDidLoad()
scheduleLocalNotification(delay: 10)
}
func scheduleLocalNotification(delay: TimeInterval) {
let notification = UILocalNotification()
notification.alertAction = "Call"
notification.alertBody = "You have a call right now"
notification.fireDate = Date().addingTimeInterval(delay)
UIApplication.shared.scheduleLocalNotification(notification)
}

Related

Swift - Using a Timer to detect user inactivity then display an Alert after 4 minutes to prompt the user if they are still there

I have a basic application for iPads which consists of 4 views. Across the whole app, I want to be able to detect user inactivity and then display an Alert after 4 minutes which will ask the user if they are still there.
I have found some useful resources for the Timer and Alert functions. I have played around with these tutorials and can get the Timer working on it's own. However, this is my first time developing in Swift so I would like some guidance on the best way to connect the Timer to the Alert is? I would like the Timer to run for 4 minutes and then create the Alert.
I would also like to know where the best place is to put the code for these elements so that they work across all 4 of my views. E.g is there a single place I can put the code and reuse it rather than having it repeated in each of the 4 views?
First you can follow the instructions here to set up your timer: https://blog.gaelfoppolo.com/detecting-user-inactivity-in-ios-application-684b0eeeef5b
Then you can do something like this to handle the timeout notifications:
extension UIViewController {
func observeTimeout() {
NotificationCenter.default.addObserver(
self,
selector: #selector(handleTimeout),
name: .appTimeout,
object: nil)
}
#objc func handleTimeout() {
let alert = UIAlertController(title: "Timeout", message: "Oh no!", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { _ in
}))
present(alert, animated: true, completion: nil)
}
}
And then in each of your view controllers do this to register for the timeout:
class SomeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
observeTimeout()
}
}
It's been a while since this question was asked however, since I came across the same problem I slightly changed the answer #rob-c provided using this guide:
Tested on Swift 5.5 and iOS 15.1
1- Create a subclass of UIApplication
import Foundation
import UIKit
class TimerApplication: UIApplication {
private var timeoutInSeconds: TimeInterval {
return 40.0
}
private var idleTimer: Timer?
override init() {
super.init()
resetIdleTimer()
}
private func resetIdleTimer() {
if let idleTimer = idleTimer {
idleTimer.invalidate()
}
idleTimer = Timer.scheduledTimer(timeInterval: timeoutInSeconds,
target: self,
selector: #selector(TimerApplication.timeHasExceeded),
userInfo: nil,
repeats: false
)
}
#objc private func timeHasExceeded() {
NotificationCenter.default.post(name: .appTimeout, object: nil)
}
override func sendEvent(_ event: UIEvent) {
super.sendEvent(event)
if idleTimer != nil {
self.resetIdleTimer()
}
if let touches = event.allTouches {
for touch in touches where touch.phase == UITouch.Phase.began {
self.resetIdleTimer()
}
}
}
}
2- Add notification name
import Foundation
extension Notification.Name {
static let appTimeout = Notification.Name("appTimeout")
}
3- Create main.swift file and add this
import Foundation
import UIKit
/// NOTE: comment out #UIApplicationMain in AppDelegate
UIApplicationMain(
CommandLine.argc,
CommandLine.unsafeArgv,
NSStringFromClass(TimerApplication.self),
NSStringFromClass(AppDelegate.self)
)
4- Remove #UIApplicationMain from AppDelegate
5- Add observer for the notification
Add the observer where appropriate for your case, in AppDelegate or any view controller:
func addObservers() {
NotificationCenter.default.addObserver(self,
selector: #selector(idleTimeLimitReached(_:)),
name: .appTimeout,
object: nil)
}
#objc func idleTimeLimitReached(_ notification: Notification) {
print("***** IDLE Time called")
}
You can run Timer in AppDelegate itself and display alert like below from AppDelegate
func showAlertFromAppDelegates(){
let alertVC = UIAlertController(title: "Oops" , message: "Presented Alert from AppDelegates", preferredStyle: UIAlertController.Style.alert)
let okAction = UIAlertAction(title: "Okay", style: UIAlertAction.Style.cancel) { (alert) in
}
alertVC.addAction(okAction)
var presentVC = self.window!.rootViewController
while let next = presentVC?.presentedViewController
{
presentVC = next
}
presentVC?.present(alertVC, animated: true, completion: nil)
}
Edit:
Timer related code added, call startTimer() when you want to start inactivity and call stopTimer() when inactivity ends
func startTimer()
{
let interval = 4*60
timer = Timer.scheduledTimer(timeInterval: TimeInterval(interval), target: self, selector: #selector(self.showAlertFromAppDelegates), userInfo: nil, repeats: true)
}
func stopTimer()
{
if let timer = timer
{
timer.invalidate()
}
timer = nil
}

How to subtract current time from a target time?

I figured out how to display the live current time in "hh:mm:ss" (code below). I want the label to display the time left until a specific target time, like a countdown. I want it to count down until e.g. 3pm each day and then start over. I know I need to subtract my target time from the current time but I don't know how to do that.
(I am new to programming)
class ViewController: UIViewController {
#IBOutlet weak var Label: UILabel!
var timer = Timer()
override func viewDidLoad() {
super.viewDidLoad()
getCurrentTime()
}
private func getCurrentTime() {
timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector:#selector(self.currentTime) , userInfo: nil, repeats: true)
}
#objc func currentTime() {
let formatter = DateFormatter()
formatter.dateFormat = "hh:mm:ss"
Label.text = formatter.string(from: Date())
}
}
Use timeIntervalSince to find TimeInterval between two dates. Like below
let secondsBetween: TimeInterval = targetDate.timeIntervalSince(currentDate)
self.counter = Int(secondsBetween)
func timerAction() {
counter -= 1
label.text = timeString(time: TimeInterval(counter))
}

I want to make datePicker on IOS by swift

all
I am a new developer on IOS. nowdays I study Swift by books.
there are some trouble. I use swift3 but the book consists of swift2.
so I don't know what is wrong code.
could you help me?
thanks you for reading and helping me.
this is datacode.
import UIKit
class ViewController: UIViewController {
let timeSelector: Selector = #selector(ViewController.updateTime)
let interval = 1.0
var count = 0
#IBOutlet weak var IbICurrentTime: UILabel!
#IBOutlet weak var IbIPickTime: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
Timer.scheduledTimer(withTimeInterval: interval, repeats: true, block: timeSelector)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func changeDatePicker(_ sender: UIDatePicker) {
let datePickerView = sender
let formatter = DateFormatter()
formatter.dateFormat = "YYYY-MM-dd HH:mm:ss EEE"
IbIPickTime.text = "선택시간: " + formatter.string(from: datePickerView.date)
}
func updateTime() {
IbICurrentTime.text = String(count)
count = count+1
}
}
there are problem
Timer.scheduledTimer(withTimeInterval: interval, repeats: true, block: timeSelector)
}
i don't know what i have to input at "block"?
"Block" is the Objective-C term of Swift's closure. The "Blocks" variant works like this:
Timer.scheduledTimer(timeInterval: interval, repeats: true, block: { timer in
self.IbICurrentTime.text = String(self.count)
self.count += 1
})
You don't need to define a separate updateTime() function with the block syntax.
In Swift the above can be written more naturally as
Timer.scheduledTimer(timeInterval: interval, repeats: true) { _ in
self.IbICurrentTime.text = String(self.count)
self.count += 1
}
If you want to use selectors, use scheduled​Timer(time​Interval:​target:​selector:​user​Info:​repeats:​) instead:
Timer.scheduledTimer(time​Interval: interval,
target: self,
selector: #selector(updateTime),
userInfo: nil,
repeats: true)

Call a function using NSTimer

So as the code shown below, the function loadView 3 is supposed to run 300 seconds(5 minutes) after the user tapped the button. But when I build and run, it does not. I also did a few experiments, I changed the timer to 5 seconds, it worked. So after the app is suspended from iOS system, the NSTimer doesn't run anymore? So what's the problem, how can I fix it?
#IBAction func buttonTapped(sender: AnyObject) {
NSTimer.scheduledTimerWithTimeInterval(300, target: self, selector: #selector(ViewController.loadView3), userInfo: nil, repeats: false)
createLocalNotification()
}
func createLocalNotification() {
let localnotification = UILocalNotification()
localnotification.fireDate = NSDate(timeIntervalSinceNow: 300)
localnotification.applicationIconBadgeNumber = 1
localnotification.soundName = UILocalNotificationDefaultSoundName
localnotification.alertBody = "Hello!"
UIApplication.sharedApplication().scheduleLocalNotification(localnotification)
}
func loadView3() {
label.text = "e89saa"
}
You could try something like this. The approach is, if timer works proceed with same logic, otherwise (probably app is killed or went to background), save firedDate and update UI before showing controller in method viewWillAppear.
#IBAction func buttonTapped(sender: AnyObject) {
NSTimer.scheduledTimerWithTimeInterval(300, target: self, selector: #selector(ViewController.loadView3), userInfo: nil, repeats: false)
createLocalNotification()
}
func createLocalNotification() {
let localnotification = UILocalNotification()
localnotification.fireDate = NSDate(timeIntervalSinceNow: 300)
localnotification.applicationIconBadgeNumber = 1
localnotification.soundName = UILocalNotificationDefaultSoundName
localnotification.alertBody = "Hello!"
UIApplication.sharedApplication().scheduleLocalNotification(localnotification)
// save in UserDefaults fireDate
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject(localnotification.fireDate, forKey: "firedDate")
defaults.synchronize()
}
func loadView3() {
// reset in UserDefaults fireDate
let defaults = NSUserDefaults.standardUserDefaults()
defaults.removeObjectForKey("firedDate")
defaults.synchronize()
label.text = "e89saa"
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated) // No need for semicolon
// retrieve fireDate from UserDefaults
let defaults = NSUserDefaults.standardUserDefaults()
let fireDate = defaults.objectForKey("firedData")
// check if we should update UI
if let _ = fireDate as? NSDate! {
if currentDate.compare(firedDate) == NSComparisonResult.OrderedDescending {
loadView3()
}
}
}

Widget data doesn't update when scrolling - Swift

I have a widget with data. When I launch today extension my widget data is updating and showing in real time. But when I scroll the notification center and return to my widget, it doesn't update. I tried several different methods but they didn't help me. I wrote below the last method which I tried.
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: "updateLabels", userInfo: nil, repeats: true)
}
func widgetPerformUpdateWithCompletionHandler(completionHandler: ((NCUpdateResult) -> Void)) {
// Perform any setup necessary in order to update the view.
// If an error is encountered, use NCUpdateResult.Failed
// If there's no update required, use NCUpdateResult.NoData
// If there's an update, use NCUpdateResult.NewData
completionHandler(.NewData)
}
func updateLabels() {
runtimeLabel.text = returnTime() + " " + returnDay()
}
func returnTimeInterval() -> NSTimeInterval {
let uptime = NSProcessInfo().systemUptime
return uptime
}
func returnTime() -> String {
dateFormatter.unitsStyle = .Short
dateFormatter.allowedUnits = [.Day, .Hour, .Minute, .Second]
dateFormatter.zeroFormattingBehavior = .Pad
let time = dateFormatter.stringFromTimeInterval(returnTimeInterval())!
return time
}
func returnDay() -> String {
dateFormatter.unitsStyle = .Short
dateFormatter.allowedUnits = [.Year, .Month, .Day]
dateFormatter.zeroFormattingBehavior = .Pad
let date = NSDate(timeInterval: -returnTimeInterval(), sinceDate: NSDate())
let formatter = NSDateFormatter()
formatter.locale = NSLocale.currentLocale()
formatter.dateStyle = .MediumStyle
let megaDate = formatter.stringFromDate(date)
return megaDate
}
I tried the same and it worked with the code below:
#IBOutlet weak var infoLabel: UILabel!
var timer = NSTimer()
var counter = 0
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("updateLabel"), userInfo: nil, repeats: true)
}
func updateLabel(){
counter += 1
myLabel.text = "Test \(counter)"
}
Update:
viewWillAppear and viewDidAppear should be called after when the widget is active and viewDidDisappear should be called when scrolling (leaving the widget). As for now the viewDidDisappear is working as expected but not viewWillAppear and viewDidAppear on scrolling.
It´s a known bug that this is not working properly, you can read more information in this post at Apples forum and check the bug status report here.
You should write your update code in widgetPerformUpdateWithCompletionHandler method.

Resources