Passing Data Between Two NSOperations - swift

I watched with a great attention the WWDC 2015 sessions about Advanced NSOperations and I played a little bit with the example code.
The provided abstraction are really great, but there is something I may did not really good understand.
I would like to pass result data between two consequent Operation subclasses without using a MOC.
Imagine I have a APIQueryOperation which has a NSData? property and a second operation ParseJSONOperation consuming this property. How do I provide this NSData? intance to the second operation ?
I tried something like this :
queryOperation = APIQueryOperation(request: registerAPICall)
parseOperation = ParseJSONOperation(data: queryOperation.responseData)
parseOperation.addDependency(queryOperation)
But when I enter in the execute method of the ParseJSONOperation the instance in not the same as the same as in the initialiser.
What did I do wrong ?

Your issue is that you are constructing your ParseJSONOperation with a nil value. Since you have two operations that rely on this NSData object I would suggest you write a wrapper object to house this data.
To try and be aligned with the WWDC talk lets call this object the APIResultContext:
class APIResultContext {
var data: NSData?
}
now we can pass this object into both the APIQueryOperation and the ParseJSONOperation so that we have a valid object that can store the data transferred from the API.
This would make the constructors for the query:
let context = APIResultContext()
APIQueryOperation(request: registerAPICall, context: context)
ParseJSONOperation(context: context)
Inside your ParseJSONOperation you should be able to access the data assuming the query completes after it sets the data.
Thread Safety
As #CouchDeveloper pointed out, data is not strictly speaking thread safe. For this trivial example since the two operations are dependent we can safely write and read knowing that these accesses wont take place at the same time. However, to round the solution up and make the context thread safe we can add a simple NSLock
class APIResultContext {
var data: NSData? {
set {
lock.lock()
_data = newValue
lock.unlock()
}
get {
lock.lock()
var result = _data
lock.unlock()
return result
}
}
private var _data: NSData?
private let lock = NSLock()
}

Related

Retrieving NSOrderedSet from Core Data and casting it to entity managedObjectSubclasss

