App Delegate : Service call coming back from background - ios

I want to call a web service whenever application coming back in the foreground. I am calling it from didBecomeActive().
What's the best way to handle it and pass data to Root view controller?

Since the data you want to pass is always going to the same view controller you should instead set the observer in that view controller instead of app delegate. This way you won't need to pass any data in the first place.
class YourViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default
.addObserver(self, selector: #selector(activityHandler(_:)),
name: UIApplication.didBecomeActiveNotification, object: nil)
}
#objc func activityHandler(_ notification: Notification) {
//Call your web service here
}
}

You have two choices. Get rootViewController and pass the data, handle it.
func applicationDidBecomeActive(_ application: UIApplication) {
// 1
let rootVC1 = self.window?.rootViewController
// 2
let rooVC2 = application.windows.first?.rootViewController
...
/*
pass data to rootVC1 or rootVC2
*/
}

Related

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.

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.

Pass data from one view controller to the other using unwind segue with identifier

Here is my code:
I am making the user to be able to select image from phone and then want to jump back to the previous view controller passing this image file too..
#IBAction func unwindToThisViewController(segue: UIStoryboardSegue) {
if (segue.identifier == "unwindToThis") {
}
}
If your original view controller is still loaded in memory you can use an NSNotification via NSNotificationCenter.
In the view controller that needs the data, in viewDidLoad:
NSNotificationCenter.defaultCenter().addObserver(self, selector: "imageReceived:", name: "imageReceived", object: self.videoDeviceInput?.device)
Then add the deinit method to the same controller, this will remove the observer when the view controller is no longer loaded.
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self, name: "imageReceived", object: nil)
}
Still in the same class create a method to handle the notification:
func imageReceived(notification: NSNotification) {
if let image = notification.object as? UIImage {
// Do your image stuff here.
}
}
In the view controller where you create or get the data you need to pass back you can to trigger the notification with the data like so:
NSNotificationCenter.defaultCenter().postNotificationName("imageReceived", object: yourUIImage)

Pausing timer when app is in background state Swift

My ViewController.swift
func startTimer() {
timer = NSTimer().scheduleTimerWithTimerInvterval(1.0,target: self,selctor: Selector("couting"),userinfo: nil, repeats: true)
}
func pauseTimer() {
timer.invalidate()
println("pausing timer")
}
and this is appDelegate.swift
func applicateWillResignActive(application: UIApplication) {
viewController().pauseTimer()
println("closing app")
}
It is printing pausing timer and closing app but when I open again I see it never paused. How do I do it correctly?
You have to set an observer listening to when the application did enter background. Add the below line in your ViewController's viewDidLoad() method.
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("myObserverMethod:"), name:UIApplicationDidEnterBackgroundNotification, object: nil)
Add the below function to receive the notification.
func myObserverMethod(notification : NSNotification) {
println("Observer method called")
//You may call your action method here, when the application did enter background.
//ie., self.pauseTimer() in your case.
}
Happy Coding !!!
Updated Answer for Swift 5:
NotificationCenter.default.addObserver
(self,
selector: #selector(myObserverMethod),
name:UIApplication.didEnterBackgroundNotification, object: nil)
#objc func myObserverMethod() {
print("Write Your Code Here")
}
Accepted answer by #Suresh in Swift 3
Set an observer listening to when the application did enter background in your ViewController's viewDidLoad() method.
NotificationCenter.default.addObserver(self, selector: #selector(myObserverMethod), name:NSNotification.Name.UIApplicationDidEnterBackground, object: nil)
Add the below function to receive the notification.
func myObserverMethod(notification : NSNotification) {
print("Observer method called")
//You may call your action method here, when the application did enter background.
//ie., self.pauseTimer() in your case.
}
Updated Answer for Swift 4:
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(myObserverMethod), name:NSNotification.Name.UIApplicationDidEnterBackground, object: nil)
}
#objc func myObserverMethod() {
// Call your action method here, when the application did enter background.
}
You are creating a new object and calling pauseTimer() on it:
viewController().pauseTimer() // This line causes issue viewController(), creates a new instance
Instead of creating new object, either you should pass that instance to AppDelegate and call pauseTimer() on existing object or Listen for UIApplicationDidEnterBackgroundNotification notification in your view controller class and pause timer from there.

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