My core data fetch is not recognizing context - swift

In my swift code below I am trying to fetch all of my names in core data to print. I am getting a compile error on context saying it can't be found in scope. I have attached a photo as well so you can see what is in my core data.
#objc func pressRight(){
var locations = [Place]() // Where Locations = your NSManaged Class
var fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "name")
locations = context.executeFetchRequest(fetchRequest, error: nil) as [Place]
// Then you can use your properties.
for location in locations {
print(location.name)
}
}

This isn't a Core Data problem, it's a Swift problem. All variables and properties in Swift have to be declared before you can use them. For example you can't just write
i = 5
Unless somewhere you already have something like
var i: Int
In your code you're using something called context that was never declared. Swift doesn't know what it is, which is what it's telling you.
Since you're using Core Data it looks like you want context to be an instance of NSManagedObjectContext. If your app already has an NSPersistentContainer somewhere, you can get a context from it, using either viewContext or newBackgroundContext (which one depends on exactly how you're using it; here it's probably viewContext).
If you don't already have an NSPersistentContainer somewhere in your app, you may want to read up a little on how to use Core Data. You can't just declare the context; it needs to be configured correctly with the container before use.

Related

Core Data - Generic Multithreading Violation

I'm running this code on app launch and it crashes with:
#0 0x00000001e4133088 in +[NSManagedObjectContext Multithreading_Violation_AllThatIsLeftToUsIsHonor] ()
There are other SO questions alluding to this but not in the sense of what I'm working with. I have a method that returns entities generically. The generic part works fine, the threading of my setup is somehow off. Here is the bare bones of my Core Data setup:
class Manager {
static let shared: NSPersistentContainer = {
/// sets up and returns the container
}
static func fetch<T: NSManagedObject>() throws -> [T] {
let request = NSFetchRequest<T>(entityName: String(describing: T.self))
do {
return try shared.viewContext.fetch(request) as [T] // <---- Crash line
} catch {
throw error
}
}
}
// ...
func fetchFoos() {
do {
let foos: [Foo] = try Manager.fetch()
}
}
The weird thing is that this only happens when I run the app on a device from Xcode. If I disconnect and just open the app without Xcode, it launched fine.
What stands out here that needs rearchitecting? I can't use iOS 15's perform or performAndWait due to supporting earlier OSs.
This code would be OK if you only ever use it on the main thread. Since you're getting this specific crash when using viewContext, you seem to be calling this code from some thread other thread. That's not allowed with Core Data; managed object contexts are not thread safe, nor are managed objects.
A quick-but-probably-inadequate fix would be to surround the fetch call with either perform or performAndWait (both part of NSManagedObjectContext). That would make it safe to use viewContext on a different thread. But the objects you fetch would still be tied to the main thread, so you couldn't use them on the current thread without using perform or performAndWait again.
A more thorough fix would be to call newBackgroundContext() to create a new context for the current thread, and use that to fetch objects. But the fetched objects could only be used on that thread. You could also use performBackgroundTask to run a closure with a temporary context-- as long as you only use the fetched objects in that closure. (These methods are part of NSPersistentContainer). Both of these fixes will probably require other changes to your code, and you'll have to consider where and when you use the fetch results to figure out what those changes are.

Adding a deleted or invalidated object to a Realm is not permitted

I am trying to delete(truncate table) object and than adding it again, but getting this exception at runtime:
Adding a deleted or invalidated object to a Realm is not permitted
My code:
let realm = try! Realm()
let objFolder = realm.objects(FolderColor.self)
do{
try realm.write {
realm.delete(objFolder)
for obj in arrFolderColors {
realm.add(obj)
}
}
}
catch{}
I like the other answer but I think what's going on here is you have a class var
#objc dynamic var folderColorResults = Results<FolderColor>
and at some point you've populated that var with some FolderColor objects
self.folderColorResults = realm.objects(FolderColor.self).filter("color == 'blue'")
So then, when you call the code in the question, it's deleting all of the FolderColor objects from Realm - when that happens, the folderColorResults results var is also getting updated (all objects removed).
Therefore there are no objects to write back out to disk.
Remember that Realm is a live database and Results objects are always keep in sync with the actual data; change an object on one place, it changes it everywhere that object is being used.
A suggested fix is to cast the folderColorResults to an Array.
let myArray = Array(self.folderColorResults)
which disconnects those objects from Realm and they will not be updated.
Of course, I could be totally wrong on this assumption so I can update further if more information is provided.
Realm objects are simply pointers to their data in the Realm database. When you call realm.delete, you completely delete the contents from memory and disk. The Realm object itself is still in memory, but you can’t call any properties or try and re-add it.
There’s a object.invalidated property you can use to check if an object has been deleted before you try and add it to Realm to avoid accidentally triggering that exception.
I’d recommend you rethink your logic to not need to call ‘realm.delete’. If it’s in an array, you can just remove it from the array but keep it in the database.

