Unable to refresh UITableView with modified data - objective-c

I have a iPad app, using XCode 4.5, Storyboards, Core Data and iOS 6. I select a row, make a change to the contents of the record (which is successful), but the row doesn't change. I have tried to refresh the UITableView, but cellForRowAtIndexPath is never called. I have searched SO and Google to no avail; I don't see what's wrong. Can someone please tell me how to fix this? (with an explanation of what I'm doing wrong for the next time?)
Here is the pertinent code:
- (IBAction)btnModify:(UIButton *)sender {
//NSLog(#"btnModify clicked");
NSManagedObjectContext *localContext = [NSManagedObjectContext MR_contextForCurrentThread];
// find client by primary telephone number
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"aClientPrimaryPhone ==[c] %#", cvPrimaryPhone.text];
ClientInfo *clientDataFound = [ClientInfo MR_findFirstWithPredicate:predicate inContext:localContext];
if(clientDataFound) {
clientDataFound.aClientName = cvCustName.text; // now start moving the data
clientDataFound.aClientAddr1 = cvAddress1.text;
clientDataFound.aClientAddr2 = cvAddress2.text;
clientDataFound.aClientCity = cvContactCity.text;
clientDataFound.aClientPostalCode = cvPostalCode.text;
clientDataFound.aClientCellPhone = cvCellPhone.text;
clientDataFound.aClientPrimaryPhone = cvPrimaryPhone.text;
clientDataFound.aClientEMail = cvPersonalEmail.text;
clientDataFound.aClientNotes = cvNotes.text;
[localContext MR_saveNestedContexts];
[self reloadClientList];
}
}
-(void) reloadClientList {
//Init Array to hold TableView Data
tableDataArray = [NSMutableArray new];
[tableDataArray addObjectsFromArray:[ClientInfo findAll]]; // Load
[self.clientList reloadData];
}
and this is ClientInfo.m
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#interface ClientInfo : NSManagedObject
#property (nonatomic, retain) NSString * aClientAddr1;
#property (nonatomic, retain) NSString * aClientAddr2;
#property (nonatomic, retain) NSString * aClientCellPhone;
#property (nonatomic, retain) NSString * aClientCity;
#property (nonatomic, retain) NSString * aClientEMail;
#property (nonatomic, retain) NSData * aClientImage;
#property (nonatomic, retain) NSString * aClientName;
#property (nonatomic, retain) NSString * aClientNotes;
#property (nonatomic, retain) NSString * aClientPostalCode;
#property (nonatomic, retain) NSString * aClientPrimaryPhone;
#end

I found it... my "clientList" was NOT connected to the object... don't know how I missed that one!

There are a few reasons I can think of:
Your table view reference clientList is nil. (not connected)
Your table view's DataSource & Delegate is not set (Actually I'm not sure if it compiles when DataSource is not set)
Your table view is a subclass of UITableView and in that subclass reloadData method is overridden.

Related

How to do deep copying Objective-C

There is a class Patient with properties:
#property (strong, nonatomic) NSString *name;
#property (strong, nonatomic) Symptoms *symptoms;
#property (assign, nonatomic) Status status;
#property (weak, nonatomic) id <PatientDelegate> delegate;
There is a class Symptoms with properties:
#property (assign, nonatomic) CGFloat temperature;
#property (assign, nonatomic) BOOL headache;
#property (assign, nonatomic) BOOL stomach_ache;
Both classes implement protocol NSCopying:
- (nonnull id)copyWithZone:(nullable NSZone *)zone {
Patient *newPatient = [[[self class] allocWithZone:zone] init];
[newPatient setName:self.name];
[newPatient setSymptoms:self.symptoms];
[newPatient setStatus:self.status];
[newPatient setDelegate:self.delegate];
return newPatient;
}
- (nonnull id)copyWithZone:(nullable NSZone *)zone {
Symptoms *newSymptoms = [[[self class] allocWithZone:zone] init];
[newSymptoms setTemperature:self.temperature];
[newSymptoms setHeadache:self.headache];
[newSymptoms setStomach_ache:self.stomach_ache];
return newSymptoms;
}
Also there is a class Doctor:
#property (strong, nonatomic) NSString *name;
#property (strong, nonatomic) NSMutableArray *history;
- (void)makeNoteIn:(Patient *)patient card:(NSMutableArray *)history;
- (void)report;
When patient gets well, doctor calls method makeNoteIn:
- (void)makeNoteIn:(Patient *)patient card:(NSMutableArray *)history {
Patient *newRecord = [patient copy];
[history addObject:newRecord];
}
After the record is made, all property of the patient return to the original values. While we are in the method makeNoteIn and the current patient is proceeded, in history there is link to this object which has correct property values. As soon as we exit the method or start to proceed another patient, all the property values reset to the initial value.
I tried to realise copying, but something still wrong.
When you want to deep-copy an object, you have to implement copy on all the substructures:
[newPatient setName:[self.name copy]];
[newPatient setSymptoms:[self.symptoms copy]];
Otherwise they will still reference the same object and changing one will affect all.
Note that you can do that automatically by declaring the properties as copy:
#property (copy, nonatomic) NSString *name;
#property (copy, nonatomic) Symptoms *symptoms;
It's common to use copy with NSString and NSArray to prevent assigning NSMutableString and NSMutableArray which could be changed externally by mistake. Make sure you implement NSCopying on Symptoms.

