swift ios - How to run function in ViewController from AppDelegate - ios

I am trying to run a function in certain ViewController using AppDelegate
func applicationDidBecomeActive(_ application: UIApplication) {
ViewController().grabData()
}
But somehow the function does not seem to run at all when the app has become active after entering the app from the background.
The function looks like this
func grabData() {
self._DATASERVICE_GET_STATS(completion: { (int) -> () in
if int == 0 {
print("Nothing")
} else {
print(int)
for (_, data) in self.userDataArray.enumerated() {
let number = Double(data["wage"]!)
let x = number!/3600
let z = Double(x * Double(int))
self.money += z
let y = Double(round(1000*self.money)/1000)
self.checkInButtonLabel.text = "\(y) KR"
}
self.startCounting()
self.workingStatus = 1
}
})
}
And uses this var
var money: Double = 0.000
What have I missed?
Thanks!

ViewController().grabData() will create a new instance of the ViewController and call this function. Then.. as the view controller is not in use it will be garbage collected/removed from memory. You need to be calling this method on the actual view controller that is in use. Not a new instance of it.
The best option would be to listen for the UIApplicationDidBecomeActive notification that iOS provides.
NotificationCenter.default.addObserver(
self,
selector: #selector(grabData),
name: NSNotification.Name.UIApplicationDidBecomeActive,
object: nil)
make sure that you also remove the observer, this is usually done in a deinit method
deinit() {
NotificationCenter.default.removeObserver(self)
}

I simply solved it like this:
func applicationDidBecomeActive(_ application: UIApplication) {
let viewController = self.window?.rootViewController as! ViewController
viewController.grabData()
}

Related

How to listen for change in variable inside a function

I have a class called Observers to observe Firebase Storage Upload Tasks, but before observing the progress, it waits for PHPickerviewcontroller to upload the video. I have an instance variable in my class, hasUploaded so that I can know when I can start to change the progress bar, however, with the way it's set up, the block of code in the if statement will never be called. I know there is didSet but that doesn't help me in this case, because I need to listen for change inside the function. How do I do that?
func observeProgress(progressWheel: UIActivityIndicatorView, errorLabel: UILabel, progressView: UIProgressView, progressLabel: UILabel)
{
progressLabel.text = "Downloading from Device...Please Wait (1/2)"
progressWheel.startAnimating()
progressWheel.alpha = 1
inProgress = true
//RIGHT HERE - Wait for hasUploaded to == true
if hasUploaded
{
progressWheel.alpha = 0
self.taskReference!.observe(.progress)
{ (snapshot) in
guard let progress = snapshot.progress?.fractionCompleted else { /**alert**/ return }
progressView.progress = Float(progress)
progressLabel.text = "\(round(100 * Float(progress)))% (2/2)"
if progress == 1
{
progressLabel.text = "Upload Successful!"
progressLabel.textColor = .black
progressView.progress = 0
}
}
}
}
I thought about it again and maybe it is easier for you to use the NotificationCenter.
In the Model or ViewController add
let nc = NotificationCenter.default
and thenadjust the hasUploaded variable to
var hasUploaded = false {
didSet {
let statusChange = ["userInfo": ["hasUploaded": hasUploaded]]
NotificationCenter.default
.post(name:
NSNotification.Name("com.user.hasUploaded"),
object: nil,
userInfo: statusChange)
}
}
In the controller with the function observeProgress, also add
let nc = NotificationCenter.default
Add the following to the viewDidLoad() function
NotificationCenter.default
.addObserver(self, selector:#selector(hasUploadedNotificationReceived(_:)),
name: NSNotification.Name ("com.user.hasUploaded"),
object: nil)
}
Finally create the function hasUploadedNotificationReceived() (which is called above whenever the notification will be received) to add the magic that should happen after the the change. For example:
#objc func hasUploadedNotificationReceived(_ notification: Notification){
let notification = notification.userInfo?["userInfo"] as? [String: Bool] ?? [:]
if (notification["hasUploaded"] as? Bool)! {
observeProgress(...) {
[...]
}
}
}
Please read also the documentation to figure out what options you have and what you can add or modify.
Beside this implementation, I also can imagine that the a Delegate as well as Combine and as #matt mentioned async/await could help to achieve your desired behavior.

NSNotification being fired off more then one time?