How to pass data from an api in a tableViewCell, to another tableViewCell in swift 4?

I am working on a tv tracking app where you can search for tv shows and save them to see when a new episode comes out.
My question is, how would I get the movie data from the cell in the searchTableViewController and present the selected data into the cell on myShowsTableViewController?
This is a very broad question and is generally not suitable for StackOverflow, which is for more "granular questions".
That said, I would recommend creating a class called DataManager or something like that. In terms of making your API calls, I would recommend using something like Alamofire for making API calls. Lastly, I would recommend reading up on the Codable protocol for decoding the JSON the API sends back. When you decode your JSON, you'll want to store the structs somewhere where your view controller can see it.
To use CocoaPods, here's a good primer (You'd use them for Alamofire).
Your data manager would look something like this (I'll use NSObject for simplicity's sake, but you could just create your own class):
struct Shows: Codable {
// Whatever the API sends back, you've got to map it here
}
class DataManager: NSObject {
var shows: [Shows] = [] // empty array of shows to start
func methodToGetAPIResponse() {
// Alamofire call & JSON decoding here
// Once you get a valid response, shows = try JSONDecoder().decode...
}
}
You could so something like this for your DataManager in your view controller class:
let dataManager = DataManager()
Then, in your UITableViewDataSource methods, numberOfRowsAtIndexPath would be return dataManager.shows.count and cellForRowAtIndexPath you'd get a hold of the object to populate your labels like so:
let show = dataManager.shows[indexPath.row]
Anyway, that ought to be enough to get you started. Good luck! Try to be more granular with your questions in the future.

Array pass by value by default & thread-safety

