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

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.

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.

In iOS11, how to keep a background task running past 10 min?

My question involves keeping an app that monitors user interactions in the background, for example time spent in one or the app. The issue arises when you can not have a background process run for more than 10 min or violate Apple's sandbox restrictions. Since I am relatively new to the Apple API, and could not find a direct answer that didn't involve location services or VOIP (which are both interesting options, but my application will not be able to use either viably), I come to ask for options in which I can understand when another app opens, when it closes, when it fetches data, and when user holds phone in certain orientation (ie when people hold their phone at certain angles to read text and etc.) for certain amount of time.
The purpose of this analyzation is to predict an attention span for the user, so I need to be able to run in the background, as the user will not be using my app while it predicts attention span.
My thoughts on this are possibly accessing the system log, and somehow parse previous statements (I don't think sandbox will allow), but inevitably the iOS system will suspend my processes after some point unless I put a timer. There is also the option of having the system wake up my app via opportunistic fetching, but that won't be useful if I don't collect the data.
Keep in mind this is within IOS 11, so it is much more restrictive than previous iterations. I understand this may seem like a complex problem, but even a direction in which to head could be useful to me.
This solution might work, (not recommended since it drains the battery quicker).
Just update your current location, every 10 mins. It will reset the background thread timer.

iOS: Is it necessary to remove Firebase observers?

Why should I remove firebase references observers? I'm ok if all references get observed as long as the app is running. I'm not sure if references will still be observed after the user leaves the app, but if memory is only consumed while the user is using the app, why bother remove the observers? Especially if the data is mostly light weight texts.
I've looked into firebase documentation and there is no clear info about it.
There are several snippets saying that is a good practice to remove them on the opposite method you created them.
If you create them on onCreate, remove them on onDestroy. If you created them on viewWillAppear, remove them on viewWillDisappear.
In my experience I found out that is less cpu demanding to just leave them around if they are not firing very often. I have several observers on my app it's less cpu intensive to just quit the app than to remove them.
The case is not what can happen if the user leaves the app, after it's closed/killed it'll not be observed anymore. Neither is a problem because of CPU or internet usage, since you'll get light weight texts. The case is what can happen if the user is using the app and an observable is fired.
Let's say that in your app's home page you'll have an observable sending the user to a specific page if a value on the user's node changes, and if in the middle of the usage this value changes the user may be redirected to another page without knowing what happened, so bad UX.
Something that happened to me was leaving observables and them i signed out of my account, my app broke because there was no user connected, therefore i wasn't able to observe a node where i needed to be authenticated to query a value.
So that's why you should remove every observable as you go off a page or when quiting the app or signing out, because it can give the user some errors or uncommom behaviours that'll make them think that the app is broken or is not a good app.
Hope this helps :D

Where to put code that gets checked frequently?

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.

GameKit Turn-based listener is not reliably called when matchData changes

I am using Game Center turn-based matches for my card game. It feels like a good fit because people want to check their email or write a text sometimes between turns. Some users have said that they do really want to be able to have a very asynchronous playing experience.
For the people that are keeping the game open between their turns, I want to update the screen to reflect things that the other players are doing on their turns. I have set up a listener on GKLocalPlayer that responds to player:receivedTurnEventForMatch:didBecomeActive. The documentation says that this will get called when match data is saved by another player even if it doesn't become the player's turn (the player on the current device). That doesn't appear to be true 100% of the time. In fact, it appears that it only gets called about 1 in 3 times that match data is saved by other players. It seems more reliable when it becomes the player's turn, but even that isn't 100% reliable.
I am using saveCurrentTurnWithMatchData:completionHandler: on GKTurnBasedMatch to save match data that doesn't end the current player's turn and I'm calling endTurnWithNextParticipants:turnTimeout:matchData:completionHandler: on GKTurnBasedMatch to save the data when it does end the current player's turn. There are a few scenarios where I want to call saveCurrentTurn… with updated matchData. In my game, you can have computers playing in your multiplayer game as well. So, a human player may play a card and then a computer may play a card before that GKPlayer's turn ends. There are also scenarios where an individual player may play twice. (eg. A player plays the last card on a trick. That player takes the trick and gets to lead the next trick.)
I have set up a ton of logging around this and I can see clear scenarios where one device calls saveCurrentTurn… and the completionHandler is called without an error and the other device doesn't get notified with a call to player:receivedTurnEvent… I have also added logging to verify that each time I'm calling saveCurrentTurn… that I'm calling it with new matchData. I'm not making redundant calls.
If I go to the device that didn't get the updated matchData and force it to load the matchData for the match again, it gets the updated data. So, it's definitely getting saved.
I have tried throttling the calls to saveCurrentTurn… so that they don't happen in immediate succession and that didn't help.
Both devices in my testing are running iOS 8.4. There appears to have been an issue in iOS 8.3 that is fixed now (see this question). This Apple forum post also reports this issue 2 years ago and it appears that bug reports were filed and marked fixed.
Has anyone else seen this? I would love to know that I'm doing something wrong. Any ideas are very welcome.
I do something similar. In my game, each player has multiple pieces, saving the match when each piece moves so that other players--if they're in the game--can watch what's happening in real time. Like you describe, the Game Center messaging is almost completely useless.
As you referenced, in 8.3, the "end of turn" messages were completely broken. As of 8.4, they happen most, but not all, of the time. As you're seeing, the "match has been saved" notifications are also erratic. Here are some tips I've used to increase the success rate:
Slow down the saves. If you save too fast, only the last one arrives at the recipient. I set up an NSArray queue, and each time I want to save the match, I add the new matchData to that queue. I have a timer loop running that does the actual saveCurrentTurnWithMatchData, pops the item off the stack if the save was successful, and then sets up a new timer to call itself again a little later. I'm using 2 second intervals which seems to be working well.
Append each new piece of data, don't overwrite. Put a sequence number on each piece of data. So, if you save seq numbers 1, 2, 3 and 4, but the recipient only receives a notice for #4, the records for 1, 2 and 3 are there in the match object. The recipient needs to track the last record it read, and then iterate through any new records from that point when it receives an updated matchData.
I also use the queue's NSArray writeToFile: function to maintain a list of the pending saves. If the user exits the game before the queue is flushed, I reload the queue NSArray from disk at the next startup
Note that even with this mechanism, the notifications to the recipient are erratic. Generally speaking, they arrive in batches of 4+. Then nothing happens until 3 or 4 more saves happen, which again all show up together. Making 1 save and letting the game sit for 10 minutes will probably never generate a notice on the recipient's machine. But, if you save 4 or 6 times in a row, all of them tend to show up in a burst.
Sometimes, the notifications just stop for a few hours. Not sure if this a sandbox flaw or a game-center-in-general flaw. There are no failures of any kind, the messages just stop working for a while. Sometimes, the next morning, they show up in a burst. Sometimes not. In the end, I've stopped relying on the notifications. I set up another timer loop to continuously download the match. It checks if it has become my turn or not, it checks if new updates have been added to the matchData. Then calls player:receivedTurnEventForMatch:didBecomeActive. As far a receivedTurnEventForMatch: knows, it was launched because of an event and it merrily goes on about its business.
It does seem that saving the match is pretty prompt. If you don't get an error, it seems pretty certain that the updated match is immediately available for other players to consume... they just need to know to consume it. The messaging framework, though, has to be viewed as completely unreliable and non-guaranteed. Hence, the timer loop to continuously poll the match.
Edit: arguably, once I implemented #2, #1 shouldn't really matter. Any notification received by the recipient will trigger reading all new records in the data. But, this "hardening" has evolved over the past few months as I wrestle with Game Center's shortcomings. I just haven't gotten around to removing #1.

Resources