Swift 2: Save coredata with one-to-many conditions - ios

I want to bind my 2 models with swift 2:
The "BodyPart" table :
The "Muscle" table :
I just want to save a "BodyPart" with its "Muscles":
if let managedObjectContext = self.managedObjectContext {
do{
// create a bodyPart
let bodyPart = NSEntityDescription.insertNewObjectForEntityForName("BodyPart",inManagedObjectContext: managedObjectContext) as! BodyPart
// create a Muscle
let muscle = NSEntityDescription.insertNewObjectForEntityForName("Muscle",inManagedObjectContext: managedObjectContext) as! Muscle
//muscles attributes
muscle.name = "test"
muscle.image = "myimage.png"
// mobdypart attributes
bodyPart.name="mybody-part test"
bodyPart.muscles = [muscle]
//save
try managedObjectContext.save()
// get all muscles
let fetchRequest = NSFetchRequest(entityName: "BodyPart")
/* Get result array from ManagedObjectContext */
let fetchResults = try managedObjectContext.executeFetchRequest(fetchRequest)
// list body parts
if let results: Array = fetchResults {
for obj:AnyObject in results {
let name:String? = obj.valueForKey("name") as? String
print("name for the BodyPart: \(name) ")
// list muscles => always empty !!
if let muscles: Array<Muscle> = obj.valueForKey("muscles") as? Array<Muscle> {
for ob:Muscle in muscles {
print("### name for the muscles: \(ob.name)")
}
}
}
} else {
print("Could not fetch")
}
} catch let error as NSError {
print(error)
}
}
BodyPart is saved in the CodeData, however the Muscles list is empty.
Thank you

The default collection type of Core Data is NSSet rather than NSArray
You can get allObjects from the set which returns an array
if let muscles: Array<Muscle> = (obj.valueForKey("muscles") as! NSSet).allObjects as? Array<Muscle> { ...
But since you know from the Core Data model that muscles exists, I'd recommend to declare the attribute as non-optional and omit the optional binding.
let muscles = obj.valueForKey("muscles") as! NSSet

Related

How can you get attribute names from an Entity from CoreData at an iOS app

I am reading data with following code from CoreData but instead of that can we read first attribute names "firstName", "lastName", "age" from CoreData into an array and read their values instead of writing all the names in code.
It is repeated work because they are written in DataModel as well.
loadData() {
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Entity")
do {
let result = try context.fetch(fetchRequest)
dump(result)
for data in result as! [NSManagedObject] {
fNames = data.value(forKey: "firstName") as! String
lNames = data.value(forKey: "lastName") as! String
age = data.value(forKey: "age") as! Int
print("first \(fNames), last : \(lNames), last : \(age)")
}
} catch {
print("Could not load data: \(error.localizedDescription)")
}
}
Use the class that Xcode has generated for you that has the same name as the entity name
loadData() {
//Declare fetch request to hold the class you want to fetch
let fetchRequest = NSFetchRequest<Entity>(entityName: "Entity")
do {
let result = try context.fetch(fetchRequest)
dump(result)
for data in result {
// result is now [Entity] so you can access properties directly
// and without casting
let firstName = data.firstName
let lastName = data.lastName
let age = data.age
print("first \(firstName), last : \(lastName), age : \(age)")
}
} catch let error as NSError {
print("Could not load data: \(error.localizedDescription)")
}
}
Try this, access your entity name from NSManagedObject
e.g.
For AppDelegate.SharedInstance(), just declare this func in AppDelegate.swift
class func sharedInstance() -> AppDelegate
{
return UIApplication.shared.delegate as! AppDelegate
}
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName:"CallHistory") //Here CallHistory is my entity name which i can access as NSManagedObject
let arr_callhistory = try AppDelegate.sharedInstance().persistentContainer.viewContext.fetch(fetchRequest) as! [CallHistory]
if arr_callhistory.count != 0
{
for callhistory_dict in arr_callhistory
{
let callStatus = callhistory_dict.callStatus
}
}