Im making a Fitness app to learn Core data, and I have found that I need to let the user store every performed workout as a WorkoutLog item, and in there, there should be a series of ExerciseLogs which represents performances of that exercise (it contains each lift and also a reference to the actual exercise design).
Problem is that after a while i realize that i need to have these ordered, so that the next time i want to show the user their workout, the order that the exercisese were performed should be the same.
So I checked "ordered" in the top right of the image there, and now my code is in dire need of an update. I have tried to read as much as I could about working with NSOrderedSet and how to fetch them from core data and then manipulate them, but I havent really found much of use to me. (I have no experice in objective-c)
For example my code that used to be:
static func deleteWorkoutLog(_ workoutLogToDelete: WorkoutLog) {
guard let exerciseLogsToDelete = workoutLogToDelete.loggedExercises as? Set<ExerciseLog> else {
print("error unwrapping logged exercises in deleteWorkoutLog")
return
}
I get the error: .../DatabaseFacade.swift:84:77: Cast from 'NSOrderedSet?' to unrelated type 'Set' always fails
So what ive learned about sets and core data no longer seems applicable.
Im far from an expert in programming, but im very eager to learn how to get access to the loggedExercises instances.
TLDR; Is there a way to cast NSOrderedSet to something I can work with? How do we usually work with NSManagedSets from core data? Do we cast them to Arrays or MutableSets? I would very much appreciate an example or two on how to get started with retrieving and using these ordered sets!
Thanks
For anyone else wondering how to get started with orderedSets in core data:
After setting my the WorkoutLog.loggedExercises "to-many" relationship to be ordered, I managed to access them through the mutableOrderedSetValue function like this:
static func deleteWorkoutLog(_ workoutLogToDelete: WorkoutLog) {
let orderedExerciseLogs: NSMutableOrderedSet = workoutLogToDelete.mutableOrderedSetValue(forKey: "loggedExercises")
let exerciseLogsToDelete = orderedExerciseLogs.array
for exerciseLog in exerciseLogsToDelete {
guard let exerciseLog = exerciseLog as? ExerciseLog else {
return
}
Works great so far.
And to rearrange the NSOrderedSet I ended up doing something like this:
// Swap the order of the orderedSet
if let orderedExerciseLogs: NSOrderedSet = dataSourceWorkoutLog.loggedExercises {
var exerciseLogsAsArray = orderedExerciseLogs.array as! [ExerciseLog]
let temp = exerciseLogsAsArray[indexA]
exerciseLogsAsArray[indexA] = exerciseLogsAsArray[indexB]
exerciseLogsAsArray[indexB] = temp
let exerciseLogsAsOrderedeSet = NSOrderedSet(array: exerciseLogsAsArray)
dataSourceWorkoutLog.loggedExercises = exerciseLogsAsOrderedeSet
}

How can I deallocate all references elements from an array?

I'm trying to create a retrying mechanism for our network calls. I have created 2 classes. One a retry Class and another a Manager in case I wanted I can cancel all classes.
class Retry {
var url: String
var maxRetries: Int
init (url: String, retryCount: Int){
self.url = url
self.maxRetries = maxRetries
poll()
RetryManager.shared.add(self)
}
private func poll(){
guard retryCount == 0 else{
print("error")
}
getRequest()
}
private func getRequest(){
// make network request
// if no response is received poll again
}
func cancel(){
maxRetries = 0
}
}
Manager class
class RetryManager{
static let sharedInstance = RetryManager()
var retries : [Retry?] = []
private init(){
}
func register(retry: Retry){
retries.append(retry)
}
func remove(retry: Retry){
retry.cancel() // XX; Do I also need this or just removing it is fine?
retries = retries.filter({$0 !== retry})
}
func cancelAll(){
retries.forEach({$0?.cancel()}) // ZZ; Do I also need this? or just removing them is fine?
retries.removeAll()
}
}
My Retry instances are to be used for making network calls.
My major question is about my cancel mechanism. Will doing a RetryManager.shared.cancelAll() suffice for deallocation? Or I need to run cancel or each cancel instance (ie XX, ZZ are also necessary)?
Currently everything works fine, but I'm not sure if I how it would work if I have multiple pointers...would I need to do:
for index..<retries.count{
retries[index] = nil
}
As far as I understand that won't help, it's same as doing retries.removeAll()
I also read In Swift, how do I set every element inside an array to nil? question but was told to open a new question
Not sure if I can answer your question, but I will try with my best understanding :).
Apple's Swift handbook on Automatic Reference Counting (ARC) covers your question very well.
Usually you don't need to have an array of optionals,
var retries = [Retry]()
...
retries.removeAll()
will nicely remove all containing objects and delete the references to these objects. From your context presented above I do not understand why you need to declare an array of optionals. As you know Swift optionals under the hood are just a typed wrapper class Optional<Type>, which doesn't work around the memory allocation problem.
How does array reference objects?
The array will increase the contained objects' reference count by one, that is, a strong reference.
To ensure the objects in the array to be deallocated, one must make their reference count equal zero. Removing them from the array will do the trick, if nothing else is referencing the contained objects.
Beware of the reference cycle though. In your case, you should not hold reference to the retries array in Retry instance. Otherwise even if you set the retries array to nil, the array and its contained objects still have strong reference to each other, meaning their reference counts will never reduce to zero, causing memory leak.

Storing a simple array in Realm without creating a managed List in Swift

I have an array of data that doesn't need to be a managed List, meaning I don't need Realm to create a new model for the items with links and the ability to query on the items, etc. I just want a simple array, typically of primitives that don't inherit from Object anyway, that will be persisted with my main object.
The only solution I can think of is to use NSData and NSKeyedArchiver/NSKeyedUnarchiver. Is that the best/only way to do this? Should I just use List even if I don't think I'll need it — what's the best practice for this situation?
Realm doesn't support arrays of primitives (although that functionality is coming soon), so the most straightforward solution is to use a List filled with model objects that just wrap your primitives. There's nothing wrong with archiving to and from NSData and storing the data in your Realm model, though, if you feel that better suits your particular use case.
Here's how I decided to deal with this:
var instructions: [String] {
get {
if _instructions == nil {
_instructions = NSKeyedUnarchiver.unarchiveObject(with: instructionsData) as? [String]
}
return _instructions!
}
set {
instructionsData = NSKeyedArchiver.archivedData(withRootObject: newValue)
_instructions = newValue
}
}
fileprivate var _instructions: [String]? = nil
dynamic var instructionsData = Data()
override static func ignoredProperties() -> [String] {
return ["instructions", "_instructions"]
}
That way I can use the array as I normally would and still have it persisted in a simple way (without having to create an actual List and having to manage a bunch of new models/objects).

Function returning specified values but structure doesn't append its values

https://github.com/mateo951/ISBN-Vista-Jera-rquica- Github Link
The structure I have is supposed to be appending values after an internet search. The internet search is called within a function and returns two strings and an image. When I try to append the returned values in the structure, the image is saved but strings are nil.
var datosLibros = [bookData]()
#IBAction func Search(sender: UITextField) {
let (title1, author1, cover1) = (internetSearch(sender.text!))
let libro = bookData(title: title1, author: author1,image:cover1)
datosLibros.append(libro)
print(datosLibros)
}
The saved structured that is printed to the console is the following:
bookData(title: "", author: "", image: <UIImage: 0x7f851a57fbf0>, {0, 0})
Structure:
struct bookData {
var title: String
var author: String
var image: UIImage
init(title: String, author: String, image: UIImage) {
self.title = title
self.author = author
self.image = image
}
}
Thanks in advanced for any advice of help provided. I'm new to swift so there are a lot of stuff uncovered.
The problem is not with the code you posted but with internetSearch.
But before I explain what is going on there, just a quick note about Swift structs. Structs come with one free initializer that takes as its parameters one value for each stored property defined on the struct. Argument labels correspond to the variable labels.
So for your struct bookData (which really should be BookData since types should be capitalized), you do not need to include that initializer you wrote because it will be automatically provided for you as long as you do not create any additional BookData initializers.
Now for the reason your results are not what you expect. Your Strings are not coming back as nil. Instead, they are coming back as empty Strings, or "". In Swift, "" is very different from nil, which means a complete absence of a value. So your Strings are indeed there, they are just empty.
Okay, our Strings are coming back empty. How about our image? No, our image is not coming back either. You thought it was because you saw a UIImage reference printed in the console, but if you look closer you will notice it is a bogus image. Notice "{0, 0}" after the memory address for the instance. As far as I'm aware, this means the image has a size of 0 x 0. How many useful images do you know that have a size of 0 x 0?
So now we have discovered that our Strings are coming back empty and effectively so is our image. What is going on here?
Well, in your implementation of internetSearch I found on GitHub, this is the first thing you do:
var bookTitle = String()
var bookAuthor = String()
var bookCover = UIImage()
Naturally, you do this so that you have some variables of the correct types ready to plop in some actual results if you find them. Just for fun, let's see what the result of the code above would be if there were no results.
Well, the initializer for String that accepts no parameters results in an empty String being created.
Okay, how about our image. While the documentation for UIImage does not even mention an initializer that takes no parameters, it does inherit one from NSObject and it turns out that it will just create an empty image object.
So we now have discovered that what internetSearch is returning is actually the same as what it would be if there were no results. Assuming you are searching for something that you know exists, there must be a problem with the search logic, right? Not necessarily. I noticed that your implementation of the rest of internetSearch relies on an NSURLSession that you use like so:
var bookTitle = String()
var bookAuthor = String()
var bookCover = UIImage()
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url) { (data, response, error) -> Void in
// Lots of code that eventually sets the three variables above to a found result
}
task.resume()
return (bookTitle, bookAuthor, bookCover)
That seems fine and dandy, except for the fact that NSURLSession performs its tasks asynchronously! Yes, in parts you even dispatch back to the main queue to perform some tasks, but the closure as a whole is asynchronous. This means that as soon as you call task.resume(), NSURLSession executes that task on its own thread/queue/network and as soon as that task is set up it returns way before it completes. So task.resume() returns almost immediately, before any of your search code in the task actually runs, and especially before it completes.
The runtime then goes to the next line and returns those three variables, just like you told it to. This, of course, is the problem because your internetSearch function is returning those initial empty variables before task has a chance to run asynchronously and set them to helpful values.
Suggesting a fully-functional solution is probably beyond the scope of this already-long answer, but it will require a big change in your implementation detail and you should search around for using data returned by NSURLSession.
One possible solution, without me posting any code, is to have your internetSearch function not return anything, but on completion of the task call a function that would then append the result to an array and print it out, like you show. Please research this concept.
Also, I recommend changing your implementation of internetSearch further by declaring your initial values not as:
var bookTitle = String()
var bookAuthor = String()
var bookCover = UIImage()
…but as:
var bookTitle: String?
var bookAuthor: String?
var bookCover: UIImage?
This way, if you find a result than you can represent it wrapped in an Optional and if not you can represent that as nil, which will automatically be the default value of the variables in the code directly above.

MagicalRecord's MR_InContext method. Safe to use?

I was wondering if it was safe to call MR_InContext in conjunction with saveWithBlock. I know apple suggests not even READING from core data objects in a different thread, so I'm wondering if I should be passing the objectId instead. Here is an example of what I'm talking about:
// outside of save with block
var someManagedObject = SomeManagedObject.MR_first()
MagicalRecord.saveWithBlock({ (localContext : NSManagedObjectContext!) in
// Is this ok? should I pass the objectId instead?
var managedObjectInThread = someManagedObject.MR_InContext(localContext)
managedObjectInThread.date = NSDate()
}, completion: { (success : Bool, error : NSError!) in
// This block runs in main thread
})
Absolutely. This is the type of scenario for which this method was intended.
Based on the GDCoreDataConcurrencyDebugging framework:
Invalid concurrent access to managed object (Question) calling 'release' when I used MR_InContext
Instead of I had to keep a reference to the objectID and use it inside the block.