Best way to implement iOS badge notifications in Tab Bar - ios

Generally speaking I want to query my DB for all new FeedItems in the last 10 minutes. This Feed should have a nice batch of new FeedItems every 10 minutes.
I've written a function:
func getRecentFeedItems() {
// Set up timing
let date = NSDate()
let calendar = NSCalendar.autoupdatingCurrentCalendar()
let dateMinusTenMin = calendar.dateByAddingUnit(.Minute, value: -10, toDate: date, options: [])
//Query the DB
let getRecentFeedItems = FeedItem.query()
getRecentFeedItems!.whereKey("createdAt", greaterThan: dateMinusTenMin!)
let newBadgeCount: Int = (getRecentFeedItems?.countObjects())!
if newBadgeCount > 0 {
self.navigationController?.tabBarItem.badgeValue = String(newBadgeCount) //update the Badge with the new count
print("update the badge with \(newBadgeCount)")
} else {
self.navigationController?.tabBarItem.badgeValue = nil
}
}
This function works to update the Badge Notification however when I set a timer for it to run every ten minutes:
var updateBadgeQueryTimer = NSTimer.scheduledTimerWithTimeInterval(600.0, target: self, selector: Selector("updateBadgeQuery"), userInfo: nil, repeats: true)
It does work, but I get the following warning which I know to be a serious one:
Warning: A long-running operation is being executed on the main thread.
Given the following parameters:
If user is on Tab1 where the FeedItems, the query runs and the badge is populated, I want the Badge to show up and then disappear when the user reloads the feed via UIRefreshControl()
If user is on Tab2 or another View, I want the Badge to show up and only disappear when user presses Tab1.
I want the query for the amount of new items to run every 10 minutes.
I've tried running getRecentFeedItems() in viewWillAppear as well as viewDidAppear().
Is there a better way to do this?

As far as I can tell the only thing you'll need to change is to run getRecentFeedItems in the background and only update the badge value after you get a success or failure message from the query. This will prevent the warning of an operation occurring on the main thread.
Hope this helps!

Related

Send Local Notification when Timer reaches zero SwiftUI

Learning SwiftUI. I have an app that counts down timers from 30 min. As the timer doesn't work when the app is in the background, I have used user notification to get the current time app goes into the background and the time it comes to the foreground and subtracts the difference between those two times from the countdown timer I have going on so it reflects the time that has passed. Everything works fine. However, I need to be able to send a notification when the timer reaches zero.
As the timer is suspended every time the app goes into the background and the difference between how much time has passed is only calculated when the app comes into the foreground, I'm not able to find a way to send a notification when the timer reaches zero ( as the difference is only calculated when the app is in the foreground ) which negates the whole point of sending notification to let the user know the timer has ended.
Is there a way to figure out how to send a notification when the timer has reached zero without the app coming into the foreground? or any way to keep the timer running in the background so I can check if the timer has reached zero to send a notification?
Snippet of the code:
HStack {
// some code
}
.onReceive(NotificationCenter.default.publisher(
for: UIScene.didEnterBackgroundNotification)) { _ in
if isTimerStarted {
movingToBackground()
}
}
.onReceive(NotificationCenter.default.publisher(
for: UIScene.didActivateNotification)) { _ in
if isTimerStarted {
movingToForeground()
}
}
// the functions created:
func movingToBackground() {
print("Moving to the background")
notificationDate = Date()
fbManager.pause()
}
func movingToForeground() {
print("Moving to the foreground")
let deltaTime: Int = Int(Date().timeIntervalSince(notificationDate))
let deltaTimefill : Double = Double(deltaTime) / Double(300)
fbManager.breakElapsed -= deltaTime
if fbManager.breakElapsed <= 0 {
notify.sendNotification(
date: Date(),
type: "time",
timeInterval: 5,
title: "AppName+",
body: "Your timer has ended")
}
fbManager.breakFill += deltaTimefill
fbManager.startBreak()
}
Let me know if you need more code.
You can queue up the notification at any time, with the date parameter set to when you want it to be displayed.

how to create countdowns in swift that will start after a countdown has finished

[New to Swift]
I am making an app on Swift 5 that displays time left before my next task is to start.
My app:
Task 1: Start at 9.30 am
Task 2: Start at 10.15 am
Let's say Current Time: 09.00 am
Countdown: Time before next task start = 00:30:02 --> 00:30:01 ...
I would like to display countdown of the nearest task.
Currently, I can only have one countdown timer on my viewonLoad() that picks up the current countdown. It continues and once it finishes it does not start next timer after it has finished. I understand I have to deal with Background state at a later date, but since I am slowly starting. My idea is to make sure I can initiate next countdown timer once my current one has expired.
So at any point when I open my app, it will always display countdown till my next task.
var count = 30 // calculation simplified ... this data is calc from two different date obj
override func viewDidLoad() {
super.viewDidLoad()
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(update), userInfo: nil, repeats: true)
}
#objc func update() {
if(count > 0) {
count = count - 1
countdownLabel.text = String(count)
}else{
timer!.invalidate()
getNextTaskTimes()
}
}
func getNextTaskTimes(){
count = 5 // not working
}
How can I achieve this please? Also, is there a better way of doing this? Given that, my user may choose to close the app and come back at a later time, I still would want my countdown to continue in the background.
Any help is appreciated. :D Thanks
I realized the above code works. I just need to make sure I do not invalidate the timer in the else block. Invalidating the timer just kills it.

