I have a UITableView populated from a JSON data feed using an NSDictionary and NSArray and I am trying to search the UITableView data using this code, but it is not working some other related info based on how I set it up is in this question Data from JSON Parsing straight to UITableView
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
// Update the filtered array based on the search text and scope.
// Remove all objects from the filtered search array
[self.filteredCandyArray removeAllObjects];
// Filter the array using NSPredicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.name contains[c] %#",searchText];
NSArray *tempArray = [airportsArray filteredArrayUsingPredicate:predicate];
NSLog(#" text %#", searchText);
filteredCandyArray = [NSMutableArray arrayWithArray:tempArray];
NSLog(#"NSLog %#", scope);
}
This is how the data is parsed
NSString* path = #"https://api.flightstats.com/flex/airports/rest/v1/json/active?appId=532ca6af&appKey=50d6d3351e5953152b2688f10d10d276";
NSMutableURLRequest* _request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:path]];
[_request setHTTPMethod:#"GET"];
NSURLResponse *response = nil;
NSError *error = nil;
NSData* _connectionData = [NSURLConnection sendSynchronousRequest:_request returningResponse:&response error:&error];
if(nil != error)
{
NSLog(#"Error: %#", error);
}
else
{
NSMutableDictionary* json = nil;
if(nil != _connectionData)
{
json = [NSJSONSerialization JSONObjectWithData:_connectionData options:NSJSONReadingMutableContainers error:&error];
}
if (error || !json)
{
NSLog(#"Could not parse loaded json with error:%#", error);
}
else
{
routeRes = [json objectForKey:#"airports"];
airportsArray = [json objectForKey:#"airports"];
}
_connectionData = nil;
NSLog(#"connection done");
}
NSLog(#" end array %#", candyArray);
// Initialize the filteredCandyArray with a capacity equal to the candyArray's capacity
filteredCandyArray = [NSMutableArray arrayWithCapacity:[airportsArray count]];
// Reload the table
[NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES];
[[self tableView] reloadData];
You can manually build the array via the build in loop methods,
NSMutableArray *filteredResults = [NSMutableArray new];
[airportsArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj.name rangeOfString: searchText].location != NSNotFound) {
[filteredResults addObject:obj];
}
}];
self.filteredCandyArray = [NSArray arrayWithArray:filteredResults];
You will need to replace the id obj with what ever object model you are using. For example if you had a plane model you might do Plane *plane in replace of id obj.
I might add how to use it via predicates later, but this should do what you request. You may view the enumerateObjectsUsingBlock: and arrayWithArray: methods in the NSArray documentation. rangeOfString: can be viewed in the NSString documentation.
-NSArray Documentation
-NSString Documentation
Related
I have an issue with NSPRedicate; it returns nil value. But my ObjectType sees it; maybe problem in predecateFormat?
I have 3 objects and I get it by Type. Source:
typedef NS_ENUM(NSUInteger, ObjectType) {
FirstType,
SecondType,
ThdType,
};
Then I process with CoreData request:
- (NSArray *)objectsByType:(ObjectType)type{
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[self objectEntity]];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"self.objectType == %d", type];
[request setPredicate:pred];
NSError *err = nil;
NSArray *objects = [[self managedContext] executeFetchRequest:request error:&err];
if(err){
DLog(#"Failed to fetch objects %#", [err localizedDescription]);
}
return objects;
}
I think problem is here.
Set your type to an NSNumber as that is what is stored in Core Data. #"self.objectType == %#", #(type)
I want to use a JSON file to send app data updates. The data are managed objects.
However:
I don't want to overwrite existing data (that will likely include user generated records and modifications), and
I want this operation to be as swift as possible (no pun.. I'm still in Obj-C).
Can someone point me in the right direction here..
This is how I'm starting the app now:
NSError* err = nil;
NSArray *jsonArray = [[NSArray alloc] init];
NSString* dataPath = [[NSBundle mainBundle] pathForResource:#"Words" ofType:#"json"];
jsonArray = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:dataPath]
options:kNilOptions
error:&err];
NSManagedObjectContext *context = [self managedObjectContext];
[jsonArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
WordEntity *word = [NSEntityDescription
insertNewObjectForEntityForName:#"WordEntity"
inManagedObjectContext:context];
word.greekText = [obj objectForKey:#"greektext"];
word.englishText = [obj objectForKey:#"englishtext"];
word.uid = [NSString stringWithFormat:#"%#", [obj objectForKey:#"uid"]];
word.category = [obj valueForKey:#"category"];
NSError *error;
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
}];
}
Thanks for any help..
I have a NSManagedObjectModel with an entity, "Project". I get all the projects to show them in a UITableView with NSFetchedResult controller. Now if the JSON has new Projects I insert them and if the JSON has updated items I update the items in the Core Data context.
So, my problem is when I get a JSON with less items than the context. I have thought about two ways to delete items in my context. One way is Delete all the context and save it again with the new items. The other way is create an array with all the items in the context and check it with the items in the JSON by id and if there is not one item, then remove it.
I have the idea, but I don't know which is the best way. I have thought also in a backgroundContext.
I use this method right now with out deleting methods:
#pragma mark - Project List service
- (void)getProjectListWithCpompletionBlock:(CompletionBlock)completionBlock{
NSMutableURLRequest *request = [self requestWithMethod:#"GET" path:kAPIProjectList parameters:nil];
[request setTimeoutInterval:kTimeOutRequest];
AFJSONRequestOperation *requestOperation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
NSDictionary *projects = [JSON valueForKey:kTagProjects];
for (NSDictionary *projectDic in projects) {
Project *pro = [Project getProjectWithId: [projectDic objectForKey:kTagProjectId] ];
if (pro) {
[Project updateProjectWithDictionary:projectDic];
NSLog(#"update %# ",[projectDic objectForKey:kTagProjectId]);
} else {
[Project createProjectWithDictionary: projectDic];
NSLog(#"create %# ",[projectDic objectForKey:kTagProjectId]);
}
}
[ypCoreDataManager saveContext];
if (completionBlock) {
completionBlock(NO, nil);
}
}
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *aError, id JSON) {
NSLog(#"%# Failure JSNON Error%#", NSStringFromSelector(_cmd), aError);
if (completionBlock) {
completionBlock(YES, aError);
}
}];
[self enqueueHTTPRequestOperation:requestOperation];
}
Project+Helper is my project category and here it is the code.
+ (Project *)createProjectWithDictionary:(NSDictionary *)dic {
Project *project = nil;
project = [NSEntityDescription insertNewObjectForEntityForName:#"Project" inManagedObjectContext:mainContext];
project.projectId = [NSNumber numberWithInt:[[dic valueForKey:kTagProjectId] intValue]];
project.title = [[dic valueForKey:kTagProjectTitle]description];
project.estimatedPrice = [NSNumber numberWithInt:[[dic valueForKey:kTagProjectEstimatedPrice] floatValue]];
NSMutableArray *tags = [[NSMutableArray alloc] init];
tags = [dic objectForKey:kTagProjectsTags];
NSMutableSet *tagSet = [[NSMutableSet alloc]init];
for (NSDictionary * tagDic in tags){
NSString *tagName = [tagDic objectForKey:kTagProjectTagName];
Tag *tag = [Tag insertTagName:tagName inManagedObjectContext:mainContext];
[tagSet addObject:tag];
}
[project addTags:tagSet];
return project;
}
// Return project by id
+ (Project *)getProjectWithId:(NSString *) projectId {
Project *project = nil;
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Project"];
request.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"projectId" ascending:YES]];
request.predicate = [NSPredicate predicateWithFormat:#"projectId = %#", [projectId description]];
// Execute the fetch
NSError *error = nil;
NSArray *matches = [mainContext executeFetchRequest:request error:&error];
if (!matches || ([matches count] > 1)) { // nil means fetch failed; more than one impossible (unique!)
// handle error
} else { // found the Project, just return it from the list of matches (which there will only be one of)
project = [matches lastObject];
}
return project;
}
// Update project
+ (Project *)updateProjectWithDictionary:(NSDictionary *)dic {
Project *project = nil;
// Build a fetch request to see if we can find this Project in the database.
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Project"];
request.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"title" ascending:YES]];
request.predicate = [NSPredicate predicateWithFormat:#"projectId = %#", [dic[kTagProjectId]description]];
// Execute the fetch
NSError *error = nil;
NSArray *matches = [mainContext executeFetchRequest:request error:&error];
// Check what happened in the fetch
if (!matches || ([matches count] > 1)) { // nil means fetch failed; more than one impossible (unique!)
// handle error
} else {
project = [matches lastObject];
project.projectId = [NSNumber numberWithInt:[[dic valueForKey:kTagProjectId] intValue]];
project.title = [[dic valueForKey:kTagProjectTitle]description];
project.estimatedPrice = [NSNumber numberWithInt:[[dic valueForKey:kTagProjectEstimatedPrice] floatValue]];
NSMutableArray *tags = [[NSMutableArray alloc] init];
tags = [dic objectForKey:kTagProjectsTags];
NSMutableSet *tagSet = [[NSMutableSet alloc]init];
for (NSDictionary * tagDic in tags){
NSString *tagName = [tagDic objectForKey:kTagProjectTagName];
Tag *tag = [Tag insertTagName:tagName inManagedObjectContext:mainContext];
[tagSet addObject:tag];
}
[project addTags:tagSet];
}
return project;
}
You have to add this method in your Project's category and in your code after you add the new item call this method where you pass you array objects living in the Core Data and it remove all the objects more that you haven't in the array
+(void)removeExpiredProjectBy:(NSMutableArray *)ProjectLiving inContext:(NSManagedObjectContext *)context{
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Project"];
if (projectLiving.count) {
request.predicate = [NSPredicate predicateWithFormat:#"NOT (projectId IN %#)", [projectLiving copy]];
NSError *error = nil;
NSArray *matches = [context executeFetchRequest:request error:&error];
if (matches.count != 0) {
for (Project *pro in matches) {
[context deleteObject:pro];
}
}
}
}
As you process the JSON you can build a list of all of the ids that have been added / updated. Then, after that is complete you can create a fetch request with a predicate which finds all items where NOT (id IN %#) and supply the list of ids. This will return you only the items that need to be deleted.
Or, from an efficient API point of view, the server should give you a deletion list because it doesn't need to confirm items that haven't changed if you send it a 'last request date'...
Trying to populate core data structure using JSON,
The code is listed below?
NSManagedObjectContext *context = managedObjectContext();
// Save the managed object context
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Error while saving %#", ([error localizedDescription] != nil) ? [error localizedDescription] : #"Unknown Error");
exit(1);
}
NSError* err = nil;
NSString* dataPath = [[NSBundle mainBundle] pathForResource:#"Exercises" ofType:#"json"];
NSArray* Exercises = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:dataPath]
options:kNilOptions
error:&err];
NSLog(#"Imported Exercises: %#", Exercises);
NSManagedObject *object = [NSEntityDescription insertNewObjectForEntityForName:#"Exercise" inManagedObjectContext:context];
NSString *theJSONString = #"{\"key\":\"value\"}";
NSError *theError = NULL;
NSDictionary *jsonDict = [NSDictionary dictionaryWithJSONString:theJSONString error:&theError];
Exercise *exercise = [NSEntityDescription insertNewObjectForEntityForName:#"Exercise"
inManagedObjectContext:context];
[Exercises enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSDictionary *attributes = [[object entity] attributesByName];
for (NSString *attribute in attributes) {
id value = [jsonDict objectForKey:attribute];
if (value == nil) {
continue;
}
[exercise setValue:value forKey:attribute];
}
NSError *error;
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
}];
The code compiles and after analysing the sqlite database created, all attributes are filled with null values.
NSString* secondDataPath = [[NSBundle mainBundle] pathForResource:#"Weights" ofType:#"json"];
NSArray* weightsFromJSON = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:secondDataPath]
options:kNilOptions
error:&err];
NSLog(#"Imported weightsFromJSON: %#", weightsFromJSON);
[weightsFromJSON enumerateObjectsUsingBlock:^(NSDictionary *weightDictionary, NSUInteger idx, BOOL *stop) {
Weight *weight = [NSEntityDescription insertNewObjectForEntityForName:#"Weight"
inManagedObjectContext:context];
NSDictionary *attributes = [[weight entity] attributesByName];
for (NSString *attribute in [attributes allKeys]) {
id value = [weightDictionary objectForKey:attribute];
if (value == nil) {
continue;
}
[weight setValue:value forKey:attribute];
}
NSError *error;
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
}];
Edited the code above for the second entity, but fails stating data parameter is hill
You're enumerating every object loaded from the JSON data on disk that you put into Exercises (as an aside, don't capitalize the names of local variables) but then you are not using those objects as the data source, instead you're using jsonDict which is hard-coded from a string and has only one key/value pair. Try changing the line
id value = [jsonDict objectForKey:attribute];
To instead say:
id value = [(NSDictionary *)obj objectForKey:attribute];
That should actually apply the data you loaded. However, there are other problems in your code - you only insert a single entity, not one for each element in Exercises, as well as inserting an extraneous one that you assign to object. Here's some code that I think may do what you're trying to do:
NSError* err = nil;
NSString* dataPath = [[NSBundle mainBundle] pathForResource:#"exercisesFromJSON" ofType:#"json"];
NSArray* exercisesFromJSON = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:dataPath]
options:kNilOptions
error:&err];
NSLog(#"Imported exercisesFromJSON: %#", exercisesFromJSON);
[exercisesFromJSON enumerateObjectsUsingBlock:^(NSDictionary exerciseDictionary, NSUInteger idx, BOOL *stop) {
Exercise *exercise = [NSEntityDescription insertNewObjectForEntityForName:#"Exercise"
inManagedObjectContext:context];
NSDictionary *attributes = [[exercise entity] attributesByName];
for (NSString *attribute in [attributes allKeys]) {
id value = [exerciseDictionary objectForKey:attribute];
if (value == nil) {
continue;
}
[exercise setValue:value forKey:attribute];
}
NSError *error;
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
}];
I have two arrays, one is an array from a fetch request of core-data (array1), the other is from data that is being pulled from the web(array2). What I want to do is compare array1 to array2 and any items that are in array2 that are not in array1 need to be added to core-data.
The data I'm pulling from has id's associated with each person. When I create a new "person" entity I save this id with it as well. I'm not sure how to compare the arrays using the Person's id, or even how to access that within an array.
Here is the fetch request:
NSFetchRequest *fetch = [[NSFetchRequest alloc] init];
NSError *error = nil;
[fetch setEntity:[NSEntityDescription entityForName:#"Person" inManagedObjectContext:self.managedObjectContext]];
NSSortDescriptor *sorter = [NSSortDescriptor sortDescriptorWithKey:#"Pid" ascending:YES];
request.sortDescriptors = #[sorter];
NSArray *items = [self.managedObjectContext executeFetchRequest:request error:&error];
I have the fetch sorted in the same way array2 with the new data is sorted. I'm just not sure how to compare the two and then add the new items into core-data. Please help?
Update:
NSDictionary *qDict = [JSON objectForKey:#"person"];
NSArray *qArry = [JSON objectForKey:#"person"];
//Used to print the id's
_testArray = [NSMutableArray arrayWithArray:[qDict valueForKey:#"id"]];
for (NSNumber *numb in _testArray) {
NSLog(#"id = %#", numb);
}
NSError *error = nil;
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Person"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"Pid IN %#", [qDict valueForKey:#"Pid"]];
[fetchRequest setPredicate:predicate];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"Pid" ascending:YES]]];
NSArray *items = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
NSLog(#"%d how many items from the fetch", items.count);
for (NSDictionary *qdt in qArry) {
NSUInteger currentIndex = 0;
Person *q = nil;
if ([items count] > currentIndex) {
q = [items objectAtIndex:currentIndex];
}
if ([q.Pid integerValue] == [[qdt objectForKey:#"id"] integerValue]) {
// Either update the object or just move on
}
else {
// Add the object to core data
q = [NSEntityDescription insertNewObjectForEntityForName:#"Person" inManagedObjectContext:self.managedObjectContext];
q.url = [qdt valueForKey:#"url"];
q.Pid = [qdt objectForKey:#"id"];
q.text = [qdt valueForKey:#"personText"];
NSError *error = nil;
[_managedObjectContext save:&error];
}
currentIndex++;
}
//[_managedObjectContext save:&error];
}
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
UIAlertView *av = [[UIAlertView alloc] initWithTitle:#"Error retrieving data" message:#"Please try again" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[av show];
}];
First you can fetch the list of objects stored in your core data based on person's IDs that you retrieve from the web (idArray) and sort them based on the ID:
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Person"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"id IN %#", idArray];
[fetchRequest setPredicate:predicate];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"id" ascending:YES]]];
This will return you the list of objects that the id can also be found in the objects you retrieve from the web. Let us call this storedRecords array. Then you can do the following:
Set a counter,
Iterate over the downloadedArray (this is the array containing the objects retrieved from the web and it must be sorted by id too),
Use objectAtIndex in the storedRecords array,
Check the id of the storedManagedObject if it matches the id of the record object
if it does not match, it is a new object that you can save into core data. Otherwise, it is an existing object.
Increase the counter.
Here is an example:
int currentIndex = 0;
for (NSDictionary *record in downloadedArray) {
NSManagedObject *storedManagedObject = nil;
if ([storedRecords count] > currentIndex) {
storedManagedObject = [storedRecords objectAtIndex:currentIndex];
}
if ([[storedManagedObject valueForKey:#"id"] integerValue] == [[record valueForKey:#"id"] integerValue]) {
//this will be existing object in core data
//you can update the object if you want
} else {
//this will be new object that you can store into core data
}
currentIndex++;
}
This is similar to what is mentioned here:
http://www.raywenderlich.com/15916/how-to-synchronize-core-data-with-a-web-service-part-1
maybe you can do something like this:
NSMutableArray *webData = [NSMutableArray arrayWithObjects:#"object 1",#"object 2",#"object 3",#"object 4", nil];
NSMutableArray *coreData = [NSMutableArray arrayWithObjects:#"object 2",#"object 4", nil];
NSMutableArray *newData = [NSMutableArray arrayWithArray:webData];
[newData removeObjectsInArray:coreData];
//output "object 1", "object 3"
or if you have a custom NSObject inside the array and compare it with its primary key maybe you can do something like:
NSMutableArray *webData = [NSMutableArray arrayWithObjects:test1,test2,test3,test4, nil];
NSMutableArray *coreData = [NSMutableArray arrayWithObjects:test2,test3, nil];
__block NSMutableArray *newData = [NSMutableArray new];
[webData enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
__block testObject *object = (testObject *)obj;
__block BOOL isExisting = NO;
[coreData enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
testObject *innerObject = (testObject *)obj;
if(object._id == innerObject._id)
isExisting = YES;
}];
if(!isExisting)
[newData addObject:object];
}];
//output will be object 1 and object 4
Try this
NSMutableArray *array1 = [[NSMutableArray alloc] initWithObjects:#"a",#"a",#"a",#"d",#"b",#"b",#"c",#"a",#"c",nil];
NSMutableArray *array2 = [[NSMutableArray alloc] initWithObjects:[NSString stringWithFormat:#"%#",[array1 objectAtIndex:0]], nil];;
for(int i = 0;i<[array1 count];i++)
{
if ([array2 containsObject:[array1 objectAtIndex:i]]) {
NSLog(#"do nothing");
}
else{
[array2 addObject:[array1 objectAtIndex:i]];
NSLog(#"array2 is %#",array2);
}
NSLog(#"finally array2 is %#",array2);
}
array2 will be as { a,d,b,c }