Hi i have a problem with xmppframework using swift, that i always get 0 element when fetch objects
func fetchedResultsController() -> NSFetchedResultsController? {
if fetchedResultsControllerVar == nil {
let moc = managedObjectContext_roster() as NSManagedObjectContext?
let entity = NSEntityDescription.entityForName("XMPPUserCoreDataStorageObject", inManagedObjectContext: moc!)
let sd1 = NSSortDescriptor(key: "sectionNum", ascending: true)
let sd2 = NSSortDescriptor(key: "displayName", ascending: true)
let sortDescriptors = [sd1, sd2]
let fetchRequest = NSFetchRequest()
fetchRequest.entity = entity
fetchRequest.sortDescriptors = sortDescriptors
fetchRequest.fetchBatchSize = 10
fetchedResultsControllerVar = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: moc!, sectionNameKeyPath: "sectionNum", cacheName: nil)
fetchedResultsControllerVar?.delegate = self
do {
try fetchedResultsControllerVar!.performFetch()
} catch let error as NSError {
print("Error: \(error.localizedDescription)")
abort()
}
// if fetchedResultsControllerVar?.performFetch() == nil {
//Handle fetch error
//}
}
return fetchedResultsControllerVar!
}
I have activate in my setup using this code
xmppStreams = externalStream
xmppRosterStorage = XMPPRosterCoreDataStorage()
xmppRoster = XMPPRoster(rosterStorage: xmppRosterStorage)
xmppRoster!.autoFetchRoster = true;
xmppRoster!.autoAcceptKnownPresenceSubscriptionRequests = true;
xmppRoster!.addDelegate(self, delegateQueue: dispatch_get_main_queue())
xmppRoster!.activate(xmppStreams)
But when i print in delegate functoin fetchedResultControllerVar.fetchedObjects it always return 0,but for jidList i can get my rosterlists.
func xmppRosterDidEndPopulating(sender: XMPPRoster?){
var jidList = xmppRosterStorage!.jidsForXMPPStream(xmppStreams)
fetchedResultsControllerVar!.fetchedObjects
print("List=\(jidList)")
}
Did i miss something? am i must init XMPPUserCoreDataStorageObject in my class? Thanks
You have to make sure that you are connected to xmpp before fetching your roster, even if you use XMPPCoreData.
I made a Swift Wrapper around the XMPPFramework, it will simplify the development of a basic chat app, and it will show you how to properly fetch your roster.
You can check it out here.
Related
I have Parent entity called Card and child entity CardHistoryItem with the to-many relationship. I'm trying to fetch histories for my current selected card, but it returns nothing just empty array.
My code:
lazy var cardHistoryItemsFRC: NSFetchedResultsController<CardHistoryItem> = {
let fetchRequest = NSFetchRequest<CardHistoryItem>(entityName: "CardHistoryItem")
if let currentCard = self.currentEntity as? Card {
fetchRequest.predicate = NSPredicate(format: "card = %#", currentCard)
}
let sort = NSSortDescriptor(key: "date", ascending: true)
fetchRequest.sortDescriptors = [sort]
fetchRequest.returnsObjectsAsFaults = false
let mainContext = CoreDataController.sharedInstance.mainContext
let frc = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: mainContext, sectionNameKeyPath: nil, cacheName: nil)
frc.delegate = self
do {
try frc.performFetch()
} catch {
fatalError("NSFetchedResultsController perform fetch error!")
}
return frc
}()
Can someone please help me to find correct predicate?
Thanks
EDIT:
Shout out to pbasdf for helping me solve this issue.
Fixed code:
lazy var fetchedResultsController = self.getFetchedResultsController()
var sectionKeyPath: String? = #keyPath(Object.sectionKey)
var searchPredicate: NSCompoundPredicate?
// MARK: - Return FRC:
private func getFetchedResultsController() -> NSFetchedResultsController<Object> { // var fetchedResultsController:
print("Lazy: getFetchedResultsController()")
let fetchRequest: NSFetchRequest<Object> = Object.fetchRequest()
fetchRequest.predicate = searchPredicate
let sortByKey = NSSortDescriptor(key: #keyPath(Object.sectionKey), ascending: true)
let sortByName = NSSortDescriptor(key: #keyPath(Object.name), ascending: true)
fetchRequest.sortDescriptors = [sortByKey, sortByName]
fetchRequest.fetchBatchSize = 20
let fetchedResultsController = NSFetchedResultsController(
fetchRequest: fetchRequest,
managedObjectContext: coreDataStack.managedContext,
sectionNameKeyPath: sectionKeyPath ?? nil,
cacheName: nil)
fetchedResultsController.delegate = self
return fetchedResultsController
}
private func refreshFRC() {
fetchedResultsController = getFetchedResultsController() // Reset FRC
do { // Load Data:
try fetchedResultsController.performFetch()
} catch let error as NSError {
print("Fetching error: \(error), \(error.userInfo)")
}
}
This gives you the FRC with an optional predicate and sectionNameKeyPath. Which you can then set to your needs, and then set the changes with refreshFRC().
I'm working on adding search to a tableview using a NSFetchedResultsController. My goal:
Return multiple sections based on first letter of object
Return all objects into a single section when searching.
I have working code. And I can make the table do both depending on my sectionKey, I just cant figure out how to do both in the same build.
Is this normal behavior and I'm trying to do something thats not possible by changing the FRC's sectionNameKeyPath and sortDescriptors? Or am I just missing something?
private func getFetchedResultsController() -> NSFetchedResultsController<Object> {
let fetchRequest: NSFetchRequest<Object> = Object.fetchRequest()
let sortByKey = NSSortDescriptor(key: #keyPath(Object.sectionKey), ascending: true)
let sortByName = NSSortDescriptor(key: #keyPath(Object.name), ascending: true)
switch sectionKeyPath {
case nil:
fetchRequest.sortDescriptors = nil
fetchRequest.fetchBatchSize = 20
default:
fetchRequest.sortDescriptors = [sortByKey, sortByName]
fetchRequest.fetchBatchSize = 20
}
fetchRequest.sortDescriptors = [sortByKey, sortByName]
fetchRequest.fetchBatchSize = 20
let fetchedResultsController = NSFetchedResultsController(
fetchRequest: fetchRequest,
managedObjectContext: coreDataStack.managedContext,
sectionNameKeyPath: sectionKeyPath ?? nil,
cacheName: nil)
fetchedResultsController.delegate = self
return fetchedResultsController
}
I'm also curious if it's better to use a a single FRC for the entire viewController, or if it would be a better approach to make one for the entire list of objects, and a second only for when the search is active?
func updateSearchResults(for searchController: UISearchController) {
let searchBar = searchController.searchBar
searchBar.barStyle = .default
switch searchBar.text?.count {
case nil:
searchPredicate = nil
sectionKeyPath = #keyPath(Object.sectionKey)
tableView.reloadData()
case 0:
searchPredicate = nil
sectionKeyPath = #keyPath(Object.sectionKey)
tableView.reloadData()
default:
sectionKeyPath = nil
guard let searchText = searchBar.text else { return }
setSearchPredicate(search: searchText)
}
fetchFRC()
tableView.reloadData()
} // End: updateSearchResults()
func fetchFRC() {
do {
try fetchedResultsController.performFetch()
} catch let error as NSError {
print("Fetching error: \(error), \(error.userInfo)")
}
}
As per comments:
You need to re-call getFetchedResultsController before fetchFRC in that updateSearchResults code; and
You need to assign the result to the fetchedResultsController var defined in your view controller.
I have a UITableView that can be filtered and sorted. When the user selects a filter option I'm trying to use a lazy var called liftUuidPredicate to construct the predicate. The user's selection is stored in UserDefaults but if there is no filter selected, there won't be any value in UserDefaults.logFilterLiftUuid() and this is causing my app to crash with an error:
NSInvalidArgumentException', reason: '*** -copyWithZone: cannot be
sent to an abstract object of class NSPredicate: Create a concrete
instance!
Clearly I need to create a concrete instance of NSPredicate, but I don't know how to do this when there is no predicate.
// throws the 'Create a concrete instance' error
lazy var liftUuidPredicate: NSPredicate = {
guard let liftUuid = UserDefaults.logFilterLiftUuid() else { return NSPredicate() }
return NSPredicate(format: "uuid = %#", liftUuid)
}()
// fetchedResultsController
func fetchLiftEvents() -> NSFetchedResultsController<NSFetchRequestResult> {
let moc = stack.managedObjectContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "LiftEvent")
fetchRequest.predicate = liftUuidPredicate
if let sortBy = UserDefaults.logSortSelection() {fetchRequest.sortDescriptors = [NSSortDescriptor(key: sortBy, ascending: false)]}
fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: moc, sectionNameKeyPath: nil, cacheName: nil)
do {
try fetchedResultsController!.performFetch()
} catch {
fatalError("Unable to fetch Lift Events: \(error)")
}
return fetchedResultsController!
}
The available methods of NSPredicate() expect a format and some form of argument(s) which won't work when I want there to be no predicate. In the guard let statement, I also tried return NSPredicate(value: false) but, as I expected the fetchRequest then returned no records.
How can I make this work?
NSPredicate is an abstract class. You can't have an NSPredicate(), you must instantiate it with the required info to get a concrete instance.
The key is to change the type of liftUuidPredicate from NSPredicate to NSPredicate? and instead of returning NSPredicate() return nil if the guard fails.
I added some boiler plate code to make my snippet compile so you will want to change that as your needs dictate.
`extension UserDefaults {
class func logFilterLiftUuid() -> String? {
return "bar"
}
class func logSortSelection() -> String? {
return "bar"
}
}
class Foo {
let stack = NSFetchedResultsController<NSManagedObject>()
var fetchedResultsController = NSFetchedResultsController<NSFetchRequestResult>()
// throws the 'Create a concrete instance' error
lazy var liftUuidPredicate: NSPredicate? = {
guard let liftUuid = UserDefaults.logFilterLiftUuid() else { return nil }
return NSPredicate(format: "uuid = %#", liftUuid)
}()
// fetchedResultsController
func fetchLiftEvents() -> NSFetchedResultsController<NSFetchRequestResult> {
let moc = stack.managedObjectContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "LiftEvent")
fetchRequest.predicate = liftUuidPredicate
if let sortBy = UserDefaults.logSortSelection() {fetchRequest.sortDescriptors = [NSSortDescriptor(key: sortBy, ascending: false)]}
fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: moc, sectionNameKeyPath: nil, cacheName: nil)
do {
try fetchedResultsController.performFetch()
} catch {
fatalError("Unable to fetch Lift Events: \(error)")
}
return fetchedResultsController
}
}
How do you make an NSPredicate that filters more than one object at the same time? The following works perfect for one object (in this case KDED) but I'm trying to get for example KDED, KDAB and KOMN at the same time, using swift2.
let fetchRequest = NSFetchRequest(entityName: "Airport")
let sortDescriptor = NSSortDescriptor(key: "code", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
let predicate = NSPredicate(format: "code ==%#", "KDED")
fetchRequest.predicate = predicate
if let managedObjectContext = (UIApplication.sharedApplication().delegate as? AppDelegate)?.managedObjectContext {
fetchResultController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
fetchResultController.delegate = self
do {
try fetchResultController.performFetch()
Airports = fetchResultController.fetchedObjects as! [Airport]
} catch {
print(error)
}
}
Use the IN operator:
let predicate = NSPredicate(format: "code IN %#", ["KDED", "KDAB", "KOMN"])
I have a UITableView fed from Core Data with an NSFetchedResultsController returning the Location entities. The default sorting (and section headers) is via the first letter of the entity's name. This works (albeit I'm still trying to combine upper and lower case into the same section properly.) The user can choose to order the table by one of three optional categories (which are attributes of the entity) and then these categories are sorted by entity name.
When I set to sorting by category I get the following runtime error:
[_TtCSs23_ContiguousArrayStorage00007F80513B59D0 key]: unrecognized selector sent to instance 0x7f80513b5720
This is my NSFetchedResultsController:
var sectionNameKeyPathString1: String?
var sectionNameKeyPathString2: String?
var fetchedResultsController: NSFetchedResultsController {
if _fetchedResultsController != nil {
return _fetchedResultsController!
}
let fetchRequest = NSFetchRequest()
// Edit the entity name as appropriate.
let entity = NSEntityDescription.entityForName("Location", inManagedObjectContext: self.managedObjectContext!)
fetchRequest.entity = entity
// Set the batch size to a suitable number.
fetchRequest.fetchBatchSize = 20
// Edit the sort key as appropriate.
if sectionNameKeyPathString1 != nil {
let sortDescriptor1 = NSSortDescriptor(key: sectionNameKeyPathString1!, ascending: true)
let sortDescriptor2 = NSSortDescriptor(key: sectionNameKeyPathString2!, ascending: true)
let sortDescriptors = [sortDescriptor1, sortDescriptor2]
fetchRequest.sortDescriptors = [sortDescriptors]
} else {
let sortDescriptor = NSSortDescriptor(key: "locationName", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
}
var sectionNameKeyPath: String
if sectionNameKeyPathString1 == nil {
sectionNameKeyPath = "firstLetterAsCap"
} else {
sectionNameKeyPath = sectionNameKeyPathString1!
}
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext!, sectionNameKeyPath: sectionNameKeyPath, cacheName: "Locations")
aFetchedResultsController.delegate = self
_fetchedResultsController = aFetchedResultsController
var error: NSError? = nil
if !_fetchedResultsController!.performFetch(&error) {
// TODO: Handle this error
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
//println("Unresolved error \(error), \(error.userInfo)")
abort()
}
return _fetchedResultsController!
}
Using breakpoints I am confident that, for example, sectionNameKeyPathString1 = "category1" and sectionNameKeyPathString2 = "locationName" and also that sectionNameKeyPath = "category1" so the key path matches the first sort descriptor.
I had this working in Obj-C but now I'm pulling my hair out and sure I'm suffering from bug-blindness.
Is it that you just have too many []?
let sortDescriptors = [sortDescriptor1, sortDescriptor2] // <- an [NSSortDescriptor]
fetchRequest.sortDescriptors = [sortDescriptors] // <- now an [[NSSortDescriptor]]
should just be:
fetchRequest.sortDescriptors = [sortDescriptor1, sortDescriptor2]