where to put a model's method? - ios

I'm new to Core Data so I thought I'd ask this here.
I have a model, User Recording, which, for now, has the following:
#property (nonatomic, retain) NSDate * dateCreated;
#property (nonatomic, retain) NSData * audioData;
#property (nonatomic, retain) NSString * name;
What I'd really like is to have a method in there, called "play", to play the recording. Right now, I'm putting it in my view controllers but that's clearly bad because I've got that same method in two controllers. I've looked around a bit (and will keep looking) but can't figure it out - where should it go? Should I have a model controller (manager)?
Thanks.

your controller should have a hold to the model (that is, an instance variable or property), so that when the user clicks a button in the view (which should have an outlet to connect to the controller) you can invoke the message you want.
Just add the play method to your model (in the header file, so that is's public) and let the controller call that method when the user interacts with the view.

Related

Different methods for IBOutlet creation

There are at least 3 methods of creating an IBOutlet in Objective-C, for making iOS 10 App, in Xcode 8.
Method 1: in ViewController.h
#interface ViewController : UIViewController
#property (nonatomic, strong) UILabel *textLabel;
#end
Method 2: in the interface of ViewController.m
#interface ViewController () {
IBOutlet UILabel *textLabel;
}
#end
Method 3: in the interface of ViewController.m, using #property
#interface ViewController ()
#property (nonatomic, strong) UILabel *textLabel;
#end
Given that the textLabel has to be accessed & its text is needed to be updated frequently, which method is the correct way to do so?
That all depends on whether you need your outlet to be accessible to classes outside of the containing one; generally I would discourage this because it is good practice to keep your view controllers responsible for updating your UI and not pass this task around to other classes. With this being said, Method 3 would be the best option, however, if you do have to access your object from another class, then simply use Method 1 so it is exposed in your class header.
Method 2 utilises iVars rather than object properties and is not the proper way to declare outlets, it may even cause unexpected behaviour so it is best to avoid this method.
Your code contains no proper IBOutlet. Outlets are connections to Storyboard.
Method 1
This is a property. As it is in .h file, it can be reached from outside. The Objective-C pattern for public.
Method 2
This is an iVar. Do not use iVars if you do not have to.
Method 3
This is a property. As it is in .m file, it can not be reached from outside. The Objective-C pattern for private.
Method 4
A proper IBOutlet looks like this:
#interface ViewController ()
#property (nonatomic, weak) IBOutlet UILabel *label;
#end
It is a simple property. You have to decide if you put it in .h or .m file depending on whether or not you want to publish it.
The IBOutlet simply makes the property connect-able to Storyboard. It's an annotation for Xcode and does not alter the semantic of your code.
Edit 1:
As Sulthan correctly mentions in the comments:
In most situations the correct design pattern is to hide outlets because it's an implementation detail. External classes should not set data directly using views.
Edit 2:
Why "not to use iVars if you do not have to" (2)
Opinion based:
I consider it as good OOP practice to use getters & setters (and thus not to access the variables directly). Also code is easier to read as you know while reading what x = self.variable (property) and x = variable (local variable) are.
If you have to use iVars for some reason, it is common to (and I would recommend to) prefix the name with _. x = _variable (iVar).

Nonatomic strong / copy?

I'm building an app whereas I have a ViewController viewing a custom object, lets call this object "CustomObject". Upon a button press, a segue is triggered and hence prepareForSegue is called where I get the destination ViewController and pass self.myObject. The destination ViewController may change a few parts of the CustomObject, but those changes should not be reflected in the original ViewController if the user decides to go back to the original ViewController. The changes should only be reflected if the user pressed "Save" in the destination ViewController and hence triggering an NSNotification with a version of the CustomObject that should be reloaded in the original ViewController like so:
self.myObject = (CustomObject *)notification.object;
So my question is as follows: Which of these should I use (or any other that would be correct) - and why?
#property (nonatomic, strong) CustomObject *myObject;
#property (nonatomic, copy) CustomObject *myObject;
Thanks!
Update:
header file:
#interface CustomObject : NSObject <NSCopying>
implementation file:
- (id)copyWithZone:(NSZone *)zone
{
id copy = [[[self class] alloc] init];
if (copy)
{
// Copy NSObject subclasses
[copy setRegisterDate:[self.registerDate copyWithZone:zone]];
}
return copy;
}
You should use strong but (in prepareForSegue) create and pass a copy (or simply a different object, but in any case, don't pass the original object).
This is the opposite of the situation for which the copy property attribute was designed. With the copy property attribute, the recipient wants to ensure that the object is not mutated later behind his back: e.g., I accept an NSString but the caller passes me an NSMutableString and retains it as well, so that my string can now be changed behind my back. By calling copy, I turn the NSMutableString into an NSString, which is immutable.
Your situation, as I said, is just the opposite. Your first view controller wants to pass an object without any risk of affecting his own object. Therefore, it is up to your first view controller to make a new object and pass it, rather than passing a pointer to his own sacred object. It is not your second view controller's job to know that your first view controller needs protecting; it is up to your first view controller to protect himself.
I feel you can go for copy. Since it can be used when the object is mutable. Use this if you need the value of the object as it is at this moment. You don't want that value to reflect any changes made by other owners of the object. You will need to release the object when you are finished with it because you are retaining the copy.
Pl. refer to the below link also which gives good insight when to use which property.
Objective-C declared #property attributes (nonatomic, copy, strong, weak)
You should use "copy", because "copy" creates a duplicate instance of that object, and the new object is independent of the original object. "strong" adds a "link" to the object, it's only one object.

Does every ivar have to be in CoreData?

This might be a completely dumb question, but I'm a CD noob...
I have an object that tracks rectangular points on maps by defining the top left and bottom right corners, like this...
#property (nonatomic) float latitudeNorth; // same as "northwest"
#property (nonatomic) float latitudeSouth; // same as "southeast"
#property (nonatomic) float longitudeEast; // same as "southeast"
#property (nonatomic) float longitudeWest; // same as "northwest"
Now I need to add four more points, so that we can have polys instead of pure rectangles. So I want to add this...
#property (nonatomic) float latitudeNorthEast;
#property (nonatomic) float latitudeSouthWest;
#property (nonatomic) float longitudeNorthEast;
#property (nonatomic) float longitudeSouthWest;
These data points are ephemeral and exist only as long as the app is running. They were, however, originally built in the xcdatamodeld. Is this a problem? Or am I find just adding the additional properties and using the old xcdatamodeld as-is?
I depends on you complete structure.
If you have some more entities that are fetched several times and saved again, and you need every time you fetch also the ephemeral properties, you have to hold them of course in the DB.
But if you only need them while you fetched and holding the Object, you can handle them just in class and there is no need to put them into the DB.
Not every item in the DB also has to be in the extracted class ;)
But the most attributes will also be saved, because they have to be updated via other services i.e. or have to present these updates in other views. Or maybe also because you don't want to hold the attributes all the time or will fetch them again (i.e. after a tableView.reloadData) and don't want to lose the calculated results
You can have other properties declared in your managed object subclass. They won't be initialized when you load up your object from a persistent store (when you close your app and open it up again, the values will go away), but you can get around that. However, there are better ways to do this. For example, you can write methods that calculate the values on the spot:
-(float)latitudeNorthEast;
-(float)latitudeSouthWest;
-(float)longitudeNorthEast;
-(float)longitudeSouthWest;
In your case, all that these methods do is return one of the other property values, so this seems like the way to go.