Say I have a class which has an Array of object Photo:
class PhotoManager {
fileprivate var _photos: [Photo] = []
var photos: [Photo] {
return _photos
}
}
I read one article which says the following:
By default in Swift class instances are passed by reference and
structs passed by value. Swift’s built-in data types like Array and
Dictionary, are implemented as structs.
Meaning that the above getter returns a copy of [Photo] array.
Then, that same article tries to make the getter thread-safe by refactoring the code to:
fileprivate let concurrentPhotoQueue = DispatchQueue(label: "com.raywenderlich.GooglyPuff.photoQueue",
attributes: .concurrent)
fileprivate var _photos: [Photo] = []
var photos: [Photo] {
var photosCopy: [Photo]!
concurrentPhotoQueue.sync {
photosCopy = self._photos
}
return photosCopy
}
The above code explictly make a copy of self._photos in getter.
My questions are:
If by default swift already return an copy (pass by value) like the article said in the first place, why the article copy again to photosCopy to make it thread-safe? I feel myself do not fully understand these two parts mentioned in that article.
Does Swift3 really pass by value by default for Array instance like the article says?
Could someone please clarify it for me? Thanks!
I'll address your questions in reverse:
Does Swift3 really pass by value by default for Array instance like the article says?
Simple Answer: Yes
But I'm guessing that is not what your concern is when asking "Does Swift3 really pass by value". Swift behaves as if the array is copied in its entirety but behind the scenes it optimises the operation and the whole array is not copied until, and if, it needs to be. Swift uses an optimisation known as copy-on-write (COW).
However for the Swift programmer how the copy is done is not so important as the semantics of the operation - which is that after an assignment/copy the two arrays are independent and mutating one does not effect the other.
If by default swift already return an copy (pass by value) like the article said in the first place, why the article copy again to photosCopy to make it thread-safe? I feel myself do not fully understand these two parts mentioned in that article.
What this code is doing is insuring that the copy is done in a thread-safe way.
An array is not a trivial value, it is implemented as multi-field struct and some of those fields reference other structs and/or objects - this is needed to support an arrays ability to grow in size, etc.
In a multi-threaded system one thread could try to copy the array while another thread is trying to change the array. If these are allowed to happen at the same time then things easily can go wrong, e.g. the array could change while the copy is in progress, resulting in an invalid copy - part old value, part new value.
Swift per se is not thread safe; and in particular it will not prevent an array from being changed while a copy is being performed. The code you have addresses this by using a GCD queue so that during any alteration to the array by one thread all other writes or reads to the array in any other thread are blocked until the alteration is complete.
You might also be concerned that their are multiple copies going on here, self._photos to photoCopy, then photoCopy to the return value. While semantically this is what happens in practice there will probably only be one complex copy (and that will be thread safe) as the Swift system will optimise.
HTH
1) In code example what you provided will be returned copy of _photos.
As wrote in article:
The getter for this property is termed a read method as it’s reading
the mutable array. The caller gets a copy of the array and is protected
against mutating the original array inappropriately.
that's mean what you can access to _photos from outside of class, but you can't change them from there. Values of photos could be changed only inside class what make this array protected from it accidental changing.
2)Yes, Array is a value-type struct and it will be passed by value. You can easily check it in Playground
let arrayA = [1, 2, 3]
var arrayB = arrayA
arrayB[1] = 4 //change second value of arrayB
print(arrayA) //but arrayA didn't change
UPD #1
In article they have method func addPhoto(_ photo: Photo) what add new photo to _photos array what makes access to this property not thread-safe. That's mean what value of _photos could be changed on few thread in same time what will lead to issues.
They fixed it by writing photos on concurrentQueue with .barrier what make it thread-safely, _photos array will changed once per time
func addPhoto(_ photo: Photo) {
concurrentPhotoQueue.async(flags: .barrier) { // 1
self._photos.append(photo) // 2
DispatchQueue.main.async { // 3
self.postContentAddedNotification()
}
}
}
Now for ensure thread safety you need to read of _photos array on same queue. That's only reason why they refactored read method

How to give unit tests access to Core Data models

Let's say I've made Core Data models in my project. I have other classes with methods that take in these models and perform some function based on their properties. How do I unit test those classes?
With normal Swift files I select them in Xcode and tick a box which makes any classes defined in those files visible to the unit test part of the project. My question is essentially, how do I make my Core Data models also visible to the tests?
Note that I don't want to perform any operations on the Core Data stack, I just want to be able to create an instance of a model and pass it into a method.
Since CoreData uses Managed Objects, any subclass of NSManagedObject is functionally worthless unless attached to a context.
One trick for testing is to create an NSManagedObjectContext in memory and create objects test using that context. Outlined Here
Here's the code for creating the context in memory:
func setUpInMemoryManagedObjectContext() -> NSManagedObjectContext {
let managedObjectModel = NSManagedObjectModel.mergedModelFromBundles([NSBundle.mainBundle()])!
let persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel)
do {
try persistentStoreCoordinator.addPersistentStoreWithType(NSInMemoryStoreType, configuration: nil, URL: nil, options: nil)
} catch {
print("Adding in-memory persistent store failed")
}
let managedObjectContext = NSManagedObjectContext()
managedObjectContext.persistentStoreCoordinator = persistentStoreCoordinator
return managedObjectContext
}
Update: The accessor implementations are generated at runtime, but they are not usable if the model object is not associated with a NSManagedObjectContext. So PEEJWEEJ's answer is the way to go.
If I am not mistaken, the Objective-C classes for your entities (in particular e.g. their property accessors) are generated at runtime, when they are used. So I would expect that you'd need to call NSManagedObjects initializer with the appropriate NSEntityDescription:
NSManagedObject(entity: entityDescription, insertInto: nil)
You can get the appropriate entity descriptions by loading your object model with NSManagedObjectModel and using e.g. that object's entitiesByName property.
If you use custom code for your NSManagedObject subclasses (e.g. generated with mogenerator), I would assume the same process would need to apply - make the generated code visible to your tests and use the MyModelClass(entity: entityDescription, insertInto: nil) initializer.