Can anybody explain to me how MVC works when it comes to UITableView especially when getting data from the internet.
I would exactly like to know what is the model,
In terms of UITableViewController, typically all the roles Model, View and Controller (MVC) is played by your UITableViewController itself. That is the case with your code as well.
Now, to adopt a different approach you could have Model separated out from your controller class. For that have a subclass from NSObject and have it set its state which could be used by Controller.
Hope this makes sense to you.
It's a pretty big question you are asking. But let me answer by making it as simple as possible.
There are a lot of approaches to coordinating between your web data and your table view but one I might suggest would be to refactor your web service calls into a separate store class - say iTunesStore - have that class be responsible for making the calls to the service and setting an internal array with the results, it should also be able to return a row count as well as a specific item for a given row index.
You then have this class respond to calls for the required table view delegate methods. Other things to consider, make this other class a singleton, have it conform to UITableviewDatasource protocol itself and assign it as the table views' data source.
Like I said, a big question with a lot of options for you, but I've given you some things to consider in terms of where to go next.
UPDATE
I'm adding some code examples to help clarify. At the outset, I want to make clear that I am not going to provide the total solution because doing so would require me to assume too much in terms of the necessary actual solution -- and because there are a few different ways to work with AFNetworking, web services, etc....and I don't want to get side tracked going down that rabbit hole. (Such as caching data on the client, background tasks & GCD, etc...) Just showing you how to wire up the basics -- but you will definitely want to learn how to use AFNetworking on a background task, look into Core Data or NSCoding for caching, and a few other topics to do this sort of thing correctly.
Suffice it to say that in a proper solution:
- You don't want to be calling your web service synchronously
- You also don't want to be re-requesting the same data every time - ie don't re-download the same record from the service unless its changed
- I am not showing how to do those things here because its way beyond the scope; look a the book recommendation below as well as this link to get an idea about these topics Ray Wenderlich - sync Core Data with a web service
For your data services code, I would create a 'store' class. (do yourself a favor and get the Big Nerd Ranch iOS book if you don't already have it.
iOS Programming 4th Edition
Take the following the code with a grain of salt - for reasons I can't go into I am not able to do this from my Mac (on a Win machine) and I also am not able to copy or even email myself the code ... so I am doing all in the StackOverflow editor...
My iTunesStore contract (header file) would look something like:
// iTunesStore.h
#import <Foundation/Foundation.h>
@interface iTunesStore : NSObject
- (NSUInteger)recordCount;
- (NSDictionary*)recordAtIndex:(NSUInteger)index; // could be a more specialized record class
+ (instancetype)sharedStore; // singleton
@end
...and the implementation would look something like:
// iTunesStore.m
#import "iTunesStore.h"
// class extension
@interface iTunesStore()
@property (nonatomic, strong) NSArray* records;
@end
@implementation iTunesStore
-(id)init
{
self = [super init];
if(self) {
// DO NOT DO IT THIS WAY IN PRODUCTION
// ONLY FOR DIDACTIC PURPOSES - Read my other comments above
[self loadRecords];
}
return self;
}
- (NSUInteger)recordCount
{
return [self.records count];
}
- (NSDictionary*)recordAtIndex:(NSUInteger)index
{
NSDictionary* record = self.records[index];
}
-(void)loadRecords
{
// simulate loading records from service synchronously (ouch!)
// in production this should use GCD or NSOperationQue to
// load records asynchrononusly
NSInteger recordCount = 10;
NSMutableArray* tempRecords = [NSMutableArray arrayWithCapacity:recordCount];
// load some dummy records
for(NSInteger index = 0; index < recordCount; index++) {
NSDictionary* record = @{@"id": @(index), @"title":[NSString stringWithFormat:@"record %d", index]};
[tempRecords addObject:record];
}
self.records = [tempRecords copy];
}
// create singleton instance
+ (instancetype)sharedStore
{
static dispatch_once_t onceToken;
static id _instance;
dispatch_once(&onceToken, ^{
_instance = [[[self class] alloc] init];
});
return _instance;
}
@end
I now have a 'store' object singleton I can use to get records, return a given record and also tell me a record count. Now I can move a lot of the logic doing this from the viewcontroller.
Now I don't need to do this in your VC viewDidLoad method. Ideally, you would have an async method in your store object to get records and a block to call you back once records are loaded. Inside the block you reload records. The signature for something like that 'might' look like:
[[iTunesStore sharedStore] loadRecordsWithCompletion:^(NSError* error){
... if no error assume load records succeeded
... ensure we are on the correct thread
[self.tableView reloadData]; // will cause table to reload cells
}];
Your view controller data source methods now look like:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection(NSInteger)section {
[[iTunesStore sharedStore] recordCount];
}
Inside cellForRowAtIndexPath - I also call my store object to get the correct record
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// ... get cell
// ... get record
NSDictionary* record = [[iTunesStore sharedStore] recordAtIndex:indexPath.row];
// ... configure cell]
return cell;
}
That's the gist of it. Other things to do, as noted above would be:
Hopefully this will help to give you some ideas about different directions you might explore. Happy coding!