I am writing an iOS app with several (maybe 8) survey-style modules where the user will answer a series of questions (on Family Life, Health, etc.). Some modules have as many as 12 questions. Each question has its own nib and its own UIViewController. The answers will be written to a database, ideally, at the end of the module. Each module corresponds with one record in one table. There will only ever be one user, hence, one record. I want to leave the Core Data stack in the app delegate. I am currently creating Core Data objects from the View Controllers.
I have been struggling to find either:
- The best way to pass accumulating data down a long linear chain of UIViewControllers.
- An alternate solution.
I am aware of the following possibilities:
- Pass data data down the chain via overloaded initializers for the view controllers, using a collection that gets added to at each pass
- Pass data to a property in the child view controller, again using a collection
- Use global variables in the app delegate (I don't seriously consider this an option)
- Use singleton classes
I think singletons are considered the best option in this circumstance, at least in terms of design, but my research so far I haven't heard of anyone using so many (8, maybe more) singletons. The fact that they provoke so much argument confuses me. And I sometimes see multi-threading discussed along with singletons, but I have no understanding of why they go hand-in-hand (if they even do) and how I would handle that. Finally, if I were to use singletons, would I call the Core Stack from the singleton or from the view controller?
EDIT: Another question, if I use singletons, is - what do I do with my Core Data entity classes? Can or should I use the singleton classes to replace them?
Answers to the above and/or alternate suggestions for a solution would be greatly appreciated. If possible, I want to find a compromise between good design and easy/quick implementation. Changing the app design is not an option (the client is determining the layout and flow).
As an engineer who has worked on numerous iOS projects, I've tried all of the methods you've just mentioned. By far, the cleanest and most maintainable solution is to employ a singleton.
Why? Think about your code from a refactoring point of view. You have a process (pushing an object of type answer to a Core Data stack) which you repeat numerous times in different view controllers. If you didn't like the way something is being done in your answer-handling code, you'd need to make changes to the relevant parts of that code where ever it has landed in your app. It would be far more tedious to go view controller by view controller to make the appropriate change, than it would to make a change in one place. This might seem obvious, but in designing any application, it is a profound fact.
Let's consider passing an accumulating object down the chain of view controllers. Whether doing this by property or by creating a new designated initializer for your view controller subclass, you'd need to write boilerplate code to pass the objects from one view controller to the next. Given what you have described about how each question has its very own UIViewController subclass, that means you will be at the mercy of type-safety every step of the way, just to move data around. What if you decided tomorrow you wanted to change the type of the data you're passing? Even if you have a special UIViewController subclass that centralized your boilerplate code that all other controllers inherit from, you still have the same problem of having random assignments strewn about your code just to keep the data there. And you'll also potentially have to handle cases of the user going back to reanswer a question, requiring even more assignments/designated initialization.
Now consider a singleton. A singleton is awesome in that it can own and manage best one unitary set of data, yet make it accessible to any part of the application you'd like it to. Think of a singleton like you'd think about UIDevice. You're only ever going to deal with one UIDevice -- analogously, you are only ever going to deal with one Core Data stack, or one set of data. Why not recognize this and try to factor your code from there?
It is unfortunate that Apple's examples in Storyboards make it appear as thought dependency injection is the only way you can pass data from one view controller to the next. Instead of trying to think about what other engineers think about certain practices, try to determine the pros/cons of each method you yourself use. If you don't fully understand them, try one out and see how it turned out. A lot of this is trial and error, and architectural design is no exception.
EDIT As Inafziger has said, singletons are tricky when it comes to storing (and in some cases reading) data on multiple threads. If you are writing to your singleton from multiple threads, build a dispatch queue of any type and size you'd like and run it on a separate thread (or add it to the run loop on the main thread). If you aren't dealing with multiple threads, singletons are awesome :)
BTW Best practice for creating singletons in Objective C can be found here.