Where to put code that gets checked frequently? - ios

I have some code that needs to get called frequently, such as check what day it is, if it's the next day then move the day strings in the tableView.
Now I thought that the viewDidLoad would get called all the time and so it would be 'fine' to put it in there. However, I've left the simulator overnight, and I've pressed the home button and clicked again, changed VCs etc. and viewDidLoad hasn't been hit.
What are my options for doing sporadic checks such as, is it a new day? As x happened etc.

In this specific case, you can subscribe to NSCalendarDayChangedNotification to be notified when the date changes and respond accordingly in your view controller. In general, didBecomeActive or viewDidAppear would likely work.

What are my options for doing sporadic checks such as, is it a new day
It depends what the meaning of "is" is! In particular, "is" when? You say "sporadic", but that's just fluff. When do you need to know this? To what stimulus do you want to respond? When the user opens your app? Then put it in applicationDidBecomeActive. Every day at noon? Then run an NSTimer. Really, the problem here is that you don't seem to know, yourself, just when you need to perform these checks.

Whilst in your app, its quite easy to continually check for something. You simply create a background thread. However, what you describe is a thread that persists from outside the app's lifecycle.
Have a read on this documentation provided by Apple itself. You have to have good excuse to put a background thread. The scope of such thread is limited to only certain scenarios such as downloading background stuff, playing sounds etc.
For your scenario, I'd look at applicationDidBecomeActive(_:) found in your Application Delegate. There you can mimic such continual check. Beware however, don't put heavy word load on start up or your app might be killed automatically if it fails to become active in reasonable amount of time.

Related

What would be a good way to handle "snoozed" functions?

I've tried a few solutions so far but I'm not sure if there is a "right" way. I'm working on a todo list with a snooze function. Each task has a "wake from snooze" time when I need to run a function to move it into a different list.
I've tried looping through each task and moving it when viewWillAppear and/or viewDidAppear runs. This generally works but seems to misfire occasionally and wouldn't happen while the app is running and the user isn't switching views.
I've tried using the Timer() function to move items at the actual specific times which only works while the app is running.
I've tried moving the items in the background using performFetchWithCompletionHandler and checking every hour.
Anything else I should try to make this run more smoothly? Is it a combination of all of these? If that's the case, how would I make sure that I'm not trying to move items twice at the same time?
In general the question seems to be how to ensure that something happens at a certain date-time.
Work out what the target date-time is and write it down somewhere stable (a file, user defaults, whatever) along with what has to happen then.
Watch the clock. A timer that fires every minute will probably be good enough; just look at the clock and see if that time has passed. If it has, do the thing and erase the info saying that this thing needs to happen at this date-time.
If the app goes into the background, stop the timer.
When the app comes to the foreground again, check your date-time.
If the time has passed, do the thing and erase the info saying that this thing needs to happen at this date-time.
If not, start the timer again and keep watching the clock.

Is it mandatory to call NSUserDefaults synchronize method?

Please, anyone, help me: Is calling NSUserDefaults's synchronize() method mandatory?. If I don't call it, what will happen? My application is working fine without it.
No.
Since iOS12, it isn't mandatory anymore.
Apple says:
This method is unnecessary and shouldn't be used.
You can find more information on iOS12 release note:
UserDefaults
NSUserDefaults has several bug fixes and improvements:
Removed synchronization requirements. It's no longer necessary to use synchronize, CFPreferencesAppSynchronize, or CFPreferencesSynchronize. These methods will be deprecated in a future version of the OS.
Now that you don't need to call these synchronization methods, the performance characteristics of NSUserDefaults and Preferences Utilities are slightly different: The time taken for enqueueing write operations is now paid by the writing thread, rather than by the next thread to call synchronize or do a read operation.
From the docs:
Because this method is automatically invoked at periodic intervals, use this method only if you cannot wait for the automatic synchronization (for example, if your application is about to exit) or if you want to update the user defaults to what is on disk even though you have not made any changes.
Meaning that if you kill the app right after something is written to the defaults without the periodic interval catching it, it will get lost. You probably did not kill the app right after a write event yet which is why your app seems to work fine so far.
Normally it works perfectly fine and you only have to use it in special cases, for example when the app will close directly after writing to NSUserDefaults. So you can simply add the synchronize method to the corresponding AppDelegate-method.
As others have said, you don't normally have to call synchronize at all.
Normally the system calls it for you so your defaults changes get written.
However, when working with Xcode it's pretty common to terminate your app by pressing command period or clicking the stop button. In that case it terminates the app without warning and your user defaults changes will quite likely not be written out, and will be lost.
This can be a good thing or a bad thing, depending on what you want. It's certainly confusing.
You can "fix" it by calling synchronize each time you make a change, or on some time interval. However that does slow your app down and increase it's power requirements (both by very small amounts.) If you are in a loop, writing changes to 10,000 server or Core Data records, and you change user defaults after each pass, then calling synchronize after each one might have a measurable effect on app speed and on battery life. In most cases you're not likely to notice the difference however.

iOS UIATarget detect when UIViewController is loaded after a tap()?

