viewWillAppear is not being called after clicking the home button - ios

i have this view controller
class ViewController: UIViewController {
override func viewWillAppear(animated: Bool) {
let user = NSUserDefaults()
let mobileNumber = user.valueForKey("mobileNumber") as? String
if let mobileNumber = mobileNumber {
print("mobile number = \(mobileNumber)")
}else {
print("no mobile number")
}
}
#IBAction func makePhoneCall(sender: UIButton) {
if let phoneCall = phoneCall {
let user = NSUserDefaults()
user.setValue(phoneCall, forKey: "mobileNumber")
when the user clicks on a button, i save the mobileNumber in nsuserdefault.
then i click the button, then i open the app again, but problem is that when i open the app agian, i don't bet any message from the viewWillAppear even though i am printing in the if and in the else part.

tylersimko is correct that viewWillAppear(_:) is not called when the application enters the foreground and that event is instead captured by "application will enter background".
That said, you don't need to observe this from the app delegate but could instead use the UIApplicationWillEnterForegroundNotification notification:
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "applicationDidEnterForeground", name: UIApplicationWillEnterForegroundNotification, object: nil)
}
func applicationDidEnterForeground() {
// Update variable here.
}
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
The above code:
When your view loads, your view controller registers to have the function applicationDidEnterForeground() called whenever the application enters the foreground.
The function applicationDidEnterForeground() does whatever needs to be done.
The view controller unregisters from all notifications when it deallocates to avoid a zombie reference in iOS versions before 9.0.
Given that you are working with NSUserDefaults, you could instead consider observing NSUserDefaultsDidChangeNotification.

In AppDelegate.swift, make your change in applicationWillEnterForeground:
func applicationWillEnterForeground(application: UIApplication) {
// do something
}
Alternatively, if you want to keep your changes in the ViewController, you could set up a function and call it like this:
func applicationWillEnterForeground(application: UIApplication) {
ViewController.refreshView()
}

Related

How can I create an Instance of NSManagedObject in a NotesApp without a Button - Apple's NoteApp Style?

