calling cancelAllOperations from nested NSOperations - ios

I'm struggling with the following situation, pleaes bear with me as I've tried to be explain this as clearly as possible:
I have a class CoummintyOperation which is a subclass of a GroupOperation. CommuityOperation is called and added to an NSOperationQueue.
CommunityOperation is in turn calling a whole bunch of NSOperations which are also sublcasses of GroupOperation and in turn are calling NSoperations within them.
I've added dependencies for the GroupOperations in the CommunityOperation class.
The issue I'm dealing with is if a nested operation fails I need to cancel ALL NSOperations in the CommunityOperations class, but I don't have access to the operationQueue that CommunityOperation was added to in order to call the cancelAllOperations method.
Can anyone help me out and explain how I can call this method, cancel All operations in this class (and therefore all nested Operations) and display an error message to the user.
Thanks in advance
Here's some sample code to help explain my issue:
CommunityOperation gets put on an operationQueue from another class (not included)
public class CommunityOperation: GroupOperation {
var updateOperation: UpdateOperation!
var menuOperation: MenuOperation!
var coreDataSaveOperation: CoreDataSaveOperation!
var hasErrors = false
public init() {
super.init(operations: [])
updateOperation = UpdateOperation()
menuOperation = MenuOperation()
coreDataSaveOperation = CoreDataSaveOperation()
coreDataSaveOperation.addDependencies([updateOperation, menuOperation])
self.addOperation(updateOperation)
self.addOperation(menuOperation)
self.addOperation(coreDataSaveOperation)
}
}
MenuOperation class, which has nested Operations in it as well:
class UpdateMenuOperation: GroupOperation {
let downloadGroupsOperation: DownloadGroupsOperation
let downloadMembersOperation: DownloadMembersOperation
init() {
downloadGroupsOperation = DownloadGroupsOperation()
downloadMembersOperation = DownloadMembersOperation(])
super.init(operations: [downloadGroupsOperation,
downloadMembersOperation
])
}
}
DownloadGroupOperation class is again a subclass of GroupOperation. It has 2 operations - the first to download data and the second to parse the data:
class DownloadTopGroupsOperation: GroupOperation {
let downloadOperation: DownloadOperation
let importTopGroupsOperation: ImportOperation
init() {
downloadOperation = DownloadOperation()
importOperation = ImportOperation()
importOperation.addDependency(downloadOperation)
importOperation.addCondition(NoCancelledDependencies())
super.init(operations: [downloadOperation, importOperation])
}
}
And finally (whew) the DownloadOperation class uses a NSURLSession and the method downloadTaskWithURL, it's in this method's completion handler that I want to call cancelAllOperations on the main operatioQueue if an error is returned:
class DownloadOperation: GroupOperation {
init() {
super.init(operations: [])
if self.cancelled {
return
}
let task = session.downloadTaskWithURL(url) { [weak self] url, response, error in
self?.downloadFinished(url, response: response as? NSHTTPURLResponse, error: error)
}
}
func downloadFinished(url: NSURL?, response: NSHTTPURLResponse?, error: NSError?) {
if error {
*cancel allOperations on queue*
}
}
}

It should work in a bit different way. I'd check the isCancelled from the GroupOperation by the end of each NSOperation execution. If operation was cancelled then cancel the current GroupOperation and so on. In the end your CommunityOperation should be cancelled as well.
Here is the rough implementation of the proposed solution:
extension GroupOperation {
func addCancellationObservers() {
self.operations.forEach() { $0.willCancelObservers.append() { [unowned self] operation, errors in
self.cancel() // cancel the group operation. will force it to cancel all child operations
}
}
}
}
Then call addCancellationObservers from the init method of each group operations you have.