I am writing automation testing for my iOS app and I am trying to figure out how to detect in the javascript script when a view controller fully loaded and is on screen...
So right now for example the script taps on a button:
target.frontMostApp().mainWindow().buttons()["loginButton"].tap();
Then, once the app logs in (which could take a few seconds or less) I need to press another button.
Right now, I made it work by simply putting a delay:
target.delay(3);
But I want to be able to detect when the next view controller is loaded so that I know I can access the elements on the new screen just loaded.
Any suggestions?
There are some ways to achieve that:
Tune Up Library
https://github.com/alexvollmer/tuneup_js
I would really recommend to check this out. They have some useful wrapper function to write more advanced test. Some methods is written to extend the UIAElement class. They have waitUntilVisible() method.
But there might be possibility that the element itself is nil. Its not the matter of visibility but its just not in the screen yet.
UIATargetInstance.pushTimeout(delay) and UIATargetInstance.popTimeout()
Apple documentation says :
To provide some flexibility in such cases and to give you finer
control over timing, UI Automation provides for a timeout period, a
period during which it will repeatedly attempt to perform the
specified action before failing. If the action completes during the
timeout period, that line of code returns, and your script can
proceed. If the action doesn’t complete during the timeout period, an
exception is thrown. The default timeout period is five seconds, but
your script can change that at any time.
You can also use UIATarget.delay(delay); function, but this delays the execution of the next statement, not nescessarily waiting to find the element. setTimeout(delay) can also be used to globally set the timeout before UIAutomation decides it could not find the element.
There is Apple's guide explaining more understanding towards the framework here. http://developer.apple.com/library/IOS/#documentation/DeveloperTools/Conceptual/InstrumentsUserGuide/UsingtheAutomationInstrument/UsingtheAutomationInstrument.html
In most cases simply identifying an element specific to the next view controller should be sufficient. The description of the element must be something unique to the new UI however.
i.e.
target.frontmostApp().mainWindo().navigationBar()["My Next View"];
The script should delay until the element can be identified or the timeout has been reached (see pushTimeout popTimout).
In some cases it might be necessary to specify that the element is visible.
.withValueForKey(1, "isVisible")
It's also possible to use waitForInvalid to do the reverse (wait for something in the previous UI to go away).
Search for tuneup library here
Refer function waitUntilVisible for the same. you may include other files to for supporting functions in it.
Hope it help.

how to use GANTracker for long running iOS apps, including background audio

My App is typically run overnight as a baby monitor, either as foreground app, or with background audio running.
Goals:
Track total app startups ie. active user count.
Track total usage time in foreground vs background and total session time.
Track various page-views if they navigate the settings screens.
As recommended, I start the tracker in didFinishLaunchingWithOptions, and track my first ViewController as my first 'page-view'. My App might stay on this page then for the next 8 hours...
A couple of issues then appear:
When do I call stopTracker and what does it do? I'm hoping that it terminates the tracking session. But since google kindly hid their code in a static lib, I have no idea what's going on under the covers, and the .h doesn't say much. First instinct is to put stopTracker in applicationWillResignActive however, if the user decides to enable background audio my app is still running...
Next I read that a session can timeout after 30mins with no new pageviews, or at midnight. I could set a repeating timer to send the same page-view every 20mins, that should keep my session alive, at least until midnight, but then my page views are going to be much larger? unless it's smart enough to know I'm on the same page with every call. google analytics blog
[Update: each call seems to be counted as a new pageview, and numbers are thus skewed, so still an issue how to handle this]
If my timer above runs past midnight and the session has expired, I'm going to end up with a new session and double the actual active user count?
If I do call stopTracker in applicationWillResignActive, will the next call to track a page-view restart the tracker? or do I need to call startTrackerWithAccountID again?
If instead I start the tracker in applicationDidBecomeActive, I lose the session that might have been running in the background.
[update: this seems to be the best approach so far, but testing is very slow due to time lag on analytics reports, I will report back soon]
PS EasyTracker doesn't seem to handle this any better.
I got this working by using a pageview called 'Backgrounded', and when the user has selected no background functionality, then instead the app is calling stopTracker. I see multiple hits, with an average session of 20mins, but i can multiple pageview by time to see total time for goal 2. I found two solutions for goal one, events (which were not exposed in easy tracker), and also in my applicationDidBecomeActive (if it's not a restore of backgrounded app) then i track a pageview for AppStarted. I ended up wrapping the whole thing in a utility class and rolled it into a couple of my apps, so will be interesting too see the results. If anyone else tries this, you might want to think about using the custom variables too. I added my app version to this, so I can also monitor how many users are migrating to the latest app releases.

What actions are required when your app get an applicationWillResignActive call?

This question io3->ios4 upgrade said to support applicationWillResignActive. While implementing this call, I also implemented applicationDidEnterBackground and applicationWillEnterForeground. However, I found that my app would crash. After some debugging on the simulator I determined that I needed to reinitialize a key data structure in applicationWillEnterForeground. So my question is how would I have known that from reading the documentation? (In fact, I may be doing the wrong thing and just so happened to get it working again.) Is there an exact description of what to do when these methods are called?
Thanks.
The only things you should do when supporting multitasking, is saving the state of your app when it enters the background, and reload it when it becomes active. (If you generate a new template in Xcode, you'll see this.)
Saving state means writing any user preferences or data to disk. Reloading the state involves reading saved preferences and data, recreating any in memory data structures that might need it (like in your example you gave).
In most circumstances, there's little else you need to do. The only thing that would crash your app that is unique to multitasking would be trying to run code in the background for longer than the allotted amount of time, (which is 10 minutes.) Otherwise, it sounds like you have got other problems with your code.

Resources