Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 8 years ago.
Improve this question
I have a same set of coding that have to be used in different view controller.what I have to do, to avoid duplication of coding in every view controller.I couldn't find the exact solution in google.Can any one help me please.
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
//NSLog(#"%d",rowno);
NSString *urlString=[NSString stringWithFormat:#"http://www.tranzlogix.com/tranzlogix_webservice/vehiclelist.php?format=json"];
NSURL *url=[NSURL URLWithString:urlString];
NSData *data=[NSData dataWithContentsOfURL:url];
NSError *error;
//NSLog(#"%#",data);
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
//NSLog(#"%#",json);
results = [json valueForKey:#"posts"];
//NSLog(#"%#", results);
//NSLog(#"Count %d", results.count);
NSArray *res = [results valueForKey:#"post"];
//NSLog(#"%#", res);
Vehicle_No=[res valueForKey:#"vehicle_no"];
//NSLog(#"%#", Vehicle_No);
Vehicle_No_Org =[Vehicle_No objectAtIndex:rowno];
NSString *CellText=[NSString stringWithFormat:#"%#",Vehicle_No_Org];
//NSLog(#"%#",CellText);
//MAP VIEW WebService
NSString *urlMapString=[NSString stringWithFormat:#"http://www.tranzlogix.com/tranzlogix_webservice/map.php?format=json&truckno=%#",CellText];
//NSLog(#"%#",urlMapString);
NSURL *urlMap=[NSURL URLWithString:urlMapString];
NSData *dataMap=[NSData dataWithContentsOfURL:urlMap];
NSError *errorMap;
//NSLog(#"%#",dataMap);
NSDictionary *jsonMap = [NSJSONSerialization JSONObjectWithData:dataMap options:kNilOptions error:&errorMap];
//NSLog(#"%#",jsonMap);
NSArray *resultsMap = [jsonMap valueForKey:#"posts"];
NSLog(#"%#", resultsMap);
//NSLog(#"Count %d", resultsMap.count);
NSArray *resMap = [resultsMap valueForKey:#"post"];
//NSLog(#"%#", resultsMap);
NSArray *latitudeString=[resMap valueForKey:#"latitude"];
NSLog(#"%#", latitudeString);
NSString *latOrgstring = [latitudeString objectAtIndex:0];
NSLog(#"%#", latOrgstring);
double latitude=[latOrgstring doubleValue];
//NSLog(#"latdouble: %f", latitude);
NSArray *longitudeString=[resMap valueForKey:#"longitude"];
NSLog(#"%#", longitudeString);
NSString *longOrgstring = [longitudeString objectAtIndex:0];
NSLog(#"%#", longOrgstring);
double longitude=[longOrgstring doubleValue];
NSLog(#"latdouble: %f", longitude);
This is what i need in more than two view controller one in map view and next in table view...
Create a base view controller with your main code and create subclasses of it.
For example, your main view controller would be:
#interface MainViewController : UIViewController
And then subclass it:
#interface OneViewController : MainViewController
Those subclasses will inherit the code.
Here you can use Custom Delegates in ios..
just create Custom Delegate(Protocol) in AppDelegate or Singleton Class.Here im using AppDelegate
#import <UIKit/UIKit.h>
**AppDeleagte.h**
#protocol parserDelegate <NSObject>
-(void)sendDataToCorrespondingViewController:(id)data andServiceName:(NSString *)serviceName;
#end
#interface AppDelegate : UIResponder <UIApplicationDelegate>
{
}
#property (strong, nonatomic) UIWindow *window;
#property (nonatomic,strong) id <parserDelegate> delegate;
-(void)getMethodURL:(NSString *)urlString andServiceName:(NSString *)serviceName;
#end
**AppDeleagte.m**
//Implementing getMethod() in AppDelegate.m File
-(void)getMethodURL:(NSString *)urlString andServiceName:(NSString *)serviceName;
{
NSURL *url =[NSURL URLWithString:urlString];
NSError *error;
{
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 2), ^{
NSData *data =[[NSData alloc]initWithContentsOfURL:url];
if (data == nil) {
NSLog(#"error bcz data is nil:%#",error.localizedDescription);
}
else
{
NSError *error;
id response =[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:&error];
[self.delegate sendDataToCorrespondingViewController:response andServiceName:serviceName];
}
});
}
}
Now import AppDelegate in required ViewController and Create instance For AppDelegate
**ViewController.h**
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#interface ViewController : UIViewController<parserDelegate>
{
}
#property (nonatomic,strong) AppDelegate *appDelegate;
#end
**ViewController.m**
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize appDelegate;
- (void)viewDidLoad
{
self.appDelegate =(AppDelegate *)[[UIApplication sharedApplication] delegate];
self.appDelegate.delegate=self;
[super viewDidLoad];
}
-(IBAction)getSeviceData:(id)sender
{
//call AppDeleagte method for Webservice
[self.appDelegate getMethodURL:#"http://www.tranzlogix.com/tranzlogix_webservice/vehiclelist.php?format=json" andServiceName:#"VehicleList"];
}
//implement Deleagte method
-(void)sendDataToCorrespondingViewController:(id)data andServiceName:(NSString *)serviceName
{
NSLog(#"response data: %#",data); //Here is the response from websevice
NSLog(#"serviceName: %#",serviceName); // Here Service name differentiate,if we call 2 webservices in ViewController
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Related
I am having the view controller class like this
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#property (nonatomic, strong) NSDictionary *dictionary;
#end
ViewController.m
#import "ViewController.h"
#import "GoogleMaps.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//HOW TO ACCESS THE PROPERTY VALUE HERE
self.dictionary = #{};
/ DUMP ALL FOUND ITEMS
for(DummyContainer* geoItem in geoItems) {
NSDictionary *item = #{
#"latitude":geoItem.latitude,
#"longtitude":geoItem.longtitude
};
self.dictionary[geoItem.geoPoint.name] = item;
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
The expected format is
var container = {
'location':{
'latitude':1233,
'longtitude':124
}
}
This can be accessible via
let obj = container['location'];
for latitude access like this
obj.latitude;
Question1: How to create a class property as dictionary and access inside the class?
Question2: How to create JSON structure and access the values?
I am new to iOS please help me thanks in advance.
For creating non extendable/immutable Dictionary Object
#property (strong, nonatomic) NSDictionary *myClassDictionary;
For creating extendable/mutable Dictionary Object
#property (strong, nonatomic) NSMutableDictionary *myClassMutableDictionary;
Insert all of your values inside a Dictionary like this
You exampleData
'location':{
'latitude':1233,
'longtitude':124
}
NSDictionary *dict = #{#"lattitude":#"1233" , #"longitude":#"124"};
self.myClassDictionary = #{#"location":dict};//Convert this dictionary into JSON.
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject: self.myClassDictionary options:NSJSONWritingPrettyPrinted error:&error];
NSString jsonString;
if (! jsonData) {
NSLog(#"Got an error: %#", error);
} else {
jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
I am relatively new to iOS Development and I wanted to implement an autocomplete textfield in my application. Upon doing research, I have come across this library, MLPAutoCompleteTextField. I downloaded it, ran the Demo, and tried to understand how it works.
From what I got, the demo uses a custom class for the Array and a custom cell view that's why the autocomplete in the demo contains the flag of the country.
However, what I want to implement is a much simpler version, one that would only use an Array, no more custom classes for the data and the cell layout.
Here is what I have so far:
My FirstViewController.h File
#import <UIKit/UIKit.h>
#import "MLPAutoCompleteTextFieldDataSource.h"
#import "MLPAutoCompleteTextFieldDelegate.h"
#interface FirstViewController : UIViewController <UITextFieldDelegate, MLPAutoCompleteTextFieldDataSource, MLPAutoCompleteTextFieldDelegate>
#property (strong, nonatomic) NSArray *groupID;
#property (strong, nonatomic) NSMutableArray *part;
#property (strong, nonatomic) NSMutableArray *brand;
#property (strong, nonatomic) NSMutableArray *barcode;
#property (strong, nonatomic) NSMutableArray *itemName;
#property (weak) IBOutlet MLPAutoCompleteTextField *groupIDInput;
#property (weak) IBOutlet MLPAutoCompleteTextField *partInput;
#property (weak) IBOutlet MLPAutoCompleteTextField *brandInput;
#property (weak) IBOutlet MLPAutoCompleteTextField *barcodeInput;
#property (weak) IBOutlet MLPAutoCompleteTextField *itemNameInput;
#property (strong, nonatomic, retain) IBOutlet UIButton *searchButton;
#property (assign) BOOL testWithAutoCompleteObjectsInsteadOfStrings;
#end
As you can see, I have 5 AutoCompleteTextViews and I intend to use the 5 Arrays to supply the data for the autoCompleteTextViews.
This is my FirstViewController.m File:
#import "FirstViewController.h"
#import "FMDatabase.h"
#import "FMDatabaseAdditions.h"
#import "MLPAutoCompleteTextFieldDataSource.h"
#import "MLPAutoCompleteTextFieldDelegate.h"
#import "MLPAutoCompleteTextField.h"
#interface FirstViewController ()
#end
#implementation FirstViewController
#synthesize groupID;
#synthesize part;
#synthesize brand;
#synthesize barcode;
#synthesize itemName;
#synthesize groupIDInput;
#synthesize partInput;
#synthesize brandInput;
#synthesize barcodeInput;
#synthesize itemNameInput;
#synthesize searchButton;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad{
[super viewDidLoad];
// Do any additional setup after loading the view.
[self setType];
}
- (void)didReceiveMemoryWarning{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - MLPAutoCompleteTextField DataSource
- (void)groupIDInput:(MLPAutoCompleteTextField *)textField
possibleCompletionsForString:(NSString *)string
completionHandler:(void (^)(NSArray *))handler{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_async(queue, ^{
NSArray *completions;
if(self.testWithAutoCompleteObjectsInsteadOfStrings){
completions = [self allCountryObjects];
} else {
completions = [self allCountries];
}
handler(completions);
});
}
-(void) setType{
[self.groupIDInput setAutoCompleteTableAppearsAsKeyboardAccessory:NO];
}
- (NSArray *)allCountryObjects{
if(!self.groupID){
NSArray *countryNames = [self allCountries];
NSMutableArray *mutableCountries = [NSMutableArray new];
for(NSString *countryName in countryNames){
[mutableCountries addObject:countryName];
}
[self setGroupID:[NSArray arrayWithArray:mutableCountries]];
}
return self.groupID;
}
- (NSArray *)allCountries{
NSArray *countries =
#[/* Insert Long List of Countries Here */];
return countries;
}
#end
However, my problem now is that in the demo, there is a line that goes [self.autocompleteTextField registerAutoCompleteCellClass:[DEMOCustomAutoCompleteCell class] wherein the custom cell class is used. I get the feeling that I'm also supposed to create my own custom cell class even though I'm not implementing anything fancy.
So, question is:
Do I have to implement my own CustomAutoCompleteObject and CustomAutoCompleteCell? If not, how can I implement this library just by using simple Arrays?
Any help is appreciated. I have been working on this for the past 4-5 hours and my lack of iOS Dev knowledge is taking it's toll on me.
UPDATE 1:
I tried to use a predeclared array instead of a mutable one populated by a database query, I also made some changes as follows:
- (void)autoCompleteTextField:(MLPAutoCompleteTextField *)textField
possibleCompletionsForString:(NSString *)string
completionHandler:(void (^)(NSArray *))handler{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_async(queue, ^{
NSArray *completions;
//completions = [self allCountries];
completions = [self initializeGroupIDArray];
handler(completions);
});
}
This function is attached to the storyboard.
And my initializeGroupIDArray is as follows:
-(NSArray *)initializeGroupIDArray{
// Getting the database path.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsPath = [paths objectAtIndex:0];
NSString *dbPath = [docsPath stringByAppendingPathComponent:#"itemList.db"];
NSMutableArray *groupArray = #[/* Insert List of Countries Here */];
FMDatabase *database = [FMDatabase databaseWithPath:dbPath];
[database open];
NSString *sqlSelectQuery = #"SELECT DISTINCT GROUPID FROM ItemList";
// Query result
FMResultSet *resultsWithNameLocation = [database executeQuery:sqlSelectQuery];
while([resultsWithNameLocation next]) {
NSString *queryResult = [NSString stringWithFormat:#"%#",[resultsWithNameLocation stringForColumn:#"GROUPID"]];
// loading your data into the array, dictionaries.
NSLog(#"GroupID = %#", queryResult);
[groupArray addObject:queryResult];
}
[database close];
NSArray *groupID;
[groupID = groupArray copy];
return groupID;
}
However, it seems to me that I am not adding my results from the database query properly. Does anyone have ideas?
I didn't initialize my mutable array. Now goes goes as such:
#pragma mark - MLPAutoCompleteTextField DataSource
- (void)autoCompleteTextField:(MLPAutoCompleteTextField *)textField
possibleCompletionsForString:(NSString *)string
completionHandler:(void (^)(NSArray *))handler{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_async(queue, ^{
NSLog(#"autoCompleteTextField Entered");
NSArray *completions;
completions = [self allGroups];
handler(completions);
});
}
allGroups function:
-(NSArray *)allGroups{
NSLog(#"allGroups Entered");
// Getting the database path.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsPath = [paths objectAtIndex:0];
NSString *dbPath = [docsPath stringByAppendingPathComponent:#"itemList.db"];
NSMutableArray *groupArray = [[NSMutableArray alloc] init]; //This bad body right here.
FMDatabase *database = [FMDatabase databaseWithPath:dbPath];
[database open];
NSString *sqlSelectQuery = #"SELECT DISTINCT GROUPID FROM ItemList";
// Query result
FMResultSet *resultsWithNameLocation = [database executeQuery:sqlSelectQuery];
while([resultsWithNameLocation next]) {
NSString *groupIDName = [NSString stringWithFormat:#"%#",[resultsWithNameLocation stringForColumn:#"GROUPID"]];
// loading your data into the array, dictionaries.
NSLog(#"Group ID = %#", groupIDName);
[groupArray addObject:groupIDName];
}
[database close];
NSLog(#"size of groupArray (mutbale): %d", [groupArray count]);
for (NSUInteger i = 0; i < [groupArray count]; i++){
NSLog(#"Group Array (mutable) :%#", groupArray[i]);
}
NSArray *groupID;
[groupID = groupArray copy];
NSLog(#"size of groupID (immutbale): %d", [groupID count]);
for (NSUInteger i = 0; i < [groupID count]; i++){
NSLog(#"Group ID (non mutable) :%#", groupID[i]);
}
NSLog(#"allGroups before return statement");
return groupID;
}
I am trying to download a pdf file from a server to the device. Here is the code that I am using
- (id)initwithURL:(NSString*)remoteFileLocation andFileName:(NSString*)fileName{
//Get path to the documents folder
NSString *resourcePathDoc = [[NSString alloc] initWithString:[[[[NSBundle mainBundle]resourcePath]stringByDeletingLastPathComponent]stringByAppendingString:#"/Documents/"]];
localFilePath = [resourcePathDoc stringByAppendingString:fileName];
BOOL fileExists = [[NSFileManager defaultManager]fileExistsAtPath:localFilePath];
if (fileExists == NO) {
NSURL *url = [NSURL URLWithString:remoteFileLocation];
NSData *data = [[NSData alloc] initWithContentsOfURL: url];
//Write the data to the local file
[data writeToFile:localFilePath atomically:YES];
}
return self;
}
where remoteFileLocation is a NSString and has the value http://topoly.com/optimus/irsocial/Abs/Documents/2009-annual-report.pdf
On running the app crashes, just on NSData giving a SIGABRT error. The only useful information it gives is
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSURL length]: unrecognized selector sent to instance 0xc87b600'
How can this be fixed ?
As your PDF file is too large in size so if you do Synchronous Download, it will take too Long to download, so i insist you to create an Asynchronous Downloader and Use it. I have put code for the same.
Step 1 :Create a file 'FileDownloader.h'
#define FUNCTION_NAME NSLog(#"%s",__FUNCTION__)
#import <Foundation/Foundation.h>
#protocol fileDownloaderDelegate <NSObject>
#optional
- (void)downloadProgres:(NSNumber*)percent forObject:(id)object;
#required
- (void)downloadingStarted;
- (void)downloadingFinishedFor:(NSURL *)url andData:(NSData *)data;
- (void)downloadingFailed:(NSURL *)url;
#end
#interface FileDownloader : NSObject
{
#private
NSMutableURLRequest *_request;
NSMutableData *downloadedData;
NSURL *fileUrl;
id <fileDownloaderDelegate> delegate;
double totalFileSize;
}
#property (nonatomic, strong) NSMutableURLRequest *_request;
#property (nonatomic, strong) NSMutableData *downloadedData;
#property (nonatomic, strong) NSURL *fileUrl;
#property (nonatomic, strong) id <fileDownloaderDelegate> delegate;
- (void)downloadFromURL:(NSString *)urlString;
#end
Step 2 : Create a .m file with FileDownloader.m
#import "FileDownloader.h"
#implementation FileDownloader
#synthesize _request, downloadedData, fileUrl;
#synthesize delegate;
- (void)downloadFromURL:(NSString *)urlString
{
[self setFileUrl:[NSURL URLWithString:[urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
self._request = [NSMutableURLRequest requestWithURL:self.fileUrl cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:60.0f];
NSURLConnection *cn = [NSURLConnection connectionWithRequest:self._request delegate:self];
[cn start];
}
#pragma mark - NSURLConnection Delegate
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
if([delegate respondsToSelector:#selector(downloadingStarted)])
{
[delegate performSelector:#selector(downloadingStarted)];
}
totalFileSize = [response expectedContentLength];
downloadedData = [NSMutableData dataWithCapacity:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[downloadedData appendData:data];
if([delegate respondsToSelector:#selector(downloadProgres:forObject:)])
{
[delegate performSelector:#selector(downloadProgres:forObject:) withObject:[NSNumber numberWithFloat:([downloadedData length]/totalFileSize)] withObject:self];
}
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
if([delegate respondsToSelector:#selector(downloadingFailed:)])
{
[delegate performSelector:#selector(downloadingFailed:) withObject:self.fileUrl];
}
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
if([delegate respondsToSelector:#selector(downloadingFinishedFor:andData:)])
{
[delegate performSelector:#selector(downloadingFinishedFor:andData:) withObject:self.fileUrl withObject:self.downloadedData];
}
}
#end
Step 3 : Import file #import "FileDownloader.h" and fileDownloaderDelegate in your viewController
Step 4: Define following Delegate methods in .m file of your viewCOntroller
- (void)downloadingStarted;
- (void)downloadingFinishedFor:(NSURL *)url andData:(NSData *)data;
- (void)downloadingFailed:(NSURL *)url;
Step 5 : Create Object of FileDownloader and set URL to Download thats it.
FileDownloader *objDownloader = [[FileDownloader alloc] init];
[objDownloader setDelegate:self];
[objDownloader downloadFromURL:#"Your PDF Path URL here];
Step 6 : Save your file where you want in
- (void)downloadingFinishedFor:(NSURL *)url andData:(NSData *)data; method.
It appears that your remoteFileLocation parameter value is really an NSURL object and not an NSString. Double check how you get/create remoteFileLocation and verify it really is an NSString.
There are also several other issues with this code. The proper way to create a path to the Documents directory is as follows:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = paths[0];
NSString *localFilePath = [resourcePathDoc stringByAppendingPathComponent:fileName];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:localFilePath];
if (!fileExists) {
NSURL *url = [NSURL URLWithString:remoteFileLocation];
NSData *data = [[NSData alloc] initWithContentsOfURL:url];
//Write the data to the local file
[data writeToFile:localFilePath atomically:YES];
}
GCD can be used for massive files. You can download the file synchronous on a second thread and post back to the main thread if you like. You can also use Operation queues.
You can indeed also use the delegate method from NSURLConnection allowing you to handle the callbacks on the main thread. It is however obsolete to define your own delegate since you can just implement the delegate from NSURLConnection itself.
I'm working on my diploma project, which includes an iOS client with a Core Data database and a Ruby on Rails server. I'm using RestKit for the communication between them. Currently I'm having a big issue getting the whole system to work: as I try to map a response to objects from the server, I get the following exception:
2013-02-08 22:40:43.947 App[66735:5903] *** Assertion failure in -[RKManagedObjectResponseMapperOperation performMappingWithObject:error:], ~/Repositories/App/RestKit/Code/Network/RKResponseMapperOperation.m:358
2013-02-08 23:04:30.562 App[66735:5903] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Unable to perform mapping: No `managedObjectContext` assigned. (Mapping response.URL = http://localhost:3000/contacts?auth_token=s78UFMq8mCQrr12GZcyx)'
*** First throw call stack:
(0x1de9012 0x1c0ee7e 0x1de8e78 0x16a4f35 0x8f56e 0x8d520 0x1647d23 0x1647a34 0x16d4301 0x23a253f 0x23b4014 0x23a52e8 0x23a5450 0x90ac6e12 0x90aaecca)
libc++abi.dylib: terminate called throwing an exception
I'm trying to load a list (an array) of contacts from the server, which should be saved as "Users" in Core Data.
I've structured all my Core Data code in a Data Model class, like I saw in this video: http://nsscreencast.com/episodes/11-core-data-basics. Here it is:
Header file:
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#interface AppDataModel : NSObject
+ (id)sharedDataModel;
#property (nonatomic, readonly) NSManagedObjectContext *mainContext;
#property (nonatomic, strong) NSManagedObjectModel *managedObjectModel;
#property (nonatomic, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;
- (NSString *)modelName;
- (NSString *)pathToModel;
- (NSString *)storeFilename;
- (NSString *)pathToLocalStore;
#end
Implementation file:
#import "AppDataModel.h"
#interface AppDataModel ()
- (NSString *)documentsDirectory;
#end
#implementation AppDataModel
#synthesize managedObjectModel = _managedObjectModel;
#synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
#synthesize mainContext = _mainContext;
+ (id)sharedDataModel {
static AppDataModel *__instance = nil;
if (__instance == nil) {
__instance = [[AppDataModel alloc] init];
}
return __instance;
}
- (NSString *)modelName {
return #"AppModels";
}
- (NSString *)pathToModel {
return [[NSBundle mainBundle] pathForResource:[self modelName]
ofType:#"momd"];
}
- (NSString *)storeFilename {
return [[self modelName] stringByAppendingPathExtension:#"sqlite"];
}
- (NSString *)pathToLocalStore {
return [[self documentsDirectory] stringByAppendingPathComponent:[self storeFilename]];
}
- (NSString *)documentsDirectory {
NSString *documentsDirectory = nil;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
documentsDirectory = [paths objectAtIndex:0];
return documentsDirectory;
}
- (NSManagedObjectContext *)mainContext {
if (_mainContext == nil) {
_mainContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
_mainContext.persistentStoreCoordinator = [self persistentStoreCoordinator];
}
return _mainContext;
}
- (NSManagedObjectModel *)managedObjectModel {
if (_managedObjectModel == nil) {
NSURL *storeURL = [NSURL fileURLWithPath:[self pathToModel]];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:storeURL];
}
return _managedObjectModel;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (_persistentStoreCoordinator == nil) {
NSLog(#"SQLITE STORE PATH: %#", [self pathToLocalStore]);
NSURL *storeURL = [NSURL fileURLWithPath:[self pathToLocalStore]];
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc]
initWithManagedObjectModel:[self managedObjectModel]];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
NSError *e = nil;
if (![psc addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:storeURL
options:options
error:&e]) {
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:e forKey:NSUnderlyingErrorKey];
NSString *reason = #"Could not create persistent store.";
NSException *exc = [NSException exceptionWithName:NSInternalInconsistencyException
reason:reason
userInfo:userInfo];
#throw exc;
}
_persistentStoreCoordinator = psc;
}
return _persistentStoreCoordinator;
}
#end
The User class is pretty straightforward, auto-generated with xCode.
Header file:
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#interface User : NSManagedObject
#property (nonatomic, retain) NSString * email;
#property (nonatomic, retain) NSString * firstName;
#property (nonatomic, retain) NSString * lastName;
#property (nonatomic, retain) NSNumber * userID;
#end
Implementation file:
#import "User.h"
#implementation User
#dynamic email;
#dynamic firstName;
#dynamic lastName;
#dynamic userID;
#end
Just like the data model class, I have a server manager class which I use for communication:
Header file:
#import <Foundation/Foundation.h>
#import <RestKit/RestKit.h>
#import "AppServerProtocol.h"
#import "AppDataModel.h"
#interface AppServer : NSObject <AppServerDelegate>
+ (id)sharedInstance;
#property (strong, nonatomic) RKObjectManager *objectManager;
#property (strong, nonatomic) RKEntityMapping *userMapping;
#end
And implementation file:
#import "AppServer.h"
#import "User.h"
#import "Device.h"
#import "Ping.h"
#import "AppAppDelegate.h"
#interface AppServer ()
#property BOOL initialized;
#end
#implementation AppServer
+ (id)sharedInstance {
static AppServer *__instance = nil;
if (__instance == nil) {
__instance = [[AppServer alloc] init];
__instance.initialized = NO;
}
if (![__instance initialized]) {
[__instance initServer];
}
return __instance;
}
- (void)initServer {
// initialize RestKit
NSURL *baseURL = [NSURL URLWithString:#"http://localhost:3000"];
_objectManager = [RKObjectManager managerWithBaseURL:baseURL];
// enable activity indicator spinner
[AFNetworkActivityIndicatorManager sharedManager].enabled = YES;
// initialize managed object store
_objectManager.managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:[[AppDataModel sharedDataModel] managedObjectModel]];
_userMapping = [RKEntityMapping mappingForEntityForName:#"User" inManagedObjectStore:_objectManager.managedObjectStore];
[_userMapping addAttributeMappingsFromDictionary:#{
#"email" : #"email",
#"firstName" : #"first_name",
#"lastName" : #"last_name"
}];
[_userMapping setIdentificationAttributes: #[#"userID"]];
RKResponseDescriptor *contactsResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:_userMapping pathPattern:#"/contacts" keyPath:nil statusCodes:nil];
[_objectManager addResponseDescriptor:contactsResponseDescriptor];
_initialized = YES;
}
// contacts
- (void)getContactsForCurrentUser {
NSString *authToken = [[NSUserDefaults standardUserDefaults] objectForKey:#"AppAuthenticationToken"];
[_objectManager getObjectsAtPath:#"/contacts" parameters:#{#"auth_token": authToken} success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
RKLogInfo(#"Load collection of contacts: %#", mappingResult.array);
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
RKLogError(#"Operation failed with error: %#", error);
}];
}
#end
So when I open the Contacts Table View, which is set up correctly to use a fetched results controller (successfully pulling entities out of the DB), I have a dangerous refresh button, which calls the method you've just read above:
- (void)downloadContacts {
[[AppServer sharedInstance] getContactsForCurrentUser];
}
Here is the format of the response:
[
{
"created_at":"2013-01-11T14:03:57Z",
"email":"john#example.com",
"first_name":"John",
"id":2,
"last_name":"Doe",
"updated_at":"2013-02-07T10:57:16Z"
},
{
"created_at":"2013-01-11T14:03:57Z",
"email":"jane#example.com",
"first_name":"Jane",
"id":3,
"last_name":"Doe",
"updated_at":"2013-02-07T10:57:16Z"
}
]
And before the exception the console states the following:
2013-02-08 22:40:36.892 App[66735:c07] I restkit:RKLog.m:34 RestKit logging initialized...
2013-02-08 22:40:36.994 App[66735:c07] SQLITE STORE PATH: ~/Library/Application Support/iPhone Simulator/6.0/Applications/D735548F-DF42-4E13-A7EF-53DF0C5D8F3B/Documents/AppModels.sqlite
2013-02-08 22:40:37.001 App[66735:c07] Context is ready!
2013-02-08 22:40:43.920 App[66735:c07] I restkit.network:RKHTTPRequestOperation.m:154 GET 'http://localhost:3000/contacts?auth_token=s78UFMq8mCQrr12GZcyx'
2013-02-08 22:40:43.945 App[66735:c07] I restkit.network:RKHTTPRequestOperation.m:181
The line of the RestKit library, that fails before the whole exception is thrown is:
NSAssert(self.managedObjectContext, #"Unable to perform mapping: No `managedObjectContext` assigned. (Mapping response.URL = %#)", self.response.URL);
I have followed that back to the initServer method in the AppServer.m file, in which, before the method returns, the properties of the RKObjectManager class are like this: http://imgur.com/LM5ZU9m
As I have debugged, I've traced that the problem is not with the server side or the communication of the app - I can see the JSON received and deserialized into an array, but the moment it's passed to the next method which is supposed to save it to Core Data, the whole app goes kaboom because of the NSAssert of the managed object context.
Any help is greatly appreciated!
After a few days of debugging, I finally found out what went wrong: it looks like I also had to set the path to my local persistence store and generate managed object contexts for the managed object store myself.
Here's where I found the solution: https://github.com/RestKit/RestKit/issues/1221#issuecomment-13327693
I've just added a few lines in my server init method:
NSError *error = nil;
NSString *pathToPSC = [[AppDataModel sharedDataModel] pathToLocalStore];
_objectManager.managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:[[AppDataModel sharedDataModel] managedObjectModel]];
[_objectManager.managedObjectStore addSQLitePersistentStoreAtPath:pathToPSC fromSeedDatabaseAtPath:nil withConfiguration:nil options:nil error:&error];
if (error != nil) {
NSLog(#"\nSerious object store error!\n");
return;
} else {
[_objectManager.managedObjectStore createManagedObjectContexts];
}
I managed to do it using this function RKApplicationDataDirectory() to get the application directory and set my database path.
// Initialize HTTPClient
NSURL *baseURL = [NSURL URLWithString:#"http://myapiaddress.com"];
AFHTTPClient* client = [[AFHTTPClient alloc] initWithBaseURL:baseURL];
//we want to work with JSON-Data
[client setDefaultHeader:#"Accept" value:RKMIMETypeJSON];
// Initialize RestKit
RKObjectManager *objectManager = [[RKObjectManager alloc] initWithHTTPClient:client];
NSURL *modelURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"NameOfMyCoreDataModel" ofType:#"momd"]];
//Iniitalize CoreData with RestKit
NSManagedObjectModel *managedObjectModel = [[[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL] mutableCopy];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
NSError *error = nil;
NSString *path = [RKApplicationDataDirectory() stringByAppendingPathComponent:#"nameOfDB.sqlite"];
objectManager.managedObjectStore = managedObjectStore;
[objectManager.managedObjectStore addSQLitePersistentStoreAtPath:path fromSeedDatabaseAtPath:nil withConfiguration:nil options:nil error:&error];
[objectManager.managedObjectStore createManagedObjectContexts];
Apple rejected our app siting that page loads times between tabs was too long. Before I was simply calling a webview to display content managed through a CMS. Now we have implemented JSON and I am tring to preload the 5 tabs' data using the singleton design pattern. I can't seem to set the singleton value as I see in examples. On to the code:
header.h
#import <UIKit/UIKit.h>
#interface FirstViewController : UIViewController {
NSString *someProperty;
...
}
#property (nonatomic, retain) NSString *someProperty;
+ (id)sharedManager;
#property (strong, nonatomic) NSString* tab3data;
#end
Implementation.m
//Create a seperate thread to download JSON thread
#define kBgQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) //1
//Set JSON URL
#define GWDiOSURL [NSURL URLWithString:#"http://m.web.org/cms_mapper.php"]
#import "FirstViewController.h"
#interface FirstViewController ()
#end
#implementation FirstViewController
#synthesize someProperty;
- (id)init {
if (self = [super init]) {
someProperty = #"Default Property Value";
}
return self;
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.title = NSLocalizedString(#"First", #"First");
self.tabBarItem.image = [UIImage imageNamed:#"first"];
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
FirstViewController *sharedManager = [FirstViewController sharedManager];
NSLog(#"Toll%#",sharedManager);
// Do any additional setup after loading the view, typically from a nib.
//Get JSON and load into 'data'
dispatch_async(kBgQueue, ^{
NSData* data = [NSData dataWithContentsOfURL:GWDiOSURL];
[self performSelectorOnMainThread:#selector(fetchedData:) withObject:data waitUntilDone:YES];
});
}
//Begin JSON Data Parsing and loading
- (void)fetchedData:(NSData *)responseData {
//parse out the json data
NSError* error;
//Parse JSON
NSDictionary* json = [NSJSONSerialization
JSONObjectWithData:responseData
options:kNilOptions
error:&error];
// Load JSON into a dictionary
NSDictionary *tabData = [json objectForKey:#"mapper"];
// Get Tab3 data from dictionary
NSDictionary *tab3 = [tabData objectForKey:#"#tab3_content"];
// Load Tab3 data into a string from dictionary
NSString *html = [NSString stringWithFormat:#"%#",tab3];
// Verify content via counsel
//NSLog(#"Second Data:%#",html);
// Load content into webView
[webView loadHTMLString:html baseURL:nil];
[FirstViewController sharedManager].someProperty = #"asdf";
}
+ (id)sharedManager {
static FirstViewController *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}
I need to set the value of html to the singleton. The follow line
[FirstViewController sharedManager].someProperty = #"asdf";
produces this error
Propery 'someProperty' not found on object of type 'id'.
I have been trying to get this whole process to work for days.. I appreciate the insight.
Well, your class method, sharedManager, returns an id. Try returning FirstViewController* in sharedManager.
+ (FirstViewController *)sharedManager;