iOS Which is better design comparing passing the whole class to view or setting piece by piece

I have a User class has some properties such as name, email, location. And I have another class
called Post which has some properties such as title, contentand poster that is a instance of User.
There is a ViewController, inside the view controller there is a PostDetailView which is used to show post details. Now the view controller has a post object and I need to pass some values to PostDetailView. I have two options:
Pass the whole post object through initializer, which means PostDetailView has a method -initWithFrame:post. Once the view get the post object, it can get every data inside the post.
PostDetailView has some setter method such as setTitle:, setContent, setPosterName, etc. Initializer only initialize frame of view, and then using setters to pass value.
Option 1 can save lots of work in view controller but may increase coupling. Option 2 has better structure (I thought), but need additional work in both view and view controller. Therefore, my question is which one is better in terms of architecture?
Passing a model object to the view does increase the coupling unnecessarily. Setting the individual fields is better, because the binding logic is in the controller.
A third option is to build a class that contains only the data needed by the view, and pass an object of this class to the view, instead of passing the entire Post:
#interface PostViewData : NSObject
#property (nonatomic, copy) NSString *title;
#property (nonatomic, copy) NSString *content;
#property (nonatomic, readonly) MyUser *poster;
#end
Although the controller is still required to house a view-specific logic, the view remains insulated from the model, and the code becomes explicit about the content of the view-specific data.
In favor of good design and architecture it make sense to type a little more and have views decoupled from model. One technic that I've used to is to design view with properties of primitive types (String, Number etc.) and create a category for the view that has a method to configure itself with a model object of a particular type.
You can read more about this here: http://www.objc.io/issue-1/table-views.html
They talk about table view but approach can be used for any view and model with mediating controller.

Persistence of cell values in a hierarchy of views with tables

I'm going to explain my scenario: I have a custom class whose properties are intended to get the information provided by the user through a form. This is the custom class:
#interface CustomClass : NSObject
#property NSInteger iD;
#property (strong, nonatomic) NSString *profilePicUrl;
#property (strong, nonatomic) NSString *email;
#property (strong, nonatomic) NSString *firstName;
#property (strong, nonatomic) NSString *lastName;
#property (strong, nonatomic) NSString *address;
#property NSInteger zipCode;
#property (strong, nonatomic) NSMutableArray *products;
#property BOOL isEnabled;
#end
The form consist of three views with an UITableView, and their respective view controllers are within a UINavigationController. This way, it is like a multi-step form: there is a first view requesting user input, then navigate to a second view requesting user input, then navigate to the third view displaying information before submitting the data provided. The "back" button of the navigation bar is enabled, so users could go back to a previous step to complete/change their inputs.
Cells of the tables are of different types, corresponding to the properties of the custom class I described: some of them have a text field (cells for entering first name and last name, for example), some others a switch ("isEnabled" property), and I have one cell with a button that displays an UIImagePickerController to take a picture (cell for profile picture).
I want to create an instance of my CustomClass and have its data completed at third step of the form to submit it. A part of the information is requested in the first view (profile picture, email, first and last name), and the rest is requested in the second view (address, zip code, products), so I'd need to pass the CustomClass object through the three view controllers of the navigation hierarchy.
My question is: how should I manage the persistence of the values in cells when the user enters them? Should I "bind" somehow the CustomClass properties to each corresponding cell? I'd want the user to be able to go fordward and back in the form and keep the data that she already entered. It looks like the values I type in text fields are retained and if I navigate from first view to second, and I go back to first again, the text field values are kept, but not the rest of the cells' content. And what if the app suddenly terminates and I donĀ“t want the user to enter again all the information? Note: my app has to support iOS 5.
What should be the best way to manage this scenario? Thanks!
You have to persist the info in your CustomClass. You could do this by writing it to a plain text file, a property list or use a database, or even use the NSUserDefaults infrastructure.
The save should occur immediately whenever data has been entered, for example in textField:didEndEditingand such callbacks. You would update the property of your CustomClass object and then persist it.

Resources