What are good strategies to debug iOS apps with 'dangling UIViews'? - ios

I recently inherited maintenance of a relatively small iOS application. The app was created by external contractors with very little guidance and oversight. Needless to say it needs "a little" cleaning - I am evaluating whether to redo the entire thing or not.
One thing that got me stumped is a crash in the app whereby the debugger shows "Applications are expected to have a root view controller at the end of application launch". In the module where this occurs, I researched all UIViews to make sure they are created with a parent (addSubView sets the root view controller, right?) - this seems all prim and proper.
Being rather new to XCode, I am not familiar with facilities that help me figure out what might be going on here. E.g., how can I quickly see/investigate the status of all UIViews created by a module? How can I 'watch' a variable just to be alerted when it changes? And in general, is there a best strategy to use to tackle issues like the one I described above?
Sorry to stay a bit vague but I don't think that publishing a bunch of ugly code helps you to understand the problem better :-)

That particular problem happens when the application's UIWindow doesn't have a value for its rootViewController property by the time it finishes launching. Take a look in your application delegate file - usually, the root view controller is set on the window there.
With regards to your more general questions: there are a variety of ways to inspect the state of your program as it runs. A very basic way to dump some info is to use an NSLog statement - you can print out messages to the console in much the same way as a C printf would. You can also set breakpoints in your application and use the debugger to inspect different variables - take a look at the lldb documentation for more info.

Whether you have "dangling" UIViews or not has not much to do with having a root view controller or not.
Instead, you should make sure that your app's UIWindow has a rootViewController at the end of your app delegates appDidFinishLaunching method.

Related

AudioKit Lifecyle confusion

I can't wrap my head around this. I read everywhere (rightly) that Audiokit lifecyle should be managed in a singleton and kept away from the View Controllers lifecyle. Then I checked all the example project's code and found a bit confusing that
AudioKit.start()
is always called in ViewDidload. But anyway thats not my real issue..
I have multiple ViewControllers that uses the AKMicrophone in a different way. For instance they need different categories, different AKSettings, and some must be plain microphones while some needs a long chain of initialization with effects and mixers (Just like the "Recorder" example project).
I have two related questions here:
I read on Github that when we change the routing or category, we need to reinitialize the AudioKit. But in this case where do we really do it? In my case I really only can think of "ViewWillAppear". (if Mic needs to start listening without needing a button click, with a different setup)
And real question is how do I do this correctly at all? For example in my project AKMicrophone is set up just like the "Recorder" example but in a Singleton class. And when I switch to another ViewController which requires a "plain" AKMicrohone right after view appears. How do I remove all those mixers and delays from the chain? This is not only a matter of changing some AKSetting and restarting the engine.

Are memory retain cycles impossible in a single-ViewController-app? (Swift / IOS)