What can i use instead of 'for' in Swift

I have a very beginner question.
First of all, i have an Entity(Person) with an Attribute(name).
I want to fetch the name attributes to one array to pick a randomElement from. The following code successfully returns data to separate arrays:
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Person")
request.returnsObjectsAsFaults = false
var myArray: [String]?
do {
let results = try context.fetch(request)
for result in results as! [NSManagedObject] {
if let username = result.value(forKey: "name") as? String {
myArray = [username]
print(myArray!)
}
}
}
catch {
print("not successful")
}
What can I use instead of 'FOR'? And how?
Thank you so much, and sorry for my soo beginner question.
You may cast to actual entity name
var myArray = [String]()
do {
let results = try context.fetch(request)
myArray = (results as! [Person]).compactMap { $0.name }
print(myArray)
}
catch {
print("not successful")
}

Core Data fetching data and looking for duplicate

I want to fetch data from Core data and look for duplicats and then only save the data then there is no duplicate of the movieid.
Maybe some one can help me ..
How can I compare the result with the movieid string ?
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "MovieData")
//request.predicate = NSPredicate(format: "movieid = %#", movieID)
request.returnsObjectsAsFaults = false
do {
let result = try context.fetch(request)
for data in result as! [NSManagedObject] {
print(data.value(forKey: "movieid") as! String)
}
} catch {
print("Failed")
}
Almost. Apply the predicate to get only the record with the specific movieID. However it assumes that movieID is an object (NSNumber), if it's an scalar Int you have to use %ld as placeholder.
If the fetch returns an empty array there is no duplicate and you can insert a new object
let request = NSFetchRequest<NSManagedObject>(entityName: "MovieData")
request.predicate = NSPredicate(format: "movieid = %#", movieID)
do {
let result = try context.fetch(request)
if result.isEmpty {
let newMovie = NSEntityDescription.insertNewObject(forEntityName: "MovieData", into: context) as! MovieData
newMovie.movieid = movieID
try context.save()
}
} catch {
print(error)
}
While saving in core data you need to create predicate and in there you need to check if there are values already saved with same "movieid" then it has to be updated , this way you won't have duplicate data . Please refer the method and try using the same for saving the values in DB . This way duplicate values won't be saved in DB
class func insertupdaterecord (movieID:String, context: NSManagedObjectContext)
{
let entityDescription = NSEntityDescription.entity(forEntityName: "movie", in: context)
let pred = NSPredicate(format: "movieid = %#", movieID)
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "movie")
fetchRequest.entity = entityDescription
fetchRequest.predicate = pred
let result = try! (context.fetch(fetchRequest) as NSArray).lastObject
let updateInsertInfo : movie
if result != nil
{
updateInsertInfo = result as! movie
}
else
{
print("Record not found!")
}
do
{
try context.save()
}
catch let error as NSError
{
print("Error while saving \(error) in database.")
}
}
Create a cache for movieid values to check for duplicates and loop through the fetched result and delete any objects with a movieid already in the cache and then save once the loop is done.
var selection: [String] = []
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "MovieData")
request.returnsObjectsAsFaults = false
do {
let result = try context.fetch(request)
for data in result as! [NSManagedObject] {
guard let movieId = data.value(forKey: "movieid") as? String else {
context.delete(data) // or however you want to handle this situation
continue
}
if selection.contains(movieId) {
context.delete(data)
} else {
selection.append(movieId)
}
}
try context.save()
} catch {
print("Failed")
}

Update core data object swift 3

