I am in the process of cleaning up my code as my TableViewController class has become rather full and long. To clean up the code, I would like to create a number of utility classes that provide methods for the TableViewController. These methods/functions, in my current implementation, are able to change variables in the main TableViewController. Here is an example of my current setup:
class FooImplementation: NSObject {
var viewController: Foo
init(viewController: TableViewController) {
self.viewController = viewController
}
}
class FooUtility1: FooImplementation {
// Methods
}
class FooUtility2: FooImplementation {
// Methods
}
class Foo: TableViewController {
var fooUtility1: FooUtility1
var fooUtility2: FooUtility2
override viewDidLoad() {
fooUtlity1 = FooUtility1(self)
fooUtlity2 = FooUtility2(self)
}
// Use of the methods...
}
Is there a better/universally defined way of creating and using these classes? And can these classes be combined into one Utility class that has access to all of the methods FooUtility1 and FooUtility2 provide?
Thanks!
I don't consider adding utility classes a good design pattern, for at least the following reasons:
they can change the internal status of the view controller
you have to "upgrade" member properties to internal or public when they are private by nature
Of course I don't know what kind of behaviors you want to move from view controllers to the utility classes - my advice is to use one or more of the following:
create view controller extensions
create your own set of base view controllers, inherited from UIViewController, to be used as super classes for your final view controllers
move some logic to external classes, but use the delegation pattern to interact
The second case is one I frequently use, and the 3rd is one I've applied to my latest project, using better separation of concerns by implementing for each view to be displayed:
a view controller, responsible of managing (but not displaying) the view, and handling events received from its view(s)
a data source, responsible of providing the data to be displayed in the view, along with methods to add/delete/update if needed
a (hierarchy of) view, implemented in a separate class in its own xib file.
It makes development a little more complicated, but in the end I have a light view controller, and a view that is responsible of displaying data, handling events and forwarding them to the view controller.
Note that this is not the solution, just one possible solution. There are several variables to take into account, for sure what you want to move from the view controller, and I think also personal preference.
Related
So I have a Swift file with some class Whatever. This class has a number of private properties. Like this:
class Whatever
{
private let privateString = "Blabla"
private let privateInt = 125
// a lot of code here
}
I would like to create an extension for this class in a separate file. Just in order to avoid having a large file with enormous amount of code. But I can't. An extension in a separate file cannot access private properties of the class. So I'm forced to either make private properties internal or maintain a single large file. Is there any technical solution to this problem except creating a module for this functionality?
You can't access private properties from another files.
The only thing I can think of that may help you is to use to replace private with private(set) which provide you a read-only access from other files.
This doesn't come up in iOS programming much, but I think a lot of iOS programmers know this instinctively, make you model classes just store values, and maybe some really basic methods on them, then you have model controllers that can modify the model classes, in Mac OS we always talked about model, model controllers, view controllers, and views, but in reality I think this was the model, view, control pattern and the model, view, presenter pattern combined, and model controls where the real controllers, and view controllers where the presents.
I have a maybe slightly unconventional question. I'm working on an app that has two different core data entities (sessions and routines). Subsequently I have two different view controllers in interface builder to fill the attributes of those entities. The thing is that those two entities are identical aside from the fact that I need sessions to have a many to one relationship with a clients entity and routines to have no relationship.
The question is this. Id like to use the same view controller in interface builder for both my session and routine classes, is there a way that I can programatically determine what class the interface builder viewcontroller is assigned to based on a segue identifier? I know it would have to be some sort of if segue.identifier == "myIdentifier" {
/*code for changing destination view controller's class*/} statement but I'm not sure what the code would be, and I also want to hear other suggestions of ways to possibly do this more efficiently.
Thanks!
The view-controller in your Storyboard is an archived object of a certain class, and you can't just assign it to some other class. (Just as if you have a String object and want to assign it to be an Int wouldn't work well)
You can check the destinationViewController property of the segue within prepareForSegue: if it is a certain class like this:
if let dvc = segue.destinationViewController as? MyExpectedVC {
// do something
}
but that is probably not what you want/should do.
I'd go along the lines of what the commenters of your question have said and would suggest that your view controller class implements a configuration method for both your models. Either a separate one for each class like this:
func prepareWithSession(session: Session) {/* ... */}
func prepareWithRoutine(session: Routine) {/* ... */}
or (imho a bit nicer) create a protocol for all classes that can be displayed by the view controller and have only one method:
func prepareWithDisplayable(session: DisplayableType) {/* ... */}
where DisplayableType would be a protocol that defines all properties that this view controller needs to know about.
You can have custom logic in the view controller that displays some fields only for specific Types. (e.g. sets views hidden for other Types)
Being able to share data between multiple view controllers and doing that in a way that makes use of recommended patterns such as MVC seems to be essential to create good apps, but my problem is that these things aren't clear at all for me.
I am conscient that this question is really dense, but for things to be clear I think you really need to understand the whole thing.
First of all we need to be sure of what Model, View and Controller are doing, here is how I would describe them, please tell me if I'm right about that:
Model : a class that's responsible for managing data, and only that (for example, a class that will go on the web to retrieve information, such as weather forecast).
View : a view is an object that's displayed to the user, who can often interact with it, that's the objects that you can drag and drop in Interface Builder (for example a button) and you might also create one from scratch, or custom an already existing one by subclassing it.
Controller : a controller is responsible for managing a view and its subviews, it receives events (such as viewDidLoad, or even when the user taps a button) and can react to it, for example, it might change the text of a label.
Now about the way they are interacting between each other, I'd say that the controller is between the view and the model, it's managing the view and might ask for data to the model. In addition to receiving events from the view, it might also receive events from the model, for example, if the controller asks to the model for a specific data on the web (let's say if it asks weather for a specific city) the data won't be available immediately, instead, the model will notify the controller so that it can update the view with the data it received. Am I right?
One of the first thing I'm wondering is if an object could be considered as a model if it isn't here to retrieve data, but to do other things that are simply not related to the view, for example, could an object that's responsible for communicating and managing a bluetooth accessory considered as a model ? Could an object that sends data to a cloud considered as a model ? And what about a Tic Tac Toe AI ?
Then, singleton instances, I often heard of them when an app had to share data between multiple views, but first of all, I never really understood why it was necessary to use them in this case ?
Then, here is a singleton that I found in an article of the We Heart Swift website.
class Singleton {
struct Static {
static let instance = Singleton()
}
class var sharedInstance: Singleton {
return Static.instance
}
}
Singleton.sharedInstance
The problem if that I have had difficulties to find anywhere more details about why it's written in this way, and most of all, can a singleton have an initializer that takes arguments? How to add properties and methods to a singleton like this one? What are exactly the Static structure and the sharedInstance?
My last question is about why, technically, does a singleton makes it possible to get an access to things we have defined somewhere else? What I mean is that if I create an instance of let's say, a Dog class in my AppDelegate, and if I want to access to this specific instance in a view controller, then it wouldn't be possible, so how does singleton makes that possible under the hood?
EDIT : Oh, and, is the use of singletons recommended by Apple?
Thank you.
It has to do with the static in the struct. Static is essentially a class variable that persists for every instance of that class, so when you make the shared instance static, every time you access it, even from another instance of Singleton.instance it is the same variable because it is static. It persists amongst instances. However, Swift does not support class variables yet, so when it does, that should quickly replace the Struct syntax that is common of singletons. It is very similar to static variables in java.
For example:
class Singleton {
var someVar = 0
struct Static {
static let instance = Singleton()
}
}
to create a singleton with a variable and the following to access it:
let foo = Singleton.Static.instance
foo.someVar = 11
let bar = Singleton.Static.instance
println(bar.someVar) // Prints 11
As you can see, bar.someVar was never set, and that is because the variable for the shared instance was set, so it prints 11.
How can I eliminate to write $object = new Application_Model_Database() in every controller?
For example for an article controller, I have to type $articles = new Application_Model_Articles() for every controller. Should I put it under viewer controller, action helpers, or any other way?
Your question almost sounds like an OOP best practices question as opposed to a Zend Framework specific question. Regardless of whether or not I'm using a framework, and regardless of what framework I choose, I base when and where I create new objects on testability how many times I have to write $object = new My_Random_Object();.
Speaking specifically to the Zend Framework: Objects I'm going to use everywhere, or almost everywhere, get created in Bootstrap.php. These objects generally include a database adapter, logger, view object, and any plugins I might use. To access these across the application, I'll create private properties in the appropriate controllers and assign the objects to those properties in the controller's init() method.
class ExampleController extends Zend_Controller_Action
{
public function init()
{
$bootstrap = $this->getInvokeArg('bootstrap');
$this->_db = $bootstrap->getResource('db');
$this->_log = $bootstrap->getResource('log');
// and so on, and so forth
}
}
Ideally, models, services, daos, etc, will all be relatively tightly grouped by controller and by action. In my experience, and this is speaking generally, if I have the same model or service class showing up across all of the controllers in my application, I have an organization problem. That being said, any model that shows up in only one action gets created in that action. If it's across actions in a controller, it gets created in the init() method and assigned to a property. If it shows up across multiple controllers, it gets created in my Bootstrap.php.
(Ideally, everything gets created in the Bootstrap.php, so you can swap out that bootstrap for testing purposes. Sadly, I don't always do that, and I most often use the principles I outlined above.)
Well do you really need it in every controllers? Because that's pretty much by design. You implement models when you need them. Its not that much code really.
Now if its to be used across actions from a controller you could always:
class MyController extends Zend_Controllers{
$protected $_articleModel;
...
and in your constructor or __init() function initialize it so you can use it in every action thru $this->_articleModel
If you REALLY want it everywhere in your application just initialize it in your bootstrap and store it in the registry.
public function __initModels(){
$articles = new Application_Model_Articles()
Zend_Registry::set('articles', $articles );
}
And access it in your controllers like so:
Zend_Registry::get('articles')->fetchAll();
But then your still writing a couple of characters.
Hope this help!
IF you want to use models in the controllers you must call it..anyway some shortcuts are here
1.You can initialize it in the init section of your controller like
public function init(){
$this->object = new Application_Model_Database();
}
So that the this->object is available in all the actions of that particular controller
2.Use Zend_registry as suggested in the above answer
Another possibility is to use a Dependency Injection container, such as the Symfony DI component. It takes care of instantiating your objects, and you get some additional benefits:
Separation of concerns. You have a component devoted to create your object tree.
Easier testability of the objects.
Last, but not least, the performance benefits given by lazy instantiation (objects are created only when you ask for them). Thus, if some object is not used by the particular controller serving your request, it's not instantiated).
It's a bit more laborious than the above solutions, but much more flexible if you need to maintain and extend your application in the future.
Hope that helps,
If you are using this object to just display data in your view and are using your controller to grab the data and assign it to your view, like so:
//someControllerAction
$object = new Application_Model_Articles();
$object->fetchAll();
//assign to view
$this->view->articles = $object;
You might be better off making a view helper similar to:
//Articles.php put in /application/views/helpers
class Zend_View_Helper_Articles extends Zend_View_Helper_Abstract {
public function Articles() {
$articles = new Application_Model_Articles();
$articles->fetchAll();
//return rowset object
return $articles;
Then in your view (phtml) you could do something like:
//someView.phmtl
<?php $articles = $this->Articles(); ?>
<h1><?php echo $this->escape($articles->title); ?></h1>
<p><?php echo $this->escape($articles->body); ?></p>
building a view helper allows you to bypass the controller completely if you just need to display data from the model. This is a very simple example and can be used with partials and partialLoops.
REF:ZF reference Custom View Helper
ZF partial view helper reference
I'm working with the MVVM pattern + a simple ServiceLocator implementation, now to my problem how am I supposed to setup the services when the views are running in design time?
Iv tried this but it does not seem to work in VS 2010 or some thing, I know it worked on my old computer but on my new it does not. so does any one know a good alternative?
Edit: (On behalf of Merlyn Morgan-Graham)
Well what I'm trying to do is this, I have my view, ViewModel and services now the difference here is that I have 2 implementations of each service one for design time and one for run time.
for a better explanation look here.
If you want to decouple your view from your viewmodel, and your viewmodel from your model/dal (basically, if you want to use MVVM), then your view model and data model shouldn't know anything about design time. Design time only applies to the view.
This article shows a way to define your design time data via XML/XAML, so your code underneath doesn't have to know anything about it:
http://karlshifflett.wordpress.com/2009/10/21/visual-studio-2010-beta2-sample-data-project-templates/
After Edit: It turns out that you'll still have to use your view model for your existing XAML bindings to work. This will just populate the view model rather than having to create a new data model. I'm not sure, but there might be classes that allow you to use the WPF binding mechanism to take care of this... Views?
Resume Before Edit...:
As far as the solution in the article you linked first, the designer doesn't instantiate anything but your class, and the code it references. That means that assembly attributes won't be instantiated unless your view code somehow directly references them.
If you really want to couple your view models to your views during design time, and make it so that design time services are registered, then you have to place the service registration code in your view class, or a class the view class directly references.
To do that, you could use static constructors of your views to register your design time services. You could also write a static method on some other class (application?) to (conditionally) register the design time services. Then, call that method in the constructor of your views.
Or you could simply register them in the constructor for each of your views.
Basically, what you want to do is possible, but that method linked in the first article isn't. If you read farther in the comments, you'll see that his method is broken.
You may also want to question the idea of hooking your view model to your view during design time, because the MVVM pattern was made to avoid that sort of thing.
You usually don't need to access services at design-time... Typically, you don't even use your real ViewModels at design-time, you use dummy design data, as explained here. If you really need to use your real ViewModels, you can implement dummy versions of your services, and use them instead of the real services :
if (DesignerProperties.GetIsInDesignMode(new DependencyObject()))
{
// Design time
ServiceLocator.Instance.Register<IService1>(new DummyService1());
ServiceLocator.Instance.Register<IService2>(new DummyService2());
}
else
{
// Run time
ServiceLocator.Instance.Register<IService1>(new RealService1());
ServiceLocator.Instance.Register<IService2>(new RealService2());
}
Also I do agree to all who have concerns regarding the use of the service locator at design time, I do believe that this is a valid scenario in some use cases.
This is not a discussion on why/why not, this is simple the way it (almost) worked for me.
There is still a problem which I did not solve yet: this only works for one view at a time.
Create a simple bootstrapper for setting up your IoC of choice. Notice the ISupportInitialize interface.
public class Bootstrapper: ISupportInitialize
{
#region ISupportInitialize Members
public void BeginInit() { }
public void EndInit()
{
if (DesignerProperties.GetIsInDesignMode(new DependencyObject()))
Setup();
}
#endregion
public static void Setup() { SetupServiceLocator(); }
static void SetupServiceLocator()
{
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<ConfigService>().As<IConfigService>().ExternallyOwned().SingleInstance();
IContainer container = builder.Build();
ServiceLocator.SetLocatorProvider(() => new AutofacServiceLocator(container));
}
}
Use the Bootstrapper as before for runtime mode, e.g.:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
Bootstrapper.Setup();
}
}
Additionally you need to add it to the application resources for design mode support:
<Application x:Class="MonitoringConfigurator.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyBootstrapperNamespace"
StartupUri="MainWindow.xaml">
<Application.Resources>
<local:Bootstrapper x:Key="Bootstrapper" />
</Application.Resources>
</Application>