I am trying to save the imageview to core data as a binary data. In my function I have already saved a string and it works perfectly. However when I try to do the exact same thing with (theTitle2) it does not work. I also have class cdhandler in my question below which is what is saved in app deleagate.I think what I have to do is somehow convert the image view to a uiimage then save it.
func enterData() {
let appDeldeaget = UIApplication.shared.delegate as! AppDelegate
let context = appDeldeaget.persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: "User", in: context)
let theTitle = NSManagedObject(entity: entity!, insertInto: context)
theTitle.setValue(enter.text, forKey: "userName")
let theTitle2 = NSManagedObject(entity: entity!, insertInto: context)
theTitle2.setValue(imageV.image, forKey: "pit")
do {
try context.save()
itemName.append(theTitle)
}
catch { }
//
}
APP DELEGATE
class cdHandler: NSObject {
class func saveObject(userName: String) -> Bool {
let context = getContext()
let entity = NSEntityDescription.entity(forEntityName: "User", in: context)
let managedObject = NSManagedObject(entity: entity!, insertInto: context)
managedObject.setValue(userName, forKey: "userName")
managedObject.setValue(userName, forKey: "pit")
do {
try context.save()
return true
} catch {
return false
}
}
}
pic of Core Data
If you want to save image in core data, convert image to data then store it. Like
let data = imageV.image?.jpegData(compressionQuality: 1) //image in JPEG format
or
let data = imageV.image?.pngData() // image in PNG format
theTitle2.setValue(data, forKey: "pit")
Related
I have entities as so:
Entity 1
Entity 2
I am saving data as so:
#IBAction func saveButton(_ sender: UIButton) {
print("save")
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
return
}
let context = appDelegate.persistentContainer.viewContext
let workout = Workout(context: context)
workout.name = workoutName.text
workout.noOfSets = Int16(setsStepper.value)
for index in 0..<setsVal {
let sets = Sets(context: context)
let test = IndexPath(row: index, section: 0)
let cell = tableView.cellForRow(at: test) as! RepsTableViewCell
sets.repAmount = Int16(cell.repsStepper.value)
// Does this line not create the relationship between Workout and Set Entities?
workout.addToSets(sets)
}
try! context.save()
}
And I am fetching data as so:
func fetch() {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
return
}
let context = appDelegate.persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Workout")
request.relationshipKeyPathsForPrefetching = ["Sets"]
do {
let result = try context.fetch(request)
for data in result as! [NSManagedObject] {
print(data.value(forKey: "name") as! String)
print(data.value(forKey: "noOfSets") as! Int16)
}
}
catch {
print("failed")
}
}
I've set up the relationship between entities Workout and Sets as one-many, yet cannot retrieve Set's attributes from Workout.
How can I retrieve an entities relationship attributes?
Do I need to specify the relationship programmatically despite the relationship being setup in the xcdatamodel file?
Does Workout.addToSets(sets) create the relationship between the entities?
You should be able to access your relationship entities as an attribute so for instance
for workout in result as! [Workout] {
print(workout.name)
if let sets = workout.sets { //assuming you have name your to-many relationship 'sets'
print(sets.count)
}
}
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.
I need to insert records to the coredata for 2 entities from one push notification, when application is in background, but when I try to insert, it stops execution while fetching data from existing records and continues after another notification occurs or when user clicks on notification.
I want data to be inserted when the user did not click on the push notification and when app is in background state.
Here is my code part to fetch record and to insert:
static func insertFromNotificationMessage(_ context: NSManagedObjectContext, message: [AnyHashable: Any]) -> myData{
var myData : myData?
context.performAndWait {
myData = NSEntityDescription.insertNewObject(
forEntityName: "myData", into: context) as? myData
myData?.guid = message["id"] as? String
myData?.title = message["ttl"] as! String
let anotherData = anotherData.getByUUID(context, UUID: message["id"] as! String) ?? anotherData.insertFromNotificationMessage(context, details: message)
myData?.author = author
do{
try context.saveContextAndWait()
}catch let error{
print("Error\(error)")
}
}
return myData!
}
static func getByUUID(_ context: NSManagedObjectContext, UUID : String)-> anotherData?{
let fetchSingleRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "anotherData")
fetchSingleRequest.predicate = NSPredicate(format: "guid =[c] %#", UUID)
do{
let fetchedUser = try context.fetch(fetchSingleRequest) as! [anotherData]
if fetchedUser.count > 0{
return fetchedUser.first
}
}catch{
print("Failed to fetch: \(error)")
}
return nil
}
Do it in the app delegate:
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
YOUR OBJECT CLASS.saveMessage(userInfo) // code to save into core data
completionHandler(.NewData)
}
Here is my example function to save the data
static func saveMessage(userInfo: [NSObject : AnyObject]) -> Message {
let alertInfo = userInfo["aps"]?["alert"]!
let title:String?
let body:String?
if let alertTitle = alertInfo!["title"] as? String{
title = alertTitle
body = alertInfo!["body"] as? String
} else {
title = userInfo["title"] as? String ?? "CUSTOM TITLE"
body = alertInfo as? String
}
let message = Message(date: NSDate(), title: title!, message: body!, isReaded: false)
DBUtil.saveContext()
return message
}
Entity name :- Article
let entityInstance = Article()
I want to update its attributes, but don't know how to create its instance.
I've used this:
let entityInstance = NSEntityDescription.insertNewObject(forEntityName: "ArticleDetails", into: managedObjectContext) as? ArticleDetails
But it creates new instance instead of updating in the previous one.
To update an entity, you can try the following:
let empId = "001"
let fetchRequest:NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "EmpDetails")
let predicate = NSPredicate(format: "empId = '\(empId)'")
fetchRequest.predicate = predicate
do
{
let test = try context?.fetch(fetchRequest)
if test?.count == 1
{
let objectUpdate = test![0] as! NSManagedObject
objectUpdate.setValue("newName", forKey: "name")
objectUpdate.setValue("newDepartment", forKey: "department")
objectUpdate.setValue("001", forKey: "empID")
do{
try context?.save()
}
catch
{
print(error)
}
}
}
catch
{
print(error)
}
I had a function that cleared all objects from given entity, in swift 2:
private static func clearTable(tableName : String)
{
let appDel = UIApplication.sharedApplication().delegate as! AppDelegate
let context : NSManagedObjectContext = appDel.managedObjectContext
let request = appDel.persistentStoreCoordinator
let fetchRequest = NSFetchRequest(entityName: tableName)
let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
do {
try request.executeRequest(deleteRequest, withContext: context)
} catch let error as NSError {
debugPrint(error)
}
}
recently i migrated to swift 3 and now it looks like this:
static func clearTable(_ tableName : String)
{
let appDel = UIApplication.shared.delegate as! AppDelegate
//let context : NSManagedObjectContext = appDel.managedObjectContext
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let request = NSFetchRequest(entityName: tableName)
let fetchRequest: NSFetchRequest = NSFetchRequest(entityName: tableName)
let deleteRequest = NSBatchDeleteRequest(fetchRequest: NSFetchRequest(entityName: tableName))
do {
try request.execute(deleteRequest, with: context)
} catch let error as NSError {
debugPrint(error)
}
}
As i understood, now i have to declare request and fetchRequest like
let fetchRequest: NSFetchRequest<SomeEntity> = NSFetchRequest(entityName: "SomeEntity")
The problem is that i don't know the entity beforehand. Is there any workaround or reflexion in swift 3? And i'm new to swift and core data, is this the normal way to fetch or delete objects?
All result types in Core Data including NSManagedObject conform to NSFetchRequestResult so use that as type
static func clearTable(_ tableName : String)
{
let appDel = UIApplication.shared.delegate as! AppDelegate
let context = appDel.persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: tableName)
let deleteRequest = NSBatchDeleteRequest(fetchRequest: request)
let persistentStoreCoordinator = context.persistentStoreCoordinator!
do {
try persistentStoreCoordinator.execute(deleteRequest, with: context)
} catch let error as NSError {
debugPrint(error)
}
}
This is the recommended way to delete all items of an entity.
However the deployment target must be ≥ iOS 9 / macOS 10.11.
PS: In Swift 3 I'd declare the function
static func clear(table tableName : String) { ...
Checkout this batch delete example for a better way.
I use the CodeDataStack pattern for separation of concern to make the code even more clean.
You can use extension to make your code more dynamic. Instead of passing the table name, you can simply invoke model.delete() or Model.deleteAll(). This is a common pattern used in other web frameworks.
ModelExtension.swift
extension NSManagedObject {
static func context() -> NSManagedObjectContext {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
return appDelegate.coreData.context()
}
public func delete() {
managedObjectContext?.delete(self)
}
public static func deleteAll() {
let batchDeleteRequest = NSBatchDeleteRequest(fetchRequest: self.fetchRequest())
do {
try context().execute(batchDeleteRequest)
} catch {
// Error Handling
}
}
}
Unit Test:
// My entity name is `Location`
func test_deleteAll() {
_ = fixture.buildTestLocation()
_ = fixture.buildTestLocation()
coreData.saveContext()
Location.deleteAll()
XCTAssertEqual(0, Location.all()?.count)
}
func test_delete() {
let location = fixture.createTestLocation()
XCTAssertEqual(1, Location.all()?.count)
location.delete()
coreData.saveContext()
XCTAssertEqual(0, Location.all()?.count)
}
Note that Location.all() is a custom method.