I started learning programming and I decided to try out my first Note Taking App.
My Goal is to create an App similar to the iPhone's NoteApp. Therefore, I wanted the note's title be set when the User writes in the TextView as the first line. Therefore, I created a NoteViewController, which contains a TextView and a NoteIndexViewController, which is a TableViewController, both embedded in a NavigationController.
I'm also using Core Data to store the data.
The problem is that I don't know how I can commit those changes to the DataBase without using a button. I know how to create an instance of the NSManagedObject - in NoteIndexViewController to create new notes in the TableView using a Button:
#IBAction func addNotePressed(_ sender: UIBarButtonItem) {
let newNoteIndex = NoteIndex(context: self.context)
newNoteIndex.name = "Temporal Name"
notesArray.append(newNoteIndex)
saveNoteIndex()
performSegue(withIdentifier: K.segueToNote, sender: self)
}
But I'm completely lost if I want to commit the changes without a "Save Button" to create the instance and also committing changes. This is the code I got so far. Notice that I did not set any Note() object.
class NoteViewController: UIViewController {
var noteArray = [Note]()
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
var selectedNote: NoteIndex? {
didSet {
loadData()
}
}
var firstLine: String?
#IBOutlet weak var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
loadData()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(true)
if !textView.text.isEmpty {
if let newLine = textView.text.firstIndex(of: "\n") {
let firstLetter = textView.text.startIndex
let lineBrake = textView.text.index(before: newLine)
let lettersTillPosition = textView.text.distance(from: firstLetter, to: lineBrake)
firstLine = (textView.text as NSString).substring(to: lettersTillPosition)
} else {
if textView.text.count >= 30{
firstLine = (textView.text as NSString).substring(to: 30)
} else {
firstLine = (textView.text as NSString).substring(to: textView.text.count)
}
}
selectedNote!.name = firstLine
saveCurrentNote()
}
}
//MARK: - Data Manipulation Methods
func saveCurrentNote() {
do {
try context.save()
} catch {
print("Error saving cateogry \(error)")
}
}
func loadData(with request: NSFetchRequest<Note> = Note.fetchRequest()) {
// goToIndex is the relationship between the IndexNote entity and Note. And when Back button is pressed the code tend also to break in this part.
request.predicate = NSPredicate(format: "goToIndex.name MATCHES %#", selectedNote!.name!)
do {
noteArray = try context.fetch(request)
} catch {
print("This is a load error: \(error)")
}
}
}
extension NoteViewController: UITextViewDelegate {
func textViewDidChange(_ textView: UITextView) {
saveCurrentNote()
}
}
Here is a possible solution for your question. You can use Notification Center to monitor if the user is interrupted and if so you can do a quick save.
Place these in the scene delegate
func sceneWillResignActive(_ scene: UIScene) {
let notificationName = NSNotification.Name(ReuseIdentifier.pause)
NotificationCenter.default.post(name: notificationName , object: nil)
}
func sceneDidDisconnect(_ scene: UIScene) {
let notificationName = NSNotification.Name(ReuseIdentifier.quit)
NotificationCenter.default.post(name: notificationName, object: nil)
}
Place something like this where the user data is being saved.
/// Monitors application state for major changes.
/// - Pause Observer: Adds observer that notifies application if application is no longer active (enters foreground).
/// - Quit Observer: Adds observer that notifies application if terminated.
private func checkForPauseOrQuit(){
NotificationCenter.default.addObserver(self,
selector: #selector(autoSave),
name: NSNotification.Name(ReuseIdentifier.pause),
object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(autoSave),
name: NSNotification.Name(ReuseIdentifier.quit),
object: nil)
}
And then for your selector method you create your NSManagedObject and capture whatever values the user may have started typing.
On startup you do the reverse, and make sure to erase the values. This should function only as a temporary holding container not your main entity. Check out my note application for reference:
https://github.com/victis23/AwesomeNote/tree/WorkingBranch/AwesomeNote

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()
}

how to call all methods and functions from view controller to app delegate?

Hi am developing a app using swift2 in my app i want to track location for every 5 minutes when user successfully logged in now i can successfully track location of the user now i want to track when the app is in backgrounded for that i have to use app delegate but all functions and methods are i wrote in view controllers only so how can i call view controllers method and function to app delegate.
code in viewController:
class ThirdViewController: UIViewController{
In view did load:
{
////// i created timer scheduled with time internal /////
}
////// Here am fetched current locations and performed some function for timer to execute //////
}
In my appDelegate:
func applicationDidEnterBackground(application: UIApplication) {
if UIApplication.sharedApplication().applicationState != .Active{
//here i have to call that timer function execute how to do that????
}
You can fire notification when app goes to background
func applicationDidEnterBackground(application: UIApplication) {
if UIApplication.sharedApplication().applicationState != .Active{
NSNotificationCenter.defaultCenter().postNotificationName("Notificationname", object: nil)
}
}
in your controller view did load dd observer for notification
// add observer for notification.
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.notificationReceived), name: "Notificationname", object: nil)
Method handler for received Notification
func notificationReceived() {
// do what you want to do here.
}
You would want to create a class with your function inside it and then call it from your app delegate, you can do this like this:
Create a swift file with the name of your class e.g. MyClass.swift
Then you create the class in your file and add the function in it:
class MyClassName {
Add your function in here
}
Then you call it from appDelegate.swift like this:
let myClassVar: MyClassName?
Then call the function like this -> myClassVar.functionName
Like this,
func applicationDidEnterBackground(application: UIApplication) {
if UIApplication.sharedApplication().applicationState != .Active{
let thirdViewController = ThirdViewController()
thirdViewController.functionYouWantToCall()
}
Thanks :)

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