Core Data migration testing: relationships - core-data

I'm trying to test a Core Data migration, but I'm not able to fetch the objects from the database and assert their relationships:
func testV1toV2Migration() async throws {
// read and load the old model
let oldModelURL = Bundle(for: PersistenceController.self).url(forResource: "Remoti.momd/Remoti", withExtension: "mom")!
let oldManagedObjectModel = NSManagedObjectModel(contentsOf: oldModelURL)
XCTAssertNotNil(oldManagedObjectModel)
let coordinator = NSPersistentStoreCoordinator(managedObjectModel: oldManagedObjectModel!)
try! coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: nil)
let managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = coordinator
let jobType = createJobType(in: managedObjectContext)
let category = createCategory(in: managedObjectContext)
let location = createLocation(in: managedObjectContext)
let jobList = createJobList(in: managedObjectContext)
let job = createJob(in: managedObjectContext, location, category, jobType, jobList)
try! managedObjectContext.save()
// migrate the store to the new model version
let newModelURL = Bundle(for: PersistenceController.self).url(forResource: "Remoti.momd/Remoti 2", withExtension: "mom")!
let newManagedObjectModel = NSManagedObjectModel(contentsOf: newModelURL)
XCTAssertNotNil(newManagedObjectModel)
let mappingModel = try? NSMappingModel.inferredMappingModel(
forSourceModel: oldManagedObjectModel!,
destinationModel: newManagedObjectModel!
)
let migrationManager = NSMigrationManager(sourceModel: oldManagedObjectModel!, destinationModel: newManagedObjectModel!)
try! migrationManager.migrateStore(
from: self.url,
sourceType: NSSQLiteStoreType,
with: mappingModel,
toDestinationURL: self.newUrl,
destinationType: NSSQLiteStoreType)
let newCoordinator = NSPersistentStoreCoordinator(managedObjectModel: newManagedObjectModel!)
try! newCoordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: self.newUrl, options: nil)
let newManagedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
newManagedObjectContext.persistentStoreCoordinator = newCoordinator
// Testing the migration: Category
let categoryFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Category")
let categories = try! newManagedObjectContext.fetch(categoryFetchRequest) as! [NSManagedObject]
XCTAssertNotNil(categories)
XCTAssertEqual(categories.count, 1)
XCTAssertEqual(categories.first?.value(forKey: "name") as? String, "Software Development")
// Testing the migration: Job
let jobFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Job")
let jobs = try! newManagedObjectContext.fetch(jobFetchRequest) as! [NSManagedObject]
XCTAssertNotNil(jobs)
XCTAssertEqual(jobs.count, 1)
let jobToAssert = try? XCTUnwrap(jobs.first) as? Job
XCTAssertEqual(jobToAssert?.category, categories.first) <-- `XCTAssertEqual failed: ("nil") is not equal to ("Optional(<Category: 0x600002521040> (entity: Category; id: 0x8536c4beaabab28c <x-coredata://E5`
...
How can the relationships be tested?
Thanks in advance!

Related

how to add AuiliaryData(kCGImageAuxiliaryDataTypeHDRGainMap) to a heic image?

