Change tabBar badge when an app is in background mode (swift ios) - ios

I know that if I register UIApplicationDelegate in UNUserNotificationCenter and an app in foreground mode I will change TabBarItem.badgeValue easily. I only need to insert a piece of code in func userNotificationCenter(...)
But how to do the same thing when an app is in background mode? (like WhatsApp does)

Simple answer...you don't. UI changes must be on the main thread. Instead, I would suggest you register for the UIApplicationDidBecomeActive notification in NotificationCenter and update the badge value there.
(I use UserDefaults in the code below out of convenience and just for example. I might recommend using another method of storage based on how you're storing state in your app.)
Code while in background:
let badgeValue = 100
UserDefaults.standard.set(badgeValue, forKey: "badgeValue")
In your view controller:
class MyViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//set it here for when view controller is loaded
let badgeValue = UserDefaults.standard.string(forKey: "badgeValue")
self.tabBarItem.badgeValue = badgeValue
NotificationCenter.default.addObserver(forName: Notification.Name.UIApplicationDidBecomeActive, object: nil, queue: OperationQueue.main) { [weak self] (notification) in
//set it here for when you come back from the background
let badgeValue = UserDefaults.standard.string(forKey: "badgeValue")
self?.tabBarItem.badgeValue = badgeValue
}
}
}

Related

Apple PDFKit Notifications not working properly

I try to get notifications as soon as the currently visible pages in my pdfView change.
Actually this should work according to the documentation via .PDFViewVisiblePagesChanged.
However, I only get a notification the very first time when the pdfView didLoad.
As soon as I then scroll (and thus the visible pages change) I do not receive a single notification. The funny thing is that the notification .PDFViewPageChanged works normally (but in this case it is not enough). The same behaviour is also with .PDFViewScaleChanged: here I never get a notification when zooming inside the pdfView.
Code Snippet from my ViewController:
ovverride func viewDidLoad() {
//...
NotificationCenter.default.addObserver(self, selector: #selector(onDidVisiblePagesChanged(_:)), name: .PDFViewPageChanged, object: nil)
//...
}
#objc func onDidVisiblePagesChanged(notification:Notification) {
print("visible Pages changed!")
}
The problem is located in your function "onDidVisiblePagesChanged, you're waiting for a parameter type notification, you need to cast the parameter inside your function
#objc func onDidVisiblePagesChanged(_ sender:Any) {
if let notification = sender as? Notification {
print("visible Pages changed!")
print(notification)
}
}

Change UILabel text from appdelegate

I have a UIViewcontroller let's say "DialerViewController" which has a UILabel
#IBOutlet weak var statusText: UILabel!
,
which has a default value of "pending", how can I change the value of statusText using an app delegate, let's assume the app delegate downloads a text from the server and needs to update the statusText after completion.
I am new to swift development, what is the best way to go around this?
If the DialerViewController is the only view controller in your app you can address it like this...
(window?.rootViewController as? DialerViewController)?.statusText?.text = "YOURTEXT"
Another option would be to make the DialerViewController instance observe some specific notification and post this notification in the app delegate when the text was downloaded from the server.
// create an extension for your own notification
extension Notification.Name {
static let textWasDownloadedNotification = Notification.Name("textWasDownloadedNotification")
}
class DialerViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// make your dialer view controller listen to your new notification
NotificationCenter.default.addObserver(self, selector: #selector(updateLabel), name: .textWasDownloadedNotification, object: nil)
}
// function that gets called when a notification is received
#objc func updateLabel(_ notification: Notification) {
// get the new text from the notification's `userInfo` dictionary
let text = notification.userInfo?["text"] as? String
// update the label
statusText.text = text
}
}
// somewhere in your app delegate...
// prepare the `userInfo` dictionary with the information that is needed
let userInfo = ["text": "This is the new text."]
// post the notification
NotificationCenter.default.post(name: .textWasDownloadedNotification,
object: nil,
userInfo: userInfo)
See https://developer.apple.com/documentation/foundation/notificationcenter.
I think you can't do that because AppDelegate methods are called at specifics state of your application and the one that could be good is this one :
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool
But when it's called, your viewController isn't yet loaded.
Loading from network it’s not AppDelegate responsibility, add new network service class and inject it to the view controller. Just get knowledges about layers architecture, solid. It’s very powerful for new devs, good luck.

