I simply have an extension:
extension NSManagedObject {
class func all(sortedBy: String?, ascending: Bool, predicate: NSPredicate?, context: NSManagedObjectContext) -> [NSManagedObject] {
let request = fetchRequest()
if let predicate = predicate {
request.predicate = predicate
}
if let sortedBy = sortedBy {
request.sortDescriptors = [NSSortDescriptor(key: sortedBy, ascending: ascending)]
}
do {
return try context.fetch(request) //Cannot convert return expression of type '[any NSFetchRequestResult]' to return type '[NSManagedObject]'
} catch let error as NSError {
print("❌ Core Data Fetch All Error \(error), \(error.userInfo)")
return []
}
}
}
I want to use it like this for example:
Person.all(sortedBy: nil, ascending: true, predicate: nil, context: context)
Home.all(sortedBy: "streer", ascending: true, predicate: nil, context: context)
Implementation of Fetchable protocol:
protocol Fetchable {
associatedtype FetchableType: NSManagedObject = Self
func delete()
}
extension Fetchable where Self: NSManagedObject, FetchableType == Self {
func delete() {}
}
Related
This is my private function:
private static func all(
sortedBy: String?,
ascending: Bool,
predicate: NSPredicate?,
limit: Int? = nil,
deletion: Bool = false,
context: NSManagedObjectContext
) -> [FetchableType] {
let request = NSFetchRequest<FetchableType>(entityName: entityName)
if let predicate = predicate {
request.predicate = predicate
}
if let sortedBy = sortedBy {
request.sortDescriptors = [NSSortDescriptor(key: sortedBy, ascending: ascending)]
}
if let limit = limit {
request.fetchLimit = limit
}
if deletion {
request.returnsObjectsAsFaults = true
request.includesPropertyValues = false
}
do {
return try context.fetch(request)
} catch let error as NSError {
print("❌ Core Data Fetch All Error \(error), \(error.userInfo)")
return []
}
}
and image representation to show the issue:
This is what I receive on console when printing parameters in line of the issue:
How do I call that functon?
Service.all(sortedBy: nil, ascending: true, predicate: predicate, context: NSManagedObjectContext.defaultContext())
I am trying to fetch results from a class that is not a NSFetchedResultsControllerDelegate is that why I am getting 0 results?
public static func getTopics() -> Array<ipTopic> {
if (topics == nil) {
var fetchedResultsController: NSFetchedResultsController<ipTopic>!
let pc = CoreDataHub.getPersistentContainer()
let blogIdeasFetchRequest = NSFetchRequest<ipTopic>(entityName: "ipTopic")
let primarySortDescriptor = NSSortDescriptor(key: "ipTopicsClip", ascending: true)
blogIdeasFetchRequest.sortDescriptors = [primarySortDescriptor]
fetchedResultsController = NSFetchedResultsController<ipTopic>(
fetchRequest: blogIdeasFetchRequest,
managedObjectContext: pc.viewContext,
sectionNameKeyPath: nil,
cacheName: nil)
do {
try fetchedResultsController.performFetch()
} catch {
print("An error occurred")
}
print(fetchedResultsController.fetchedObjects?.count)
for topic in fetchedResultsController.fetchedObjects! {
topics.append(topic)
}
}
return topics
}
This will creates new private queue and takes a block of code to run. This can live with your container or you can use container as argument
func read(_ block: #escaping (NSManagedObjectContext) -> Void) {
persistentContainer.performBackgroundTask {
block($0)
}
}
This is how you would use it
func fetchEvents() {
read { context in
let request: NSFetchRequest<Event> = Event.fetchRequest()
let events = try? context.fetch(request)
print(events)
}
}
When trying to set up a generic fetched results controller I get Cannot convert value of type 'NSFetchRequest<T>' to expected argument type 'NSFetchRequest<_>', Insert ' as! NSFetchRequest<_> when initializing the controller.
fileprivate var fetchedResultsController: NSFetchedResultsController<T>!
guard let request: NSFetchRequest<T> = T.fetchRequest() as? NSFetchRequest<T> else {
assertionFailure("Can't set up NSFetchRequest")
return
}
request.sortDescriptors = [NSSortDescriptor(key: key, ascending: ascending)]
fetchedResultsController = NSFetchedResultsController(fetchRequest: request, managedObjectContext: dataStore.viewContext, sectionNameKeyPath: nil, cacheName: nil)
do {
try fetchedResultsController.performFetch()
} catch let error {
DDLogError("Error fetching entities: \(error)")
}
Anyone knows what's going on here?
For FRC you should explicitly specify that it have a type T.
Also, there is not enough of context from your code snippet, so it's unclear how did you define this class. However, here is how I think it should look like:
import CoreData
class Model<T> where T: NSManagedObject {
fileprivate var context: NSManagedObjectContext! // assume that somewhere during initialization we setting context
fileprivate lazy var fetchedResultscontroller: NSFetchedResultsController<T> = { [weak self] in
guard let this = self else {
fatalError("lazy property has been called after object has been descructed")
}
guard let request = T.fetchRequest() as? NSFetchRequest<T> else {
fatalError("Can't set up NSFetchRequest")
}
request.sortDescriptors = [NSSortDescriptor(key: "key", ascending: true)]
let tmp = NSFetchedResultsController<T>(fetchRequest: request, managedObjectContext: this.context, sectionNameKeyPath: nil, cacheName: nil)
return tmp
}()
}
I am trying to use NSFetchedResultsController to fetch records from CoreData
I am using the following code:
let fetchedResultsController: NSFetchedResultsController<Appointment> = {
let fetchRequest = NSFetchRequest<Appointment>(entityName: "Appointment")
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "date", ascending: false)]
let moc:NSManagedObjectContext = CoreDataManager.managedObjectContext
let frc = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: moc, sectionNameKeyPath: nil, cacheName: nil)
return frc
}()
And in numberOfItemsInSection delegate I have defined
if let count = fetchedResultsController.sections?[0].numberOfObjects {
return count
}
return 0
Last in cellForItemAt delegate method, I have defined
let appointment = self.fetchedResultsController.object(at: indexPath)
The issue, is it does not display any records from core data. Nor does it show any error. the list just comes empty.
If I fetch the record using the following code it works.
public func fetchAppointmentList() -> [Appointment] {
var list:[Appointment] = []
let request: NSFetchRequest<Appointment> = Appointment.fetchRequest()
do {
list = try moc.fetch(request)
} catch {
fatalError("\(error)")
}
return list
}
override func viewDidLoad() {
super.viewDidLoad()
self.appointmentList = self.fetchAppointmentList()
}
// Removed rest of the code to keep it simple.
Why is my fetchedResultsController not working?
Thanks.
Okay, I forgot to call performFetch() in viewDidLoad(), defining the following code in viewDidLoad solved my issue.
do {
try fetchedResultsController.performFetch()
} catch {
print("An error occurred")
}
I am trying to implement method chaining for success and failure calls in my code but I seem to be having trouble getting the onSuccess methods to actually be called.
A view controller calls the getProduct(_:) function.
getProduct(_:) makes an API call and then calls storeProduct(_:) with the retrieved json
storeProduct(_:) calls fetchProduct(_:)
fetchProduct(_:) calls doSuccess(_:) but this never gets back into the onSuccess of the previous calls.
Some Code Snippets
BSProductChainable.swift
import Foundation
class BSProductChainable<SuccessParams, FailureParams> {
var successClosure: ((SuccessParams) -> ())? = nil
var failureClosure: ((FailureParams) -> ())? = nil
func onSuccess(closure: (SuccessParams) -> ()) -> BSProductChainable {
successClosure = closure
return self
}
func onFailure(closure: (FailureParams) -> ()) -> BSProductChainable {
failureClosure = closure
return self
}
func doSuccess(params: SuccessParams) {
if let closure = successClosure {
closure(params)
}
}
func doFailure(params: FailureParams) {
if let closure = failureClosure {
closure(params)
}
}
}
BSProductManagerSwift.swift
class BSProductManagerSwift: NSObject {
typealias productResponseChain = BSProductChainable<Product, NSError?>
typealias productsResponseChain = BSProductChainable<[Product], NSError?>
var serviceClient: BSNetworkingServiceClient!
var objectContext: NSManagedObjectContext!
var productChains: BSProductChainable<Product, NSError?>!
var productsChains: BSProductChainable<[Product], NSError?>!
convenience init(serviceClient: BSNetworkingServiceClient) {
self.init()
self.serviceClient = serviceClient
self.objectContext = managedObjectContext
self.productChains = BSProductChainable<Product, NSError?>()
self.productsChains = BSProductChainable<[Product], NSError?>()
}
func getProduct(ean: String) -> productResponseChain {
let urlString = BSConstants.BarcodeScanner.productEndpoint.stringByAppendingString(ean)
serviceClient.GET(urlString, failure: { (error) in
print("Could not get product")
}) { (response) in
if let json = response {
self.storeProduct(json).onSuccess({ (returedProduct) in
print("Stored product")
})
}
}
return productChains
}
func storeProduct(json: JSON) -> productResponseChain {
fetchProduct(json["ean"].stringValue).onSuccess { (returedProduct) in
self.productChains.doSuccess(returedProduct)
}
return productChains
}
func fetchProduct(ean: String) -> productResponseChain {
let fetchRequest = NSFetchRequest(entityName: "Product")
let predicateEAN = NSPredicate(format: "%K == %#", "ean", ean)
let predicateMarket = NSPredicate(format: "%K == %#", "market", BSCountryManager.sharedInstance().getCurrentCountry().market)
let predicateLocale = NSPredicate(format: "%K == %#", "locale", BSLocalizationManager.sharedManager().currentLocalization.localeIdentifier())
let predicateCurrency = NSPredicate(format: "%K == %#", "currency", BSLocalizationManager.sharedManager().currentLocalization.country.currencyIdentifierDMW)
let compoundPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [predicateEAN, predicateMarket, predicateLocale, predicateCurrency])
fetchRequest.predicate = compoundPredicate
do {
let matchingProuducts = try objectContext.executeFetchRequest(fetchRequest)
if matchingProuducts.count == 0 {
print("No matching products found")
let entity = NSEntityDescription.entityForName("Product", inManagedObjectContext: objectContext)
productChains.doSuccess(Product(entity: entity!, insertIntoManagedObjectContext: objectContext))
} else {
print("Found matching product")
let d = matchingProuducts.first as! Product
productChains.doSuccess(d)
}
} catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
productChains.doFailure(error)
}
return productChains
}
I initially initialised the chainable class per function but this had its own issues from which I thought (possibly incorrectly) that I should only initialise the chainable class once and pass around its reference.
Some input as to where I am going wrong/what I could try next would be great.
As recommended by #john elements, I decided to use PromiseKit
This didn't require to much of a code change and here are what the functions now look like (still need to do a bit of a code cleanup but it works!):
func getProduct(ean: String) -> Promise<Product> {
return Promise { fullfill, reject in
let urlString = BSConstants.BarcodeScanner.productEndpoint.stringByAppendingString(ean)
serviceClient.GET(urlString, failure: { (error) in
reject(error!)
}) { (response) in
if let json = response {
self.storeProduct(json).then ({ returnedProduct in
print("We stored the product: \(returnedProduct.ean)")
fullfill(returnedProduct)
}).error { returnedError in
print("We had a problem storing the product: \(returnedError)")
}
}
}
}
}
func storeProduct(json: JSON) -> Promise<Product> {
return Promise { fullfill, reject in
fetchProduct(json["ean"].stringValue).then ({ returnedProduct in
var storedProduct: Product!
var isNewProduct = false
print("Fetched Product: \(returnedProduct.ean)")
isNewProduct = returnedProduct.valueForKey("ean") == nil
storedProduct = returnedProduct
storedProduct.setValue(json["name"].stringValue, forKey: "name")
storedProduct.setValue(json["ean"].stringValue, forKey: "ean")
storedProduct.setValue(json["image"].stringValue, forKey: "image")
storedProduct.setValue(json["price"].doubleValue, forKey: "price")
storedProduct.setValue(json["status"].intValue, forKey: "status")
storedProduct.setValue(json["pdp"].stringValue, forKey: "pdp")
storedProduct.setValue(BSCountryManager.sharedInstance().getCurrentCountry().market, forKey: "market")
storedProduct.setValue(BSLocalizationManager.sharedManager().currentLocalization.localeIdentifier(), forKey: "locale")
storedProduct.setValue(BSLocalizationManager.sharedManager().currentLocalization.country.currencyIdentifierDMW, forKey: "currency")
do {
try self.objectContext.save()
print("Stored Product: \(returnedProduct.ean)")
fullfill(returnedProduct)
if isNewProduct {
NSNotificationCenter.defaultCenter().postNotificationName("DidAddScanEntry", object: nil)
}
} catch let error as NSError {
print("Could not save \(error), \(error.userInfo)")
reject(error)
}
}).error { returnedError in
print("We had a problem fetching the product: \(returnedError)")
reject(returnedError)
}
}
}
func fetchProduct(ean: String) -> Promise<Product> {
return Promise { fullfill, reject in
let fetchRequest = NSFetchRequest(entityName: "Product")
let predicateEAN = NSPredicate(format: "%K == %#", "ean", ean)
let predicateMarket = NSPredicate(format: "%K == %#", "market", BSCountryManager.sharedInstance().getCurrentCountry().market)
let predicateLocale = NSPredicate(format: "%K == %#", "locale", BSLocalizationManager.sharedManager().currentLocalization.localeIdentifier())
let predicateCurrency = NSPredicate(format: "%K == %#", "currency", BSLocalizationManager.sharedManager().currentLocalization.country.currencyIdentifierDMW)
let compoundPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [predicateEAN, predicateMarket, predicateLocale, predicateCurrency])
fetchRequest.predicate = compoundPredicate
do {
let matchingProuducts = try objectContext.executeFetchRequest(fetchRequest)
if matchingProuducts.count == 0 {
print("No matching products found")
let entity = NSEntityDescription.entityForName("Product", inManagedObjectContext: objectContext)
fullfill(Product(entity: entity!, insertIntoManagedObjectContext: objectContext))
} else {
print("Found matching product")
let d = matchingProuducts.first as! Product
fullfill(d)
}
} catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
reject(error)
}
}
}