I don't understand what map: is, in the tutorial using blocks, I could not find a map method in RACSignal (link) or RACDisposable (link) .
- (RACSignal *)fetchJSONFromURL:(NSURL *)url {
NSLog(#"Fetching: %#",url.absoluteString);
// 1
return [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
// 2
NSURLSessionDataTask *dataTask = [self.session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
// TODO: Handle retrieved data
}];
// 3
[dataTask resume];
// 4
return [RACDisposable disposableWithBlock:^{
[dataTask cancel];
}];
}] doError:^(NSError *error) {
// 5
NSLog(#"%#",error);
}];
}
- (RACSignal *)fetchCurrentConditionsForLocation:(CLLocationCoordinate2D)coordinate {
// 1
NSString *urlString = [NSString stringWithFormat:#"http://api.openweathermap.org/data/2.5/weather?lat=%f&lon=%f&units=imperial",coordinate.latitude, coordinate.longitude];
NSURL *url = [NSURL URLWithString:urlString];
// 2
return [[self fetchJSONFromURL:url] map:^(NSDictionary *json) {
// 3
return [MTLJSONAdapter modelOfClass:[WXCondition class] fromJSONDictionary:json error:nil];
}];
}
The link of the tutorial from raywenderlich website : link
Thanks
//...'map' callback
- (RACSignal *)fetchCurrentConditionsForLocation:(CLLocationCoordinate2D)coordinate {
// 1
NSString *urlString = [NSString stringWithFormat:#"http://api.openweathermap.org/data/2.5/weather?lat=%f&lon=%f&units=imperial",coordinate.latitude, coordinate.longitude];
NSURL *url = [NSURL URLWithString:urlString];
// 2
return [[self fetchJSONFromURL:url] map:^(NSDictionary *json) {
// 3
return [MTLJSONAdapter modelOfClass:[WXCondition class] fromJSONDictionary:json error:nil];
}];
}
//... 'map' method
- (instancetype)map:(id (^)(id value))block {
NSCParameterAssert(block != nil);
Class class = self.class;
return [[self flattenMap:^(id value) {
return [class return:block(value)];
}] setNameWithFormat:#"[%#] -map:", self.name];
}
apply the block to every item in the given array and then return the results of each mapping
It's defined in RACSignal's superclass, RACStream:
/// Maps `block` across the values in the receiver.
///
/// This corresponds to the `Select` method in Rx.
///
/// Returns a new stream with the mapped values.
- (instancetype)map:(id (^)(id value))block;
Related
I am having this error when my app enter background.
NSURLConnection finished with error - code -1001 Task
<09B84034-9F73-4DB6-A685-D891B1B1068A>.<2> finished with error - code:
-1001
I am using this code
- (id<XCDYouTubeOperation>) getVideoWithIdentifier:(NSString *)videoIdentifier completionHandler:(void (^)(XCDYouTubeVideo * __nullable video, NSError * __nullable error))completionHandler
{
NSLog(#"Getting Video Identfifier");
if (!completionHandler)
#throw [NSException exceptionWithName:NSInvalidArgumentException reason:#"The `completionHandler` argument must not be nil." userInfo:nil];
XCDYouTubeVideoOperation *operation = [[XCDYouTubeVideoOperation alloc] initWithVideoIdentifier:videoIdentifier languageIdentifier:self.languageIdentifier];
operation.completionBlock = ^{
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-retain-cycles"
if (operation.video || operation.error)
{
NSAssert(!(operation.video && operation.error), #"One of `video` or `error` must be nil.");
completionHandler(operation.video, operation.error);
}
else
{
NSAssert(operation.isCancelled, #"Both `video` and `error` can not be nil if the operation was not canceled.");
}
operation.completionBlock = nil;
#pragma clang diagnostic pop
}];
};
NSLog(#"Operation - %#", operation ) ;
[self.queue addOperation:operation];
return operation;
}`
- (void) startRequestWithURL:(NSURL *)url type:(XCDYouTubeRequestType)requestType
{
if (self.isCancelled)
return;
// Max (age-restricted VEVO) = 2×GetVideoInfo + 1×WatchPage + 1×EmbedPage + 1×JavaScriptPlayer + 1×GetVideoInfo
if (++self.requestCount > 6)
{
// This condition should never happen but the request flow is quite complex so better abort here than go into an infinite loop of requests
[self finishWithError];
return;
}
XCDYouTubeLogDebug(#"Starting request: %#", url);
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10];
[request setValue:self.languageIdentifier forHTTPHeaderField:#"Accept-Language"];
NSLog(#"Request Type - ",requestType);
// NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request];
// [task resume];
self.dataTask = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
if (self.isCancelled)
return;
if (error)
[self handleConnectionError:error];
else
[self handleConnectionSuccessWithData:data response:response requestType:requestType];
}];
[self.dataTask resume];
self.requestType = requestType;
}
#pragma mark - Response Dispatch
- (void) handleConnectionSuccessWithData:(NSData *)data response:(NSURLResponse *)response requestType:(XCDYouTubeRequestType)requestType
{
NSLog(#"XCDDRequestType - ",requestType);
CFStringEncoding encoding = CFStringConvertIANACharSetNameToEncoding((__bridge CFStringRef)response.textEncodingName ?: CFSTR(""));
// Use kCFStringEncodingMacRoman as fallback because it defines characters for every byte value and is ASCII compatible. See https://mikeash.com/pyblog/friday-qa-2010-02-19-character-encodings.html
NSString *responseString = CFBridgingRelease(CFStringCreateWithBytes(kCFAllocatorDefault, data.bytes, (CFIndex)data.length, encoding != kCFStringEncodingInvalidId ? encoding : kCFStringEncodingMacRoman, false)) ?: #"";
NSAssert(responseString.length > 0, #"Failed to decode response from %# (response.textEncodingName = %#, data.length = %#)", response.URL, response.textEncodingName, #(data.length));
XCDYouTubeLogVerbose(#"Response: %#\n%#", response, responseString);
switch (requestType)
{
case XCDYouTubeRequestTypeGetVideoInfo:
[self handleVideoInfoResponseWithInfo:XCDDictionaryWithQueryString(responseString) response:response];
break;
case XCDYouTubeRequestTypeWatchPage:
[self handleWebPageWithHTMLString:responseString];
break;
case XCDYouTubeRequestTypeEmbedPage:
[self handleEmbedWebPageWithHTMLString:responseString];
break;
case XCDYouTubeRequestTypeJavaScriptPlayer:
[self handleJavaScriptPlayerWithScript:responseString];
break;
}
}
This code will automatically run in background but after a few minutes it will stop and gives me the above error. How to fix this ?
EDIT 1 (Vinay Kiran Method) #
i changed the nsurlsessionconfiguration to background.
- (instancetype) initWithVideoIdentifier:(NSString *)videoIdentifier languageIdentifier:(NSString *)languageIdentifier
{
if (!(self = [super init]))
return nil; // LCOV_EXCL_LINE
_videoIdentifier = videoIdentifier ?: #"";
_languageIdentifier = languageIdentifier ?: #"en";
// _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:#"YouTubeID"];
_session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
_operationStartSemaphore = dispatch_semaphore_create(0);
NSLog(#"Initialize the Video Identifier");
return self;
}
then change the completion handler since background it will give this error if i use handler
Swift: 'Completion handler blocks are not supported in background sessions. Use a delegate instead.'
- (void) startRequestWithURL:(NSURL *)url type:(XCDYouTubeRequestType)requestType
{
if (self.isCancelled)
return;
// Max (age-restricted VEVO) = 2×GetVideoInfo + 1×WatchPage + 1×EmbedPage + 1×JavaScriptPlayer + 1×GetVideoInfo
if (++self.requestCount > 6)
{
// This condition should never happen but the request flow is quite complex so better abort here than go into an infinite loop of requests
[self finishWithError];
return;
}
XCDYouTubeLogDebug(#"Starting request: %#", url);
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10];
[request setValue:self.languageIdentifier forHTTPHeaderField:#"Accept-Language"];
NSLog(#"Request Type - ",requestType);
// NEWLY ADDED
NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request];
[task resume];
// self.dataTask = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
// {
// if (self.isCancelled)
// return;
//
// if (error)
// [self handleConnectionError:error];
// else
// [self handleConnectionSuccessWithData:data response:response requestType:requestType];
// }];
// [self.dataTask resume];
self.requestType = requestType;
}
the problem now is that i originally use this
self.dataTask = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
if (self.isCancelled)
return;
if (error)
[self handleConnectionError:error];
else
[self handleConnectionSuccessWithData:data response:response requestType:requestType];
}];
[self.dataTask resume];
which handleConnectionSuccessWithData will take in data, response and request type. Now i don't know where can i get the data, response and request type if i use backgroundSessionConfigurationWithIdentifier.
Use background thread instead of the main queue
backgroundSessionConfigurationWithIdentifier:
For reference
https://developer.apple.com/documentation/foundation/nsurlsessionconfiguration/1407496-backgroundsessionconfigurationwi?language=objc
I want to display the activity indicator while waiting for the API to return. The problem is after all the result I get from API, then the spinner only display. The result I want is while waiting for API call, then the spinner will running.
I'm calling this method in here
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
[self startLoadingSpinner]
//Calling API...
[self stopLoadingSpinner]
}
Here is the method for the activity indicator
-(void)startLoadingSpinner {
self.activityIndicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 0, 70, 70)];
self.activityIndicator.opaque = YES;
self.activityIndicator.backgroundColor = [UIColor colorWithWhite:0.0f alpha:0.4f];
self.activityIndicator.center = self.view.center;
self.activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray;
[self.activityIndicator setColor:[UIColor whiteColor]];
[self.view addSubview:self.activityIndicator];
[self.activityIndicator startAnimating];
}
This is how I stop the activity indicator
-(void)stopLoadingSpinner {
[self.activityIndicator performSelector:#selector(removeFromSuperview) withObject:nil afterDelay:0.5];
}
Don't add activity indicators in tableview datasource method - numberOfRowsInSection .
Add these two functions calling in the same method where you are making an API call. Make an API call in ViewDidLoad, some life cycle method or in action methods.
Below is the example of using it.
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
NSURL *URL = [NSURL URLWithString:#"http://httpbin.org/get"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
[self startLoadingSpinner]
NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:request completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
if (error) {
NSLog(#"Error: %#", error);
} else {
NSLog(#"%# %#", response, responseObject);
}
[self stopLoadingSpinner]
}];
[dataTask resume];
In Swift
func makeAPIRequest(to endPoint: String) {
// here you can showActivetyIndicator start progressing here
self.startLoadingSpinner()
Alamofire.request(endPoint).responseJSON{ response in
if let value = response.result.value {
let responseInJSON = JSON(value)
self._responseInJSON = responseInJSON
}
// here you can hide Your ActivetyIndicator here
self.stopLoadingSpinner()
}
}
My detailed answer is below
-(void)simpleGetResponse{
#try {
//Call the Activity Indicator show method here
[self startLoadingSpinner];
NSString *strURL = #"Your URL";
NSURL *urlStr = [NSURL URLWithString:strURL];
NSMutableURLRequest *mutaURL = [NSMutableURLRequest requestWithURL:urlStr];
[mutaURL setHTTPMethod:#"GET"];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:mutaURL completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if(httpResponse.statusCode == 200)
{
NSError *parseError = nil;
id response = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];
if(response != nil){
if([response isKindOfClass:[NSDictionary class]]){
NSLog(#"response is in dictionary format %#",response);
NSDictionary *dictRes = [response copy];
NSLog(#"The dictRes is - %#",dictRes);
}
else{
NSLog(#"response is in array format %#",response);
NSDictionary *arrRes = [response copy];
NSLog(#"The arrRes is - %#",arrRes);
}
dispatch_async(dispatch_get_main_queue(), ^{
//Call the Activity Indicator hidden method inside the dispatch_main_queue method
[self stopLoadingSpinner]
[yourTableView reloadData];
});
}
}
else
{
NSLog(#"Error");
}
}];
[dataTask resume];
}
#catch (NSException *exception) {
NSLog(#"%#", [exception description]);
}
#finally {
}
}
I have an iOS method that is now deprecated --NSURLConnection sendSynchronousRequest. This method worked and was fast.
I must be doing something wrong with the new method, as it is unacceptably slow.
The new method code I'm showing the whole routine is:
- (void)getData {
NSLog(#"%s", __FUNCTION__);
pathString = #"https://api.wm.com/json/jRoutes/.......";
NSURL *url = [NSURL URLWithString:pathString......];
NSURLSessionDataTask *downloadTask = [[NSURLSession sharedSession]
dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if ([response respondsToSelector:#selector(statusCode)]) {
if ([(NSHTTPURLResponse *) response statusCode] == 404) {
dispatch_async(dispatch_get_main_queue(), ^{
// alert
NSLog(#" NO DATA");
return;
});
}
}
// 4: Handle response here
[self processResponseUsingData:data];
}];
[downloadTask resume];
}
- (void)processResponseUsingData:(NSData*)data {
NSLog(#"%s", __FUNCTION__);
NSError *error = nil;
NSMutableDictionary* json = nil;
if(nil != data)
{
json = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
}
if (error || !json)
{
NSLog(#"Could not parse loaded json with error:%#", error);
} else {
dispatch_async(dispatch_get_main_queue(), ^{
allRoutesArray = [json valueForKey:#"Routes"];
NSLog(#"allRoutesArray count: %lu", (unsigned long)allRoutesArray.count);
[self.tableView reloadData];
});
}
}
I am fetching a list of images that I want to download from a server. After filtering the list , i add all my network calls to an array of signals and merge them. The thing is that the network calls start, but meanwhile the racdisposable is destroyed and in the destroy method I call cancel on the download task. Calling cancel stops the request and the image is not pulled. Any suggestions on how I should do this? I am pretty new to IOS and ReactiveCocoa
This method starts the requests
- (void)fetchTagsFromServer{
[self CreateIfNotExistsTagsFolder];
//define URL object
NSString *urlString = [NSString stringWithFormat:#"http://studiotest.cloudapp.net:5508/api/sideviewapi/gettags"];
NSURL *url = [NSURL URLWithString:urlString];
//fetch tag list from server. After the json is here, start filtering and pulling the images
//from the server
[[self fetchJSONFromURL:url] subscribeNext:^(id x){
NSDictionary* json = (NSDictionary*)x;
//filter and pull images
[[self handleTagsFromJson:json] subscribeNext:^(id x) {
initTagsDone = false;
} error:^(NSError *error) {
NSLog([error description]);
}] ;
}];
}
This ia a generic method to pull json from server
- (RACSignal *)fetchJSONFromURL:(NSURL *)url {
NSLog(#"Fetching: %#",url.absoluteString);
// RAC signal
return [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
//Create session task
NSURLSessionDataTask *dataTask = [self.session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (! error) {
NSError *jsonError = nil;
id json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&jsonError];
if (! jsonError) {
//if json returned with success sent to subscriber
[subscriber sendNext:json];
}
else {
// send json error to subscriber
[subscriber sendError:jsonError];
}
}
else {
// send general error
[subscriber sendError:error];
}
// send request completed
[subscriber sendCompleted];
}];
//Starts the the network request once someone subscribes to the signal.
[dataTask resume];
// Creates and returns an RACDisposable object which handles any cleanup when the signal when it is destroyed.
return [RACDisposable disposableWithBlock:^{
[dataTask cancel];
}];
}] doError:^(NSError *error) {
// log error
NSLog(#"%#",error);
}] ;
}
Here I combine all the signals
(RACSignal *) handleTagsFromJson:(NSDictionary*) json {
//hold tags dto's
NSMutableArray* tags = [[NSMutableArray alloc] init];
NSMutableArray* signals = [[NSMutableArray alloc]init];
//create dto object from json
for (NSDictionary * item in json) {
TagsDTO* dto = [[TagsDTO alloc]init];
dto.Id = [[item valueForKey:#"Id"] intValue];
dto.BigIcon = [item valueForKey:#"BigIcon"];
dto.SmallIcon = [item valueForKey:#"SmallIcon"];
dto.SmallIconRaster = [item valueForKey:#"SmallIconRaster"];
dto.Date = [TagsDTO dateWithJSONString:[item valueForKey:#"Modified"]];
dto.Type = [[item valueForKey:#"Type"] intValue];
[tags addObject:dto];
}
//foreach dto do stuff with db
for (TagsDTO* tagDTO in tags){
//get DB tag by external ID
TAG* dbTag = [self getTagsByExternalID:tagDTO.Id];
//create holder strings for image names
NSString* pickerTag;
NSString* overlay;
NSString* raster;
//if db tag is null create null,else set values from server
if(dbTag == NULL){
TAG* tag = [NSEntityDescription insertNewObjectForEntityForName:#"TAG" inManagedObjectContext:self.managedObjectContext];
tag.externalId = [NSNumber numberWithInt:tagDTO.Id];
tag.pickerITag = pickerTag = tagDTO.BigIcon;
tag.overlayTag = overlay = tagDTO.SmallIcon;
tag.rasterTag = raster = tagDTO.SmallIconRaster;
tag.modified = tagDTO.Date;
tag.type = [NSNumber numberWithInt:tagDTO.Type];
}else{
dbTag.externalId = [NSNumber numberWithInt:tagDTO.Id];
dbTag.pickerITag = pickerTag = tagDTO.BigIcon;
dbTag.overlayTag = overlay = tagDTO.SmallIcon;
dbTag.rasterTag = raster = tagDTO.SmallIconRaster;
dbTag.modified = tagDTO.Date;
dbTag.type = [NSNumber numberWithInt:tagDTO.Type];
}
NSError *error ;
if (![self.managedObjectContext save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
//start downloading images
//because there are 3 different types for each images, download from coressponding serveer folder
//get picker picture
[signals addObject: [self fetchTagImage:pickerTag area:#"picker"]];
//get overlay picture
[signals addObject: [self fetchTagImage:pickerTag area:#"overlay"]];
//get raster picture
[signals addObject:[self fetchTagImage:pickerTag area:#"raster"]];
}
return [RACSignal combineLatest:signals];
}
And this is the signal for pulling the images from the server
(RACSignal *) fetchTagImage:(NSString*)tag area: (NSString*) area {
//create Url object, area is the folder on the server
NSString *urlStringIcon = [NSString stringWithFormat:#"http://studiotest.cloudapp.net:5508/content/uploads/%#/%#",area,tag];
NSURL *urlIcon = [NSURL URLWithString:urlStringIcon];
NSLog(#"Fetching: %#",urlIcon.absoluteString);
// RAC signal
return [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
//Create session task
NSLog(#"internal image fetch: %#",urlIcon.absoluteString);
NSURLSessionDownloadTask* dataTask = [self.session downloadTaskWithURL:urlIcon completionHandler:^(NSURL*location, NSURLResponse *response, NSError *error) {
if(error == nil || error.code == NSURLErrorCancelled)
{
NSLog(#"Temporary file =%#",location);
NSError *err = nil;
//create file mananger
NSFileManager *fileManager = [NSFileManager defaultManager];
//get app document folder path
NSString *docsDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
//create actual store path
NSString *dataPath = [docsDir stringByAppendingPathComponent:#"/tags"];
//compile filename
NSString* filename = [NSString stringWithFormat:#"%#-%#",area,[response suggestedFilename]];
//create URL to move temporary item to
NSURL *docsDirURL = [NSURL fileURLWithPath:[dataPath stringByAppendingPathComponent:filename]];
if ([fileManager moveItemAtURL:location
toURL:docsDirURL
error: &err])
{
[subscriber sendNext:filename];
NSLog(#"File is saved to =%#",docsDir);
}
else
{
[subscriber sendError:err];
NSLog(#"failed to move: %#",[err userInfo]);
}
}else {
// send general error
[subscriber sendError:error];
}
// send request completed
[subscriber sendCompleted];
}];
//Starts the the network request once someone subscribes to the signal.
[dataTask resume];
// Creates and returns an RACDisposable object which handles any cleanup when the signal when it is destroyed.
return [RACDisposable disposableWithBlock:^{
[dataTask cancel];
}];
}] doError:^(NSError *error) {
// log error
NSLog(#"%#",error);
}];
}
Let's check a couple things first:
racdisposable is destroyed
It's OK for a RACDisposable to be deallocated. Note that that doesn't call the disposable's dispose block.
I don't see any obvious reason that the disposable would be disposed of. Is the signal erroring? Could you set a breakpoint and see the stack trace that calls the cancel?
#interface URLClass : NSObject
{
id target;
SEL funObj;
}
+ (URLClass *)sharedInstance;
-(void)theBigFunction:(SEL)func :(id)target;
#property (nonatomic,retain) SEL funObj;
#import "URLClass.h"
static URLClass *instance = NULL;
#implementation URLClass
{
NSMutableData *webData;
}
- (id)init
{
if ( self = [super init] )
{
}
return self;
}
+ (URLClass *)sharedInstance
{
#synchronized([URLClass class])
{
if (!instance)
instance = [[super alloc] init];
return instance;
}
return nil;
}
-(void)theBigFunction:(SEL)func :(id)targetObj{
funObj =func;
target=targetObj;
NSURL *URL = [NSURL URLWithString:#"urlString"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:self];
if( connection )
{
webData = [NSMutableData data] ;
}
else
{
NSLog(#"theConnection is NULL");
}
}
-(BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
return YES;
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[webData setLength: 0];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[webData appendData:data];
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"ERROR with theConenction");
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSError *error;
id jsonObj = [NSJSONSerialization JSONObjectWithData:webData options:0 error:&error];
if (jsonObj!=nil && error==nil) {
if ([jsonObj isKindOfClass:[NSDictionary class]]) {
NSDictionary *dic=(NSDictionary*)jsonObj;
NSLog(#"DIC jsonObj %# ",dic);
NSArray *array=[dic objectForKey:#"items"];
NSLog(#"array jsonObj %# %d",array,[array count]);
}else if ([jsonObj isKindOfClass:[NSArray class]]) {
NSArray *arr=(NSArray*)jsonObj;
NSLog(#"arr jsonObj %# ",arr);
}
}
[target performSelector:funObj];
// Not gEtting called the aboue line
//performSelector may cause a leak because its selector is unknown warning in above line
}
When am planing to execute below lines form code from any class . its not get called.
-(void)loginPress:(id)sender{
URLClass *rlOBJ=[URLClass sharedInstance];
[rlOBJ theBigFunction:#selector(upd_Handler:) :self];
}
- (void) upd_Handler: (id) value{
NSLog( #"Seccess");
}
The modern approach would be for your class to accept a completion block instead of a target / selector. Then you don’t have to insert a bunch of ugly compiler-inhibiting lines, and you get more flexibility.
Here are complete replacements of [target performSelector:selector withObject:object]; to avoid the warning. Use either of the replacements like this:
[NSObject target:target performSelector:selector withObject:object];
#interface NSObject (NSPerformSelector)
+ (id)target:(id)target performSelector:(SEL)selector;
+ (id)target:(id)target performSelector:(SEL)selector withObject:(id)object;
+ (id)target:(id)target performSelector:(SEL)selector withObject:(id)object1 withObject2:(id)object2;
#end
#implementation NSObject (NSPerformSelector)
+ (id)target:(id)target performSelector:(SEL)selector {
IMP imp = [target methodForSelector:selector];
id (*func)(id, SEL) = (void *)imp;
return func(target, selector);
}
+ (id)target:(id)target performSelector:(SEL)selector withObject:(id)object {
IMP imp = [target methodForSelector:selector];
id (*func)(id, SEL, id) = (void *)imp;
return func(target, selector, object);
}
+ (id)target:(id)target performSelector:(SEL)selector withObject:(id)object1 withObject2:(id)object2 {
IMP imp = [target methodForSelector:selector];
id (*func)(id, SEL, id, id) = (void *)imp;
return func(target, selector, object1, object2);
}
#end
Simply use [sender performSelector:selector withObject:object afterDelay:0];
This will remove the warning and the code will run fine.
That is a warning, not an error. Your code should still work.
When you invoke a selector and the compiler can't tell what the selector is, it can't tell if the called method is going to take ownership of the passed object or not, or release it, or whatever. Thus ARC can't be sure that memory management of the parameter will be handled correctly.
You should be able to enclosed the performSelector call in a compiler directive to disable that warning. Then the burden will be on you to make sure the called method does not keep any strong references to the object passed to it, or release the object.
You don't need to pass your success method in your bigFunction. Instead you should be using a completion block, as suggested by [Wil Shipley][1]
Here is an example of a NSURLConnection making a Asynchronous request and returning; a response, data and an error; if one occurs. In the future you should refer to the documentation.
NSURLConnection Class Reference
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSURLConnection_Class/index.html
+ (void)sendAsynchronousRequest:(NSURLRequest *)request
queue:(NSOperationQueue *)queue
completionHandler:(void (^)(NSURLResponse *response,
NSData *data,
NSError *connectionError))handler
You could re-write your bigFunction method like this --
- (void)bigFunctionWithYourInputData:(NSDictionary*)userInfo withCompletionHandler:(void(^)(NSData* data, NSError* error))completionHandler {
NSURL *theUrl = [NSURL URLWithString:[userInfo objectForKey:#"url"];
NSURLRequest *req = [NSURLRequest requestWithURL:theUrl];
[NSURLConnection sendAsynchronousRequest:req queue:nil completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (!connectionError) {
//do what you want with your data
NSLog(#"response :%#", response);
NSLog(#"data :%#", data);
//pass to your completion handler
if(completionHandler) {
completionHandler(data, nil);
}
} else {
//something went wrong
NSLog(#"Got the error with code :%ld", (long)connectionError.code);
//pass to your completion handler
if(completionHandler) {
completionHandler(nil, error);
}
}
}];
}
Then you would implement it somewhere else, via your singleton like so --
[URLClass sharedInstance] bigFunctionWithYourInputData:_someDictionaryData withCompletionHandler:^(NSData* data, NSError* error) {
if (!error) {
//success
NSLog(#"returned the data:%#", data);
} else {
//handler the error somehow
}
}];