Swift Timer lags - swift

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.

Related

Nil cannot be assigned to type 'Timer'?

Hi there I am completing some research for Alzheimers Disease - I want to be able to record the time it takes to complete a drawing (it should only take a few seconds for the patient to draw). I want to record both the time spent with apple pencil on tablet and the time spent overall to complete a drawing (time on tablet plus time in between strokes).
I have created this application so far but can't get the timer to work.
I have the drawing/scribble board down pat.
I have tried many different approaches but I am just not experienced enough in code to work out why it is not starting the timer when the apple pencil hits the tablet. The code below is for the ViewController script.
Only one of the timers for = time spent drawing, has been created so far. But I can't even get that to work.
Tried changing the script, tried asking friends. Im quite new to swift so any help is much appreciated.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var canvasView: CanvasView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func clearCanvas(_ sender: Any) {
canvasView.clearCanvas()
timer.invalidate()
seconds = 0
timeDrawing.text = "\(seconds)"
}
#IBOutlet weak var timeDrawing: UILabel!
var seconds = 0
var timer = Timer()
var isTimerRunning = false //This will be used to make sure only one timer is created at a time.
var resumeTapped = false
var touchPoint:CGPoint!
var touches:UITouch!
func runTimer() {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: (#selector(ViewController.updateTimer)), userInfo: nil, repeats: true)
}
private(set) var from: Date?
#objc func updateTimer() {
let to = Date()
let timeIntervalFrom = (from ?? to).timeIntervalSince1970
let time = to.timeIntervalSince1970 - timeIntervalFrom
timeDrawing.text = "\(round(time))" //This will update the label.
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
guard let touch = touches.first else { return }
let touches = touch.location(in: canvasView) // or maybe ...(in: self)
if touch.type == .pencil {
if !isTimerRunning {
from = Date()
runTimer()
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
if !isTimerRunning {
timer.invalidate()
timer = nil
from = nil
timeDrawing.text = "0"
}
}
}
I was hoping when the apple pencil touched the tablet it would start the timer. And then when the pencil left the tablet it would stop one timer and start one of the timers (yet to be implemented). (i have yet to add another timer for the time in between strokes, any help with that would be appreciated too.)
Only optionals can take nil values, you can make the timer object as optional like this
var timer:Timer?
Yes it makes sense. Edited

Preventing macOS App Nap in Swift 4

I'm using Swift 4 and Xcode 9.2 to build a timer app and I'm running into issues with macOS App Nap. I know that the issues are caused by App Nap since when I disable App Nap globally the issues go away. However I only want to disable App Nap for this particular app when I need to for it to run properly. I know that similar questions have been answered before and I have seen them, but either I don't understand how to use them in my case or they do not work anymore (possibly because of changes to the Swift language).
Here is a small example of code that produces the issues I'm having:
import Cocoa
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
#IBOutlet weak var window: NSWindow!
var timer = Timer()
var seconds = 300
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
#IBAction func goButton(_ sender: NSButton) {
if !timer.isValid {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(AppDelegate.ticker), userInfo: nil, repeats: true)
}
}
#objc func ticker() {
seconds -= 1
if seconds == 0 {
timer.invalidate()
print("Timer Ended.")
}
}
}
Any help is greatly appreciated.

Hold down label value increment button in Swift 3 on xcode 8.3.3

I have a simple app which does some probability calculations. I have a plus button which increments a label value 0.1% at a time. What I want to do is increment it faster if I hold down the plus button. All the code I've searched for is for older versions of Swift or Xcode and I can't get my head around how to do it!
At the moment, I have returned to a functioning app with an #IBAction func called plusButton which just adds 0.1% to a label #IBOutlet called preAssessmentProbability.
I would be most grateful if anyone could help with telling me how to keep this functionality but add the ability to hold down the plusButton to increment the preAssessmentProbability label more rapidly (bonus gratitude if you can help with telling me how to set the rate of this).
Here is the implementation. First, make sure that you connect the three actions of your button in this way.
Then your code should look like
#IBAction func buttonUpInside(_ sender: Any) {
self.buttonUp()
}
#IBAction func buttonUpOutside(_ sender: Any) {
self.buttonUp()
}
#IBAction func buttonTouchDown(_ sender: Any) {
self.buttonDown()
}
func buttonDown() {
increaseSpeed()
holdTimer = Timer.scheduledTimer(timeInterval: 2, target: self, selector:#selector(increaseSpeed), userInfo: nil, repeats: true)
}
func buttonUp() {
speed = 1.0
holdTimer.invalidate()
addPercentTimer.invalidate()
}
func increaseSpeed(){
if speed != 1 {
addPercentTimer.invalidate()
}
if speed > 0 {
speed -= 0.2 //make it faster!
}
addPercentTimer = Timer.scheduledTimer(timeInterval: speed, target: self, selector:#selector(addPercent), userInfo: nil, repeats: true)
}
func addPercent(){
preAssessmentProbability += 0.1
}

How to calculate and format a period of time in NSDate

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.

How do I add a timer using Swift?

I have come across a lot of questions similar to this, but many were for older versions of Xcode, or simply did not work.
I'm using Xcode Version 8.3.2 (8E2002) and Swift coding language. I don't know much about coding, but am young and eager to learn!
I'm creating a clicker game that will give you money per second that you are on the game itself. So if you idle for 2 minutes, it would give you $120 ($1per second #120 sec). In addition to this, you also can earn money from clicking the main object.
Here is my coding so far:
import UIKit
class ViewController: UIViewController {
var score = 0
var add = 1
func addpersec() {
score += 1
}
//func used to add to the score based timer. Aka, adding 1 per second
#IBOutlet weak var scorecount: UILabel!
#IBAction func clicks(_ sender: Any) {
score += 1
scorecount.text = "Honey: \(score)"
}
#IBOutlet weak var Bees: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
class ViewController: UIViewController {
var timer: Timer? = nil // Property
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(handleTimer), userInfo: nil, repeats: true)
}
func handleTimer(_ timer: Timer) {
print("Timer ticking!")
}
}
To invalidate the timer, call self.timer?.invalidate()
Your question seems to be related to iOS UI, so I don't know if my answer makes sense.
For general purpose delayed function execution (like Javascript's setTimeout), you can use a DispatchQueue
// have this as a global somewhere
let bg = DispatchQueue(label: "bg", qos: .background, target: nil)
// anywhere else in your code:
// First decide the time to execute your function
let delayInSeconds = 3
let when = DispatchTime.now() + delayInSeconds
// call it
bg.asyncAfter(deadline: when) {
// code to execute later (this is the body of an anonymous function)
}