I created category for NSURLSession, and then I faced a problem. In iOS 7, [[NSURLSession sharedSession] isKindOfClass:[NSURLSession class]] returns NO.
I know that [NSURLSession sharedSession] is returning instance of __NSCFURLSession, but it is not a subclass of NSURLSession. So, it doesn't make any sense. At compile time everyting is ok, but in runtime I get unrecognized selector exception.
How can I get around with this? Is there is any runtime magic feature, that I can use? Because
[[NSURLSession sharedSession] respondsToSelector:#selector(dataTaskWithRequest:completionHandler:)]
returns YES, and that is the only method I use in my category.
As an alternative to creating an instance method in your category you could create a class method which accepts an instance of NSURLSession.
Assuming a category like this:
#interface NSURLSession (MyCategory)
- (void)doSomethingWithCompletion:(MyCompletionHandler)completionHandler;
#end
The replacement could be:
#interface NSURLSession (MyCategory)
+ (void)doSomethingWithSession:(NSURLSession *)session completion:(MyCompletionHandler)completionHandler;
#end
With usage as follows:
[NSURLSession doSomethingWithSession:[NSURLSession sharedSession]
completion:^{ ... }];
Related
In my objective C project i am using
Segment 1).
[(NSInputStream *)CFReadStreamCreateForStreamedHTTPRequest(kCFAllocatorDefault, request,(CFReadStreamRef)[self postBodyReadStream]) autorelease]
but CFReadStreamCreateForStreamedHTTPRequest is deprecated
what can i write alternatively ?
I tried
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
Error throws:-
Incompatible pointer types sending 'CFHTTPMessageRef' (aka 'struct >__CFHTTPMessage *') to parameter of type 'NSURLRequest * _Nonnull'
Segment 2).
(NSString *)kCFStreamPropertyHTTPProxyHost
'kCFStreamPropertyHTTPProxyHost' is deprecated:
Haven't found any replacement API.
To use dataTaskWithRequest:, you need to create an NSURLRequest, not a CFHTTPMessageRef.
As for kCFStreamPropertyHTTPProxyHost, this question asks “How to programmatically add a proxy to an NSURLSession” and has an answer that looks good to me.
I am new to NSURLSession and i did not find the answer in other stackoverflow question. So i am posting this.
I am having a Button and ProgressBar in my ViewController. Using NSURLSessionDownloadTask's instance, i am calling resume as follows
#property (nonatomic, strong) NSURLSessionDownloadTask *downloadTask;
Specified above line in #interface
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:self delegateQueue:[NSOperationQueue mainQueue]];
self.downloadTask = [session downloadTaskWithURL:url];
Specified above lines in #implementation and called resume method on buttonclick as follows
-(void) buttonpressed:(id)sender{
[self.downloadTask resume];
}
Here what happens is,
When i click the button for first time, it downloads perfectly
(ie. Calling the proper delegate methods
downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite & didFinishDownloadingToURL
But when i click the button again, it's not downloading (ie. Delegate methods are not calling)
1) Where i am doing mistake?
2) I want it to download again if i click the button second time. What
should i do for that?
Any help appreciated, thanks for the time (:
resume is only for suspended tasks, and yours is completed. The simple fix is to create and begin (really, resume) the task in the same function.
- (void)setupAndStartDownload {
// your setup code, from the OP
// then start it here
[self.downloadTask resume];
}
-(void) buttonpressed:(id)sender{
[self setupAndStartDownload];
}
Following upgrading to Xcode 7 (7A21B) an AFNetworking error is thrown when building my current project in AFURLSessionManager.m. I've linked AFNetworking as a submodule in Git so it's regularly up to date.
The error is
AFURLSessionManager.m:288:87: Null passed to a callee that requires a non-null argument
The line responsible:
NSURLSessionDataTask *dataTask = [[NSURLSession sessionWithConfiguration:nil] dataTaskWithURL:nil];
Obviously the nil arguments need to either be replaced with values or the method to instantiate the dataTask object needs to change but I'm not familiar enough with AFNetworking to make the change myself.
This is the entire method:
+ (void)initialize {
if ([NSURLSessionTask class]) {
NSURLSessionDataTask *dataTask = [[NSURLSession sessionWithConfiguration:nil] dataTaskWithURL:nil];
Class taskClass = [dataTask superclass];
af_addMethod(taskClass, #selector(af_resume), class_getInstanceMethod(self, #selector(af_resume)));
af_addMethod(taskClass, #selector(af_suspend), class_getInstanceMethod(self, #selector(af_suspend)));
af_swizzleSelector(taskClass, #selector(resume), #selector(af_resume));
af_swizzleSelector(taskClass, #selector(suspend), #selector(af_suspend));
[dataTask cancel];
}
}
Is there a way I can quench this error?
Tree solutions. Either use the shared session:
NSURLSessionDataTask *dataTask = [[NSURLSession sharedSession] dataTaskWithURL:nil];
or decide on a configuration. Will you send files in background or only if your app is in foreground? Replace nil with either
[NSURLSessionConfiguration defaultSessionConfiguration]
or with
[NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:#"My Identifier"]
or use NSURLSession directly instead of an obsolete third party framework.
This was a simple mistake. Thanks to Quentin for highlighting that the submodule in Git had not been updated. Doing this rectified the error.
I know the same kind of issue has already been posted, but I think I face another one.
I created the CocoaPod called NSURLSession+PromiseKit to allow using PromiseKit with NSURLSession.
I have the definition:
#implementation NSURLSession (PromiseKit)
- (PMKPromise *)promiseDataTaskWithURL:(NSURL *)url {
return [PMKPromise ...];
}
#end
And the code sample:
NSURLSession *session = [NSURLSession sharedSession];
NSURL *url = [NSURL URLWithString:#"..."];
[session promiseDataTaskWithURL:url].then( ^(NSData *data) {
NSLog(#"Result: %#", data);
}).catch( ^(NSError *e) {
NSLog(#"Error: %#", e);
});
works on iOS 8, but not on iOS 7. I get this error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFURLSession promiseDataTaskWithURL:]: unrecognized selector sent to instance 0x7c236720'
Now of course I though that the category was not loaded so I added this line to the podspec to make sure that the code is loaded:
s.xcconfig = { 'OTHER_LDFLAGS' => '-ObjC -all_load' }
But it does not work better.
So I added a line to the category file:
+ (void)load {
NSLog(#"loaded");
}
To my surprise the load method is called, which means that the code is indeed loaded. So why is the category method not found while executing if the code is loaded?
Ok, as often, posting the question lead me to the answer.
It has been found that NSURLSession does not support categories.
In iOS 7, the underlying NSURLSession class is __NSCFURLSession, which does not support the categories.
In iOS 8, the underlying class is __NSURLSessionLocal, which does support categories.
My solution, although cringeworthy is to replace:
#implementation NSURLSession (PromiseKit)
By:
#implementation NSObject (PromiseKit)
Only on the implementation file. This will prevent a Pod user to mistakenly use the method with something else than a NSURLSession object. However, it does not prevent him to play other tricks to make the thing crash...
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.