Bad excess while accessing core data Entity - core-data

I am trying to fetch some record from some entity , but when trying to fetch frequently i am getting Bad Access error ,and app is crashing . please help .
var mContext:NSManagedObjectContext! = appDelegate.persistentContainer.viewContext
func getAllRoomName() -> [String] {
let fetchRequest: NSFetchRequest<SwitchMO> = SwitchMO.fetchRequest()
var arrRoomNames = [String]()
do {
if let arrSwitchesMo = try? mContext.fetch(fetchRequest) as? [SwitchMO]
{
for switchMo in arrSwitchesMo ?? []
{
arrRoomNames.append(switchMo.roomName ?? "")
}
}
} catch {
print("Error with request: \(error)")
}
arrRoomNames = Array(Set(arrRoomNames))
return arrRoomNames;
}
Bad Access Error
How can i get rid of this , Please help me .

If you are using a specific fetch request a type cast is redundant. And if you are using do catch don't try?
func getAllRoomName() -> [String] {
let fetchRequest: NSFetchRequest<SwitchMO> = SwitchMO.fetchRequest()
var arrRoomNames = [String]()
do {
let arrSwitchesMo = try mContext.fetch(fetchRequest)
for switchMo in arrSwitchesMo {
arrRoomNames.append(switchMo.roomName ?? "")
}
arrRoomNames = Array(Set(arrRoomNames))
} catch {
print("Error with request: \(error)")
}
return arrRoomNames
}
However you should make a function can throw if this function on its part contains a throwing function
func getAllRoomName() throws -> [String] {
let fetchRequest: NSFetchRequest<SwitchMO> = SwitchMO.fetchRequest()
var arrRoomNames = [String]()
let arrSwitchesMo = try mContext.fetch(fetchRequest)
for switchMo in arrSwitchesMo {
arrRoomNames.append(switchMo.roomName ?? "")
}
return Array(Set(arrRoomNames))
}
If the code still crashes then the managed object context is nil. Declare the context non-optional as suggested in the Core Data template.

Related

How does the main thread refresh data, after using NSAsynchronousFetchRequest?

