delegate function vs callback function [duplicate] - ios

This question already has answers here:
Problem understanding Delegating pattern & callback functions in Objective C
(3 answers)
Closed 9 years ago.
I work on iOS platform , I want to know what is a delegate function and what is a callback function ? what is the difference between the two types of function or they are the same ??
example of delegate function is numberOfRowsInSection in UITableViewDelegate protocol and example of callback function is didReceiveLocalNotification in appDelegate.m
Can we create our own callback function in Objective-C , if YES ,give an example ...
Thank you..

A couple of thoughts:
You suggest that didReceiveLocationNotification was a "callback function", but it's actually just a delegate method of the UIApplicationDelegate protocol. So, both numberOfRowsInSection and didReceiveLocalNotification are simply delegate methods.
Something more akin to a generic callback function would be the selector when scheduling a NSTimer or defining the handler for a UIGestureRecognizer, where the choice of method name is not predetermined.
Or callbacks are used extensively in CFArray.
But, the root of your question is less on the terminology, but rather a question of how to define an interface where the caller can specify a method that some other object will invoke (asynchronously) at some future date. There are a couple of common patterns:
Block parameter to method: It is increasingly common to define methods that take a block as a parameter. For example, you can have a method that is defined as follows:
- (NSURLSessionTask *)downloadAsynchronously:(NSURL *)url filename:(NSString *)filename completion:(void (^)(NSData *results, NSString *filename))completion {
NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(data, filename);
});
}];
[task resume];
return task;
}
That third parameter, completion, is a block of code that will be called with the download is done. Thus, you can invoke that method as follows:
[self downloadAsynchronously:url filename:filename completion:^(NSData *results, NSString *filename) {
NSLog(#"Downloaded %d bytes", [results length]);
[results writeToFile:filename atomically:YES];
}];
NSLog(#"%s done", __FUNCTION__);
You'll see that "done" message appear immediately, and that completion block will be called when the download is done. It admittedly takes a while to get used to the ungainly mess of punctuation that constitutes a block variable/parameter definition, but once you're familiar with the block syntax, you'll really appreciate this pattern. It eliminates the disconnect between the invoking of some method and the defining of some separate callback function.
If you want to simplify the syntax of dealing with blocks as parameters, you can actually define a typedef for your completion block:
typedef void (^DownloadCompletionBlock)(NSData *results, NSString *filename);
And then the method declaration, itself, is simplified:
- (NSURLSessionTask *)downloadAsynchronously:(NSURL *)url filename:(NSString *)filename completion:(DownloadCompletionBlock)completion {
NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(data, filename);
});
}];
[task resume];
return task;
}
Delegate-protocol pattern: The other common technique for communicating between objects is the delegate-protocol pattern. First, you define the protocol (the nature of the "callback" interface):
#protocol DownloadDelegate <NSObject>
- (NSURLSessionTask *)didFinishedDownload:(NSData *)data filename:(NSString *)filename;
#end
Then, you define your class that will be invoking this DownloadDelegate method:
#interface Downloader : NSObject
#property (nonatomic, weak) id<DownloadDelegate> delegate;
- (instancetype)initWithDelegate:(id<DownloadDelegate>)delegate;
- (NSURLSessionTask *)downloadAsynchronously:(NSURL *)url filename:(NSString *)filename;
#end
#implementation Downloader
- (instancetype)initWithDelegate:(id<DownloadDelegate>)delegate {
self = [super init];
if (self) {
_delegate = delegate;
}
return self;
}
- (NSURLSessionTask *)downloadAsynchronously:(NSURL *)url filename:(NSString *)filename {
NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
[self.delegate didFinishedDownload:data filename:filename];
});
}];
[task resume];
return task;
}
#end
And finally, the original view controller which uses this new Downloader class must conform to the DownloadDelegate protocol:
#interface ViewController () <DownloadDelegate>
#end
And define the protocol method:
- (void)didFinishedDownload:(NSData *)data filename:(NSString *)filename {
NSLog(#"Downloaded %d bytes", [data length]);
[data writeToFile:filename atomically:YES];
}
And perform the download:
Downloader *downloader = [[Downloader alloc] initWithDelegate:self];
[downloader downloadAsynchronously:url filename:filename];
NSLog(#"%s done", __FUNCTION__);
Selector pattern: A pattern that you see in some Cocoa objects (e.g. NSTimer, UIPanGestureRecognizer) is the notion of passing a selector as a parameter. For example, we could have defined our downloader method as follows:
- (NSURLSessionTask *)downloadAsynchronously:(NSURL *)url filename:(NSString *)filename target:(id)target selector:(SEL)selector {
id __weak weakTarget = target; // so that the dispatch_async won't retain the selector
NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[weakTarget performSelector:selector
withObject:data
withObject:filename];
#pragma clang diagnostic pop
});
}];
[task resume];
return task;
}
You'd then invoke that as follows:
[self downloadAsynchronously:url
filename:filename
target:self
selector:#selector(didFinishedDownload:filename:)];
But you also have to define that separate method that will be called when the download is done:
- (void)didFinishedDownload:(NSData *)data filename:(NSString *)filename {
NSLog(#"Downloaded %d bytes", [data length]);
[data writeToFile:filename atomically:YES];
}
Personally, I find this pattern to be far too fragile and dependent upon coordinating interfaces without any assistance from the compiler. But I include it for a bit of historical reference given that this pattern is used quite a bit in Cocoa's older classes.
Notifications: The other mechanism to provide the results of some asynchronous method is to send a local notification. This is generally most useful when either (a) the potential recipient of the results of the network request is unknown at the time the request was initiated; or (b) there may be multiple classes that want to be informed of this event. So the network request can post a notification of a particular name when it's done, and any object that is interested in being informed of this event can add themselves as an observer for that local notification through the NSNotificationCenter.
This is not a "callback" per se, but does represent another pattern for an object to be informed of the completion of some asynchronous task.
Those are a few examples of "callback" patterns. Clearly, the example provided was arbitrary and trivial, but hopefully it should give you an idea of your alternatives. The two most common techniques, nowadays, are blocks and delegate patterns. Blocks are increasingly being preferred when a simple and elegant interface is needed. But for rich and complicated interfaces, delegates are very common.

Related

Objective C: Is there a way to call a completion block for a method in another method?

I have multiple GET API request methods that call a completion block when it finishes. Here is an example of one.
- (void)getUserInfo
onSuccess:(void (^)(id))successBlock
onFailure:(void (^)(NSError *))failureBlock {
NSString *urlStr = [NSString stringWithFormat:#"%#/user/", baseUrl];
[manager GET:urlStr parameters:nil progress:nil
success:^(NSURLSessionTask *task, id responseObject) {
successBlock(responseObject);
}
failure:^(NSURLSessionTask *operation, NSError *error) {
failureBlock(error);
}];
}
However, I noticed that I am repeating the manager GET request code in other methods. I want to create another method that handles all of the requests and remove the repetitive code. The URL seems to be the only thing that changes. However, there is one flaw. I need to call the successBlock to let the method know the request has finished.
Maybe I need to take another path altogether and do something different.
You can pass completion blocks around and then call them from your final method which handles all the get requests. I usually make completion blocks that are going to be reused typedefs for brevity. Here's an example of what I mean (I added a second example method that also passes through to the center getRequestWithURLString:onSuccess:onFailure: method):
LLFakeManager.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
typedef void (^_Nullable SuccessCompletionBlock)(id responseObject);
typedef void (^_Nullable FailureCompletionBlock)(NSError *error);
#interface LLFakeManager : NSObject
- (void)getUserInfoOnSuccess:(SuccessCompletionBlock)successBlock onFailure:(FailureCompletionBlock)failureBlock;
- (void)getBooksCheckedOutOnSuccess:(SuccessCompletionBlock)successBlock onFailure:(FailureCompletionBlock)failureBlock;
#end
NS_ASSUME_NONNULL_END
LLFakeManager.m
#import "LLFakeManager.h"
#interface LLFakeManager()
- (void)getRequestWithURLString:(NSString *)urlString
onSuccess:(SuccessCompletionBlock)successBlock
onFailure:(FailureCompletionBlock)failureBlock;
#end
#implementation LLFakeManager
- (void)getUserInfoOnSuccess:(SuccessCompletionBlock)successBlock onFailure:(FailureCompletionBlock)failureBlock {
NSString *urlStr = #"FakeUserUrlPath";
[self getRequestWithURLString:urlStr onSuccess:successBlock onFailure:failureBlock];
}
- (void)getBooksCheckedOutOnSuccess:(SuccessCompletionBlock)successBlock onFailure:(FailureCompletionBlock)failureBlock {
NSString *urlString = #"FakeBooksUrlPath";
[self getRequestWithURLString:urlString onSuccess:successBlock onFailure:failureBlock];
}
// central method that will handle all the get requests
- (void)getRequestWithURLString:(NSString *)urlString
onSuccess:(SuccessCompletionBlock)successBlock
onFailure:(FailureCompletionBlock)failureBlock {
// some fake implementation here to do your request, then use the completion block passed in from whatever other method
if (successBlock) {
successBlock(#"responseObjectPassedBackHere");
}
}
#end
And an example of calling it:
LLFakeManager *manager = [[LLFakeManager alloc] init];
[manager getUserInfoOnSuccess:^(id _Nonnull responseObject) {
NSLog(#"Here's my response object = %#", responseObject);
} onFailure:^(NSError * _Nonnull error) {
// no implementation but same idea
}];
Would produce this log:
Here's my response object = responseObjectPassedBackHere
This site: http://goshdarnblocksyntax.com is a handy list of block syntax which may also be helpful for you.
The blocks -- if they have the same signature -- they can be passed along a chain of methods. Your GET blocks carry an unneccesary first parameter. The NSURLSessionTask *, if it is to be returned at all, should be returned synchronously. Moving that out of the block signature will allow you to standardize the blocks.
It's easier to say in code...
// changed this method name so it would compile
- (void)getUserInfoOnSuccess:(void (^)(id))successBlock
onFailure:(void (^)(NSError *))failureBlock {
NSString *urlStr = [NSString stringWithFormat:#"%#/user/", baseUrl];
// two things: get the task as a return value (if you need it)
// pass the blocks directly, without nesting them in new blocks
NSURLSessionTask *task = [manager GET: urlStr
parameters: nil
progress: nil
success: successBlock
failure: failureBlock];
// do something with the task
}
To make this work, alter the GET method return type and block signatures...
- (NSURLSessionTask *)GET:(NSString *)url parameters:(id)params progress:(id)progress success:(void (^)(id))successBlock failure:(void (^)(NSError *))failureBlock {
// return the session task created here
return task
}

Objective-C: How to give a completion block to a method that uses a result from the method execution

Object X needs to download an image. It has URL of the image (could be file of network). I have a common imageHandler class that can take a URL and get the imageData. I want this method, to be able to take a completion block to customize what to do with the downloaded image.
I am familiar with how to do it is using delegate pattern. E.g
#protocol ImageHandler: NSObject
-(void) getImageFromURL:(NSURL *)url forDelegate:(id <ImageRequestor>)delegate;
#end
#protocol ImageRequestor: NSObject
-(void) image:(UIImage *) image RetrievedForURL:(NSURL *)url withError:(NSError *)error;
#end
So, basically objectX class getImageFromURL:delegate method with delegate as self. It conforms to the ImageRequestor protocol.
The ImageHandler object, stores url to delegate mapping in a hash table. After it is done, calls image:RetrievedForURL:withError: on the delegate. And in this method I act on the image and error.
How do I achieve the same effect using completion blocks where I pass on the "what i want to do with the retrieved image" as a piece of code.
One approach I see is as below. But looks like it requires the ImageHandler method implementation call the completion with specific arguments. Is that the accepted way to implement this (i.e the completion handler relies on the method receiving it to call it with correct arguments)?
#protocol ImageHandler: NSObject
-(void) getImageFromURL:(NSURL *)url withCompletionHandler:(void (^)(UIImage *image, NSError * err))completionBlock;
#end
The implementation of getImageFromURL:(NSURL *)url withCompletionHandler:(void (^)(UIImage *image, NSError * err))completionBlock looks like
getImageFromURL:(NSURL *)url withCompletionHandler:(void (^)(UIImage *image, NSError * err))completionBlock
{
//Get UIImage from URL, store it UIIMage ObjectX
//if error happens, store it in NSError objectY, else it is nil
//call completion handler
completionBlock(<UIIMage objectX>,<NSError objectY>);
}
Yes, what you've shown is the typical way of doing this. Look at Apple's own APIs to see that they do it the same way. For example, -[NSURLSession dataTaskWithURL:completionHandler:], whose declaration is:
- (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url
completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler;
Write the following inside your ImageRequestor.h file:
#import <Foundation/Foundation.h>
// Here you define your custom response block with the desired parameters
// That in this case are UIImage and NSError
typedef void (^ResponseBlock)(UIImage *image, NSError *error);
#interface ImageRequestor : NSObject
// Here you define the method that use your custom response block
- (void)getImageFromURL:(NSURL *)url withCompletionHandler:(ResponseBlock)completionBlock;
#end
Almost done, now open your ImageRequestor.m file and add the following:
#import "ImageRequestor.h"
#implementation ImageRequestor
- (void)getImageFromURL:(NSURL *)url withCompletionHandler:(ResponseBlock)completionBlock {
// Do all your stuff to get the image and the error
// And set them into your completion block
// if there is no error that should be nil
// completionBlock(YourFetchedImage, YourFetchedError);
// completionBlock(nil, YourFetchedError);
completionBlock(YourFetchedImage, nil);
}
#end
And finally you can call it with something like this:
ImageRequestor *imageRequestor = [[ImageRequestor alloc] init];
[imageRequestor getImageFromURL:yourURL withCompletionHandler:^(UIImage *image, NSError *error) {
// Your implementation in here
}];
This kind of stuffs looks a lot way better in a singleton class :]
Hope it helps,
Good Luck!!

How can I wait for delegate to complete before returning completion handler in method?

I have a method which performs an action.
- (void)mutualDeleteDialog:(QBChatDialog *)dialog success:(void (^) ())successBlock failure:(void (^)(NSError *))failureBlock {
QBChatMessage *deleteMessage = [self generateDeleteDialogMessage:dialog];
[self sendMessage:deleteMessage success:^{
[QBChat deleteDialogWithID:dialog.ID delegate:self];
} failure:^(NSError *error) {
failureBlock(error);
}];
The deleteDialogWithID method calls a third party service and calls a delegate method when complete. When this delegate method is called I want to return the success/failure block to the caller of my original method...is this possible and how can I do it?
i.e.
//Delegate Method
- (void)completedWithResult:(QBResult *)result {
successBlock();
}
If you can guarantee that there's exactly one of these at a time, you could add a property to your class:
#property (copy) void(^successBlock)();
and then in mutualDeleteDialog:
self.successBlock = successBlock;
and then in completedWithResult:
self.successBlock();
This is pretty unconventional. You may want to reevaluate what it is you're trying to do. There might be a better way.

NSURLSession with NSBlockOperation and queues

I have an app that currently uses NSURLConnection for the vast majority of its networking. I would like to move to NSURLSession because Apple tells me that is the way to go.
My app just uses the synchronous version of NSURLConnection by way of the + (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error class method. I do this within a NSBlockOperation running on an NSOperationQueue so I am not needlessly blocking the main queue. The big advantage to doing things this way is that I can make the operations dependent on one another. For example, I can have the task that is requesting data be dependent on the login task finishing.
I have not seen any support for synchronous operations within NSURLSession. All I can find are articles deriding me for even thinking of using it synchronously and that I am a horrible person for blocking the threads. Fine. But I see no way to make NSURLSessionTasks dependent on each other. Is there a way to do that?
Or is there a description of how I would do such a thing in a different way?
The harshest criticisms of synchronous network requests are reserved for those who do it from the main queue (as we know that one should never block the main queue). But you're doing it on your own background queue, which addresses the most egregious problem with synchronous requests. But you're losing some wonderful features that asynchronous techniques provide (e.g. cancelation of requests, if needed).
I'll answer your question (how to make NSURLSessionDataTask behave synchronously) below, but I'd really encourage you to embrace the asynchronous patterns rather than fighting them. I'd suggest refactoring your code to use asynchronous patterns. Specifically, if one task is dependent upon another, simply put the initiation of the dependent task in the completion handler of the prior task.
If you have problems in that conversion, then post another Stack Overflow question, showing us what you tried, and we can try to help you out.
If you want to make an asynchronous operation synchronous, a common pattern is to use a dispatch semaphore so your thread that initiated the asynchronous process can wait for a signal from the completion block of the asynchronous operation before continuing. Never do this from the main queue, but if you're doing this from some background queue, it can be a useful pattern.
You can create a semaphore with:
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
You can then have the completion block of the asynchronous process signal the semaphore with:
dispatch_semaphore_signal(semaphore);
And you can then have the code outside of the completion block (but still on the background queue, not the main queue) wait for that signal:
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
So, with NSURLSessionDataTask, putting that all together, that might look like:
[queue addOperationWithBlock:^{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
NSURLSession *session = [NSURLSession sharedSession]; // or create your own session with your own NSURLSessionConfiguration
NSURLSessionTask *task = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (data) {
// do whatever you want with the data here
} else {
NSLog(#"error = %#", error);
}
dispatch_semaphore_signal(semaphore);
}];
[task resume];
// but have the thread wait until the task is done
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// now carry on with other stuff contingent upon what you did above
]);
With NSURLConnection (now deprecated), you have to jump through some hoops to initiate requests from a background queue, but NSURLSession handles it gracefully.
Having said that, using block operations like this means that the operations won't respond to cancellation events (while they're running, at least). So I generally eschew this semaphore technique with block operations and just wrap the data tasks in asynchronous NSOperation subclass. Then you enjoy the benefits of operations, but you can make them cancelable, too. It's more work, but a much better pattern.
For example:
//
// DataTaskOperation.h
//
// Created by Robert Ryan on 12/12/15.
// Copyright © 2015 Robert Ryan. All rights reserved.
//
#import Foundation;
#import "AsynchronousOperation.h"
NS_ASSUME_NONNULL_BEGIN
#interface DataTaskOperation : AsynchronousOperation
/// Creates a operation that retrieves the contents of a URL based on the specified URL request object, and calls a handler upon completion.
///
/// #param request A NSURLRequest object that provides the URL, cache policy, request type, body data or body stream, and so on.
/// #param dataTaskCompletionHandler The completion handler to call when the load request is complete. This handler is executed on the delegate queue. This completion handler takes the following parameters:
///
/// #returns The new session data operation.
- (instancetype)initWithRequest:(NSURLRequest *)request dataTaskCompletionHandler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))dataTaskCompletionHandler;
/// Creates a operation that retrieves the contents of a URL based on the specified URL request object, and calls a handler upon completion.
///
/// #param url A NSURL object that provides the URL, cache policy, request type, body data or body stream, and so on.
/// #param dataTaskCompletionHandler The completion handler to call when the load request is complete. This handler is executed on the delegate queue. This completion handler takes the following parameters:
///
/// #returns The new session data operation.
- (instancetype)initWithURL:(NSURL *)url dataTaskCompletionHandler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))dataTaskCompletionHandler;
#end
NS_ASSUME_NONNULL_END
and
//
// DataTaskOperation.m
//
// Created by Robert Ryan on 12/12/15.
// Copyright © 2015 Robert Ryan. All rights reserved.
//
#import "DataTaskOperation.h"
#interface DataTaskOperation ()
#property (nonatomic, strong) NSURLRequest *request;
#property (nonatomic, weak) NSURLSessionTask *task;
#property (nonatomic, copy) void (^dataTaskCompletionHandler)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error);
#end
#implementation DataTaskOperation
- (instancetype)initWithRequest:(NSURLRequest *)request dataTaskCompletionHandler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))dataTaskCompletionHandler {
self = [super init];
if (self) {
self.request = request;
self.dataTaskCompletionHandler = dataTaskCompletionHandler;
}
return self;
}
- (instancetype)initWithURL:(NSURL *)url dataTaskCompletionHandler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))dataTaskCompletionHandler {
NSURLRequest *request = [NSURLRequest requestWithURL:url];
return [self initWithRequest:request dataTaskCompletionHandler:dataTaskCompletionHandler];
}
- (void)main {
NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:self.request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
self.dataTaskCompletionHandler(data, response, error);
[self completeOperation];
}];
[task resume];
self.task = task;
}
- (void)completeOperation {
self.dataTaskCompletionHandler = nil;
[super completeOperation];
}
- (void)cancel {
[self.task cancel];
[super cancel];
}
#end
Where:
//
// AsynchronousOperation.h
//
#import Foundation;
#interface AsynchronousOperation : NSOperation
/// Complete the asynchronous operation.
///
/// This also triggers the necessary KVO to support asynchronous operations.
- (void)completeOperation;
#end
And
//
// AsynchronousOperation.m
//
#import "AsynchronousOperation.h"
#interface AsynchronousOperation ()
#property (nonatomic, getter = isFinished, readwrite) BOOL finished;
#property (nonatomic, getter = isExecuting, readwrite) BOOL executing;
#end
#implementation AsynchronousOperation
#synthesize finished = _finished;
#synthesize executing = _executing;
- (instancetype)init {
self = [super init];
if (self) {
_finished = NO;
_executing = NO;
}
return self;
}
- (void)start {
if ([self isCancelled]) {
self.finished = YES;
return;
}
self.executing = YES;
[self main];
}
- (void)completeOperation {
self.executing = NO;
self.finished = YES;
}
#pragma mark - NSOperation methods
- (BOOL)isAsynchronous {
return YES;
}
- (BOOL)isExecuting {
#synchronized(self) {
return _executing;
}
}
- (BOOL)isFinished {
#synchronized(self) {
return _finished;
}
}
- (void)setExecuting:(BOOL)executing {
#synchronized(self) {
if (_executing != executing) {
[self willChangeValueForKey:#"isExecuting"];
_executing = executing;
[self didChangeValueForKey:#"isExecuting"];
}
}
}
- (void)setFinished:(BOOL)finished {
#synchronized(self) {
if (_finished != finished) {
[self willChangeValueForKey:#"isFinished"];
_finished = finished;
[self didChangeValueForKey:#"isFinished"];
}
}
}
#end
#Rob I would encourage you to post your reply as a solution, in view of the following documentation note from NSURLSession.dataTaskWithURL(_:completionHandler:):
This method is intended as an alternative to the
sendAsynchronousRequest:queue:completionHandler: method of
NSURLConnection, with the added ability to support custom
authentication and cancellation.
If semaphore based approach doesn't work, try polling based approach.
var reply = Data()
/// We need to make a session object.
/// This is key to make this work. This won't work with shared session.
let conf = URLSessionConfiguration.ephemeral
let sess = URLSession(configuration: conf)
let task = sess.dataTask(with: u) { data, _, _ in
reply = data ?? Data()
}
task.resume()
while task.state != .completed {
Thread.sleep(forTimeInterval: 0.1)
}
FileHandle.standardOutput.write(reply)
Polling based approach works very reliably, but effectively limits maximum throughput to polling interval. In this example, it's been limited to 10 times/sec.
I made a Swift package for this.
Semaphore based approach has been worked well so far, but since Xcode 11 era, it's getting broken. (maybe only for me?)
A data task does not finish if I wait for semaphores. If I wait for semaphore on different thread, it task fails with an error.
nw_connection_copy_protocol_metadata [C2] Client called nw_connection_copy_protocol_metadata on unconnected nw_connection error.
It seems something has been changed in the implementation as Apple is moving Network.framework.

How do I get ARC to retain a delegate long enough to have it called in a block?

In the following code fragment, using ARC, how do I get the delegate to live long enough to call the two method?
Current I get a compiler error
Bad receiver type ' __autoreleasing id * '
I assume I need to do something to make ARC retain the delegate and release it when it done calling but not sure what the right thing to do is.
- (BOOL) requestFromURL:(NSString*)url withDelegate:( id<SimpleDataDelegate>*) delegate
{
[NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://..."]]
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
if ( error )
{
[delegate gotFailure:data];
}
else
{
[delegate gotResult:data];
}
}];
return YES;
}
I think your method signature should probably be
- (BOOL) requestFromURL:(NSString*)url withDelegate:(id<SimpleDataDelegate>) delegate
instead of
- (BOOL) requestFromURL:(NSString*)url withDelegate:(id<SimpleDataDelegate>*) delegate
Notice the lack of a * in the first one, in the second parameter. Try that, and see if the error goes away. Report back if not.

Resources