I have a TableViewController displaying like 40 000 rows from Core Data with NSFetchedResultsController.
I implemented a live search with a UISearchDisplayController (support for IOS 7).
It's working but typing on the keyboard when searching is very slow...
I'd really appreciate if someone could point me to the right direction and show me where I might be going wrong.
Here is the UISearchResultsUpdating part in my TableViewController
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
ItemSearchScope scopeKey = controller.searchBar.selectedScopeButtonIndex;
[self searchForText:searchString scope:scopeKey];
return YES;
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption
{
NSString *searchString = controller.searchBar.text;
[self searchForText:searchString scope:searchOption];
return YES;
}
- (void)searchForText:(NSString *)searchText scope:(ItemSearchScope)scopeOption
{
if (self.managedObjectContext)
{
NSString *predicateFormat = #"%K CONTAINS[cd] %#";
NSString *searchAttribute1 = #"attribute1";
NSString *searchAttribute2 = #"attribute2";
NSString *searchAttribute3 = #"attribute3";
if (scopeOption == searchScopeDebut) {
predicateFormat = #"%K BEGINSWITH[cd] %#";
}
if (scopeOption == searchScopeFin) {
predicateFormat = #"%K ENDSWITH[cd] %#";
}
NSPredicate *p1 = [NSPredicate predicateWithFormat:predicateFormat, searchAttribute1, searchText];
NSPredicate *p2 = [NSPredicate predicateWithFormat:predicateFormat, searchAttribute2, searchText];
NSPredicate *p3 = [NSPredicate predicateWithFormat:predicateFormat, searchAttribute3, searchText];
NSPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:#[p1, p2, p3]];
[self.searchFetchRequest setPredicate:predicate];
NSError *error = nil;
self.filteredList = [self.managedObjectContext executeFetchRequest:self.searchFetchRequest error:&error];
if (error)
{
NSLog(#"searchFetchRequest failed: %#",[error localizedDescription]);
}
}
}
I ended up using a NSTimer for delaying the shouldReloadTableForSearchString method. searchTimerPopped selector is triggered only if the user stop typing the keyboard for 2 seconds.
#property (nonatomic, retain) NSTimer *searchTimer;
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
if (self.searchTimer) {
[self.searchTimer invalidate];
self.searchTimer = nil;
}
self.searchTimer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:#selector(searchTimerPopped:) userInfo:searchString repeats:FALSE];
return NO;
}
- (void)searchTimerPopped:(NSTimer *)timer {
NSString *searchString = [timer userInfo];
ItemSearchScope scopeKey = self.searchDisplayController.searchBar.selectedScopeButtonIndex;
[self searchForText:searchString scope:scopeKey];
[self.searchDisplayController.searchResultsTableView reloadData];
}
Related
This question already has answers here:
__block variable returning nil after block runs [duplicate]
(1 answer)
Returning method object from inside block
(3 answers)
Return value for function inside a block
(3 answers)
How can I retrieve a return value from a completion block?
(1 answer)
Closed 5 years ago.
In my view controller, I've got data inside of self.acceptedFriends. In my first if statement, the mutable array populates perfectly and data is present. However, in my last portion of the statement ( else if ([neighbourDetail count] > 0) ), self.acceptedFriends is empty for some reason? Any idea why this might be?
Note: This question is different than the linked examples, as
the data IS working outside of the AFNetworking block - it's populated
inside the first if statement, just not the last.
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController <UIAlertViewDelegate>
#property (nonatomic, strong) NSMutableArray *acceptedFriends;
#end
ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableDictionary *viewParams3 = [NSMutableDictionary new];
[viewParams3 setValue:#"accepted_friends" forKey:#"view_name"];
[DIOSView viewGet:viewParams3 success:^(AFHTTPRequestOperation *operation, id responseObject) {
self.acceptedFriends = (NSMutableArray *)responseObject;
[operation responseString];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failure: %#", [error localizedDescription]);
}];
if ([self.mapuserData count] > 0 ) {
NSLog(#"This is map user data %#", self.mapuserData);
NSString *thisUserId = [self.mapuserData objectForKey:#"users_name"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"node_title CONTAINS[cd] %#",
thisUserId];
NSArray *resultArray = [self.acceptedFriends filteredArrayUsingPredicate:predicate];
if (!resultArray) {
NSLog(#"Executed!");
self.addFriend.hidden = YES;
self.orangeFriendCircle.hidden = YES;
}
} else if ([self.frienduserData count] > 0) {
self.addFriend.hidden = YES;
self.orangeFriendCircle.hidden = YES;
self.username.text = self.frienduserData[#"node_title"];
self.userBio.text = self.frienduserData[#"body"];
NSString *thirdLink = self.frienduserData[#"friendphoto"];
NSString *ImageURLTwo = thirdLink;
NSData *imageDataTwo = [NSData dataWithContentsOfURL:[NSURL URLWithString:ImageURLTwo]];
self.userPhoto.image = [[UIImage alloc] initWithData:imageDataTwo];
} else if ([neighbourDetail count] > 0) {
NSString *thisUserId = [self.neighbourDetail objectForKey:#"users_name"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"node_title CONTAINS[cd] %#",
thisUserId];
NSArray *resultArray = [self.acceptedFriends filteredArrayUsingPredicate:predicate];
if ([resultArray count] > 0) {
NSLog(#"Executed!");
self.addFriend.hidden = YES;
self.orangeFriendCircle.hidden = YES;
}
}
}
My app receives a json object the first time is executed (with three pin point locations); there is a mapKit (the first screen) and a TableView where the user can check those locations. The issue is that when I first launch the app, there are no pins on the map. But if I switch to the table I can see them - on the cells - and if I switch again to the map, the pins appear...I don't Know why this happens, shouldn't I see the pins right after the app launch? The Map code:
- (void)viewDidLoad {
[super viewDidLoad];
NSNotificationCenter *notification=[NSNotificationCenter defaultCenter];
[notification addObserver:self selector:#selector (receiveNotification:) name:#"notification" object:self];
_mapView.showsUserLocation=YES;
_mapView.showsBuildings=YES;
_locationManager = [[CLLocationManager alloc] init];
[_locationManager requestAlwaysAuthorization];
_mapView.delegate = self;
_locationManager.delegate=self;
}
-(void)viewDidAppear:(BOOL)animated{
[self receiveNotification:nil];
}
-(void)receiveNotification:(NSNotification*)notification{
NSArray *spots = [Spot spotType:#"users"];
NSArray *places = [Spot spotWithType:#"users"];
[_mapView addAnnotations:spots];
[_mapView addAnnotations:places];
}
And the table:
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.dataSource = self;
self.detailList=#[#"Your Favourite Spots",#"Our suggestion"];
}
-(void)viewDidAppear:(BOOL)animated{
_lisbonSpots = [[Spot spotType:#"users"]mutableCopy];
_users=[[Spot spotWithType:#"users"]mutableCopy];
[self.tableView reloadData];
}
EDIT - The Spot Class
#implementation Spot
#dynamic ID;
#dynamic name;
#dynamic desc;
#dynamic type;
#dynamic phone;
#dynamic latitude;
#dynamic longitude;
+ (instancetype)spotWithName:(NSString *)name andCoord:
(CLLocationCoordinate2D)coord type:(NSString*)type desc:(NSString*)desc phone:(NSString*)phone{
NSPersistentContainer *persistenceContainer = [AppDelegate sharedDelegate].persistentContainer;
NSManagedObjectContext *context = persistenceContainer.viewContext;
Spot *spot = [NSEntityDescription insertNewObjectForEntityForName:#"Spot" inManagedObjectContext:context];
spot.name = name;
spot.latitude = coord.latitude;
spot.longitude = coord.longitude;
spot.type=type;
spot.desc=desc;
spot.phone=phone;
[[AppDelegate sharedDelegate] saveContext];
return spot;
}
+ (instancetype)spotWithDict:(NSDictionary *)dict {
CLLocationCoordinate2D coord = CLLocationCoordinate2DMake([dict[#"latitude"] doubleValue], [dict[#"longitude"] doubleValue]);
return [Spot spotWithName:dict[#"name"] andCoord:coord type:dict[#"type"] desc:dict[#"desc"] phone:dict[#"phone"]];
}
+ (NSArray*)getSpotType:(NSString*)type withPredicate:(NSString*) pred andMessage:(NSString*)message {
NSPersistentContainer *persistenceContainer = [AppDelegate sharedDelegate].persistentContainer;
NSPredicate* predicate = [NSPredicate predicateWithFormat:pred, type];
NSManagedObjectContext *context = persistenceContainer.viewContext;
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Spot"];
[request setPredicate:predicate];
NSError *error;
NSArray *result = [context executeFetchRequest:request error:&error];
if (error != nil) {
NSLog(message, [error localizedDescription]);
return nil;
}
return result;
}
+ (NSArray*)spotType:(NSString*)type {
return [Spot getSpotType:type withPredicate:#"type =%#" andMessage:#"[Spot spotType] -> %#"];
}
+ (NSArray*)spotWithType:(NSString*)type {
return [Spot getSpotType:type withPredicate:#"NOT (type = %#)" andMessage:#"[Spot spotWithType] -> %#"];
}
- (CLLocationCoordinate2D)coordinate {
return CLLocationCoordinate2DMake(self.latitude, self.longitude);
}
- (NSString *)title {
return self.name;
}
- (NSString *)description {
return [NSString stringWithFormat:#"%#", self.name];
}
#end
EDIT: The SpotService class
#implementation SpotService
+ (NSURL *)serviceURL {
return [NSURL URLWithString:#"http://training.reativ.io/ios/lisbon-spots"];
}
+ (BOOL)service:(id<SpotServiceInvoker>)invoker {
NSMutableURLRequest * request = [[NSMutableURLRequest alloc] initWithURL:[SpotService serviceURL]];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error != nil) {
NSLog(#"Response: %#", response);
NSLog(#"Error: %#", error);
return;
}
NSArray *lisbonSecrets = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
dispatch_async(dispatch_get_main_queue(), ^{
if ([invoker respondsToSelector:#selector(receiveSpot:)]){
[invoker receiveSpot:lisbonSecrets];
}
for(NSDictionary *dict in lisbonSecrets) {
[Spot spotWithDict:dict];
}
});
}];
[task resume];
return YES;
}
My guess is - your Spot class retrieve data asynchronously and when you call [Spot spotType:#"users"] for the first time from viewDidAppear on your MapView there is no data retrieved yet. When you switch view controller the data appears and the everything works smoothly.
But it's better to show us your Spot class. Probably your need a completion handler or something like this to achieve expected behaviour.
Also, you call addAnnotations every time when your map appears on the screen and it means that MKMapView will add a copy of the annotations each time your call this methods. It's better to add additional checks to be sure that you do not add the same annotations more than once.
Hello i am using UISerchbar in tableview but is there any easy way to create custom searchbar in tableview headerview?
i already check below link but i am not understand.
Custom UISearchBar with UISearchController
How do I use the UISearchBar and UISearchDisplayController
I have added searchDisplayController : :
And This is my code for searchController,
self.searchController = [[UISearchController alloc] initWithSearchResultsController:nil];
self.searchController.searchResultsUpdater = self;
self.searchController.dimsBackgroundDuringPresentation = NO;
self.searchController.searchBar.delegate = self;
self.definesPresentationContext = YES;
[self.searchController.searchBar setBarTintColor:[UIColor darkGrayColor]];
self.tableView.tableHeaderView = self.searchController.searchBar;
I have set delegate for UISearchBarDelegate,UISearchResultsUpdating as well,
and implemented their method ,
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
if (_searchController.isActive && _searchController.searchBar.text.length >0){
NSPredicate *resultPredicate = [NSPredicate
predicateWithFormat:#"SELF.name contains[cd] %#",
searchText];
abcd = [sortedEventArray filteredArrayUsingPredicate:resultPredicate];
NSLog(#"%#",searchResults);
}
}
- (void)updateSearchResultsForSearchController:(UISearchController *)searchController1
{
NSString *searchString = searchController1.searchBar.text;
[self filterContentForSearchText:searchString scope:#"abc"];
//[self filterContentForSearchText:searchString :#"abc"];
[self.tableView reloadData];
}
cancel button event
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar
{
abcd=[sortedEventArray mutableCopy];
[_tableView reloadData];
}
-(void)delete{
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest * allMovies = [[NSFetchRequest alloc] init];
[allMovies setEntity:[NSEntityDescription entityForName:#"Data" inManagedObjectContext:context]];
[allMovies setIncludesPropertyValues:NO]; //only fetch the managedObjectID
NSError * error = nil;
NSArray * movies = [context executeFetchRequest:allMovies error:&error];
//error handling goes here
for (NSManagedObject * movie in movies) {
[context deleteObject:movie];
}
NSError *saveError = nil;
[context save:&saveError];
}
Still Its not working
Thanks in advance :)
There is everythings is complete but there is two little mistakes by me.
1) delete the search bar from Storyboard.
2) Add [self.searchController.searchBar sizeToFit];
in viewDidLoad
Thanks :))
I am very new to Core Data and have been trying to following many tutorials, but most of them put all of the Core Data methods into AppDelegate.so any one help me please Thanks in advance
- (NSFetchedResultsController *)fetchedResultsController
{
// NSLog(#"Calling fetchedResultsController # rootviewController");
if (is_Searching && [search_string length])
{
NSManagedObjectContext *moc = [[AppDelegate appdelegate] managedObjectContext_roster];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"XMPPUserCoreDataStorageObject"
inManagedObjectContext:moc];
NSSortDescriptor *sd1 = [[NSSortDescriptor alloc] initWithKey:#"sectionNum" ascending:YES];
NSSortDescriptor *sd2 = [[NSSortDescriptor alloc] initWithKey:#"displayName" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sd1, sd2, nil];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSMutableArray * predicateArray = [[NSMutableArray alloc]init];
if (is_Searching && [search_string length]) {
NSPredicate *predecate = [NSPredicate predicateWithFormat:#"displayName CONTAINS [c] %#",search_string];
[predicateArray addObject:predecate];
}
if ([[AppDelegate get_update_privacy_Array] count]) {
for (NSString * jids in [AppDelegate get_update_privacy_Array]) {
NSPredicate *predecate_blocked = [NSPredicate predicateWithFormat:#"NOT(nickname CONTAINS [c] %# OR jidStr CONTAINS %#)" ,jids, jids];
[predicateArray addObject:predecate_blocked];
}
}
NSPredicate *predicate_final = [NSCompoundPredicate andPredicateWithSubpredicates:
predicateArray];
[fetchRequest setPredicate:predicate_final];
[fetchRequest setEntity:entity];
[fetchRequest setSortDescriptors:sortDescriptors];
[fetchRequest setFetchBatchSize:10];
fetchedResultsController_search = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:moc
sectionNameKeyPath:#"sectionNum"
cacheName:nil];
[fetchedResultsController_search setDelegate:self];
NSError *error = nil;
if (![fetchedResultsController_search performFetch:&error])
{
//DDLogError(#"Error performing fetch: %#", error);
}
if (![[fetchedResultsController_search fetchedObjects]count] && ![groupChatArray count]) {
[AppDelegate alertWithTitle:#"Alert" message:#"No contact found!"];
}
return fetchedResultsController_search;
}
else
{
NSManagedObjectContext *moc = [[AppDelegate appdelegate] managedObjectContext_roster];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"XMPPUserCoreDataStorageObject"
inManagedObjectContext:moc];
NSSortDescriptor *sd1 = [[NSSortDescriptor alloc] initWithKey:#"sectionNum" ascending:YES];
NSSortDescriptor *sd2 = [[NSSortDescriptor alloc] initWithKey:#"displayName" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sd1, sd2, nil];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSMutableArray *predicateArray = [[NSMutableArray alloc]init];
if ([[AppDelegate get_update_privacy_Array] count]) {
for (NSString * jids in [AppDelegate get_update_privacy_Array]) {
NSPredicate *predecate_blocked = [NSPredicate predicateWithFormat:#"NOT(nickname CONTAINS [c] %# OR jidStr CONTAINS %#)" ,jids, jids];
[predicateArray addObject:predecate_blocked];
}
}
NSPredicate *predicate_final = [NSCompoundPredicate andPredicateWithSubpredicates:
predicateArray];
[fetchRequest setPredicate:predicate_final];
[fetchRequest setEntity:entity];
[fetchRequest setSortDescriptors:sortDescriptors];
[fetchRequest setFetchBatchSize:10];
fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:moc
sectionNameKeyPath:#"sectionNum"
cacheName:nil];
[fetchedResultsController setDelegate:self];
NSError *error = nil;
if (![fetchedResultsController performFetch:&error])
{
//DDLogError(#"Error performing fetch: %#", error);
}
return fetchedResultsController;
}
}
XMPPUserCoreDataStorageObject.m class
#import "XMPP.h"
#import "XMPPRosterCoreDataStorage.h"
#import "XMPPUserCoreDataStorageObject.h"
#import "XMPPResourceCoreDataStorageObject.h"
#import "XMPPGroupCoreDataStorageObject.h"
#import "NSNumber+XMPP.h"
#if ! __has_feature(objc_arc)
#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
#endif
#interface XMPPUserCoreDataStorageObject ()
#property(nonatomic,strong) XMPPJID *primitiveJid;
#property(nonatomic,strong) NSString *primitiveJidStr;
#property(nonatomic,strong) NSString *primitiveDisplayName;
#property(nonatomic,assign) NSInteger primitiveSection;
#property(nonatomic,strong) NSString *primitiveSectionName;
#property(nonatomic,strong) NSNumber *primitiveSectionNum;
#end
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#implementation XMPPUserCoreDataStorageObject
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Accessors
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#dynamic jid, primitiveJid;
#dynamic jidStr, primitiveJidStr;
#dynamic streamBareJidStr;
#dynamic nickname;
#dynamic displayName, primitiveDisplayName;
#dynamic subscription;
#dynamic ask;
#dynamic unreadMessages;
#dynamic photo;
#dynamic section, primitiveSection;
#dynamic sectionName, primitiveSectionName;
#dynamic sectionNum, primitiveSectionNum;
#dynamic groups;
#dynamic primaryResource;
#dynamic resources;
#dynamic status;
- (XMPPJID *)jid
{
// Create and cache the jid on demand
[self willAccessValueForKey:#"jid"];
XMPPJID *tmp = [self primitiveJid];
[self didAccessValueForKey:#"jid"];
if (tmp == nil) {
tmp = [XMPPJID jidWithString:[self jidStr]];
[self setPrimitiveJid:tmp];
}
return tmp;
}
- (void)setJid:(XMPPJID *)jid
{
self.jidStr = [jid bare];
}
- (void)setJidStr:(NSString *)jidStr
{
[self willChangeValueForKey:#"jidStr"];
[self setPrimitiveJidStr:jidStr];
[self didChangeValueForKey:#"jidStr"];
// If the jidStr changes, the jid becomes invalid.
[self setPrimitiveJid:nil];
}
- (NSInteger)section
{
// Create and cache the section on demand
[self willAccessValueForKey:#"section"];
NSInteger tmp = [self primitiveSection];
[self didAccessValueForKey:#"section"];
// section uses zero, so to distinguish unset values, use NSNotFound
if (tmp == NSNotFound) {
tmp = [[self sectionNum] integerValue];
[self setPrimitiveSection:tmp];
}
return tmp;
}
- (void)setSection:(NSInteger)value
{
self.sectionNum = [NSNumber numberWithInteger:value];
}
- (NSInteger)primitiveSection
{
return section;
}
- (void)setPrimitiveSection:(NSInteger)primitiveSection
{
section = primitiveSection;
}
- (void)setSectionNum:(NSNumber *)sectionNum
{
[self willChangeValueForKey:#"sectionNum"];
[self setPrimitiveSectionNum:sectionNum];
[self didChangeValueForKey:#"sectionNum"];
// If the sectionNum changes, the section becomes invalid.
// section uses zero, so to distinguish unset values, use NSNotFound
[self setPrimitiveSection:NSNotFound];
}
- (NSString *)sectionName
{
// Create and cache the sectionName on demand
[self willAccessValueForKey:#"sectionName"];
NSString *tmp = [self primitiveSectionName];
[self didAccessValueForKey:#"sectionName"];
if (tmp == nil) {
// Section names are organized by capitalizing the first letter of the displayName
NSString *upperCase = [self.displayName uppercaseString];
// return the first character with support UTF-16:
tmp = [upperCase substringWithRange:[upperCase rangeOfComposedCharacterSequenceAtIndex:0]];
[self setPrimitiveSectionName:tmp];
}
return tmp;
}
- (void)setDisplayName:(NSString *)displayName
{
[self willChangeValueForKey:#"displayName"];
[self setPrimitiveDisplayName:displayName];
[self didChangeValueForKey:#"displayName"];
// If the displayName changes, the sectionName becomes invalid.
[self setPrimitiveSectionName:nil];
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark NSManagedObject
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)awakeFromInsert
{
// Section uses zero, so to distinguish unset values, use NSNotFound.
self.primitiveSection = NSNotFound;
}
- (void)awakeFromFetch
{
// Section uses zero, so to distinguish unset values, use NSNotFound.
//
// Note: Do NOT use "self.section = NSNotFound" as this will in turn set the sectionNum.
self.primitiveSection = NSNotFound;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Creation & Updates
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ (id)insertInManagedObjectContext:(NSManagedObjectContext *)moc
withJID:(XMPPJID *)jid
streamBareJidStr:(NSString *)streamBareJidStr
{
if (jid == nil)
{
NSLog(#"XMPPUserCoreDataStorageObject: invalid jid (nil)");
return nil;
}
XMPPUserCoreDataStorageObject *newUser;
newUser = [NSEntityDescription insertNewObjectForEntityForName:#"XMPPUserCoreDataStorageObject"
inManagedObjectContext:moc];
newUser.streamBareJidStr = streamBareJidStr;
newUser.jid = jid;
newUser.nickname = nil;
newUser.displayName = [jid bare];
return newUser;
}
+ (id)insertInManagedObjectContext:(NSManagedObjectContext *)moc
withItem:(NSXMLElement *)item
streamBareJidStr:(NSString *)streamBareJidStr
{
NSString *jidStr = [item attributeStringValueForName:#"jid"];
XMPPJID *jid = [XMPPJID jidWithString:jidStr];
if (jid == nil)
{
NSLog(#"XMPPUserCoreDataStorageObject: invalid item (missing or invalid jid): %#", item);
return nil;
}
XMPPUserCoreDataStorageObject *newUser;
newUser = [NSEntityDescription insertNewObjectForEntityForName:#"XMPPUserCoreDataStorageObject"
inManagedObjectContext:moc];
newUser.streamBareJidStr = streamBareJidStr;
[newUser updateWithItem:item];
return newUser;
}
- (void)updateGroupsWithItem:(NSXMLElement *)item
{
XMPPGroupCoreDataStorageObject *group = nil;
// clear existing group memberships first
if ([self.groups count] > 0) {
[self removeGroups:self.groups];
}
NSArray *groupItems = [item elementsForName:#"group"];
NSString *groupName = nil;
for (NSXMLElement *groupElement in groupItems) {
groupName = [groupElement stringValue];
group = [XMPPGroupCoreDataStorageObject fetchOrInsertGroupName:groupName
inManagedObjectContext:[self managedObjectContext]];
if (group != nil) {
[self addGroupsObject:group];
}
}
}
- (void)updateWithItem:(NSXMLElement *)item
{
NSString *jidStr = [item attributeStringValueForName:#"jid"];
XMPPJID *jid = [XMPPJID jidWithString:jidStr];
if (jid == nil)
{
NSLog(#"XMPPUserCoreDataStorageObject: invalid item (missing or invalid jid): %#", item);
return;
}
self.jid = jid;
self.nickname = [item attributeStringValueForName:#"name"];
self.displayName = (self.nickname != nil) ? self.nickname : jidStr;
self.subscription = [item attributeStringValueForName:#"subscription"];
self.ask = [item attributeStringValueForName:#"ask"];
[self updateGroupsWithItem:item];
}
- (void)recalculatePrimaryResource
{
self.primaryResource = nil;
NSArray *sortedResources = [[self allResources] sortedArrayUsingSelector:#selector(compare:)];
if ([sortedResources count] > 0)
{
XMPPResourceCoreDataStorageObject *resource = [sortedResources objectAtIndex:0];
// Primary resource must have a non-negative priority
if ([resource priority] >= 0)
{
self.primaryResource = resource;
if (resource.intShow >= 3)
self.section = 0;
else
self.section = 1;
}
}
if (self.primaryResource == nil)
{
self.section = 2;
}
}
- (void)updateWithPresence:(XMPPPresence *)presence streamBareJidStr:(NSString *)streamBareJidStr
{
XMPPResourceCoreDataStorageObject *resource =
(XMPPResourceCoreDataStorageObject *)[self resourceForJID:[presence from]];
if ([[presence type] isEqualToString:#"unavailable"] || [presence isErrorPresence])
{
if (resource)
{
[self removeResourcesObject:resource];
[[self managedObjectContext] deleteObject:resource];
}
}
else
{
if (resource)
{
[resource updateWithPresence:presence];
}
else
{
XMPPResourceCoreDataStorageObject *newResource;
newResource = [XMPPResourceCoreDataStorageObject insertInManagedObjectContext:[self managedObjectContext]
withPresence:presence
streamBareJidStr:streamBareJidStr];
[self addResourcesObject:newResource];
}
}
[self recalculatePrimaryResource];
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark XMPPUser Protocol
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (BOOL)isOnline
{
return (self.primaryResource != nil);
}
- (BOOL)isPendingApproval
{
// Either of the following mean we're waiting to have our presence subscription approved:
// <item ask='subscribe' subscription='none' jid='robbiehanson#deusty.com'/>
// <item ask='subscribe' subscription='from' jid='robbiehanson#deusty.com'/>
NSString *subscription = self.subscription;
NSString *ask = self.ask;
if ([subscription isEqualToString:#"none"] || [subscription isEqualToString:#"from"])
{
if ([ask isEqualToString:#"subscribe"])
{
return YES;
}
}
return NO;
}
- (id <XMPPResource>)resourceForJID:(XMPPJID *)jid
{
NSString *jidStr = [jid full];
for (XMPPResourceCoreDataStorageObject *resource in [self resources])
{
if ([jidStr isEqualToString:[resource jidStr]])
{
return resource;
}
}
return nil;
}
- (NSArray *)allResources
{
NSMutableArray *allResources = [NSMutableArray array];
for (XMPPResourceCoreDataStorageObject *resource in [[self resources] allObjects]) {
if(![resource isDeleted])
{
[allResources addObject:resource];
}
}
return allResources;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Comparisons
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Returns the result of invoking compareByName:options: with no options.
**/
- (NSComparisonResult)compareByName:(XMPPUserCoreDataStorageObject *)another
{
return [self compareByName:another options:0];
}
/**
* This method compares the two users according to their display name.
*
* Options for the search — you can combine any of the following using a C bitwise OR operator:
* NSCaseInsensitiveSearch, NSLiteralSearch, NSNumericSearch.
* See "String Programming Guide for Cocoa" for details on these options.
**/
- (NSComparisonResult)compareByName:(XMPPUserCoreDataStorageObject *)another options:(NSStringCompareOptions)mask
{
NSString *myName = [self displayName];
NSString *theirName = [another displayName];
return [myName compare:theirName options:mask];
}
/**
* Returns the result of invoking compareByAvailabilityName:options: with no options.
**/
- (NSComparisonResult)compareByAvailabilityName:(XMPPUserCoreDataStorageObject *)another
{
return [self compareByAvailabilityName:another options:0];
}
/**
* This method compares the two users according to availability first, and then display name.
* Thus available users come before unavailable users.
* If both users are available, or both users are not available,
* this method follows the same functionality as the compareByName:options: as documented above.
**/
- (NSComparisonResult)compareByAvailabilityName:(XMPPUserCoreDataStorageObject *)another
options:(NSStringCompareOptions)mask
{
if ([self isOnline])
{
if ([another isOnline])
return [self compareByName:another options:mask];
else
return NSOrderedAscending;
}
else
{
if ([another isOnline])
return NSOrderedDescending;
else
return [self compareByName:another options:mask];
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark KVO compliance methods
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ (NSSet *)keyPathsForValuesAffectingJid {
// If the jidStr changes, the jid may change as well.
return [NSSet setWithObject:#"jidStr"];
}
+ (NSSet *)keyPathsForValuesAffectingIsOnline {
return [NSSet setWithObject:#"primaryResource"];
}
+ (NSSet *)keyPathsForValuesAffectingSection {
// If the value of sectionNum changes, the section may change as well.
return [NSSet setWithObject:#"sectionNum"];
}
+ (NSSet *)keyPathsForValuesAffectingSectionName {
// If the value of displayName changes, the sectionName may change as well.
return [NSSet setWithObject:#"displayName"];
}
+ (NSSet *)keyPathsForValuesAffectingAllResources {
return [NSSet setWithObject:#"resources"];
}
#end
error iam getting is
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+entityForName: nil is not a legal NSManagedObjectContext parameter searching for entity name 'XMPPUserCoreDataStorageObject''
nil is not a legal NSManagedObjectContext parameter - seems like your NSManagedObjectContext object is not valid at this part. Check the part where you are getting it from your delegate, there may be an error in initiating :)
update
NSManagedObjectContext *moc = [[AppDelegate appdelegate] managedObjectContext_roster]; // <- this seems to be nil
NSEntityDescription *entity = [NSEntityDescription entityForName:#"XMPPUserCoreDataStorageObject"
inManagedObjectContext:moc]; // <- crashing because "nil is not a legal NSManagedObjectContext parameter"
in didFinishLaunchingWithOptions
[self setupStream];
u don't init xmppRosterStorage = [[XMPPRosterCoreDataStorage alloc] init];
I am very new to Core Data and have been trying to following many tutorials, but most of them put all of the Core Data methods into AppDelegate.so any one help me please Thanks in advance
- (NSFetchedResultsController *)fetchedResultsController
{
// NSLog(#"Calling fetchedResultsController # rootviewController");
if (is_Searching && [search_string length])
{
NSManagedObjectContext *moc = [[AppDelegate appdelegate] managedObjectContext_roster];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"XMPPUserCoreDataStorageObject"
inManagedObjectContext:moc];
NSSortDescriptor *sd1 = [[NSSortDescriptor alloc] initWithKey:#"sectionNum" ascending:YES];
NSSortDescriptor *sd2 = [[NSSortDescriptor alloc] initWithKey:#"displayName" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sd1, sd2, nil];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSMutableArray * predicateArray = [[NSMutableArray alloc]init];
if (is_Searching && [search_string length]) {
NSPredicate *predecate = [NSPredicate predicateWithFormat:#"displayName CONTAINS [c] %#",search_string];
[predicateArray addObject:predecate];
}
if ([[AppDelegate get_update_privacy_Array] count]) {
for (NSString * jids in [AppDelegate get_update_privacy_Array]) {
NSPredicate *predecate_blocked = [NSPredicate predicateWithFormat:#"NOT(nickname CONTAINS [c] %# OR jidStr CONTAINS %#)" ,jids, jids];
[predicateArray addObject:predecate_blocked];
}
}
NSPredicate *predicate_final = [NSCompoundPredicate andPredicateWithSubpredicates:
predicateArray];
[fetchRequest setPredicate:predicate_final];
[fetchRequest setEntity:entity];
[fetchRequest setSortDescriptors:sortDescriptors];
[fetchRequest setFetchBatchSize:10];
fetchedResultsController_search = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:moc
sectionNameKeyPath:#"sectionNum"
cacheName:nil];
[fetchedResultsController_search setDelegate:self];
NSError *error = nil;
if (![fetchedResultsController_search performFetch:&error])
{
//DDLogError(#"Error performing fetch: %#", error);
}
if (![[fetchedResultsController_search fetchedObjects]count] && ![groupChatArray count]) {
[AppDelegate alertWithTitle:#"Alert" message:#"No contact found!"];
}
return fetchedResultsController_search;
}
else
{
NSManagedObjectContext *moc = [[AppDelegate appdelegate] managedObjectContext_roster];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"XMPPUserCoreDataStorageObject"
inManagedObjectContext:moc];
NSSortDescriptor *sd1 = [[NSSortDescriptor alloc] initWithKey:#"sectionNum" ascending:YES];
NSSortDescriptor *sd2 = [[NSSortDescriptor alloc] initWithKey:#"displayName" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sd1, sd2, nil];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSMutableArray *predicateArray = [[NSMutableArray alloc]init];
if ([[AppDelegate get_update_privacy_Array] count]) {
for (NSString * jids in [AppDelegate get_update_privacy_Array]) {
NSPredicate *predecate_blocked = [NSPredicate predicateWithFormat:#"NOT(nickname CONTAINS [c] %# OR jidStr CONTAINS %#)" ,jids, jids];
[predicateArray addObject:predecate_blocked];
}
}
NSPredicate *predicate_final = [NSCompoundPredicate andPredicateWithSubpredicates:
predicateArray];
[fetchRequest setPredicate:predicate_final];
[fetchRequest setEntity:entity];
[fetchRequest setSortDescriptors:sortDescriptors];
[fetchRequest setFetchBatchSize:10];
fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:moc
sectionNameKeyPath:#"sectionNum"
cacheName:nil];
[fetchedResultsController setDelegate:self];
NSError *error = nil;
if (![fetchedResultsController performFetch:&error])
{
//DDLogError(#"Error performing fetch: %#", error);
}
return fetchedResultsController;
}
}
XMPPUserCoreDataStorageObject.m class
#import "XMPP.h"
#import "XMPPRosterCoreDataStorage.h"
#import "XMPPUserCoreDataStorageObject.h"
#import "XMPPResourceCoreDataStorageObject.h"
#import "XMPPGroupCoreDataStorageObject.h"
#import "NSNumber+XMPP.h"
#if ! __has_feature(objc_arc)
#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
#endif
#interface XMPPUserCoreDataStorageObject ()
#property(nonatomic,strong) XMPPJID *primitiveJid;
#property(nonatomic,strong) NSString *primitiveJidStr;
#property(nonatomic,strong) NSString *primitiveDisplayName;
#property(nonatomic,assign) NSInteger primitiveSection;
#property(nonatomic,strong) NSString *primitiveSectionName;
#property(nonatomic,strong) NSNumber *primitiveSectionNum;
#end
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#implementation XMPPUserCoreDataStorageObject
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Accessors
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#dynamic jid, primitiveJid;
#dynamic jidStr, primitiveJidStr;
#dynamic streamBareJidStr;
#dynamic nickname;
#dynamic displayName, primitiveDisplayName;
#dynamic subscription;
#dynamic ask;
#dynamic unreadMessages;
#dynamic photo;
#dynamic section, primitiveSection;
#dynamic sectionName, primitiveSectionName;
#dynamic sectionNum, primitiveSectionNum;
#dynamic groups;
#dynamic primaryResource;
#dynamic resources;
#dynamic status;
- (XMPPJID *)jid
{
// Create and cache the jid on demand
[self willAccessValueForKey:#"jid"];
XMPPJID *tmp = [self primitiveJid];
[self didAccessValueForKey:#"jid"];
if (tmp == nil) {
tmp = [XMPPJID jidWithString:[self jidStr]];
[self setPrimitiveJid:tmp];
}
return tmp;
}
- (void)setJid:(XMPPJID *)jid
{
self.jidStr = [jid bare];
}
- (void)setJidStr:(NSString *)jidStr
{
[self willChangeValueForKey:#"jidStr"];
[self setPrimitiveJidStr:jidStr];
[self didChangeValueForKey:#"jidStr"];
// If the jidStr changes, the jid becomes invalid.
[self setPrimitiveJid:nil];
}
- (NSInteger)section
{
// Create and cache the section on demand
[self willAccessValueForKey:#"section"];
NSInteger tmp = [self primitiveSection];
[self didAccessValueForKey:#"section"];
// section uses zero, so to distinguish unset values, use NSNotFound
if (tmp == NSNotFound) {
tmp = [[self sectionNum] integerValue];
[self setPrimitiveSection:tmp];
}
return tmp;
}
- (void)setSection:(NSInteger)value
{
self.sectionNum = [NSNumber numberWithInteger:value];
}
- (NSInteger)primitiveSection
{
return section;
}
- (void)setPrimitiveSection:(NSInteger)primitiveSection
{
section = primitiveSection;
}
- (void)setSectionNum:(NSNumber *)sectionNum
{
[self willChangeValueForKey:#"sectionNum"];
[self setPrimitiveSectionNum:sectionNum];
[self didChangeValueForKey:#"sectionNum"];
// If the sectionNum changes, the section becomes invalid.
// section uses zero, so to distinguish unset values, use NSNotFound
[self setPrimitiveSection:NSNotFound];
}
- (NSString *)sectionName
{
// Create and cache the sectionName on demand
[self willAccessValueForKey:#"sectionName"];
NSString *tmp = [self primitiveSectionName];
[self didAccessValueForKey:#"sectionName"];
if (tmp == nil) {
// Section names are organized by capitalizing the first letter of the displayName
NSString *upperCase = [self.displayName uppercaseString];
// return the first character with support UTF-16:
tmp = [upperCase substringWithRange:[upperCase rangeOfComposedCharacterSequenceAtIndex:0]];
[self setPrimitiveSectionName:tmp];
}
return tmp;
}
- (void)setDisplayName:(NSString *)displayName
{
[self willChangeValueForKey:#"displayName"];
[self setPrimitiveDisplayName:displayName];
[self didChangeValueForKey:#"displayName"];
// If the displayName changes, the sectionName becomes invalid.
[self setPrimitiveSectionName:nil];
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark NSManagedObject
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)awakeFromInsert
{
// Section uses zero, so to distinguish unset values, use NSNotFound.
self.primitiveSection = NSNotFound;
}
- (void)awakeFromFetch
{
// Section uses zero, so to distinguish unset values, use NSNotFound.
//
// Note: Do NOT use "self.section = NSNotFound" as this will in turn set the sectionNum.
self.primitiveSection = NSNotFound;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Creation & Updates
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ (id)insertInManagedObjectContext:(NSManagedObjectContext *)moc
withJID:(XMPPJID *)jid
streamBareJidStr:(NSString *)streamBareJidStr
{
if (jid == nil)
{
NSLog(#"XMPPUserCoreDataStorageObject: invalid jid (nil)");
return nil;
}
XMPPUserCoreDataStorageObject *newUser;
newUser = [NSEntityDescription insertNewObjectForEntityForName:#"XMPPUserCoreDataStorageObject"
inManagedObjectContext:moc];
newUser.streamBareJidStr = streamBareJidStr;
newUser.jid = jid;
newUser.nickname = nil;
newUser.displayName = [jid bare];
return newUser;
}
+ (id)insertInManagedObjectContext:(NSManagedObjectContext *)moc
withItem:(NSXMLElement *)item
streamBareJidStr:(NSString *)streamBareJidStr
{
NSString *jidStr = [item attributeStringValueForName:#"jid"];
XMPPJID *jid = [XMPPJID jidWithString:jidStr];
if (jid == nil)
{
NSLog(#"XMPPUserCoreDataStorageObject: invalid item (missing or invalid jid): %#", item);
return nil;
}
XMPPUserCoreDataStorageObject *newUser;
newUser = [NSEntityDescription insertNewObjectForEntityForName:#"XMPPUserCoreDataStorageObject"
inManagedObjectContext:moc];
newUser.streamBareJidStr = streamBareJidStr;
[newUser updateWithItem:item];
return newUser;
}
- (void)updateGroupsWithItem:(NSXMLElement *)item
{
XMPPGroupCoreDataStorageObject *group = nil;
// clear existing group memberships first
if ([self.groups count] > 0) {
[self removeGroups:self.groups];
}
NSArray *groupItems = [item elementsForName:#"group"];
NSString *groupName = nil;
for (NSXMLElement *groupElement in groupItems) {
groupName = [groupElement stringValue];
group = [XMPPGroupCoreDataStorageObject fetchOrInsertGroupName:groupName
inManagedObjectContext:[self managedObjectContext]];
if (group != nil) {
[self addGroupsObject:group];
}
}
}
- (void)updateWithItem:(NSXMLElement *)item
{
NSString *jidStr = [item attributeStringValueForName:#"jid"];
XMPPJID *jid = [XMPPJID jidWithString:jidStr];
if (jid == nil)
{
NSLog(#"XMPPUserCoreDataStorageObject: invalid item (missing or invalid jid): %#", item);
return;
}
self.jid = jid;
self.nickname = [item attributeStringValueForName:#"name"];
self.displayName = (self.nickname != nil) ? self.nickname : jidStr;
self.subscription = [item attributeStringValueForName:#"subscription"];
self.ask = [item attributeStringValueForName:#"ask"];
[self updateGroupsWithItem:item];
}
- (void)recalculatePrimaryResource
{
self.primaryResource = nil;
NSArray *sortedResources = [[self allResources] sortedArrayUsingSelector:#selector(compare:)];
if ([sortedResources count] > 0)
{
XMPPResourceCoreDataStorageObject *resource = [sortedResources objectAtIndex:0];
// Primary resource must have a non-negative priority
if ([resource priority] >= 0)
{
self.primaryResource = resource;
if (resource.intShow >= 3)
self.section = 0;
else
self.section = 1;
}
}
if (self.primaryResource == nil)
{
self.section = 2;
}
}
- (void)updateWithPresence:(XMPPPresence *)presence streamBareJidStr:(NSString *)streamBareJidStr
{
XMPPResourceCoreDataStorageObject *resource =
(XMPPResourceCoreDataStorageObject *)[self resourceForJID:[presence from]];
if ([[presence type] isEqualToString:#"unavailable"] || [presence isErrorPresence])
{
if (resource)
{
[self removeResourcesObject:resource];
[[self managedObjectContext] deleteObject:resource];
}
}
else
{
if (resource)
{
[resource updateWithPresence:presence];
}
else
{
XMPPResourceCoreDataStorageObject *newResource;
newResource = [XMPPResourceCoreDataStorageObject insertInManagedObjectContext:[self managedObjectContext]
withPresence:presence
streamBareJidStr:streamBareJidStr];
[self addResourcesObject:newResource];
}
}
[self recalculatePrimaryResource];
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark XMPPUser Protocol
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (BOOL)isOnline
{
return (self.primaryResource != nil);
}
- (BOOL)isPendingApproval
{
// Either of the following mean we're waiting to have our presence subscription approved:
// <item ask='subscribe' subscription='none' jid='robbiehanson#deusty.com'/>
// <item ask='subscribe' subscription='from' jid='robbiehanson#deusty.com'/>
NSString *subscription = self.subscription;
NSString *ask = self.ask;
if ([subscription isEqualToString:#"none"] || [subscription isEqualToString:#"from"])
{
if ([ask isEqualToString:#"subscribe"])
{
return YES;
}
}
return NO;
}
- (id <XMPPResource>)resourceForJID:(XMPPJID *)jid
{
NSString *jidStr = [jid full];
for (XMPPResourceCoreDataStorageObject *resource in [self resources])
{
if ([jidStr isEqualToString:[resource jidStr]])
{
return resource;
}
}
return nil;
}
- (NSArray *)allResources
{
NSMutableArray *allResources = [NSMutableArray array];
for (XMPPResourceCoreDataStorageObject *resource in [[self resources] allObjects]) {
if(![resource isDeleted])
{
[allResources addObject:resource];
}
}
return allResources;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Comparisons
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Returns the result of invoking compareByName:options: with no options.
**/
- (NSComparisonResult)compareByName:(XMPPUserCoreDataStorageObject *)another
{
return [self compareByName:another options:0];
}
/**
* This method compares the two users according to their display name.
*
* Options for the search — you can combine any of the following using a C bitwise OR operator:
* NSCaseInsensitiveSearch, NSLiteralSearch, NSNumericSearch.
* See "String Programming Guide for Cocoa" for details on these options.
**/
- (NSComparisonResult)compareByName:(XMPPUserCoreDataStorageObject *)another options:(NSStringCompareOptions)mask
{
NSString *myName = [self displayName];
NSString *theirName = [another displayName];
return [myName compare:theirName options:mask];
}
/**
* Returns the result of invoking compareByAvailabilityName:options: with no options.
**/
- (NSComparisonResult)compareByAvailabilityName:(XMPPUserCoreDataStorageObject *)another
{
return [self compareByAvailabilityName:another options:0];
}
/**
* This method compares the two users according to availability first, and then display name.
* Thus available users come before unavailable users.
* If both users are available, or both users are not available,
* this method follows the same functionality as the compareByName:options: as documented above.
**/
- (NSComparisonResult)compareByAvailabilityName:(XMPPUserCoreDataStorageObject *)another
options:(NSStringCompareOptions)mask
{
if ([self isOnline])
{
if ([another isOnline])
return [self compareByName:another options:mask];
else
return NSOrderedAscending;
}
else
{
if ([another isOnline])
return NSOrderedDescending;
else
return [self compareByName:another options:mask];
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark KVO compliance methods
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ (NSSet *)keyPathsForValuesAffectingJid {
// If the jidStr changes, the jid may change as well.
return [NSSet setWithObject:#"jidStr"];
}
+ (NSSet *)keyPathsForValuesAffectingIsOnline {
return [NSSet setWithObject:#"primaryResource"];
}
+ (NSSet *)keyPathsForValuesAffectingSection {
// If the value of sectionNum changes, the section may change as well.
return [NSSet setWithObject:#"sectionNum"];
}
+ (NSSet *)keyPathsForValuesAffectingSectionName {
// If the value of displayName changes, the sectionName may change as well.
return [NSSet setWithObject:#"displayName"];
}
+ (NSSet *)keyPathsForValuesAffectingAllResources {
return [NSSet setWithObject:#"resources"];
}
#end
- (NSManagedObjectContext *)managedObjectContext_roster
{
return [xmppRosterStorage mainThreadManagedObjectContext];
}
- (NSManagedObjectContext *)mainThreadManagedObjectContext
{
NSAssert([NSThread isMainThread], #"Context reserved for main thread only");
if (mainThreadManagedObjectContext)
{
return mainThreadManagedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator)
{
XMPPLogVerbose(#"%#: Creating mainThreadManagedObjectContext", [self class]);
if ([NSManagedObjectContext instancesRespondToSelector:#selector(initWithConcurrencyType:)])
mainThreadManagedObjectContext =
[[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
else
mainThreadManagedObjectContext = [[NSManagedObjectContext alloc] init];
mainThreadManagedObjectContext.persistentStoreCoordinator = coordinator;
mainThreadManagedObjectContext.undoManager = nil;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(managedObjectContextDidSave:)
name:NSManagedObjectContextDidSaveNotification
object:nil];
// Todo: If we knew that our private managedObjectContext was going to be the only one writing to the database,
// then a small optimization would be to use it as the object when registering above.
}
return mainThreadManagedObjectContext;
}
- (void)managedObjectContextDidSave:(NSNotification *)notification
{
NSManagedObjectContext *sender = (NSManagedObjectContext *)[notification object];
if ((sender != mainThreadManagedObjectContext) &&
(sender.persistentStoreCoordinator == mainThreadManagedObjectContext.persistentStoreCoordinator))
{
XMPPLogVerbose(#"%#: %# - Merging changes into mainThreadManagedObjectContext", THIS_FILE, THIS_METHOD);
dispatch_async(dispatch_get_main_queue(), ^{
[mainThreadManagedObjectContext mergeChangesFromContextDidSaveNotification:notification];
[self mainThreadManagedObjectContextDidMergeChanges];
});
}
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
// This is a public method.
// It may be invoked on any thread/queue.
__block NSPersistentStoreCoordinator *result = nil;
dispatch_block_t block = ^{ #autoreleasepool {
if (persistentStoreCoordinator)
{
result = persistentStoreCoordinator;
return;
}
NSManagedObjectModel *mom = [self managedObjectModel];
if (mom == nil)
{
return;
}
XMPPLogVerbose(#"%#: Creating persistentStoreCoordinator", [self class]);
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
if (databaseFileName)
{
// SQLite persistent store
NSString *docsPath = [self persistentStoreDirectory];
NSString *storePath = [docsPath stringByAppendingPathComponent:databaseFileName];
if (storePath)
{
// If storePath is nil, then NSURL will throw an exception
if(autoRemovePreviousDatabaseFile)
{
if ([[NSFileManager defaultManager] fileExistsAtPath:storePath])
{
[[NSFileManager defaultManager] removeItemAtPath:storePath error:nil];
}
}
[self willCreatePersistentStoreWithPath:storePath options:storeOptions];
NSError *error = nil;
BOOL didAddPersistentStore = [self addPersistentStoreWithPath:storePath options:storeOptions error:&error];
if(autoRecreateDatabaseFile && !didAddPersistentStore)
{
[[NSFileManager defaultManager] removeItemAtPath:storePath error:NULL];
didAddPersistentStore = [self addPersistentStoreWithPath:storePath options:storeOptions error:&error];
}
if (!didAddPersistentStore)
{
[self didNotAddPersistentStoreWithPath:storePath options:storeOptions error:error];
}
}
else
{
XMPPLogWarn(#"%#: Error creating persistentStoreCoordinator - Nil persistentStoreDirectory",
[self class]);
}
}
else
{
// In-Memory persistent store
[self willCreatePersistentStoreWithPath:nil options:storeOptions];
NSError *error = nil;
if (![self addPersistentStoreWithPath:nil options:storeOptions error:&error])
{
[self didNotAddPersistentStoreWithPath:nil options:storeOptions error:error];
}
}
result = persistentStoreCoordinator;
}};
if (dispatch_get_specific(storageQueueTag))
block();
else
dispatch_sync(storageQueue, block);
return result;
}
error iam getting is
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+entityForName: nil is not a legal NSManagedObjectContext parameter searching for entity name 'XMPPUserCoreDataStorageObject''