I remember from watching CS193P from Stanford University on YouTube (yes, I'm a smart bunz)... that there's this thing called a memory leak or "retain cycle" -- something really bad -- that can happen when you do things like this:
referencing self. within a completion block
referencing self. within a timer callback
referencing self. within a SyncQueue.sync() method
referencing self. within a DispatchQueue.main.async() method
The solution generally seems to be to use the "weak self" reference instead.
I have 104 of these asynchronous self. references in my ViewController which is why I am a little worried.
HOWEVER... this app is a single-page app... and ALL these self. references are pointing to this main ViewController (or one of its permanent sub-views) which is always there, never dismissed, and never "popped from the stack."
My app seems to work fine... and I don't see the total memory usage going haywire or anything... So does that mean I can leave my (ViewController) code as-is in this regard?
Thanks for help!
Here are two situations where you may regret not fixing your code:
If the device runs low on memory when your app is in the background, there are aspects of your view controller and its views that can be deleted. See this (admittedly old, but still interesting) article. This could easily affect your app more significantly in a future iOS version, or maybe even now depending on what your code is doing.
Jump 6 months ahead, where you or someone else on your team is borrowing some of your code for another app. You (or they) will likely get burned. Best to just fix the code now. The fixes shouldn't cause a major refactor, but if you find one that does, you could always insert a big warning comment at that line instead.

XCode: Find which event was fired without a breakpoint

I'm sorry if this post is in the wrong area, please advise me on where to move it if needs be.
I've just been passed a project from a previous developer and to be honest the repository is horrendous, the previous developer used awful naming conventions, the code is completely serial, no XCAssets, XIB's or Storyboards were used and I'm finding the whole project very hard to navigate - oh and no comments! What sort of developer leaves no comments...
So far it has taken me 4 hours to fix problems that would normally take a matter of minutes due to having to scour through 200 different source files.
I was wondering if there is a way to tell the debugger to stop each time a function is called on a click events - basically on the UI there is a button which is displaying the wrong dialog, due to awful naming conventions I am finding it near impossible to locate the place in the source for me to make changes.
Any advice would be appreciated (I have told the team I am planning to rebuild the whole app, but we are due to launch next week).
Welcome to the world of programming. You are able to add a custom forwarding delegate to the AppDelegate on the Main start up that will intercept events such as the ones you described above via
int retVal = UIApplicationMain(argc, argv, #"MyListenerHere", nil)

IOS Saving State For Complex Apps

I'm building a fairly complex business application on the iPad IOS 4.2: 4 tabs, with potentially deep navigational paths on each tab.
In the opinion of some of your more experienced IOS developers, what would the general expectation of a user be with respect to saving application state between launches (i.e. after an app's been fully terminated and subsequently restarted)? I'm using Core Data and have all the data issues covered, but I'm concerned about the application's navigational tree. If the user had left the 1st tab on screen 3, the 2nd tab on screen 4, the third on screen 2, where he left the entry of a new record half-complete, and was, at the time of the app entering the background, working on the 4th tab on screen 3...do you think the average user would expect the application to remember all that the next time it launched? (My gut says yes, though I'm unsure for how long.)
If the answer is yes, can you suggest a general strategy for handling this (and, again, I'm talking about the navigational tree here, not Core Data stuff)? For example, if navigational controllers were used as the root view controller for each tab, it would be simple enough to record enough info about their navigational stacks to be able to restore them later. But what about things like popovers, alert/action sheets, or modal VCs created on the fly? Should each view controller record the state of its UI objects and, if so, what is the recommended way to do this?
I know a lot of this is up to the user, but I'm asking for the general perspective on these issues, i.e. the voice of experience.
It's pretty simple in principle, but it can get quite complex in practice, to go through your navigation hierarchy and storing stuff that can't be derived from the data model.
There's an open source implementation of this called DTResurectionKit. I also documented how I do it in my apps on my website. It's similar to (but simpler than) DTResurectionKit.
In the opinion of some of your more experienced IOS developers, what would the general expectation of a user be with respect to saving application state between launches?
The best way to think about this is to make sure the user never has to figure out why or how they got to where they are when the app first opens.
This depends completely on the type of app you have and the length of time since the last open. It sounds like you have a fairly complex drill-down app so I think it is definitely best to remember the navigation stack, within a pre-determined time frame. I use the three20 framework which does this automatically for me, but if you were to implement it, it would be something like this:
If the user opens up in the past 24 hours, open to the exact spot the left off
If the user opens within a week, open to the main "section" or area of the app they were in
If the user opens after a week has past, open to the root screen.
Now of course, these values will differ based on your apps function and use cases, but you get the idea. By you making some broad assumptions about how people are using your app and when, you can increase the user experience by not shoving them so deep in your app when they won't remember how they got there.
As for implementation, it is all just data.. You dont need to serialize live objects to store the stack, just implement the data needed to recreate the state on the next launch. What you need to store is highly dependent on your own setup... mileage will vary. I use NSUserDefaults to store all instance vars and the navigational stack through Three20. Check out TTNavigator for a great implementation.
I would suggest keeping the state of each tab view. Only at the "page" level. Don't worry about popovers or incomplete data entry (hopefully there's not too much interim state before you're saving it to your core data store.)
Like you said, it's easy enough to remember what tab you're on, and what controller you're navigated to in each tab. More isn't necessary.
It sounds like you've got it under control, but for the benefit of others: 1) when you change tabs, save "active tab", 2) when you navigate within a tab, save "active controller in tab", 3) when you launch the app, set the "active tab", 4) when you change tabs, set/confirm the "active controller in tab".
The reason for 4) is that the view/controllers for the tabs will be delayed in their loading, or perhaps never loaded. You don't want to set the "active controller in tab" for a tab that is not visible and may never be loaded into the app, it would just cause unnecessary loading. It will often happen (after the app has been loaded) that you don't need to change it because it's already in the correct state.
I think your time is better spent elsewhere. Of course, your app might be perfectly suited for this, but in our case data was partly online, could have gone stale, influenced view state in different navigation views in different tabs simultaneously, etc. etc. It's not an insurmountable challenge, but definitely hard and a huge time-sink.
We decided to spend our time on fixing the bugs and improving functionality. If you have to make the same kind of choice, I'm pretty sure which option your users would prefer. Particularly now that your app will survive a phone call in the background.

NSInternalConsistencyException reason +entityForName: could not locate an NSManagedObjectModel for entity name

Was using interface builder to create a view controller, and pass the NSManagedObjectContext to it, doing everything perfect. But I kept getting the error as if I didn't set something up.
I'm posting this because no other question addresses the issue, but hope others will benefit.
After using hard code instead of IB, I noticed that the root view controller's view was called before the App Delegate. That completely messed up the intended order. NSLog has showed me what comes first now.
Many times I severely question Apple's (lack) of use of English grammar, or doing things the way any normal person would expect. This, like the iPad's app store not displaying full names on related programs, is one of those things.
So yes, if you're using NIB's, hardcode the addSubview part instead of linking in Interface Builder. Even if you link it right, hardcode the root view controller. Or, someone with more experience could post another solution...
I could be off, or way off even, but if so... blame Apple for assuming anyone reading the tutorials will always need to do it "that one way written in the docs".
Core Animation suffers the same missing info and links. Ok now I'm ranting about Apple. If this issue was addressed, though, I apologize, yet all the ones I find are typos and "not linking". I did everything mentioned and it still didn't, which was very confusing.
Thanks!
I noticed that the root view
controller's view was called before
the App Delegate...
That is impossible. The app delegate's application:didFinishLaunchingWithOptions: has to be called before any view can activate. The overall display window is owned by the app delegate so if the app delegate has not loaded, there is no window for views to load into.
I've created dozens of apps with IB, Core Data and many types of views and view hierarchies and never encountered seen a view load before the app delegate. You've missed something.
The error:
NSInternalConsistencyException reason
+entityForName: could not locate an NSManagedObjectModel for entity name
... either means that your managedObject model has not loaded or that you spelled the entity name wrong. It sounds like your Core Data stack is either not loading or that you are accessing it incorrectly.
I have the same issue. What's bizarre is that on iOS 5 I get the problem, and on the iOS 6 I don't.
Some digging revealed that on iOS 6 the context seems to be there when I expect it, when on iOS 5 it's empty (hence the error message). In my case I was giving over the context via a segue. A workaround was to test if the context was nil, and if it was then request it specifically. Did the trick.
This must have been something Apple was aware of as it's changed for iOS6.

Resources