if you're using Apple's sample code (or https://github.com/danthorpe/Operations which is the evolution of that project) you can sort this out by attaching a condition to your operations which have dependencies.
Here is the init of your top level GroupOperation
init() {
updateOperation = UpdateOperation()
menuOperation = MenuOperation()
coreDataSaveOperation = CoreDataSaveOperation()
coreDataSaveOperation.addDependencies([updateOperation, menuOperation])
// Attach a condition to ensure that all dependencies succeeded
coreDataSaveOperation.addCondition(NoFailedDependenciesCondition())
super.init(operations: [updateOperation, menuOperation, coreDataSaveOperation])
}
To explain what is happening here... NSOperation has no concept of "failure". The operations always "finish" but whether they finished successfully or failed does not affect how NSOperation dependencies work.
In other words, an operation will become ready when all of its dependencies finish, regardless of whether those dependencies succeeded. This is because "success" and "failure" is something that the subclass must define. Operation (the NSOperation subclass) defines success by having finished without any errors.
To deal with this, add a condition that no dependencies must have failed. In Operations this condition got renamed to make it clearer. But, the concept exists in the Apple sample code too.

Related

How can I wrap an async function with an Observable

I have an async function that currently looks something like this
func startLoginFlow() {
IdentityProvider.shared.login { success, error in
// on success a user has completed authentication
if success {
delegate?.userIsAuthenticated()
}
// on error something wen't wrong
....
}
}
Essentially on success a delegate method is called and some action takes place as a result.
I'd like to wrap this as an observable instead. I do not have the option refactoring IdentityProvider.shared.login.
I essentially just need the observable to emit so I can subscribe and take action elsewhere using onNext.
I am currently doing the following
func startLoginFlow() -> Observable<Void> {
return Observable.create { [weak self] observable in
IdentityProvider.shared.login { success, error in
if success {
observable.onNext(Void())
}
}
return Disposables.create()
}
}
Is this the best way to do this? I wasn't sure if I should use Observable.of and subscribe to the result of IdentityProvider.shared.login
This is how I create Observables as well. The only thing I would note is to add in the errors so you can handle your observables when it errors out, and the completion, as well, to signal that your observable is complete.
func startLoginFlow() -> Observable<Void> {
return Observable.create { [weak self] observable in
IdentityProvider.shared.login { success, error in
if success {
observable.onNext(())
observable.onCompleted()
} else {
observable.onError(error)
}
}
return Disposables.create()
}
}
Observable.of's work in this case as well. It just emits the completed method. You can test this out yourself, if you were trying to create an Observable<String>, with both methods.
I find that doing Observable.create is beneficial here as you're doing network requests and that you can control how you want your observables to error, fail, or be completed.
Someone here gave a pretty good example as well:
Rxswift What difference between Observable.of and Observable<String>.create

Synchronous processing with DispatchQueue in Swift

I have a method which does processing for the events I receive from the server. The method can be called from multiple places in different classes. I want to synchronize the processing of the events using DispatchQueue/Serial Queue to discard the duplicate events in multiple calls. I know about dispatch queues and how it works but I am unable to find the best solution for my problem.
To achieve: By synchronizing I want to ensure sequential processing, to discard duplicate events.
func process(events:[Events]) {
// by synchronizing I want to ensure sequential processing, to discard duplicate events
for event in events {
// process, save to db,
}
// issue notifications, etc
}
class A {
process(events)
}
class B {
process(events)
}
Any help is appreciated. Thanks!
Try something like this:
class Event {
let id: String = ""
}
class EventManager {
static let shared = EventManager()
private let processQueue = DispatchQueue(label: "processQueue")
private var processedEvents = [Event]()
private init() {}
func process(events:[Event]) {
processQueue.async { [unowned self] in
for event in events {
if !self.processedEvents.contains(where: { $0.id == event.id }) {
// process, save to db,
self.processedEvents.append(event)
}
}
// issue notifications, etc
}
}
}

Swift - Method chaining

I'd like to implement method chaining in my swift code, likely to Alamofire methods. For example, if I have to use my function like below
getListForID(12).Success {
// Success block
}. Failure {
// Failure block
}
How would I create the function getListForID?
To expand on the great points #dasblinkenlight and #Sulthan have made – here's a small example of how you could achieve your request function to take a success and failure closure, in the convenient syntax that you want.
First, you'll have to define a new class to represent the 'result handler'. This is what your success and failure functions will pass around, allowing you to add multiple trailing closures to make up your completion block logic. You'll want it to look something like this:
class ResultHandler {
typealias SuccessClosure = RequestHandler.Output->Void
typealias FailureClosure = Void->Void
// the success and failure callback arrays
private var _successes = [SuccessClosure]()
private var _failures = [FailureClosure]()
/// Invoke all the stored callbacks with a given callback result
func invokeCallbacks(result:RequestHandler.Result) {
switch result {
case .Success(let output): _successes.forEach{$0(output)}
case .Failure: _failures.forEach{$0()}
}
}
// remove all callbacks – could call this from within invokeCallbacks
// depending on the re-usability of the class
func removeAllCallbacks() {
_successes.removeAll()
_failures.removeAll()
}
/// appends a new success callback to the result handler's successes array
func success(closure:SuccessClosure) -> Self {
_successes.append(closure)
return self
}
/// appends a new failure callback to the result handler's failures array
func failure(closure:FailureClosure) -> Self {
_failures.append(closure)
return self
}
}
This will allow you to define multiple success or failure closures to be executed on completion. If you don't actually need the capacity for multiple closures, then you can simplify the class down by stripping out the arrays – and just keeping track of the last added success and failure completion blocks instead.
Now all you have to do is define a function that generates a new ResultHandler instance and then does a given asynchronous request, with the invokeCallbacks method being invoked upon completion:
func doRequest(input:Input) -> ResultHandler {
let resultHandler = ResultHandler()
doSomethingAsynchronous(resultHandler.invokeCallbacks)
return resultHandler
}
Now you can call it like this:
doRequest(input).success {result in
print("success, with:", result)
}.failure {
print("fail :(")
}
The only thing to note is your doSomethingAsynchronous function will have to dispatch its completion block back to the main thread, to ensure thread safety.
Full project (with added example on usage): https://github.com/hamishknight/Callback-Closure-Chaining
In order to understand what is going on, it would help to rewrite your code without the "convenience" syntax, which lets you omit parentheses when a closure is the last parameter of a function:
getListForID(12)
.Success( { /* Success block */ } )
.Failure( { /* Failure block */ } )
This makes the structure of the code behind this API more clear:
The return value of getListForID must be an object
The object must have two function called Success and Failure*
Both Success and Failure need to take a single parameter of closure type
Both Success and Failure need to return self
* The object could have only Success function, and return a different object with a single Failure function, but then you wouldn't be able to re-order the Success and Failure handlers, or drop Success handler altogether.

Whether performBlockAndWait calling twice on single thread cause deadlock?

I have found something like this in performBlockAndWait documentation:
This method may safely be called reentrantly.
My question is whether it means that it never cause deadlock when I e.g. will invoke it like that on single context?:
NSManageObjectContext *context = ...
[context performBlockAndWait:^{
// ... some stuff
[context performBlockAndWait:^{
}];
}];
You can try it yourself with a small code snippet ;)
But true, it won't deadlock.
I suspect, the internal implementation uses a queue specific token in order to identify the current queue on which the code executes (see dispatch_queue_set_specific and dispatch_queue_get_specific).
If it determines that the current executing code executes on its own private queue or on a children-queue, it simply bypasses submitting the block synchronously - which would cause a dead-lock, and instead executing it directly.
A possible implementation my look as below:
func executeSyncSafe(f: () -> ()) {
if isSynchronized() {
f()
} else {
dispatch_sync(syncQueue, f)
}
}
func isSynchronized() -> Bool {
let context = UnsafeMutablePointer<Void>(Unmanaged<dispatch_queue_t>.passUnretained(syncQueue).toOpaque())
return dispatch_get_specific(&queueIDKey) == context
}
And the queue might be created like this:
private var queueIDKey = 0 // global
init() {
dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL,
QOS_CLASS_USER_INTERACTIVE, 0))
let context = UnsafeMutablePointer<Void>(Unmanaged<dispatch_queue_t>.passUnretained(syncQueue).toOpaque())
dispatch_queue_set_specific(syncQueue, &queueIDKey, context, nil)
}
dispatch_queue_set_specific associates a token (here context - which is simply the pointer value of the queue) with a certain key for that queue. And later, you can try to retrieve that token for any queue specifying the key and check whether the current queue is the same queue or a children-queue. If this is true, bypass dispatching to the queue and instead call the function f directly.

