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.
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 have just added Firebase Performance to my which is mainly Obj-C and has Firebase (Core + Analytics + Messaging + Config) I read in the docs that:
Performance Monitoring does not support network requests made using
the NSURLConnection class.
However what's not expected is that the app crashes on the first call of NSURL*
e.g. I'm using a lib called "Harpy" which checks for new version of the app in the AppStore and it crashes here:
NSURLSession *session = [NSURLSession sharedSession]; // <--- Crashes here
NSURLSessionDataTask *task = [session
dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if ([data length] > 0 && !error) { // Success
[self parseResults:data];
}
}
];
I can't really see any useful exception however the thread starts with:
Thread 1 Queue : com.google.FPRNSURLSessionInstrumentation (serial)
So issue above was not directly related to Firebase itself, but actually to a conflict between Firebase Performance and Crittercism, the solution was to disable Crittercism's monitoring for NSURLSession as follows:
CrittercismConfig *config = [CrittercismConfig defaultConfig];
config.monitorNSURLSession = false;
[Crittercism enableWithAppID:settingsManager.crittericismKey andConfig:config];
[Crittercism setValue:[NSLocale preferredLanguages].firstObject forKey:#"deviceLanguage"];
Context:
I try to call a create a task (download or upload) from an action extension, with a backgroundSessionConfiguration.
To do this I fallow the exemple in apple documention
-(void)downloadTest
{
NSURLSession *mySession = [self configureMySession];
NSURL *url = [NSURL URLWithString:#"http://www.sellcell.com/blog/wp-content/uploads/2014/03/dog-apps.jpg"];
NSURLSessionTask *myTask = [mySession downloadTaskWithURL:url];
[myTask resume];
}
- (NSURLSession *) configureMySession {
if (!_mySession) {
NSURLSessionConfiguration* config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:#"com.mycompany.myapp.backgroundsession"];
// To access the shared container you set up, use the sharedContainerIdentifier property on your configuration object.
config.sharedContainerIdentifier = #"group.com.mycompany.appname";
_mySession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
}
return _mySession;
}
My problem is that when I call [mySession downloadTaskWithURL:url]; it returns nil.
If I change the configuration to NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; then a task is created.
I don't see what I'm doing wrong , I have created an app group and I use it the app and in the extension.
I use the group name that I have created in config.sharedContainerIdentifier but I'm not sure it's necessary.
NOTE: I have the same problem with uploadTask.
Are you using ARC? If not, make sure your session is being retained properly. (It looks like you're using an ivar directly.)
Is that shared container identifier correct? If the container isn't in your entitlements or doesn't exist yet, your session will be immediately invalidated. Add an invalidation delegate method and see if it is getting called. If so, that's probably the issue.
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:^{ ... }];
I am using AFURLSessionManager, and set the manager as a singleton instance.
- (AFURLSessionManager *)backgroundSession
{
static AFURLSessionManager *backgroundSession = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfiguration:#"com.testBackground.BackgroundDownload.BackgroundSession1234"];
backgroundSession = [[AFURLSessionManager alloc]initWithSessionConfiguration:config];
[backgroundSession setDownloadTaskDidWriteDataBlock:^(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite){
NSLog(#"i am downloading my id = %d progress= %f",downloadTask.taskIdentifier, totalBytesWritten*1.0/totalBytesExpectedToWrite);
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
}];
[backgroundSession setDownloadTaskDidFinishDownloadingBlock:^NSURL *(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location){
NSLog(#"download finished");
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
return location;
}];
});
return backgroundSession;
}
//assign a download task
NSURLSessionDownloadTask *task = [manager1 downloadTaskWithRequest:request progress:&progress destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
return targetPath;
} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
NSLog("%download success");
}];
[task resume];
I found that when I switch the app to the background the download task is running but when it was finished, the system call handleEventsForBackgroundURLSession will never be called.I am feeling that I have missed some setting or options. Any idea will be useful for me, thanks a lot.
- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler;
You probably have figured this out already but anyway, the exact same thing has happened to me while writing some test code to play with the background transfer service APIs (without AFNetworking). The solution was simply to change the identifier string for my background session configuration. Somehow the one I was using got bugged and the system wouldn't trigger the handleEventsForBackgroundURLSession callback. And not even restarting the device fixes it... however just changing the identifier does.
My theory is that my code created multiple instances of NSURLSession with the same configuration, which Apple clearly advices against (they say in the documentation that it has an undefined behavior if you do). I was obtaining the NSURLSessionConfiguration in the view controller's viewDidLoad method without a dispatch_once block, so it's certainly plausible that that happened.
As stated by Apple:
If an iOS app is terminated by the system and relaunched, the app can use the same identifier to create a new configuration object and session and retrieve the status of transfers that were in progress at the time of termination. This behavior applies only for normal termination of the app by the system. If the user terminates the app from the multitasking screen, the system cancels all of the session’s background transfers. In addition, the system does not automatically relaunch apps that were force quit by the user. The user must explicitly relaunch the app before transfers can begin again.
Hope it helps.
Stefan