Swift iOS -Which viewController lifecycle event to use to send data to Firebase after a view changes

I have some information to send to Firebase. The thing is I want to send the data but I also have to pull the data from there first. The data I get is based on the users input.
I'm already making several nested async calls to Firebase. Not only do i have to wait for the calls to finish to make sure the data has been set but I don't want to have the user waiting around unnecessarily when they can leave the scene and the data can be pulled and changed in a background task.
I was thinking about using a NSNotification after the performSegueWithIdentifier is triggered. The observer for the notification would be inside viewWillDisappear.
Is this safe to do and if not what's the best way to go about it?
Code:
var ref: FIRDatabaseReference!
let uid = FIRAuth.auth()?.currentUser?.uid
let activityIndicator = UIActivityIndicatorView()
override func viewDidLoad() {
super.viewDidLoad()
self.ref = FIRDatabase.database().reference().child(self.uid!)
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(fetchSomeValueFromFBThenUpdateAndResendAnotherValue), name: "FbFetchAndSend", object: nil)
}
#IBAction func buttonPressed(sender: UIButton) {
activityIndicator.startAnimating()
levelTwoRef //send levelTwo data to FB run 1st callback
scoreRef //send score data to FB run 2nd callback
powerRef //send power data to FB run 3rd callback
lifeRef //send life data to FB run Last callback for dispatch_async...
dispatch_async(dispatch_get_main_queue()){
activityIndicator.stopAnimating()
performSegueWithIdentifier....
//Notifier fires after performSegue???
NSNotificationCenter.defaultCenter().postNotificationName("FbFetchAndSend", object: nil)
}
}
func fetchSomeValueFromFBThenUpdateAndResendAnotherValue(){
let paymentRef = ref.child("paymentNode")
paymentRef?.observeSingleEventOfType(.Value, withBlock: {
(snapshot) in
if snapshot.exists(){
if let dict = snapshot.value as? [String:AnyObject]{
let paymentAmount = dict["paymentAmount"] as? String
let updatePayment = [String:AnyObject]()
updatePayment.updateValue(paymentAmount, forKey: "paymentMade")
let updateRef = self.ref.child("updatedNode")
updateRef?.updateChildValues(updatePayments)
}
You are adding the observer in viewWillDisappear, So it won't get fired because it won't be present when your segue is performed.
Add the observer in viewDidLoad and it will work.
But if you just want to call fetchSomeValueFromFBThenUpdateAndResendAnotherValue() when the view is disappearing then there is no need for observer.
Simply call the method on viewWillDisappear like this -
override func viewWillDisappear(animated: Bool)
{
super.viewWillDisappear(animated)
fetchSomeValueFromFBThenUpdateAndResendAnotherValue()
}

AVSpeechSynthsesizer on timer

If a switch is turned on, text-to-word spoken every x seconds. The switch is on the first view controller, and the speech occurs after a segue to the second view controller.
Code in the first view controller:
#IBAction func speakwords(sender: AnyObject) {
NSNotificationCenter.defaultCenter().postNotificationName("speaknotif", object: speakwords)
Code in the second view controller:
verride func viewDidLoad() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("talk:"), name: "speaknotif", object: self.view.window)
func talk(notification: NSNotification){guard let count = notification.object else {return}
if Bool(TYPE_BOOL as! NSO) = "true"{
let speechsynth = AVSpeechSynthesizer()}
In your case, Notification Center was useless. Because you are calling post method before adding observer for that notification. So notification concept won't work there.
Instead of this, just set one Bool like "isSwitchSelected". And pass that value to next vc, check if the value is yes, then call func talk method.

Triggering a specific action when the app enters foreground from a local notification in iOS? (using swift)

I am building an iOS app using the new language Swift. Now it is an HTML5 app, that displays HTML content using the UIWebView. The app has local notifications, and what i want to do is trigger a specific javascript method in the UIWebView when the app enters foreground by clicking (touching) the local notification.
I have had a look at this question, but it does not seem to solve my problem. I have also come across this question which tells me about using UIApplicationState, which is good as that would help me know the the app enters foreground from a notification. But when the app resumes and how do i invoke a method in the viewController of the view that gets displayed when the app resumes?
What i would like to do is get an instance of my ViewController and set a property in it to true. Something as follows
class FirstViewController: UIViewController,UIWebViewDelegate {
var execute:Bool = false;
#IBOutlet var tasksView: UIWebView!
}
And in my AppDelegate i have the method
func applicationWillEnterForeground(application: UIApplication!) {
let viewController = self.window!.rootViewController;
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
var setViewController = mainStoryboard.instantiateViewControllerWithIdentifier("FirstView") as FirstViewController
setViewController.execute = true;
}
so what i would like to do is when the app enters foreground again, i want to look at the execute variable and run the method as follows,
if execute{
tasksView.stringByEvaluatingJavaScriptFromString("document.getElementById('sample').click()");
}
Where should i put the code for the logic to trigger the javascript from the webview? would it be on viewDidLoad method, or one of the webView delegate methods? i have tried to put that code in the viewDidLoad method but the value of the boolean execute is set to its initial value and not the value set in the delegate when the app enters foreground.
If I want a view controller to be notified when the app is brought back to the foreground, I might just register for the UIApplication.willEnterForegroundNotification notification (bypassing the app delegate method entirely):
class ViewController: UIViewController {
private var observer: NSObjectProtocol?
override func viewDidLoad() {
super.viewDidLoad()
observer = NotificationCenter.default.addObserver(forName: UIApplication.willEnterForegroundNotification, object: nil, queue: .main) { [unowned self] notification in
// do whatever you want when the app is brought back to the foreground
}
}
deinit {
if let observer = observer {
NotificationCenter.default.removeObserver(observer)
}
}
}
Note, in the completion closure, I include [unowned self] to avoid strong reference cycle that prevents the view controller from being deallocated if you happen to reference self inside the block (which you presumably will need to do if you're going to be updating a class variable or do practically anything interesting).
Also note that I remove the observer even though a casual reading of the removeObserver documentation might lead one to conclude is unnecessary:
If your app targets iOS 9.0 and later or macOS 10.11 and later, you don't need to unregister an observer in its dealloc method.
But, when using this block-based rendition, you really do need to remove the notification center observer. As the documentation for addObserver(forName:object:queue:using:) says:
To unregister observations, you pass the object returned by this method to removeObserver(_:). You must invoke removeObserver(_:) or removeObserver(_:name:object:) before any object specified by addObserver(forName:object:queue:using:) is deallocated.
I like to use the Publisher initializer of NotificationCenter. Using that you can subscribe to any NSNotification using Combine.
import UIKit
import Combine
class MyFunkyViewController: UIViewController {
/// The cancel bag containing all the subscriptions.
private var cancelBag: Set<AnyCancellable> = []
override func viewDidLoad() {
super.viewDidLoad()
addSubscribers()
}
/// Adds all the subscribers.
private func addSubscribers() {
NotificationCenter
.Publisher(center: .default,
name: UIApplication.willEnterForegroundNotification)
.sink { [weak self] _ in
self?.doSomething()
}
.store(in: &cancelBag)
}
/// Called when entering foreground.
private func doSomething() {
print("Hello foreground!")
}
}
Add Below Code in ViewController
override func viewDidLoad() {
super.viewDidLoad()
let notificationCenter = NotificationCenter.default
notificationCenter.addObserver(self, selector:#selector(appMovedToForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
}
#objc func appMovedToForeground() {
print("App moved to foreground!")
}
In Swift 3, it replaces and generates the following.
override func viewDidLoad() {
super.viewDidLoad()
foregroundNotification = NotificationCenter.default.addObserver(forName:
NSNotification.Name.UIApplicationWillEnterForeground, object: nil, queue: OperationQueue.main) {
[unowned self] notification in
// do whatever you want when the app is brought back to the foreground
}

Resources