Inject mock class into method to unit test method

I'm trying to unit test a method which has a dependency on another class. The method calls a class method on that class, essentially this:
func myMethod() {
//do stuff
TheirClass.someClassMethod()
}
Using dependency injection technique, I would like to be able to replace "TheirClass" with a mock, but I can't figure out how to do this. Is there some way to pass in a mock class (not instance)?
EDIT: Thanks for the responses. Perhaps I should have provided more detail. The class method I am trying to mock is in an open source library.
Below is my method. I am trying to test it, while mocking out the call to NXOAuth2Request.performMethod. This class method issues a network call to get the authenticated user's info from our backend. In the closure, I am saving this info to the global account store provided by the open source library, and posting notifications for success or failure.
func getUserProfileAndVerifyUserIsAuthenticated() {
//this notification is fired when the refresh token has expired, and as a result, a new access token cannot be obtained
NSNotificationCenter.defaultCenter().addObserver(self, selector: "didFailToGetAccessTokenNotification", name: NXOAuth2AccountDidFailToGetAccessTokenNotification, object: nil)
let accounts = self.accountStore.accountsWithAccountType(UserAuthenticationScheme.sharedInstance.accountType) as Array<NXOAuth2Account>
if accounts.count > 0 {
let account = accounts[0]
let userInfoURL = UserAuthenticationScheme.sharedInstance.userInfoURL
println("getUserProfileAndVerifyUserIsAuthenticated: calling to see if user token is still valid")
NXOAuth2Request.performMethod("GET", onResource: userInfoURL, usingParameters: nil, withAccount: account, sendProgressHandler: nil, responseHandler: { (response, responseData, error) -> Void in
if error != nil {
println("User Info Error: %#", error.localizedDescription);
NSNotificationCenter.defaultCenter().postNotificationName("UserCouldNotBeAuthenticated", object: self)
}
else if let data = responseData {
var errorPointer: NSError?
let userInfo = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &errorPointer) as NSDictionary
println("Retrieved user info")
account.userData = userInfo
NSNotificationCenter.defaultCenter().postNotificationName("UserAuthenticated", object: self)
}
else {
println("Unknown error retrieving user info")
NSNotificationCenter.defaultCenter().postNotificationName("UserCouldNotBeAuthenticated", object: self)
}
})
}
}
In Swift this is better done by passing a function. There are many ways to approach this, but here is one:
func myMethod(completion: () -> Void = TheirClass.someClassMethod) {
//do stuff
completion()
}
Now you can pass a completion handler, while existing code will continue to use the default method. Notice how you can refer to the function itself (TheirClass.someClassMethod). You don't have to wrap it up in a closure.
You may find it better to let the caller just pass this all the time rather than making it a default. That would make this class less bound to TheirClass, but either way is fine.
It's best to integrate this kind of loose coupling, design-for-testability into the code itself rather than coming up with clever ways to mock things. In fact, you should ask yourself if myMethod() should really be calling someClassMethod() at all. Maybe these things should be split up to make them more easily tested, and then tie them together at a higher level. For instance, maybe myMethod should be returning something that you can then pass to someClassMethod(), so that there is no state you need to worry about.

Resources