I'm transitioning from Google Analytics to Firebase Analytics. Unlike Google Analytics, Firebase automatically tracks screen views, which is great! But, instead of tracking the screen, it continuously attempts to track the UINavigationController. I get the following error log twice every time I navigate to a different view controller.
[Firebase/Analytics][I-ACS031006] View controller already
tracked. Class, ID: UINavigationController, -1770652405567491888
Is there some configuration required when you have a navigation controller? How do I get automatic screen tracking working in this scenario?
UPDATE: I haven't found a solution to this yet, but I at least found the cause of the problem. It looks like Firebase doesn't understand your view controller hierarchy if your initial view controller is a Tab Bar Controller. My initial view controller in my main story board is a Tab Bar Controller. If I take this out, I get good screen tracking reporting from my app.
UPDATE: It looks like I've found an OK solution to this, but I'm still wondering if someone has a better idea. Since Firebase sees all of the view controllers under my Tab Bar Controller as the same UINavigationController, I can call setScreenName manually in viewDidAppear for all of them.
Analytics.setScreenName(screenName, screenClass: screenClass)
This is OK because it's not any worse than Google Analyics, but it's not ideal because the system still tries to track the UINavigationController twice for every view controller and I'm also not getting the benefit of automatic screen tracking. I looked into trying to remove the Tab Bar Controller from Firebase as some folks seem to have done, but it looks like those methods have been removed from the current (v4.0.0) version of the Firebase SDK.
Screen is automatically tracked in Google Analytics for Firebase. You can see it in the user engagement card in the Main dashboard. This has been introduced in Firebase Version 3.8.0 for iOS. More in track screen doc. Quoting from the doc:
Events that occur on these screens are automatically tagged with the parameter firebase_screen_class (for example, menuViewController or MenuActivity) and a generated firebase_screen_id. If your app uses a distinct UIViewController or Activity for each screen, Analytics can automatically track every screen transition and generate a report of user engagement broken down by screen.
However, you can also track screens manually using the setCurrentScreen() method. The details on this screen should be available in the user engagement card when you select the ScreenName from the dropdown, all the screens manually tracked in the code should be displayed there with the breakdown of duration on average.
Please note that the setScreenName is not an event, it is rather an event parameter that goes with the event that is tracked in the logEvent() method call.
Related
For example, I have a Notification that occurs when there is an "unauthorized" error returned from the api server that should bring the user back to the login screen. This event could appear anywhere in the program.
Do I create a new viewless controller to manage states and have it monitor for this notification and then present the login view controller?
I'm thinking about push notifications too, received by the app delegate. Some of these may cause the data model to get updated and then the screen to change. Who should make the screen change? In the past I put all this in the AppDelegate. But there must be a more elegant way!
I also found out about FlowControllers. But they don't play nicely with Interface Builder, at least according to this solution.
Please let me know if you need more specific information about my project.
What I'm looking for is a mechanism to keep my app from resetting to the root view controller every time the application gets backgrounded or goes inactive. There are many apps out there that operate this way, namely Instagram, eBay, etc.
My instincts told me initially to poke into the AppDelegate's applicationWillEnterForeground method, where I would try to present the viewcontroller I'm after, however when I instantiate the viewController, I can present it, but without the navigation controller that normally be there.
This makes me think that I need to save the "state" of the application (maybe the NavigationController's stack?) and then restore the stack somehow when it gets relaunched.
I have watched the execution timings of each event and notice that closing the application and relaunching it will start the application fresh. I assume that my NSUserDefaults are still in place and thus could be checked for a logged in user. This could help determine which view in the navigation controller to push to (either login or dashboard).
Any direction is greatly appreciated.
The most revealing answer in this post was the use of some storage (NSUserDefaults) in order to store persistent data across uses.
For my specific case, this was storing a key holding user info. Then when the application loads, the would-be first view comes up, but if that key is missing, will modally pull a login view in front of it.
To do this I would use NSUserDefaults to store the current view of the app and then switch to that view when your app finishes launching. See the answers to this question: Swift: How to store user preferences?
I have "Tinder" like swipping view that is located in a CardViewController. The card View Controller is accessed by moving through two other view controllers. i.e. Load App -> FirstViewController -> SecondViewController - > CardViewController.
When I am in the Card ViewController and I go into background mode, the app launches on the FirstViewController and on going to the cards, they are loaded from the first card in a stack of about 10?
Is there anyway to load the app from the last Card I had swipped and in the CardViewController without having to navigate from the FirstView Controller again?
I would really appreciate the help as it's horribly affecting some of my users.
An example of a Tinder like card view is shown!
The problem, from the sound of it, is not what happens when the app goes into the background — that would leave it in exactly the same state when it reactivates. The problem is what happens when the app goes into the background and quits. Your app is then relaunched from scratch, which is why you find yourself in the first view controller. What's upsetting you is the difference between the app's behavior in these two situations.
Apple provides a solution to this situation: UIViewController, along with the App Delegate, has methods permitting you to save and restore state. When the app goes into the background, the current configuration (what view controller's view is showing) is saved. That way, even when the app quits, when it relaunches it can get back to that configuration before it appears to the user. Thus, coming back from background-and-quit looks just like coming back from mere backgrounding.
For full details, see Apple's documentation. This is a good place to start:
https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/PreservingandRestoringState.html
An iOS app I'm creating shows a setup screen upon first launch which requires data to be written to the database.
Upon launch, I need to access this value form the database.
If it is set, launch main view controller
Else show setup view controller.
As far as I'm aware theres two ways I can do this, programmatically setting it from the AppDelegate or using an initial View Controller as a splash screen and performing the look up and segue there.
What would be the best way to approach this? Is it wrong to do a database lookup in didFinishLaunchingWithOptions?
Using a splash screen is probably the better option as it provides better scope for modification in the future and allows you to update the user on progress. It is fine for the app delegate to run logic to determine how the app starts but you should endeavour to keep the app delegate minimal and focussed.
I very much doubt that you would get this approved (if your goal is the App Store). Your app delegate needs to create a window, and set a rootViewController to that window before it returns YES in appFinishLaunching:
You simply do not have enough time to check with a server before creating the first viewController and you'll be creating poor interface if you try. I suggest the first ViewController will need to be informing the user that it is checking with the server with an activityIndicator or something. The best :)
I have encountered an issue where an app will freeze when dismissViewControllerAnimated is called.
It is called on the root viewcontroller (something I find suspicious), and happen when the when the user has chosen Facebook from a UIActivityController. It doesn't affect Twitter or Mail (the only other activities exposed). It also only happens on device.
So the flow is:
User initiates the UIActivityViewController Controller which shows various avenues to share content.
User chooses "facebook"
Post to facebook view is shown
(essentially) [[UIWindow rootViewController] dismissViewControllerAnimated] called, which freezes before returning.
I was curious either -
if anyone has seen this behaviour and has a solution, or at least
what are things that could cause dismissViewControllerAnimated to freeze.
I don't have an answer to the source of the freeze, however I was able to resolve the issue by dismissing the UIActivityController directly. Previously it was dismissed via the controller that presented it.
The best hypothesis I can come up with is the facebook controller (unlike the ones for mail or twitter) adjusts the relationship of the UIActivityController so that its presenting controller changes. After that it cannot be dismissed the way I was (note: my knowledge is iOS UI is pretty scant so I could be terribly wrong). I don't get why that would cause it to freeze though