How can I cast my NSURLSessionDownloadTask to my custom NSURLSessionDownloadTask (inheritance)?

I have created a custom NSURLSessionDownloadTask named VJSessionTask and I have just added some custom things like a type (enum) and a custom object (id):
#interface VJSessionTask : NSURLSessionDownloadTask
typedef enum types
{
LS, LSH, DL, UL, RM, TH
} type;
#property enum types type;
#property (strong, nonatomic) id customObject;
#property (strong, nonatomic) NSString *progressNotif;
#property (strong, nonatomic) NSString *doneNotif;
#property (strong, nonatomic) NSURL *tmpFile;
#end
And when I do this:
VJSessionTask *taskSession = (VJSessionTask *)[self.prioritySession downloadTaskWithRequest:listFileRequest];
// init taskSession with its type
taskSession.type = LS;
I get this error:
-[__NSCFLocalDownloadTask setType:]: unrecognized selector sent to instance 0x1556198f0
Then I come to you as I don't understand or I don't know how to do that...
Thank you in advance ;)
NSURLSessionTasks are not strictly speaking subclass-able unfortunately. This is evident in that the system can queue a data task and return a NSCFLocalDownloadTask (presumably meaning that the task will return its content from the cache).
The best way to go about doing this is to borrow on from the architectural decision of AFNetworking and have individual taskDelegates that monitor all the responses an individual task works on. Then when you want to find the data relating to a task you can query your dictionary of taskDelegates. Each task has a unique identifier that you can use to key your dictionary with.
In AFNetworking you can see the taskDelegate is defined as follows:
#interface AFURLSessionManagerTaskDelegate : NSObject <NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate>
#property (nonatomic, weak) AFURLSessionManager *manager;
#property (nonatomic, strong) NSMutableData *mutableData;
#property (nonatomic, strong) NSProgress *progress;
#property (nonatomic, copy) NSURL *downloadFileURL;
#property (nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading;
#property (nonatomic, copy) AFURLSessionTaskCompletionHandler completionHandler;
#end
#implementation AFURLSessionManagerTaskDelegate
and subsequently retrieved as follows:
- (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task {
NSParameterAssert(task);
AFURLSessionManagerTaskDelegate *delegate = nil;
[self.lock lock];
delegate = self.mutableTaskDelegatesKeyedByTaskIdentifier[#(task.taskIdentifier)];
[self.lock unlock];
return delegate;
}
See this post for more info

CoreData How to connect with relationship entity to another entity with already preloaded data?

This is my first application in CoreData so I definitely missing something here, but I spend over a week trying to find solution and apparently nobody on Internet had the same problem ;)
I'm creating something like deckbuilder app.:
My model looks like this: coredatamodel
The relationship is to-one and inverse to-many ( every card can be in one set, and in every set can be many cards) from CARD to SET.
For sake of simplicity I want to focus on CARD and SET entities.
I preloaded data into SET from CSV file into 5 atributes - didnt fill relationship "karty" because I didn't know how to do this. There are about 25 records in this entity.
And now, problem is I am trying to fill CARDS Entity which will have over 500 records.
Trying few options I found on SO I end with connection that created 500 records in SET too:/ so it looked like sql "JOIN" command.
What I want is load data to CARDS and connect them somehow to SET not changing number of records in SET.
If i have property (nonatomic, retain) NSSet *karty; what exactly NSSet means? It is s et but set of what? Set of single rows from Card entity? Set of Card objects? Set of NSStrings? Set of NSManagedObjects?
In normal SQL primary key to connect them would be "nazwaszort" so Card.nazwaszort=Set.nazwaszort.
My Set.h
#class Card;
#interface Set : NSManagedObject
#property (nonatomic, retain) NSNumber * cykl;
#property (nonatomic, retain) NSNumber * czymajor;
#property (nonatomic, retain) NSString * nazwa;
#property (nonatomic, retain) NSString * nazwashort;
#property (nonatomic, retain) NSNumber * nrwcyklu;
#property (nonatomic, retain) NSSet *karty;
#end
#interface Set (CoreDataGeneratedAccessors)
- (void)addKartyObject:(Card *)value;
- (void)removeKartyObject:(Card *)value;
- (void)addKarty:(NSSet *)values;
- (void)removeKarty:(NSSet *)values;
#end
My Card.h
#class Frakcja, Kolekcja, Set;
#interface Card : NSManagedObject
#property (nonatomic, retain) NSNumber * czylimit;
#property (nonatomic, retain) NSString * frakcja;
#property (nonatomic, retain) NSString * icesila;
#property (nonatomic, retain) NSNumber * iloscwsecie;
#property (nonatomic, retain) NSNumber * influence;
#property (nonatomic, retain) NSString * kodkarty;
#property (nonatomic, retain) NSNumber * koszt;
#property (nonatomic, retain) NSNumber * minimumdecksize;
#property (nonatomic, retain) NSString * nazwa;
#property (nonatomic, retain) NSString * nazwasetu;
#property (nonatomic, retain) NSString * nazwaszort;
#property (nonatomic, retain) NSNumber * nrcyklu;
#property (nonatomic, retain) NSString * podtyp;
#property (nonatomic, retain) NSString * strona;
#property (nonatomic, retain) NSNumber * trashkoszt;
#property (nonatomic, retain) NSString * typ;
#property (nonatomic, retain) Kolekcja *ilewkolekcja;
#property (nonatomic, retain) Frakcja *nazwafrakcji;
#property (nonatomic, retain) Set *wjakimsecie;
#end
I preloaded cards to SET using method
-(void) preloadDataPackInfoToDatabase {
NSError *error=nil;
NSString *sciezka = [[NSBundle mainBundle]pathForResource:#"nrsets" ofType:#"csv"];
NSArray *rows = [NSArray arrayWithContentsOfCSVFile:sciezka];
for (int i=1; i <=([rows count]-1); i++) {
Set *nowyDataPack = [NSEntityDescription insertNewObjectForEntityForName:[entityset name] inManagedObjectContext:_contextdp];
NSLog(#"tablica wierszy %#",rows[i][2]);
NSString *koddodatku = rows[i][2];
NSLog(#"kod dodatku:%#",koddodatku);
NSString *nrwcyklu = rows[i][4];
NSString *nrcyklu = rows[i][3];
NSString *nazwadatapack =rows[i][3];
NSString *czymajor =rows[i][0];
[nowyDataPack setValue:nazwadatapack forKey:#"nazwa"];
NSNumberFormatter *f = [[NSNumberFormatter alloc]init];
[f setNumberStyle:NSNumberFormatterDecimalStyle];
NSNumber *nrwcykluint = [f numberFromString:nrwcyklu];
NSNumber *nrcykluint = [f numberFromString:nrcyklu];
NSNumber *czymajorbool = [f numberFromString:czymajor];
[nowyDataPack setValue:nrwcykluint forKey:#"nrwcyklu"];
[nowyDataPack setValue:nrcykluint forKey:#"cykl"];
[nowyDataPack setValue:koddodatku forKey:#"nazwashort"];
[nowyDataPack setValue:czymajorbool forKey:#"czymajor"];
}
if (![ self.contextdp save:&error]) {
NSLog(#"Nieznany błąd %#,%#",error,[error userInfo]);
}
}
And another method (on pastebin to not flood your screens --> preloadAllCardsToDatabase
Thanks in advance for any help.
OK I finally did it. Had to move some CoreData stuff outside loop.
Still dont understand this whole magic but at least it works.
for those interested this is my preloading function --> here

RestKit 0.20 mapping string array

So after reading this answer, I do not see how my case is any different, but the proposed solution just doesn't seem to work in RestKit 0.20.3
For the following json:
{
"photos": [
"872ac577-3f31-47a0-966e-6f2ed2fbabcd"
],
"imageUrl": [
"http://domain/WebAPI/FileUpload/10232013629_21382571406.439790.jpeg"
]
}
Mapping into the following object:
#class Picture, Site, Survey;
#interface Job : NSManagedObject
#property (nonatomic, retain) NSString * jobId;
#property (nonatomic, retain) NSString * jobName;
#property (nonatomic, retain) NSString * jobStatusId;
#property (nonatomic, retain) NSString * jobStatus;
#property (nonatomic, retain) NSString * projectId;
#property (nonatomic, retain) NSString * projectType;
#property (nonatomic, retain) NSString * siteId;
#property (nonatomic, retain) NSSet *surveys;
#property (nonatomic, retain) NSSet *photos;
#property (nonatomic, retain) NSDate *createdDate;
#property (nonatomic, retain) Site *site;
#end
Where photos is a Picture object that looks like this:
#interface Picture : NSManagedObject
#property (nonatomic, retain) NSData * data;
#property (nonatomic, retain) NSString * photoId;
#property (nonatomic, retain) NSString * pictureLoc;
#property (nonatomic, retain) NSString * imageUrl;
#property (nonatomic, retain) Job *job;
#property (nonatomic, retain) QuestionAnswer *question;
#end
I want to map the photos and imageUrl into the Picture's photoId and imageUrl, respectively. I thought I could use the following in my Job's mapping:
RKEntityMapping *ret = [RKEntityMapping mappingForEntityForName:#"Job" inManagedObjectStore:[RKObjectManager sharedManager].managedObjectStore ];
[ret addAttributeMappingsFromDictionary: #{
#"photos": #"photos.photoId",
#"imageUrl": #"photos.imageUrl",
}];
However, the mapped Job always ends up with nothing in the photos property.
I know that I am doing all the in-between steps correctly (like actually using ret for the mapping), as the JSON is a snippet that contains many other keys I am able to map with no problem. What am I doing wrong? please tell me
EDIT
Changed the mapping code as per Wain's answer:
RKEntityMapping *ret = [RKEntityMapping mappingForEntityForName:#"Job" inManagedObjectStore:[RKObjectManager sharedManager].managedObjectStore ];
RKEntityMapping *subMapping = [RKEntityMapping mappingForEntityForName:#"Picture" inManagedObjectStore:[RKObjectManager sharedManager].managedObjectStore ];
[subMapping addAttributeMappingsFromDictionary: #{
#"photos":#"photoId",
#"imageUrl":#"imageUrl"
}];
[ret addPropertyMapping:[RKRelationshipMapping
relationshipMappingFromKeyPath:nil
toKeyPath:#"photos"
withMapping:subMapping]];
And now I get a Picture object added, but with nil for photoId and imageUrl (and yes, I checked to make sure I have the spelling and capitalization correct for the JSON keys)
The reason photos never has anything in it is that your mapping doesn't tell it to create a new object.
The problem is how you could tell it to create a single object and set both of the parameters into it. The problem is because you have 2 different arrays. The JSON I would expect you to have is 1 array which contains dictionaries with all of the details for that particular entry. RestKit can process both of the arrays but it would result in 2 distinct photo objects being created. To do this you need a second mapping with a nil key path and a relationship from ret to that new mapping.
An alternative could be to change the Job object so that it has 2 transformable (or transient) properties which are NSArrays. If you set ret to just map #"photos" : #"photos" and #"imageUrl" : #"imageUrl" then RestKit will map all of the data, just not into a photo object. You can then mutate this data after the mapping has completed if you want to...

Getting exc_bad_access in UITableViewController while getting object from NSMutableArray

I have a subclass of UITableViewController, and I init the subclass with a NSMutableArray of another custom class:
#import <UIKit/UIKit.h>
#import "NUBCheckpointModel.h"
#interface NUBUserCheckpointModel : NSObject
#property (nonatomic,assign) NSString* objId;
#property (nonatomic,assign) NSString* userId;
#property (nonatomic,assign) NSString* checkpointId;
#property (nonatomic,assign) NSDate* dateAdded;
#property (nonatomic,assign) NUBCheckpointModel* checkpoint;
+ (NUBUserCheckpointModel*) fromJson: (NSString*)json;
#end
This array that is generated from another ViewController, gets passed into this subclassed TableViewController, of which contain this property
#property (nonatomic,retain) NSMutableArray* userCheckpointData;
This property is set like this:
- (id)initWithFrame: (CGRect)frame withType: (TableType)typeOfTable fromParent: (UIViewController*)parent data: (NSMutableArray*)ucpData
{
self = [self init];
if (self) {
self.tableView = [[UITableView alloc] initWithFrame:frame];
self.parentController = parent;
self.userCheckpointData = ucpData;
[self styleTable];
[self addPullToRefreshHeader];
typeCategory = typeOfTable;
}
return self;
}
All is fine up to this part, and any manipulation including trying to get an object from the array works fine. I tested it.
The code I used to test the array is:
NUBUserCheckpointModel* model = [self.userCheckpointData objectAtIndex:0];
NSLog(model.objId);
However, this very same code, when used here:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
Gives me exc_bad_access. May I know why this happens? I can't seem to figure out why. I'm using ARC btw. Thank you.
While adding the property, you need to take care of the memory management. For string, it is not good practice to set assign property.
Instead , do as following,
#property (nonatomic,copy) NSString* objId;
#property (nonatomic,copy) NSString* userId;
#property (nonatomic,copy) NSString* checkpointId;
#property (nonatomic,retain) NSDate* dateAdded;
#property (nonatomic,retain) NUBCheckpointModel* checkpoint;