I found two ways to get data asynchronously to avoid the main thread being stuck.How does the main thread refresh data, after using NSAsynchronousFetchRequest?
plan A:
#objc public static func fetchObjects(completed: #escaping (_ results: [DraftNote]?) -> Void) {
let taskContext = NotePersistentContainer.shared.newBackgroundContext()
taskContext.perform {
do {
let fetchRequest = DraftNote.fetchRequest()
fetchRequest.sortDescriptors = [NSSortDescriptor(key: #keyPath(DraftNote.lastModifiedDate), ascending: false)]
fetchRequest.predicate = NoteFiltrateType.predicateCurrent(by: #keyPath(DraftNote.userID))
let objects = try taskContext.fetch(fetchRequest)
DispatchQueue.main.async {
completed(objects.compactMap { $0 })
}
} catch {
completed(nil)
print("\(#function) error is: \(error as NSError)")
}
}
}
plan B:
#objc static func retrieveObjects(completed: #escaping (_ results: [DraftNote]?) -> Void) {
let fetchRequest = DraftNote.fetchRequest()
fetchRequest.sortDescriptors = [NSSortDescriptor(key: #keyPath(DraftNote.lastModifiedDate), ascending: false)]
fetchRequest.predicate = NoteFiltrateType.predicateCurrent(by: #keyPath(DraftNote.userID))
fetchRequest.fetchLimit = .max
let asyncFetchRequest = NSAsynchronousFetchRequest(fetchRequest: fetchRequest) { fetchResult in
if let values = fetchResult.finalResult {
DispatchQueue.main.async {
completed(values)
}
} else {
completed(nil)
}
}
do {
let taskContext = NotePersistentContainer.shared.newBackgroundContext()
try taskContext.execute(asyncFetchRequest)
} catch {
let err = error as NSError
print("\(#function) error: \(err), \(err.userInfo)")
}
}
Both methods are great for fetching data, they don't block the main thread. But the problem is that I can't manipulate the final result, if I call back the final result using DispatchQueue.main.async{} , the main thread can't refresh the UI. How to solve this problem please?

Nesting URLSession.shared.dataTask in Swift 4

I am trying to fetch data from an api where the JSON returned has URLs to other pieces of information that I need, such as
"value1" : "data",
"value2": {
"url": "https://example.com/stuff",
}
My logic is as follows:
func(completion: #escaping ([Data]) -> ()) {
var classArray = [myClass]()
URLSession.shared.dataTask(with: url) { (data, _, _) in
guard let data = data else { return }
do {
guard let resultArray = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else { return }
let myObject = myClass(value1: resultArray["value1"]! as! String)
guard let valueUrl = URL(string: resultArray["value2"]! as! String) else { return }
URLSession.shared.dataTask(with: valueUrl) { (data, _, _) in
myObject.value2 = data
classArray.append(myObject)
}.resume()
} catch let error {
print("Failed to create json with error: ", error.localizedDescription)
}
completion(classArray)
}.resume()
}
}
Is this a valid approach or are there better implementations? Trying to avoid a future Pyramid of Doom situation. I have tried putting the inner URLSession call in a separate private function but still receive an empty classArray in the end.

Core data context crash

I am working on save image to core data. I used image pick to select image,
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let currentDateTime = Date()
if let image = info[UIImagePickerControllerOriginalImage] as? UIImage {
self.imagePickedBlock?(image,currentDateTime)
} else {
print("Something went wrong")
}
viewController?.dismiss(animated: true, completion:{
if let addPhotoViewController = self.completionViewController as? AddPhotoViewController {
guard let image = info[UIImagePickerControllerOriginalImage] as? UIImage else {return }
addPhotoViewController.photoViewModel.image = image
self.viewController?.present(addPhotoViewController, animated: true, completion: nil)
}
})
}
and in addPhotoViewController, I have an confirme button to call the following update database method.
The Photo is an NSManagedObject.
private func updateDatabase(with photoViewModels: [PhotoViewModel]) {
container?.performBackgroundTask { [weak self] context in
for photoViewModel in (self?.photoViewModels)! {
_ = try? Photo.findOrCreatePhoto(matching: photoViewModel, in: context)
}
try? context.save()
self?.printDatabaseStatistics()
}
}
And this is the create NSManagedObject method.
static func findOrCreatePhoto(matching photoViewModel: PhotoViewModel, in context: NSManagedObjectContext) throws -> Photo {
let request : NSFetchRequest<Photo> = Photo.fetchRequest()
// request.predicate if Needed
do {
let matches = try context.fetch(request)
if matches.count > 0 {
return matches[0]
}
} catch {
throw error
}
let photo = Photo(context:context) // the crash line
photo.image = UIImageJPEGRepresentation(photoViewModel.image!, 1)
photo.uploadDate = photoViewModel.createDate
photo.text = photoViewModel.description
// photo.group = try? Group.findOrCreateGroup(matching: photoViewModel, in: context)
return photo
}.
It marks "Enqueued from com.apple.main-thread", I don't really understand where is the problem exactly about the thread, anyone has idea? Don't hesitate if I didn't explain clear enough :)
Thank you for your time.

Coredata returns duplicate values. Can anyone had the same issue?

Im using swift3. When fetching data from coredata, it returns duplicate values. Using software Datum, i understood that database only contains the original value.
class DatabaseManager: NSObject {
fileprivate static let sharedManager: DatabaseManager = DatabaseManager()
class var shared: DatabaseManager {
return sharedManager
}
/*Returns the ManagedObjectContext*/
var managedObjectContext: NSManagedObjectContext!
var privateManagedObjectContext: NSManagedObjectContext!
fileprivate var completionHandler: ((_ completed: Bool)-> Void)? = nil
override init() {
privateManagedObjectContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
if let appdelegate = UIApplication.shared.delegate as? AppDelegate {
managedObjectContext = appdelegate.managedObjectContext
privateManagedObjectContext.persistentStoreCoordinator = managedObjectContext.persistentStoreCoordinator
}
}
deinit {
managedObjectContext = nil
privateManagedObjectContext = nil
}
}
//Fetching data
func getItem()->[ListItem]{
var objects = [ListItem]()
var uniqueObjects:[ListItem] = [ListItem]()
let sort = NSSortDescriptor(key: "itemName", ascending: false)
let request : NSFetchRequest<ShoppyListItem> = ShoppyListItem.fetchRequest() as NSFetchRequest<ShoppyListItem>
//let predicate = NSPredicate(format:"excludedIDContain = %#","New")
// request.predicate = predicate
request.sortDescriptors = [sort]
do {
if objects.count > 0 {
objects.removeAll()
}
objects = try managedObjectContext?.fetch(request) ?? []
return objects
} catch {
print("Error with request: \(error)")
}
return objects
}
// objects = try managedObjectContext?.fetch(request) ?? [] returns duplicated objects
i got it. Im not mistaken about the count. It was due to concurrency. i was not running fetch on the safe thread of coredata. All i had to do was put the code inside perform block.
managedObjectContext.perform(block).
Got this from stanford ios tutorial named coredata demo. Video time 26:00. The professor explains this.

Why does localizedDescription of NSError say Optional("description")?

Whenever I do println(error.localizedDescription) I get something that says :
Optional("description of the error here")
Rather than just:
"description of the error here"
How do I get rid of the Optional() part of the description?
I tried the below method which results in a compiler error saying that it's not an optional.
func performLoginRequestWithURL(url: NSURL, email: String, password: String) {
let bodyData = "email=\(email)&password=\(password)"
var request: NSMutableURLRequest = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
request.HTTPBody = bodyData.dataUsingEncoding(NSUTF8StringEncoding)
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()){
response, data, error in
if let error = error {
let errString = error.localizedDescription
NSNotificationCenter.defaultCenter().postNotificationName(self.lh, object: nil, userInfo: ["Result": errString])
} else if data != nil {
let json = NSString(data: data, encoding: NSUTF8StringEncoding) as! String
if let dictionary = JSON().parseJSON(json) as [String: AnyObject]? {
let accesstoken = dictionary["id"] as! String
let id = dictionary["userId"] as! Int
var results = [String: AnyObject]()
results = ["at": accesstoken, "id": id]
// MARK: - Store UID & AccessToken
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "userLoggedIn")
NSUserDefaults.standardUserDefaults().setInteger(id, forKey: "userId")
NSUserDefaults.standardUserDefaults().setObject(accesstoken, forKey: "accessToken")
NSUserDefaults.standardUserDefaults().synchronize()
NSNotificationCenter.defaultCenter().postNotificationName(self.lh, object: nil, userInfo: ["Result": "Success"])
}
}
}
}
The compiler is correct - error is an optional but error.localizedDescription is not. You either need to unwrap the error first, safely by
if let unwrappedError = error {
println(unwrappedError.localizedDescription)
}
or: Use optional chaining - https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/OptionalChaining.html
let description = error?.localizedDescription
which will either cause 'description' to be the string of localizedDescription, if the error optional can be unwrapped successfully, or possibly 'nil' if not.
Code taken from example added to question. Seems to work as ok, below code can be copied into playground to show error and data string printout - just add valid http:// address into see valid return or try with just 'http:' to get an error.
import UIKit
import XCPlayground
func performLoginRequestWithURL(url: NSURL) {
let request: NSMutableURLRequest = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()){
response, data, error in
if let error = error {
let errString = error.localizedDescription
print(errString)
} else if data != nil {
let json = NSString(data: data!, encoding: NSUTF8StringEncoding) as! String
print(json)
}
}
}
performLoginRequestWithURL(NSURL(string:"http:")!)
XCPSetExecutionShouldContinueIndefinitely()
You have to unwrap error using ?:
if let errString = error?.localizedDescription {
println(errString)
}
If you already know from an earlier check that error isn't nil, you can force unwrap:
println(error!.localizedDescription)
This will crash if error is nil, so use the ! syntax with caution.

Resources