How to update a label every time minute changes?

So I've been searching Stackoverflow for a good solution on how to update label if the time change, but so far the results have been unsatisfactory. Most use a timer, and that's not what I want.
What I want is if the status bar time is 8:52 PM and if some arbitrary bus leaves at 9:00 PM then I want the label to show 8 min. Then, if the time changes to 8:53 PM I want the label to show 7 min.
I'd prefer some sort of notification or delegation method rather than setting a timer.
If anyone has any suggestions on what I should do (or any 3rd party libraries that could notify if the time changes) that would be great!
You only have limited choices. If your app is not running in the foreground, you're limited to local notifications, which display an alert to the user. Using those once a minute would be awful for the user.
When your app comes to the foreground you can start a timer that is timed to fire every minute on the minute and use that to update a label in your app:
weak var timer: Timer?
func startTimer() {
timer?.invalidate()
let interval = Double(Date().timeIntervalSinceReferenceDate)
let delay = 60 - fmod(interval, 60.0)
message.text = "Delay = \(delay)"
//Create a "one-off" timer that fires on the next even minute
let _ = Timer.scheduledTimer(withTimeInterval: delay, repeats: false ) { timer in
self.message.text = "\(Date())"
self.timer = Timer.scheduledTimer(withTimeInterval: 60.0,
repeats: true ) { timer in
//Put your repeating code here.
self.message.text = "\(Date())"
}
}
}

how to detect if a task has execute in the last 2 minutes in swift 3?

I have a project that has Json in it - when a user come back to the home page because of view didLoad method the app will start getting son again and I want this But I want the app detect that if the user has came back to the home page in the last 2 minutes the app doesn't get the json - simply I want to run a task when user go to a view controller but if user has came back to the view controller in the last 2 minutes the app doesn't execute task and for example if the user open the app and go to the another page after 3 minutes when he came back to the home page the task start - as you see here I can use timer but the timer will run the task every minutes I want to limit this as I said
weak var timer: Timer?
func startTimer() {
timer = Timer.scheduledTimer(withTimeInterval: 60.0, repeats: true) { [weak self] _ in
// do something here
}
}
func stopTimer() {
timer?.invalidate()
}
// if appropriate, make sure to stop your timer in `deinit`
deinit {
stopTimer()
}
You would need to save the Date(time) object when the task gets completed in the completion handler and then next time when you are about to start the task you would need to check the time elapsed.
Set a Date in UserDefaults in the completion handler of your task.
Before proceeding to start task check whether this Date exists and if exists, then the elapsed time is greater than 120 seconds(2 minutes) or not.
func startTaskIfPossible() {
let date = UserDefaults.standard.object(forKey: "taskCompletionDate") as? Date
guard let prevCompletionDate = date else {
startTask()
return
}
guard Date().timeIntervalSince(prevCompletionDate) > 120 else {
return
}
startTask()
}
func startTask() {
//Set Date in userdefaults in completion handler of task
// UserDefaults.standard.setValue(Date(), forKey: "taskCompletionDate")
}
Save the last json request execution time in one of the keys in your NSUserdefaults.
let userDefaults = NSUserDefaults.standardUserDefaults()
// save to user defaults
userDefaults.setObject(NSDate(), forKey: "LastExecutionDate")
Everytime your user come to home page and you want to fire json request, just compare it with last execution time. If its more than 2 mins fire it otherwise don't.
// retrieve from user defaults
let lastExecutionDate = userDefaults.objectForKey("LimitReachedOnDate") as? NSDate
Then your currentDate - lastExecutionDate > 180 seconds. This is just a algo and not the exact code for date comparison but i guess you will get it. this

How to measure how long specific actions take to complete for users in iOS app

I have an iOS app that I wrote with Swift with Firebase Auth/DB for the backend. I would like to measure how long it takes for a user to complete specific actions. I'm actually not interested in the response time, but am interested in the total time it takes to complete something.
In other words, I want to measure how long it takes to login, click on a button, receive a push notification, click "Ok" on that notification, etc. I also want to log how many seconds it took to get from one thing to the next (i.e. login time: 2.5 seconds, time to push a specific button: 4 seconds, etc.).
I am trying out Firebase Analytics, and it almost works, but not quite. I can log specific events, such as login, button presses, etc., but it just logs the that the event occurs, not how long it took.
Ideally, I would record all of this data on the specific users I give to try my app, so I could look at all the data, find averages and other useful information.
consider using a a Timer, maybe something like this.
import UIKit
class Whatever: UIViewController {
var timer = Timer()
var currentTime = 0.00
func timeCounter() {
currentTime += 0.01
}
override func viewDidLoad() {
timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(Whatever.timeCounter), userInfo: nil, repeats: true)
}
#IBAction func buttonPressed(_ sender: Any) {
timer.invalidate()
//whatever button does
}
}
This way when the application begins the timer will begin, when the final button is pushed the timer will stop. You will have the value of how long it took stored as currentTime.
Hope this helps!

Resources