I want to update a core data object in swift 3. After some googled I didn't found anything about swift 3.
So my question is: how can I update a core data object in swift 3?
Fetch the existing values using a fetch request with a predicate. Use a unique value in the predicate. Once you've fetched the object, update the object with new values and save the context.
let empId = "001"
let fetchRequest:NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "EmpDetails")
let predicate = NSPredicate(format: "empId = '\(empId)'")
fetchRequest.predicate = predicate
do {
let result = try persistentContainer.viewContext.fetch(fetchRequest)
if let objectToUpdate = result.first as? NSManagedObject {
objectToUpdate.setValue("newName", forKey: "name")
objectToUpdate.setValue("newDepartment", forKey: "department")
objectToUpdate.setValue("001", forKey: "empID")
try persistentContainer.viewContext.save()
}
} catch {
print(error)
}
Using NSManagedObject subclass
let empId = "001"
let fetchRequest: NSFetchRequest<Employee> = Employee.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "%K = %#", #keyPath(Employee.id), empId)
do {
let results = try persistentContainer.viewContext.fetch(fetchRequest)
if let employee = results.first {
employee.name = "new name"
employee.department = "new department"
}
try persistentContainer.viewContext.save()
} catch let error as NSError {
print(error.localizedDescription)
}
Batch updates
Batch updates help to update multiple Core Data objects without having
to fetch anything into memory.
let batchUpdate = NSBatchUpdateRequest(entityName: "Employee")
batchUpdate.propertiesToUpdate = [#keyPath(Employee.isActive): true]
batchUpdate.affectedStores = persistentContainer.viewContext.persistentStoreCoordinator?.persistentStores
batchUpdate.resultType = .updatedObjectsCountResultType
do {
let batchResult = try coreDataStack.managedContext.execute(batchUpdate) as? NSBatchUpdateResult
print(batchResult?.result)
} catch let error as NSError {
print(error.localizedDescription)
}
Pass unique id in variable "id"(Unique variable created in Core data model) and all the variable as you want to update values:
func context() -> NSManagedObjectContext {
let context=(UIApplication.shared.delegate as!AppDelegate).persistentContainer.viewContext
return context
}
func save() {
(UIApplication.shared.delegate as! AppDelegate).saveContext()
}
func UpdateCartByTestId(id:Int64,name:String) {
let fetchRequest =
NSFetchRequest<NSManagedObject>(entityName: "Update")
fetchRequest.returnsObjectsAsFaults = false
fetchRequest.predicate = NSPredicate(format:"id == %d",id)
let result = try? context().fetch(fetchRequest)
if result?.count == 1 {
let dic = result![0]
dic.setValue(id, forKey: "id")
dic.setValue(name, forKey: "name")
save()
}
}

saving Core Data arrays and dictionaries

referring to my previous question: Unique value of object to use in section Swift
my problem now is core data. this is code to save data following the previous code to populate section and table:
let app = UIApplication.sharedApplication().delegate as! AppDelegate
let context = app.managedObjectContext
let entity = NSEntityDescription.entityForName("Movie", inManagedObjectContext: context)!
let movie = Movie(entity: entity, insertIntoManagedObjectContext: context)
movie.title = title.text
movie.plot = plot.text
movie.genre = genre.text
context.insertObject(movie)
do {
try context.save()
} catch {
print("Could not save movie")
}
and fetch data is:
let app = UIApplication.sharedApplication().delegate as! AppDelegate
let context = app.managedObjectContext
let fetchRequest = NSFetchRequest(entityName: "Movie")
do {
let results = try context.executeFetchRequest(fetchRequest)
self.loadMovie = results as! [Movie]
} catch let err as NSError {
print(err.debugDescription)
}
}
but nothing I receive the error on loadMovie Line..
where am I wrong?
First of all, delete the line
context.insertObject(movie)
because the object has already been inserted in the let movie = line.
To load the movies you need to recreate the data structure using the insertMovie(movie : Movie) function.
let movies = try context.executeFetchRequest(fetchRequest) as! [Movie]
loadMovie.removeAll()
for movie in movies {
insertMovie(movie)
}
As the answer of your previous question suggested, the self.loadMovie now has type [String:[Movie]], so you should probably try casting results as [String:[Movie]].
Try self.loadMovie = results as! [String:[Movie]] instead.

Resources