I use the 'CGImageDestinationAddAuxiliaryDataInfo' try to add Auxiliary Data, this method can add a AVDepthData as AuxiliaryData to heic image.
But, this method can't help me.
my code is:
func combo(jpgfile:String, hdrGainMap: String, to heicfile:String){
let jpgImage = NSImage(contentsOfFile: jpgfile)
let url = URL(fileURLWithPath: jpgfile)
let cijpg = CIImage(contentsOf: url)
let outputURL: URL? = URL(fileURLWithPath: heicfile)
guard let cgImageDestination = CGImageDestinationCreateWithURL(outputURL! as CFURL, kUTTypeJPEG, 1, nil) else {
return
}
let context = CIContext(options: nil)
let dict: NSDictionary = [
kCGImageDestinationLossyCompressionQuality: 1.0,
kCGImagePropertyIsFloat: kCFBooleanTrue,
]
if let cgImage: CGImage = context.createCGImage(cijpg!, from: cijpg!.extent) {
CGImageDestinationAddImage(cgImageDestination, cgImage, nil)
}
var auxDataType: NSString?
var auxDic = {kCGImageAuxiliaryDataInfoData}
let gainurl = URL(fileURLWithPath: hdrGainMap)
let gainImage = CIImage(contentsOf: gainurl)
let cgGainImage = context.createCGImage(gainImage!, from: gainImage!.extent)
let bytesPerRow = cgGainImage!.bytesPerRow
let width = cgGainImage!.width
let height = cgGainImage!.height
let pixelFormat = cgGainImage?.pixelFormatInfo.rawValue
let gainData = cgGainImage?.dataProvider?.data
let auxDataDic = [kCGImageAuxiliaryDataInfoData:gainData,
kCGImageAuxiliaryDataInfoDataDescription:["BytesPerRow":bytesPerRow, "width":width, "height":height,"Orientation":1,"pixelFormat":pixelFormat]
] as [String : Any]
CGImageDestinationAddAuxiliaryDataInfo(cgImageDestination, kCGImageAuxiliaryDataTypeHDRGainMap, auxDataDic as CFDictionary)
CGImageDestinationFinalize(cgImageDestination)
}
But,the code above will cause a runtime error:
(Fig) signalled err=-17102

fetch core data string and place in a label (Swift4)

I am trying to call 2 different core data strings and place them each on separate labels. Right now I am getting the error Cannot invoke initializer for type 'init(_:)' with an argument list of type '([NSManagedObject])'. This error is coming from j1.text = String(itemsName). I added both view controllers for saving and displaying.
import UIKit
import CoreData
class ViewController: UIViewController {
#IBOutlet var j1 : UITextField!
#IBOutlet var j2 : UITextField!
#IBAction func save(){
let appD = UIApplication.shared.delegate as! AppDelegate
let context = appD.persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: "Team", in : context)!
let theTitle = NSManagedObject(entity: entity, insertInto: context)
theTitle.setValue(j1.text, forKey: "score")
theTitle.setValue(j2.text, forKey: "alba")
do {
try context.save()
}
catch {
print("Tom Corley")
}
}}
class twoVC: UIViewController {
#IBOutlet var j1 : UILabel!
#IBOutlet var j2 : UILabel!
var itemsName : [NSManagedObject] = []
var itemsName2 : [NSManagedObject] = []
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let appD = UIApplication.shared.delegate as! AppDelegate
let context = appD.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Team")
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "score", ascending: true)]
let fetchRequest2 = NSFetchRequest<NSManagedObject>(entityName: "Team")
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "alba", ascending: true)]
do {
itemsName = try context.fetch(fetchRequest)
itemsName2 = try context.fetch(fetchRequest2)
if let score = itemsName[0].value(forKey: "score") {
j1.text = (score as! String)
}
if let alba = itemsName2[0].value(forKey: "alba") {
j2.text = (alba as? String)
}
}catch {
print("Ashley Tisdale")
}
}}
Loop over the result from the fetch and append to a string that is then used as value for the label, this goes inside the do{...} where you do the fetch today. Note that I am only using one fetch request here.
itemsName = try context.fetch(fetchRequest)
var mergedScore: String = ""
var mergedAlba: String = ""
for item in itemsName {
if let score = item.value(forKey: "score") as? String {
mergedScore.append(score)
mergedScore.append(" ") //separator
}
if let alba = item.value(forKey: "alba") as? String {
mergedScore.append(alba)
mergedScore.append(" ") //separator
}
}
j1.text = mergedScore
j2.text = mergedAlba
Try this one it's Working for me Swift 4 I think You need to store the value as int which are used as sortDescriptor.
func FetchManagedObjectFromDatabaseForStoreData(Entity :NSEntityDescription) ->
[NSManagedObject]
{
let fetchRequest = NSFetchRequest<NSFetchRequestResult>()
// Add Sort Descriptor
let sortDescriptor = NSSortDescriptor(key: "order", ascending: true)
let sortDescriptor1 = NSSortDescriptor(key: "is_favourite", ascending: false)
fetchRequest.sortDescriptors = [sortDescriptor,sortDescriptor1]
// Create Entity Description
fetchRequest.entity = Entity
let result : [NSManagedObject] = []
// Execute Fetch Request
do{
let result = try appDelegate.managedObjectContext.fetch(fetchRequest) as! [NSManagedObject]
if result.count > 0
{
return result
}
else
{
// return result
}
}
catch{
let fetchError = error as NSError
print(fetchError)
}
return result
}
For Fetch Data
// Create Entity Description
let entityDescription = NSEntityDescription.entity(forEntityName: "Your Entity Name Here", in: appDel.managedObjectContext)
let DataObject = FetchManagedObjectFromDatabaseForStoreData(Entity: entityDescription!)
//Convert Array of NSManagedObject into array of [String:AnyObject]
for item in DataObject{
let keys = Array(item.entity.attributesByName.keys)
// Here is your result
print((item.dictionaryWithValues(forKeys: keys) as NSDictionary).value(forKey: "id") as Any) // And so On Whatewer you Fetch
}

