How to calculate and format a period of time in NSDate - swift

I´m assembling an application with regular web updates. Every update leaves an unix time stamp. Now i try to create an editor, which shows the elapsed time since last update. I´m convering the unix time stamp with this code:
import Cocoa
struct UnixConverter {
static func converUnix(_ timestamp: Int) -> NSDate? {
let date = NSDate(timeIntervalSince1970: TimeInterval(timestamp))
return date
}
}
Now I´m getting a time in this format: "2017-06-16 17:47:45 +0000"
The following function gives me a reals time push update inside a NSTextFieldCell:
import Cocoa
class ViewController: NSViewController {
var timer = Timer()
override func viewDidLoad() {
super.viewDidLoad()
// This function pushes the time NSTextFieldCell
timer = Timer.scheduledTimer(timeInterval: 1.0,
target: self, s
elector: #selector(tick),
userInfo: nil,
repeats: true)
}
// This is the NSTextFieldCell formatter/ receiving container
#objc func tick() {
timerTextField.stringValue = DateFormatter.localizedString(from: NSDate() as Date, dateStyle: .medium, timeStyle: .medium)
}
}
Now "all" I would have to do, is to make a subtraction to get the difference. At this point I must confess I don´t have any idea how to do this. After that the time difference has to be pushed inside the NSTextFieldCell.
I would be very glad if someone could give me a hint how to do it.

Related

Capture Application inactivity for certain time

I am trying to find out if user does not click or scroll or any other type of events in the application for 3 min; or in other words user is not interactive in the application, then the application will sign him out. similar bank application.
I could able to add GestureRecognizer as follows, but I want to catch up all the activities on the UI, is there a way to handle anything like that?
Set up a tap recognizer on collectionView:
In the viewDidLoad add the following:
let collectionViewTap = UITapGestureRecognizer(target: self, action: #selector(collectionViewTap))
collectionView.addGestureRecognizer(collectionViewTap)
Declare this function which will be called when the collectionView is tapped:
func collectionViewTap() {
print("collectionViewTap")
}
I could able to implement the time difference as follows to see timeDifference as well.
func isUserRequiredToLogin(){
let lastActivityDateAndTime = UserDefaults.standard.object(forKey: "lastActivityDateAndTime") as! Date
let currentDate = Date()
let minutes = currentDate.minutes(from: lastActivityDateAndTime)
print(minutes)
}
var timer: Timer?
func resetTimer() {
timer?.invalidate()
timer = Timer.scheduledTimer(timeInterval: 10.0, target: self, selector: #selector(timeIsOur), userInfo: nil, repeats: false)
}
#objc func timeIsOur() {
//logout
//log something
//alerts
}
you can add a Timer to your code and reset it in any event you want. If timer won't be reset - than timeIsOur will be executed and you do whatever you want - (logout or log something)
try to use this https://www.zerotoappstore.com/how-to-detect-user-inactivity-swift.html It works in my production app.

Swift Timer lags

I know Timer is not guaranteed to run at exactly the rate you asked for.
However, I have just started an all new project with this:
class ViewController: NSViewController {
var timer = Timer()
var count: Double = 0.0
var timeStamp: Date = Date()
override func viewDidLoad() {
super.viewDidLoad()
self.timer = Timer.scheduledTimer(timeInterval: 0.1,
target: self,
selector: #selector(self.updateCounting),
userInfo: nil,
repeats: true)
}
#objc func updateCounting(){
let duration = self.timeStamp.distance(to: Date())
print("Counting \(self.count) vs. Real life \(duration)")
self.count = self.count + 0.1
}
}
I am expecting to get roughly the same results between count and duration but I see strange things like this:
Counting 60.50000000000059 vs. Real life 60.64057409763336
Counting 60.60000000000059 vs. Real life 60.741435050964355
Counting 60.70000000000059 vs. Real life 70.84065008163452
Counting 60.800000000000594 vs. Real life 80.94094407558441
Counting 60.900000000000595 vs. Real life 82.99448704719543
Tested on Xcode 11.5, on iMac and MacBook Pro.
Am I doing something wrong here?
Not wrong, but you missed the Energy Efficiency Guide for Mac Apps:
Prioritize Work at the App Level
Prioritize Work at the Task Level
Best Practices
Your app was just napping.
Check the ProcessInfo & search for Managing Activities. An example (bad one) is to change your AppDelegate.swift to:
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
private var activity: NSObjectProtocol!
func applicationDidFinishLaunching(_ aNotification: Notification) {
let options: ProcessInfo.ActivityOptions = [.userInitiatedAllowingIdleSystemSleep]
let reason = "No napping!"
self.activity = ProcessInfo.processInfo.beginActivity(options: options, reason: reason)
}
func applicationWillTerminate(_ aNotification: Notification) {
ProcessInfo.processInfo.endActivity(activity)
}
}
But don't do this, it's bad for users, MacBook Pro battery life, etc.