I am creating a sample player for a test project. I created a NSNotification to call a function to play the next audio track inside an array. The issue is the notification calls this function about 8 times in a row? I have no idea why this is occurring. Here is my code and thanks for the help!
let player = AVPlayer()
var urlPlayerItems = [String]()
var currentTrack: Int = 0
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Checks to see if player reached end
NotificationCenter.default.addObserver(self,
selector: #selector(PlayerViewController.autoplayNextTrack(notification:)),
name: NSNotification.Name.AVPlayerItemDidPlayToEndTime,
object: player.currentItem)
}
func playTrack() {
if urlPlayerItems.count > 0 {
let newMovieURL = URL(string: urlPlayerItems[currentTrack])!
asset = AVURLAsset(url: newMovieURL, options: nil)
player.play()
}
}
func autoplayNextTrack(notefication: NSNotification) {
if (currentTrack + 1) >= urlPlayerItems.count {
currentTrack = 0
} else {
currentTrack += 1
}
playTrack()
}
Aside from the fact that an observer shouldn't be set multiple times, i think that you should reset the player to zero right before calling the play function again
func autoplayNextTrack(notefication: NSNotification) {
player.seekToTime(kCMTimeZero)
if (currentTrack + 1) >= urlPlayerItems.count {
currentTrack = 0
} else {
currentTrack += 1
}
playTrack()
}
If you add your observers in viewDidAppear method, you need to make sure you only add them ONCE. the viewDidAppear methods will get called multiple times.
Easy way is just to make a BOOL and flag it when you have added/removed it .
EDIT:
Also, I don't see any method where you remove the observer anywhere in your code, make sure you remove it too when you want to stop observing.
Example:
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
or in your viewWillDissapear method.

UIApplicationSignificantTimeChange notification is not triggered

I want to use the UIApplicationSignificantTimeChange to check when the day has changed and I encapsulated in my own class so I can easy use it in more view controllers:
public final class DayChangedObserver {
private var token: NSObjectProtocol!
public init?(handler: #escaping () -> ()) {
token = NotificationCenter.default.addObserver(forName: NSNotification.Name.UIApplicationSignificantTimeChange, object: self, queue: nil) { _ in
handler()
}
}
deinit {
NotificationCenter.default.removeObserver(token)
}
}
And I call this code from my view controller:
override func viewDidLoad() {
super.viewDidLoad()
_ = DayChangedObserver() {
print("Day has changed")
}
}
I am testing this on my iPhone and I manually change the time. But it seems that it doesn't work using my class.
Is it something wrong with my implementation ? Because it was working when I was using this event in the past (without my own class implementation).
EDIT1:
I seems that deinit is called immediately after, so I am using an instance variable to keep a strong reference and now it's not deinit anymore, but still doesn't work.
object: self change to object: nil,try it.

viewWillAppear is not being called after clicking the home button

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

RxSwift subscribe block not called

I'm playing around with RxSwift and I'm stuck with a simple toy programm. My program essentially contains a model class and a viewcontroller. The model contains an observable that gets updated on the main queue after an asynchronous network call, the viewcontroller subscribes in viewDidLoad(). The AppDelegate initializes the model and passes it to ViewController and triggers the network request.
class GalleryModel {
var galleryCount: BehaviorSubject<Int>
init() {
galleryCount = BehaviorSubject.init(value:0)
}
func refresh() {
doAsyncRequestToAmazonWithCompletion { (response) -> AnyObject! in
var counter = 0
//process response
counter = 12
dispatch_async(dispatch_get_main_queue()) {
self.galleryCount.on(.Next(counter))
}
return nil
}
}
class ViewController: UIViewController {
#IBOutlet weak var label: UILabel!
var galleryModel: GalleryModel?
override func viewDidLoad() {
super.viewDidLoad()
galleryModel?.galleryCount.subscribe { e in
if let gc = e.element {
self.label.text = String(gc)
}
}
}
}
class AppDelegate: UIResponder, UIApplicationDelegate {
var galleryModel: GalleryModel?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
//do amazon setup
galleryModel = GalleryModel()
if let viewController = window?.rootViewController as? ViewController {
viewController.galleryModel = GalleryModel()
}
return true
}
func applicationDidBecomeActive(application: UIApplication) {
galleryModel?.refresh()
}
The label gets updated only one, it shows "0". I expected the label to get updated twice, showing "0" after the first update and showing "12" after the second update after the processing of the network request. A breakpoint in the dispatch_async block gets hit, but it seems that galleryCount lost its observer. Anybody any idea what's happening or how to debug this?
Best
In case reads this anyone is interested. It was an refactoring error, after renaming variables I stopped passing the observable to the ViewController. Instead I created a new one... facepalm
Here are some useful snippets for subscribe in RxSwift (in Japanese)
For example to subscribe to different events:
let source: Observable<Int> = create { (observer: ObserverOf<Int>) in
sendNext(observer, 42)
sendCompleted(observer)
return AnonymousDisposable {
print("disposed")
}
}
let subscription = source.subscribe { (event: Event<Int>) -> Void in
switch event {
case .Next(let element):
print("Next: \(element)")
case .Completed:
print("Completed")
case .Error(let error):
print("Error: \(error)")
}
}
Clean and Build solved the problems for me

Resources