How can I filter core data based records to avoid the appearance of duplicates. (Swift 4)

I am pulling records from a core data entity. The records each contain "brand", "model", "size", and "price". There are various size and cost options.
I want to see unique records of "brand" and "model". As you can see from the screenshot, that is not working.
This is a code snip I've written so far:
let sortDescriptor: NSSortDescriptor!
let defaults: UserDefaults = UserDefaults.standard
if defaults.integer(forKey: "sequenceSwitchValue") == 0 {
sortDescriptor = NSSortDescriptor(key: "brand", ascending: true, selector: #selector(NSString.localizedCaseInsensitiveCompare(_:)))
} else {
sortDescriptor = NSSortDescriptor(key: "brand", ascending: false, selector: #selector(NSString.localizedCaseInsensitiveCompare(_:)))
}
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: filmEntity)
request.returnsDistinctResults = true
request.propertiesToFetch = ["brand", "model"]
request.sortDescriptors = [sortDescriptor]
request.predicate = nil
do {
films = try context.fetch(request) as! [FilmEntity]
} catch let error as NSError {
print("Error While Fetching Data From DB: \(error.userInfo)")
}

Core Data setting all attributes to nil for first time

I am using Xcode 8 with Swift 3
Core Data setting all the attributes to nil for first time on load with no relationships.
Here is the screenshot.
In Console, i am getting this if App is installed for first time. I have deleted and try to run & install from Xcode number of times. Every time it sets all the values to nil.
"author_id" = nil;
"channel_id" = 0;
"created_at" = nil;
desc = nil;
id = 0;
images = nil;
"is_approved" = 0;
"is_bookmarks" = 0;
"is_like" = 0;
"is_wired" = 0;
"like_count" = 0;
link = nil;
"meta_des" = nil;
"post_id" = 0;
pubDate = nil;
"publisher_id" = 0;
"rss_url" = nil;
"share_cat" = nil;
"share_title" = nil;
"tiny_url" = nil;
title = nil;
"title_tag" = nil;
type = nil;
"updated_at" = nil;
url = nil;
Don't understand whats happening there. Anyone has any idea or hint what am i doing wrong or missing here. And this is only happening for "NewsPost" Entity.
Here is the code where i am adding data to core data.
func addData(entityName: String, entityValues: Any) {
var contextt = NSManagedObjectContext()
if #available(iOS 10.0, *) {
contextt = self.persistentContainer.viewContext
} else {
contextt = self.managedObjectContext
}
/// add data
switch entityName {
case EntityName.NewsEntity:
let singlePost = entityValues as! NSDictionary
let id = singlePost.value(forKey: "id") as! NSNumber
let title = singlePost.value(forKey: "title") as! String
let description = singlePost.value(forKey: "description") as! String
let link = singlePost.value(forKey: "link") as! String
let url = singlePost.value(forKey: "url") as! String
let is_approved = singlePost.value(forKey: "is_approved") as! NSNumber
let meta_des = singlePost.value(forKey: "meta_des") as! String
let title_tag = singlePost.value(forKey: "title_tag") as! String
let share_cat = singlePost.value(forKey: "share_cat") as! String
let author_id = singlePost.value(forKey: "author_id") as! String
let type = singlePost.value(forKey: "type") as! String
let publisher_id = singlePost.value(forKey: "publisher_id") as! NSNumber
let channel_id = singlePost.value(forKey: "channel_id")
let share_title = singlePost.value(forKey: "share_title") as! String
let rss_url = singlePost.value(forKey: "rss_url")
let pubDate = singlePost.value(forKey: "pubDate") as! String
let created_at = singlePost.value(forKey: "created_at") as! String
let updated_at = singlePost.value(forKey: "updated_at") as! String
let like_count = singlePost.value(forKey: "like_count") as! NSNumber
let tiny_url = singlePost.value(forKey: "tiny_url") as! String
let publisher = singlePost.value(forKey: "publisher") as! NSDictionary
let pubName = publisher.value(forKey: "name") as! String
let post = NSEntityDescription.insertNewObject(forEntityName: "NewsPost", into: contextt) as! NewsPost
post.id = Int64(id)
post.title = title
post.desc = description
post.link = link
post.url = url
post.is_approved = Int16(is_approved)
post.meta_des = meta_des
post.title_tag = title_tag
post.share_cat = share_cat
post.author_id = author_id
post.type = type
post.publisher_id = Int64(publisher_id)
post.channel_id = (channel_id as? Int64) ?? 0
post.share_title = share_title
post.rss_url = rss_url as? String ?? ""
post.pubDate = pubDate
post.created_at = created_at
post.updated_at = updated_at
post.like_count = Int64(like_count)
post.tiny_url = tiny_url
let images = singlePost.value(forKey: "images") as! NSArray
do {
let data = try JSONSerialization.data(withJSONObject: images, options: .prettyPrinted)
let string = NSString(data: data, encoding: String.Encoding.utf8.rawValue)
post.images = string as? String
} catch {
}
print("post data -\(post.managedObjectContext)")
break
case EntityName.Categories:
entity.setValue(entityValues[0], forKey: EntityName.Entities.catName)
break
default:
break
}
/// we save our entity
do {
try contextt.save()
} catch {
fatalError("Failure to save context: \(error)")
}
}
This is how i am fetching data from Core Data.
func fetchData(entityToFetch: String, completion: #escaping (_ result: [NSManagedObject]?)->()) {
var context = NSManagedObjectContext()
if #available(iOS 10.0, *) {
context = CoreDataStack().persistentContainer.viewContext
} else {
context = DataController().managedObjectContext
}
// Fetching core data
let request = NSFetchRequest<NSFetchRequestResult>(entityName: entityToFetch)
request.returnsObjectsAsFaults = false
do {
let results = try context.fetch(request)
print("fetch results- \(results as! [NSManagedObject])")
if results.count > 0 {
completion(results as? [NSManagedObject])
} else {
completion(nil)
}
} catch let error {
completion(nil)
print("fetch error -\(error.localizedDescription)")
}
}
Thank in advance.