How to assign live time to a variable

I have a dictionary of values (dataValues) that I ultimately display in a table view. I need to display the current time as a string in that dictionary. dataValues gets its values from the Calculations app in a model file, where I perform my calculations.
I've tried appending currentTime to dataValues, but given it's a dictionary of type string:any it obvbiously doesn't work.
var dataValues : [String:Any]?
override func viewDidLoad() {
super.viewDidLoad()
dataValues = Calculations.shared.ecefToData()
var timer = Timer()
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector:#selector(self.tick) , userInfo: nil, repeats: true)
}
var currentTime: String = "00:00:00"
#objc func tick() -> String {
currentTime = DateFormatter.localizedString(from: Date(),
dateStyle: .short,
timeStyle: .medium)
return currentTime
print(currentTime)
}
}
I would like to append the current time to dataValues and have the time constantly update while in the app.
You need to reload your tableView every time you update your currentTime value, but I'm not sure that's the best way to handle that.
You could instead tie a timer directly to a label on your tableview cell and have it update repeatedly?
I solved my issue. I simply chose to assign the current time to the detailView.text label if the index row was equal to zero as I wanted to display time in the top row of the table view.

Cannot call value of non-function type "calendar"

I made a simple count down app and I get this wrong message: cannot call value of non-function type "calendar".
What I need to change?
#IBOutlet weak var deteLabelOutlet: UILabel!
let formatter = DateFormatter()
let useCalendar = NSCalendar.current()
let requesedComponent: NSCalendar.Unit = [
NSCalendar.Unit.month,
NSCalendar.Unit.day,
NSCalendar.Unit.hour,
NSCalendar.Unit.minute,
NSCalendar.Unit.second
]
override func viewDidLoad() {
super.viewDidLoad()
let timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(printTime), userInfo: nil, repeats: true)
timer.fire()
}
func printTime() {
formatter.dateFormat = "MM/dd/yy hh:mm:ss a"
let startTime = Date()
let endTime = formatter.date(from: String("12/25/16 12:00:00 a"))
let timeDiffrenece = useCalendar.components(requesedComponent,fromDate: startTime, toDate: endTime!, options: [] )
deteLabelOutlet.text = "\(timeDiffrenece.month)Month \(timeDiffrenece.day) days \(timeDiffrenece.minute) minutes \(timeDiffrenece.second) seconds"
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
In Swift 3 the current calendar is not a function. Remove the parentheses.
Anyway it's highly recommended to use the native struct Calendar as suggested in the other answer
let useCalendar = Calendar.current
Edit:
You have to unwrap all date components when assigning the values to the label because in Swift 3 all date components are optional. However as all relevant components are specified you can forced unwrap the components safely.
deteLabelOutlet.text = "\(timeDiffrenece.month!)Month \(timeDiffrenece.day!) days \(timeDiffrenece.minute!) minutes \(timeDiffrenece.second!) seconds"

Game Timer using NSDate with NSTimer? swift

I want to start a timer at a specific date and time, then use that start time as a game timer for the rest of the game. Using "timeIntervalSinceDate" will give me seconds but then trying to get the seconds to display on the gameTimerLabel won't work. I might be coming at this the wrong way. Any advice is welcome.
override func viewWillAppear(animated: Bool) {
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "EEE, dd MMM yyyy HH:mm:ss zzz"
let dateAsString1 = "Fri, 1 April 2016 11:30:00 MST"
let date1 = dateFormatter.dateFromString(dateAsString1)!
var currentTime = NSDate()
var counter = 0
gameTimerLabel.text = String(counter)
gameTimerLabel.text = counter //<- Error:Cannot assign value to type 'Int' to type 'String?'
counter = date1.timeIntervalSinceDate(currentTime) //<- Error:Cannot assign value of type 'NSTimeInterval' (aka 'Double') to type 'Int'
}
A couple of things here
First, when you declare counter, it's inferred to be of type Int
var counter = 0
You can declare it as a double by adding a .0 or specifying it's type:
var counter: NSTimeInternval = 0.0
Next, you can use string interoperability to display the count variable in a string, like this:
gameTimerLabel.text = "\(counter)"
Here's an example view controller using an NSTimer as a counter, it counts in seconds:
class ViewController: UIViewController {
// Setup a timer and a counter for use
var timer = NSTimer()
var counter : NSTimeInterval = 0
#IBOutlet weak var label: UILabel!
// Invalidest the timer, resets the counter and udpates the label
#IBAction func resetTimer(sender: AnyObject) {
// Invalidate the timer, reset the label.
self.timer.invalidate()
self.label.text = ""
self.counter = 0
}
// A button that when pressed starts and stops the timer
// There's no pause/resume, so it's invalidated & created again
// But the counter value reamins the same
#IBAction func timerBttnTouched(sender: AnyObject) {
if self.timer.valid {
self.timer.invalidate()
} else {
self.setupTimer()
}
}
// Does the actual counting everytime the timer calls this method
func timerFired() {
self.counter += 1
self.label.text = "\(self.counter)"
}
// Setups a timer, adds it to the run loop and specifies what method should fire when the timer fires
func setupTimer() {
// Setupt the timer, this will call the timerFired method every second
self.timer = NSTimer(
timeInterval: 1,
target: self,
selector: #selector(self.timerFired),
userInfo: nil,
repeats: true)
// Add the timer to the run loop
NSRunLoop.currentRunLoop().addTimer(
self.timer,
forMode: NSDefaultRunLoopMode)
}
}
An important thing to note when using timers is they may not always be called exactly when you need them to, this should be taken into account according to your desired precision when using a timer.
As discussed in comments, here's the solution using a timer to fire a method that compares two dates and uses a NSDateComponentsFormatter to generate a string for display. The initial date is generated in viewDidLoad but can be created anywhere:
class ViewController: UIViewController {
// Setup a timer and a counter for use
var timer = NSTimer()
var counter : NSTimeInterval = 0
var startDate: NSDate?
override func viewDidLoad() {
// Set the initial date
self.startDate = NSDate()
}
#IBOutlet weak var label: UILabel!
// Invalidest the timer, resets the counter and udpates the label
#IBAction func resetTimer(sender: AnyObject) {
// Invalidate the timer, reset the label.
self.timer.invalidate()
self.label.text = ""
self.counter = 0
}
// A button that when pressed starts and stops the timer
// There's no pause/resume, so it's invalidated & created again
// But the counter value reamins the same
#IBAction func timerBttnTouched(sender: AnyObject) {
if self.timer.valid {
self.timer.invalidate()
} else {
self.setupTimer()
}
}
// Does the actual counting everytime the timer calls this method
func timerFired() {
let now = NSDate()
let difference = now.timeIntervalSinceDate(self.startDate!)
// Format the difference for display
// For example, minutes & seconds
let dateComponentsFormatter = NSDateComponentsFormatter()
dateComponentsFormatter.stringFromTimeInterval(difference)
self.label.text = dateComponentsFormatter.stringFromTimeInterval(difference)
}
// Setups a timer, adds it to the run loop and specifies what method should fire when the timer fires
func setupTimer() {
// Setupt the timer, this will call the timerFired method every second
self.timer = NSTimer(
timeInterval: 1,
target: self,
selector: #selector(self.timerFired),
userInfo: nil,
repeats: true)
// Add the timer to the run loop
NSRunLoop.currentRunLoop().addTimer(
self.timer,
forMode: NSDefaultRunLoopMode)
}
}