Structuring a basic app iOS app - single view - best practice - ios

I am currently working on a single view app that does the following:
Grabs users location using CoreLocation
Sends their lon/lat to an API
Returns tide data as JSON
Dive in to objects and keys within the JSON to return: tide levels, locations, etc and display them via text labels.
I have the majority of this working, but it is more so crammed in to viewDidLoad - I am trying to see the best way to organize something like this up. Would I break it in to different methods such as:
setTideData
displayTideData
Or would it be more broken down than that? (And yes im sure it all depends on the details too). I would be displaying probably 8-10 different stats on the view.
Things like
On viewDidLoad do I grab the users location or do it prior on viewDidAppear, etc
Would I then call my displayTideData method inside of viewDidLoad
Just looking for some type of general best practice, was trying to scan for some items but nothing was what I was looking for.
EDIT: Here is an example of my returned data -- https://gist.github.com/ryancoughlin/8043604
Any ideas?
Thanks

This is pretty much a matter of preference and style, but you could simplify your logic in a number of ways by separating certain parts into their own classes, eg:
You could use something like JSON model to deserialize the data into a class, and access it in a more convenient way
Loading the data and deserializing could be handled in a different class that would only return the object with the data you need
Etc.
It's hard to give a better advice without reviewing your code directly, but this should give you some pointers how to simplify things.
EDIT: A bit of code example how to separate the the deserialization and data handling into a custom object:
- (id)initWithJSON:(NSDictionary *)JSON
{
self = [super init];
if (self) {
self.gender = [JSON objectForKey:#"gender"];
self.email = [JSON objectForKey:#"email"];
self.firstName = [JSON objectForKey:#"first_name"];
self.lastName = [JSON objectForKey:#"last_name"];
self.personId = [[JSON objectForKey:#"person_id"] integerValue];
}
return self;
}
You would then simply create an object with the data like this:
Person *person = [[Person alloc] initWithJSON:JSON];
and simply access whatever data you need in a cleaner way:
self.myLabel.text = [NSString stringWithFormat:#"Name: %#", person.firstName];

It sounds like you have a Massive View Controller, which is a bad thing. Try to reorganise your code so you have a fat model and a skinny controller. Controllers should only be responsible for creating and refreshing the view, bootstrapping the model and updating the model based on user interaction. Some possible model objects:
Fetching data from the server.
Parser for converting JSON objects into typed objects (thus encapsulating the knowledge of the JSON structure).
A simple interface (e.g. one object) for your view controller to pull data from and push data to.
Your model should contain all the business logic for the app.

Related

Iterate through all objects that have been encoded using NSCoding iOS

I have a class (SpendrList) that has an NSMutableArray property that acts a list that the user saves multiple things to while using an app (it's a datasource for a UITableView). Using the NSCoding protocol, I encode/decode this class as needed.
This works fine, as the array property holds list items from a class I created, SpendrListItem, (also adhering to NSCopying protocol) and I encode it as the user makes any edit in the UITableView, like so:
NSURL *dataFile = [FileSystemHelper pathForDocumentsFile:kFilePathList];
NSString *filePath = [dataFile path];
[NSKeyedArchiver archiveRootObject:_list toFile:filePath];
Right now, I am just dealing with one list so this was relatively easy to set up after a few tutorials on NSCoding. Now, what I want to do is code in support for the user to have multiple lists in my app.
I have a collection view in another ViewController set up where I want to display all the lists created, and I am wondering if I can iterate through all objects I have encoded contained in the app sandbox.
Psuedocode:
for (encodedObject in App Sandbox){
if([encodedObject isTypeOfClass: SpendrList]){
//Add to iVar array to show in collection view
}
}
Multiple Lists [in isolation]?
Sounds like documents. Specifically, if you have multiple chunks of data that are held in relative isolation, even if identical in structure, you should likely manage and persist them separately. Potentially, you might have a master document that keeps track of the inventories of other documents.
That is, persist those lists to separate files.
Fortunately, iOS has an entire infrastructure for helping you with this; see UIDocument.

Accessing object from array within an array (defined in model class)

I suspect the solution to what I'm trying to do is fairly straight forward--yet, I'm unable to get it working myself.
Here's what I'm trying to do: I've built a card game that is initialized with the cards when the game loads. Now, some of those cards have certain properties (card type, card name, as well as a special array). The card type and card name objects are fairly easy to retrieve since they're just one object (and I can call using objectatindex). However, the special array contains several keywords that fluctuate depending on which card is chosen. So instead of initializing these keywords one by one (like I did for card type and card name), I put them into their own special array.. or an array within an array. Here's my code:
itemObjects class:
#synthesize cardName=_cardName;
#synthesize cardType=_cardType;
-(id) initWithCardName:(NSString*)cardName initWithCardType:(NSString*)cardType initWithSpecialArray:(NSArray*) specialArray{
self=[super init];
if (self){
_cardName=cardName;
_cardType=cardType;
}
return self;
}
model class
-(NSMutableArray*)deck{
if (_deck==nil){
_deck=[[NSMutableArray alloc]initWithObjects:
[[itemObjects alloc]initWithCardName:#"The Long Way" initWithCardType:#"bill" initWithSpecialArray:[[NSArray alloc]initWithObjects:#"fast", #"high", nil]],
[[itemObjects alloc]initWithCardName:#"A Short Cut" initWithCardType:#"bill" initWithTrendArray:[[NSArray alloc]initWithObjects:#"small", #"tall", nil]],nil];
View Controller class (this is where I'm trying to call one of the objects, "fast" for example, but with no success
NSString* testing=[[[self.model.deck objectAtIndex:indexPath.row]arrayForKey:#"specialArray"]objectAtIndex:0];
NSLog(#"%#",testing);
I believe I've initialized my "specialArray" correctly and the issue is with how I'm attempting to call it but if I've made a mistake there, any advice would be much appreciated. Thanks!
EDIT: This particular issue has been solved thanks to WendiKidd. It turned out that I wasn't initializing my specialArray correctly. This has led to a separate issue which I have linked to here. I've also posted my corrected code below for those interested in the future:
#synthesize cardName=_cardName;
#synthesize cardType=_cardType;
#synthesize specialArray=_specialArray;
-(id) initWithCardName:(NSString*)cardName initWithCardType:(NSString*)cardType initWithSpecialArray:(NSArray*) specialArray{
self=[super init];
if (self){
_cardName=cardName;
_cardType=cardType;
_specialArray=specialArray;
}
return self;
}
You aren't initializing your special array at all. You pass it to initWithCardName, but you're never setting it to anything. You need to store the special array inside that class, just like you do with cardName and cardType.
Secondly, I can't make heads or tails of the line where you're trying to access the special array. You haven't given us the proper information to see where the object is being stored to tell if you're even properly accessing a card object, but I definitely don't see where you've ever used the key #"specialArray" before, so there's no reason to expect that will return anything. However the rest of your data structure works, at some point you're going to have an object of whatever class the initWithCardName function initializes (for example purposes I'm going to go ahead and call it CardObject). To get the info from the special array, you're going to have to save an object called specialArray into the CardObject class, as already mentioned. Then you can write something like this:
CardObject* card = //[whatever you have to do to access the right card object]
for(NSString* specialAttribute in card.specialArray)
{
NSLog(#"Special Attribute: %#", specialAttribute);
}
And that should print all the special attributes of the card quite nicely.
If you really do just want the first item in the list, as your example was trying to access, this should work just fine:
CardObject* card = //[whatever you have to do to access the right card object]
NSLog(#"Special Attribute: %#", [card.specialArray objectAtIndex:0]);

Get NSManagedObject attribute validation regular expression

In my data model, some of the attributes have regular expressions used for data validation. There are places in my code that I would like to use those same regular expressions.
In the interest of keeping my common regular expressions in one place, I was hoping either to set these regexes in code or to retrieve them from the data model in code.
Is there a way to do this?
I want to access the Reg. Ex. property, shown below, in code.
From a NSEntityDescription you can get its attributes with the method attributesByName. Then you can use the NSPropertyDescription methods validationPredicates and setValidationPredicates:withValidationWarnings:. I assume that a predicate is created under the hood when you set the validation regex in your datamodel file...
I am not completely sure about this, but I think you can only set these values when you are creating your core data model, not once you have your core data stack set up. Is that what you want to do?
Absolutely. Everything you do in the model editor can be done or modified in code by manipulating your NSManagedObjectModel object.
Locate where the model is retrieved in your core data stack setup (maybe in your app delegate). Before returning the model, modify it in code, using constants you can #define in a central include file.
Read all about the object model's API here. More precisely, you set the model's entities after modifying an entity description, by changing the validationPredicates of one of its attributes.
I marked e1985's answer as accepted, since that's the answer that led me here. Here's the code I used to get the predicate. It's in a category for NSEntityDescription.
- (NSPredicate*)getValidationPredicateForAttribute:(NSString*)attributeName
{
NSAttributeDescription* emailAttribute = [self.attributesByName objectForKey:attributeName];
NSArray* validationPredicates = [emailAttribute validationPredicates];
if(validationPredicates.count > 0)
{
return [validationPredicates objectAtIndex:0];
}
return nil;
}

Unsure of how to manage data in ios app

I hope this question isn't too general/ambiguous...
I'm writing an iphone quiz game app and am having trouble figuring out the best way to handle data. Currently I am thinking of having a single Model class that holds an array of "User" classes which each have an array of user-specific "Question" classes. I'd like to be able to access the overarching Model from any of my view controllers, but that means I'll probably have to pass the model object to any new view controller, use a singleton, or do something else. What is the best way to access my Model object from other classes? Another factor I'm not sure about is being able to save the data - would I have to use Core Data/SQLite to save my single Model object, or is there a simpler way?
I'd start by designing a schema using CoreData. IMO, its best to start out using CoreData because then you'll never have to convert your data layer to CoreData, in the event that your app scales beyond a simple object or two.
The other route would be to create a web service that returns your data... so you just call the service and it returns a collection of user objects. You can either send down the entire object graph with the questions, or create another service to return a collection of questions for a specific user. If you have a web server handy, this method scales the best because you don't have to rely on app updates to get new questions into your system. I would still use CoreData to cache the results... so that way you're not downloading the same information all the time.
So when it comes to accessing CoreData objects, I use a repository class that's a singleton. This makes it easy for any view controller to grab an instance of the repository and get some data. Here's what something like that might look like;
[[Repository defaultRepository] findFirst:[User class]
where:#"name == 'John'"]
There's a lot of redundant code to fetch data so wrapping that up in an object will help get all that nasty code, like predicates and sorting, out of your view controllers. You can see where I leverage a va_list in the where clause so I can inject that string right into my predicate. Here are some other methods you could implement:
- (NSArray *) findAll:(Class)entity
sortByKey:(NSString *)key
ascending:(BOOL)ascending;
- (NSArray *) findAll:(Class)entity
sortByKey:(NSString *)key
ascending:(BOOL)ascending
where:(NSString *)format, ...;
- (id) findFirst:(Class)entity
where:(NSString *)format, ...;
I'm not sure if this is the preferred way, but I've had a lot of success with this method. Hope this helps!
Check this link, this will help you a lot
Link: http://mobile.tutsplus.com/tutorials/iphone/iphone-sdk_store-data/
This cover 4 major ways to store data in iPhone with sample code.
1) NSUserDeafult
2) Property Lists
3) SQLLite
4) Core Data

Communication between Model and Controller - iOS

I am new to iOS development, so I would appreciate some feedback.
I am trying to build an iOS client for my web service. So far this is what I have done:
I am implementing two views (Utility-based app using Storyboard). In the main view, I use a text field and a search button, where the user can enter a query and then click the search button. Once the search button is clicked, my intention is to read the value of the text field, and use it in my Restful call to my web service. My web service replies back with a JSON file with the query results, which I parse and show to the secondary view's text area.
I know how to do the restful call in iOS and how to do the JSON parsing as well as displaying the results on the screen (at least the text stuff, but that's another different question). But my intention is to learn and implement MVC basics to my application.
According to MVC, the controller updates the view, and the model sends out a notification broadcast which the controller can listen to and know if there are any changes in the object. So this is what I would ideally like to do:
My Model - My model would handle the core RESTful call, get the JSON reply, parse it and get the resulting values that I want to display on the view.
My Controller - I would like my controller to listen to my model and obtain the resulting values from Model and display them on View.
Using a quick and dirty way, I can implement the RESTful call, JSON parsing and displaying resulting values - all inside the Controller, but with this technique, if my view changes tomorrow, then I have to re-write my code. Or if I want to add new features, then I have to change my controller. So ideally I would like to have a core Model that's not aware of how View looks like, and just let's the Controller take the results from Model and display them on View.
From what I have read from Google search results so far, two ways of doing this is by a) Key Value Observation and b) Notification center.
For last 2 days, I am trying to find a good decent way to implement Notification center or read more about it, I am not getting a good lead. Some of the questions I have is, can I send out the String results value using Notification center that my controller picks up? How does Notification Center really work with string values? Where can I find some good examples?
So any help regarding this will be very much appreciated.
Some of the questions I have is, can I send out the String results
value using Notification center that my controller picks up?
Yes, that would commonly done using the userInfo property of a NSNotification. userInfo is a plain NSDictionary that may contain instances of NSObject derived objects indexed by keys that are adhering to the NSCopying protocol (commonly NSString is used). Note that the dictionary (userInfo) will retain your parameter object/s.
How does Notification Center really work with string values?
Well, that depends on how you want it to work. But nitpicking aside, see below.
Where can I find some good examples?
Maybe this one helps...
Example
The receiver (controller) registers for the notification:
- (void)registerForNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(modelObjectUpdatedString:)
name:#"StringUpdated"
object:nil];
}
The sender (model) notifies the world:
- (void)stringUpdateWith:(NSString *)theString
{
self.string = theString;
[[[NSNotificationCenter defaultCenter] postNotificationName:#"StringUpdated"
object:self
userInfo:[NSDictionary dictionaryWithObjectsAndKeys:self.string, #"String", nil]];
}
The receiver (controller) receives the notification within its handler:
- (void)modelObjectUpdatedString:(NSNotification *)notification
{
ModelObject *postingObject = [notification object];
NSString *string = [[notification userInfo]
objectForKey:#"String"];
...
}
You're thinking along the right path, but still not entirely. As Till "points out" in his comment, you should not handle the RESTful communication inside your model. If I were you, I would create a utility class responsible for fetching the information, and then a class responsible for holding the data(this last class is your model).
It would be clever to create a class method that allocates and initiates a new instance of this object, created from the JSON data fetched through your RESTful communicator class.
From your controller point of view:
RESTHelper *rest = [RESTHelper restHelperWithURL:yourRESTURL];
YourModel *model = [YourModel modelWithJSON:[rest fetchObjectWithID:1]];
// Present your models data in the view.
You may benefit from using CoreData here, and I strongly encourage you to look into that.

Resources