How to display many to many relationship with nsfetchedresultcontroller?

If I use a one to many, I can display all the associated NSSet records but many to many doesn't seem to work the same.
On my segue I pass the gymnast to a VC in which I would like to display all associated meets which works fine in a one to many.
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if let objs = fetchedResultsController.fetchedObjects where objs.count > 0 {
let item = objs[indexPath.row] as! Gymnast
performSegueWithIdentifier("displayMeets", sender: item)
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "displayMeets" {
let vc = segue.destinationViewController as! SelectMeetVC
vc.selectedGymnast = sender as? Gymnast
}
}
I get a crash unless I comment out the NSPREDICATE
func loadFetchRequest() {
self.fetchedResultsController = nil
let fetchRequest = NSFetchRequest(entityName: "Meet")
let sortDescriptor1 = NSSortDescriptor(key: "meetName", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor1]
//let filter = NSPredicate(format: "gymnasts.fullName == %#", "\(selectedGymnast.fullName!)")
//fetchRequest.predicate = filter
let controller = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: AD.managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
controller.delegate = self
fetchedResultsController = controller
do {
try fetchedResultsController.performFetch()
} catch {
fatalError("Error retrieving core data")
}
}
Because the gymnasts relationship is to-many, you need to amend the predicate so that it selects a Meet if any of its gymnasts matches:
let filter = NSPredicate(format: "ANY gymnasts.fullName == %#", "\(selectedGymnast.fullName!)")
Note also that there's no need to use the fullName attribute - you can match the Gymnast object itself:
let filter = NSPredicate(format: "ANY gymnasts == %#", selectedGymnast)

Resources