I'm trying to make a small program that keeps count of the amount of times pressed on a button. I'm using Core Data to store the information. The only problem is that I can't figure out how to compute with the information in the core data. If someone can tell me how to make a variable in the code equal to the value of the information in the Core Data I know enough.
If there is another way to compute with the Core Data I would like to know about.
import UIKit
import CoreData
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
/*let newUser = NSEntityDescription.insertNewObject(forEntityName: "Data", into: context)
newUser.setValue(number, forKey: "number")
do{
try context.save()
print("Saved")
}catch{
print("Error occured in the saving process.")
}*/
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Data")
request.returnsObjectsAsFaults = false
do{
let results = try context.fetch(request)
if results.count > 0{
for result in results as! [NSManagedObject]{
if let number = result.value(forKey: "number") as? Double{
print("number is ", number)
result.setValue(number + 1, forKey: "number")
do{
try context.save()
}catch{
}
}
}
}
}catch{
print("Error with fetching data.")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
var number = 0.0
}
The error it's given on the line with nummer = number += 1:
Cannot assign value of type '()' to type 'Double'
I appreciate all of your answers.
This has nothing to do with Core Data. This line doesn't make sense:
nummer = nummerVar += 1
The += operator increases nummerVar in place. You're assigning this to number, but the return value of the += operation is not a Double, so you can't assign it to a Double.
I'm not completely sure what you intended here. You might have meant
nummer = nummerVar + 1
Or maybe you meant
nummerVar += 1
nummer = nummerVar
Either of those are valid code, but which one you need depends on what you're trying to do.
Related
developing a little app for my comic collection encountered this issue:
in my second "add comic" VC I have a button and the func below, but I save TWICE entities in manged context (ate least, I think this is the issue)
for example if I have 2 comics yet shown in main VC tableview, go to "add comic VC" and save a third one, going back to main VC I'll print 3 objects with title, number etc but also print 2 new objects with no data as I had saved twice a manger context a "right one" and another one with same number of object but empty. If I keep adding a 4th comic, I'll get 6 complete comic + the 4th and more 6 "blank itmes" with default values "no title"
let kComicEntityName = "Comic"
func addingSingleComic(gotTitle: String, gotIssue: Int16, gotInCollection: Bool ) {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {return}
let managedContext = appDelegate.persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: kComicEntityName, in: managedContext)!
let comicToAdd = Comic(entity: entity, insertInto: managedContext)
comicToAdd.comicTitle = gotTitle
comicToAdd.issueNumber = gotIssue
comicToAdd.inCollection = gotInCollection
do {
try managedContext.save()
} catch let error as NSError {
print("could not save. \(error), \(error.userInfo)")
}
print("new single comic crated: title: \(comicToAdd.comicTitle ?? "!! not title !!"), n. \(comicToAdd.issueNumber), owned?: \(comicToAdd.inCollection)")
}
in the main VC I use this to check items in core data
func asyncPrintEntities() {
self.asyncComicEntityArray.removeAll(keepingCapacity: true)
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {return}
let managedContext = appDelegate.persistentContainer.viewContext
let comicFetch : NSFetchRequest<Comic> = Comic.fetchRequest()
asyncFetchRequest = NSAsynchronousFetchRequest<Comic>(fetchRequest: comicFetch) {
[unowned self] (result: NSAsynchronousFetchResult) in
guard let AllComicEntityResult = result.finalResult else {
return
}
self.asyncComicEntityArray = AllComicEntityResult
//************************************
do {
self.asyncComicEntityArray = try managedContext.fetch(comicFetch)
if self.asyncComicEntityArray.count > 0 {
print("Ok! model is not empty!")
} else {
print("No entites availabe")
}
} catch let error as NSError {
print("Fetch error: \(error) description: \(error.userInfo)")
}
guard self.asyncComicEntityArray != nil else {return}
for comicFoundInArray in self.asyncComicEntityArray {
let entity = NSEntityDescription.entity(forEntityName: self.kComicEntityName, in: managedContext)!
var comicTouse = Comic(entity: entity, insertInto: managedContext)
// var comicTouse = Comic() //to be deleted since this kind of init is not allowed, better above insertInto
comicTouse = comicFoundInArray as! Comic
print("comic title: \(comicTouse.comicTitle ?? "error title"), is it in collection? : \(comicTouse.inCollection)")
}
self.MyTableView.reloadData()
//************************************
}
// MARK: - async fetch request 3
do {
try managedContext.execute(asyncFetchRequest)
} catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
}
//end of function
}
In your addingSingleComic you create a new Comic here:
let comicToAdd = Comic(entity: entity, insertInto: managedContext)
Then you assign values to the object's properties.
Separately, in asyncPrintEntities, you also create new Comic objects here:
var comicTouse = Comic(entity: entity, insertInto: managedContext)
This time you do not assign values to the object's properties. They will have no title, etc, because you created them but never assigned a title. This line executes once for every object in asyncComicEntityArray, so if the array has two objects, you create two new objects that contain no data. You don't use comicToUse anywhere except in the one print, but it still exists in the managed object context and will still get saved the next time you save changes.
This is why you're getting extra entries-- because you're creating them in this line of code. It's not clear why you're creating them here. You just executed a fetch request, and then you immediately create a bunch of no-data entries which you don't use. It looks like that entire for loop could just be deleted, because the only thing it does is create these extra entries.
please i have an problem with perform segue with identifier in table view didSelectRow method every time i tapped the cell the memory is increasing
the following is my code :
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// firstly i need to check if edit button is true so i can select cell
if isShowToolBar {
// her for hold selected index
let cell = tableView.cellForRow(at: indexPath) as? MovieDownloadedTableViewCell
if let cell = cell {
cell.movieCheckMarkImageView.isHidden = false
cell.movieEmptyCircleImageView.isHidden = true
operationDocumentDirectoryObject.dictionaryHoldIndexCellForDisplayWhichCellSelected.updateValue(indexPath.row, forKey: indexPath.row)
// start hold URL
operationDocumentDirectoryObject.dictionaryForHoldURLSelected.updateValue(operationDocumentDirectoryObject.arrayOfMovieURL![indexPath.row], forKey: indexPath.row)
}// end the if let cell
}else{
// her for show the content folder
let cell = tableView.cellForRow(at: indexPath) as? MovieDownloadedTableViewCell
if let cell = cell {
if cell.fetchURL!.pathExtension == "" {
performSegue(withIdentifier: "ShowFolder", sender: indexPath.row)
}else{
// playing the video
performSegue(withIdentifier: "PlayingMovie", sender: cell.fetchURL!.lastPathComponent)
}// end the if for check path extenstion
}// end the if let cell
cell = nil
}// end the if for the isShowToolbar
}
the above method have memory leak in perform segue and cause increasing memory with the (if cell.fetchURL!.pathExtension == "") also make memory leak
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "MoveFile" {
if let destination = segue.destination as? MoveMovieViewController {
destination.operationDocumentDirectoryObject.dictionaryForHoldURLSelected = self.operationDocumentDirectoryObject.dictionaryForHoldURLSelected
}
}else if segue.identifier == "ShowFolder" {
if let destination = segue.destination as? ShowContentFolderMovieViewController {
if let fetchIndex = sender as? Int {
destination.operationDocumentDirectory.folderName = self.operationDocumentDirectoryObject.arrayOfMovieURL![fetchIndex].lastPathComponent
}
}
}else if segue.identifier == "PlayingMovie" {
// make an object for the playing video view controller
if let destination = segue.destination as? PlayingMovieViewController {
if let movieName = sender as? String {
destination.operationDocumentDirectory.movieName = movieName
}
}
}// end the condition for the segue
}
although the deinit is call success in the view controller but i have still leaking and increasing memory
please help for what is my wrong code?
thank you very much
I suspect you have a strong reference cycle somewhere in your code - although, not in the bit that you have posted as far as I can tell. However, you say that your deinit is being called successfully. Did you check to see if the deinits of all of the view controllers you are segueing to are being called at the expected time (such as when you dismiss PlayingMovieViewController or ShowContentFolderMovieViewController)? Observing the xcode debug console for deinit print statements to check for the appropriate releases in memory is the best way to go. Your memory leak could be coming from somewhere else though. You should look out for strong references elsewhere in your code (maybe a delegate holding on to sender objects?).
I am trying to fetch data grouped by a given column. It works well when I have data. I want to handle the case when I have no data, because it raises an NS error that I could not catch in swift do catch block. I've seen the answers on creating an ObjC wrapper but I it does not apply to my case because I need to return an Array of String.
let request = self.fetchRequest()
request.propertiesToGroupBy = [attribute]
request.propertiesToFetch = [attribute]
request.resultType = NSFetchRequestResultType.dictionaryResultType
request.returnsDistinctResults = true
if let results = try? context().fetch(request), // raises exception in testing, but runs fine when run on simulator.
let dics = results as? [NSDictionary] {
var resultsArray: [Any] = []
for dic in dics {
if let propValue = dic[attribute] {
resultsArray.append(propValue)
}
}
return resultsArray
}
How might I do this?
It's recommended to wrap Core Data fetch lines always in a do - catch block because on success it returns reliably a non-optional array of the specified return type.
Swift has got a strong type system. Casting to unspecified Any or Foundation type NSDictionary doesn't help the compiler and could cause unnecessary compiler errors.
Assuming both key and value of the dictionary are String cast the dictionary to Swift type [String:String]. To forced unwrap the dictionary is absolutely safe because the Core Data model cannot be changed at runtime.
flatMap returns a non-optional array of all values which are not nil.
var resultsArray = [String]()
do {
let results = try context().fetch(request) as! [[String:String]]
resultsArray = results.flatMap {$0[attribute] as? String}
} catch {
print(error)
}
return resultsArray
Based on this answer
It was not immediately obvious to me that it can be done like this:
let request = self.fetchRequest()
request.propertiesToGroupBy = [attribute]
request.propertiesToFetch = [attribute]
request.resultType = NSFetchRequestResultType.dictionaryResultType
request.returnsDistinctResults = true
var result: [Any] = []
do {
try ObjC.catchException {
let results = try? context().fetch(request)
if let dics = results as? [NSDictionary] {
var resultsArray: [Any] = []
for dic in dics {
if let propValue = dic[attribute] {
resultsArray.append(propValue)
}
}
result = resultsArray
}
}
} catch {}
return result
Ideally I wanted the array to be returned by ObjC.catchException unfortunately I have no solid grasp of ObjC yet. The scope of the result var looks too wide, I welcome any suggestion to improve it.
I wanted to keep everything in swift for uniformity but I guess I am stuck with this solution for now.
Is there a way to do a batch delete of all data stored in all of the entities in core data?
I read somewhere that in iOS 9 or 10 that apple introduced a way to do batch deletes, but I can't seem to find any good information on it.
Ultimately, I just need a function that goes through an entity, and deletes all of the data in it. Seems like it should be simple enough, but documentation/tutorials for it have proven exceedingly difficult to find.
Any thoughts?
Edit
I added the following code into an IBAction attached to a button:
#IBAction func clearAllData(_ sender: AnyObject) {
let fetch = NSFetchRequest<NSFetchRequestResult>(entityName: "PLProjects")
let request = NSBatchDeleteRequest(fetchRequest: fetch)
//get the data from core data
getPLData()
//reload the table view
tableView.reloadData()
}
This does not seem to work however. If I close down the project and reopen it, the data is still there. I am assuming this is also why the table view doesn't update, because the data is not actually being deleted.
You're thinking of NSBatchDeleteRequest, which was added in iOS 9. Create one like this:
let fetch = NSFetchRequest<NSFetchRequestResult>(entityName: "Employee")
let request = NSBatchDeleteRequest(fetchRequest: fetch)
You can also add a predicate if you only wanted to delete instances that match the predicate. To run the request:
let result = try managedObjectContext.executeRequest(request)
Note that batch requests don't update any of your current app state. If you have managed objects in memory that would be affected by the delete, you need to stop using them immediately.
To flesh out Tom's reply, this is what I added to have a complete routine:
func deleteAllRecords() {
let delegate = UIApplication.shared.delegate as! AppDelegate
let context = delegate.persistentContainer.viewContext
let deleteFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "CurrentCourse")
let deleteRequest = NSBatchDeleteRequest(fetchRequest: deleteFetch)
do {
try context.execute(deleteRequest)
try context.save()
} catch {
print ("There was an error")
}
}
Declare the Method for getting the Context in your CoreDataManager
Class
class func getContext() -> NSManagedObjectContext {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
return NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
}
if #available(iOS 10.0, *) {
return appDelegate.persistentContainer.viewContext
} else {
return appDelegate.managedObjectContext
}
}
Call the above method from your NSManagedObject subClass:
class func deleteAllRecords() {
//getting context from your Core Data Manager Class
let managedContext = CoreDataManager.getContext()
let deleteFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "Your entity name")
let deleteRequest = NSBatchDeleteRequest(fetchRequest: deleteFetch)
do {
try managedContext.execute(deleteRequest)
try managedContext.save()
} catch {
print ("There is an error in deleting records")
}
}
Ok, this question even mind boggled my Computer Science teacher because nothing we thought of worked.
Below is my code. Because I am retrieving data from Parse, there is a slight delay in actually getting that data. And computers being computers, the app keeps executing the code even if the Parse data hasn't been retrieved.
All I want to do is tell my app to stop executing code for, say 5 seconds, before continuing (this should allow the Parse data to be retrieved and catch up with the program).
I've tried using the sleep (time) function but it just stops the whole program for the time imputed (so the data retrieval from Parse is also put on hold). Also, putting in a useless for-loop does the exact same thing.
The reason why I ask this is because I am getting a nil value when I print test1.
Also, it looks like "No Results" is being printed so that means something is wrong with my fetching Core Data stuff....
import UIKit
import Darwin
import CoreData
class RegisterEmail: UIViewController {
var test1: Bool?
var userID: String!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func shouldPerformSegueWithIdentifier(identifier: String!, sender: AnyObject!) -> Bool {
if identifier == "passEmail" {
var appDel:AppDelegate = (UIApplication.sharedApplication().delegate as AppDelegate)
var context:NSManagedObjectContext = appDel.managedObjectContext!
var request = NSFetchRequest(entityName: "Users")
request.returnsObjectsAsFaults = false
var results: NSArray = context.executeFetchRequest(request, error: nil)!
if(results.count > 0)
{
var res = results [0] as NSManagedObject
userID = res.valueForKey("userID") as String
}
var query = PFUser.query()
query.getObjectInBackgroundWithId(userID) {
(User: PFObject!, error: NSError!) -> Void in
if error == nil {
//NSLog("%#", User)
var checkEmail = User["emailVerified"] as Bool
println(checkEmail)
var appDel:AppDelegate = (UIApplication.sharedApplication().delegate as AppDelegate)
var context:NSManagedObjectContext = appDel.managedObjectContext!
var newEmail = NSEntityDescription.insertNewObjectForEntityForName("Email", inManagedObjectContext: context) as NSManagedObject
newEmail.setValue(checkEmail, forKey: "emailStatus")
context.save(nil)
} else {
NSLog("%#", error)
}
}
var appDel1:AppDelegate = (UIApplication.sharedApplication().delegate as AppDelegate)
var context1:NSManagedObjectContext = appDel1.managedObjectContext!
var request1 = NSFetchRequest(entityName: "Email")
request1.returnsObjectsAsFaults = false
var results1: NSArray = context1.executeFetchRequest(request1, error: nil)!
if(results1.count > 0)
{
var res1 = results1 [0] as NSManagedObject
test1 = res1.valueForKey("emailVerified") as Bool
}
else
{
println("No results")
}
println (test1) //NIL VALUE
if (test1 == false) {
let alert = UIAlertView()
alert.title = "Error"
alert.message = "The email you have provided has not been verified."
alert.addButtonWithTitle("Dismiss")
alert.show()
return false
}
else {
return true
}
}
// by default, transition
return true
}
}
An arbitrary delay time can be achieved with
-(void)performSelector: withObject: afterDelay: (NSObject)
but this is not the most efficient way tho do this by any means. because fetching the data might only take a split second on one occasion and many seconds or never on another. Instead you would be better looking at a completion block for pushing the data to print.