A good practice is indeed to split your application in two parts:
The domain: Contains all the service classes for remote communications (that is, if you need to hit a distant backend), and every classes and models related to your business logic. This part is the one that use j2objc, and you want it to be unconnected to your UI.
The UI: All the ViewControllers and Views belongs here.
Why are they distinct from each other?
Once you are set with all your business logic, you should be good to generate your code with j2objc and leave it practically untouched. Your UI, on the other end, may change a lot in the lifespan of your application (remember how iOS 7 forced every developer to refresh the visual of their applications). You want to keep updates in the domain or the UI from impacting the other part.
How do they work together?
Following an action (i.e. a tap on a button), you may ask the domain if some prerequisites are met (i.e is it a new user?), and then you instantiate a ViewController for display. The UI may give data to the domain or ask for some, but it is its responsibility to instantiate Views/ViewControllers.
Complex routing
In applications where I need to be able to navigate somewhere based on a remote push notification, or based on some complex logic, I tend to let an object (called a "router") handle all of this. It still reside in the UI part, and you pass it the domain objects required to make the decisions. It then returns a navigation object depicting a navigation stack (it could be an object handling a string similar to an URL) that you could give recursively to you viewControllers.
Let say your "router" object returns a navigation object "firstVC/secondVC/thirdVC" to the AppDelegate. The AppDelegate could call a dequeue
method that returns "firstVC", and based on this instantiate a "firstViewController" object to add in a UINavigationController's stack. It then pass the "router" object to this newly instantiated ViewController, who also call the dequeue
method to receive "secondVC". Based on this, "firstViewController" would instantiate a "secondViewController" object to add to the UINavigationController's stack. It would then pass the same "router" object to that "secondViewController" object, and that one would again call the dequeue
method to get "thirdVC" and instantiate the appropriate ViewController.
That way, you build your navigation stack by letting every ViewController instantiate another ViewController it knows about (in my previous example, "firstViewController" knows about "secondViewController" because its view has a button allowing the user to reach "secondViewController"'s view, but doesn't know about "thirdViewController").
You could also have your AppDelegate build the whole navigation stack, but since your "firstViewController" already have "secondViewController" header imported for the button to work, it is better to let him do the job associated with that ViewController, etc.
Working sample
Here is a demo project I made to illustrate what I just exposed.
Classes contained in the "Domain" folder could be your classes generated with j2objc. The buildNavigationStack
method in AppDelegate build a functional navigation stack with a 'Router' object. I made this project skeleton simple while showcasing a few patterns, like the factories for a centralized & organized class instantiations, and the viewData that encapsulate a model object and output data ready for display. It could be improved with a few protocols to make things a little more abstract, but I tried to keep it simple since the for the sake of the presentation.