ASIHTTPRequest does not update progress - ios

update
I think the problem is with the networkQueue. When I replace [self.networkQueue addOperation:request] with [request startAsynchronous] or [request startSynchronous], it works.
I update the code to make it more clear.
original
I use ASIHTTPRequest to upload a file of json with encoded image (or [request setFile:imagefile...]), but I cannot update the progress.
I got only one output: value: 1.000000, which means the uploads finished.
incrementUploadSizeBy was never triggered.
I have searched online a lot but still cannot find a answer. Here is my code.
+ (ASIEngine *)sharedInstance {
static ASIEngine *sharedInstance = nil;
static dispatch_once_t pred;
dispatch_once(&pred, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
- (id)init {
if (self = [super init]) {
_networkQueue = [ASINetworkQueue queue];
[_networkQueue setMaxConcurrentOperationCount:MAX_CONCURRENT_OPERATION_COUNT];
[_networkQueue setDelegate:self];
[_networkQueue go];
}
return self;
}
- (void)upload:(NSString *)imageJsonString onCompletion:(void(^)(NSString *responseString))onCompletion onFailed:(void(^)(NSError *error))onFailed {
__unsafe_unretained __block ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:UPLOAD_URL]];
[request setShowAccurateProgress:YES];
[request setUploadProgressDelegate:self];
[request setPostValue:imageJsonString forKey:#"imageString"];
[request setCompletionBlock:^{
NSString *responseString = [request responseString];
onCompletion(responseString);
}];
[request setFailedBlock:^{
onFailed([request error]);
}];
[self.networkQueue addOperation:request];
}
#pragma mark - ASIProgressDelegate
- (void)setProgress:(float)newProgress {
NSLog(#"value: %f", newProgress);
}
- (void)request:(ASIHTTPRequest *)request incrementUploadSizeBy:(long long)newLength {
NSLog(#"data length: %lld", newLength);
}

If you are uploading a string, you probably have so little data to upload (as opposed to when uploading a picture), that it uploads everything in one step. You could try reading a huge string from a file and see what happens.

Related

uiwebview not working with HTTP or HTTPS urls

i am trying to load a url in uiwebview in xcode and it is loading just fine but problem is that there is ZERO user interaction with it. i can not touch any button on it or can not even scroll it. i have already tried Allow Arbitrary Loads = YES in info.plist but nothing happened here is my code.
[webPage setDelegate:self];
[webPage loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://url.com"]]];
[webPage addSubview:activityIndicatorView];
here is more code from .h:
#interface ViewController : UIViewController<UIWebViewDelegate>{
IBOutlet UIWebView *webPage;
}
#property (retain, nonatomic) IBOutlet UIWebView *webPage;
its s simple uiwbview from interface builder in a simple uiviewcontroller.
and here is my info.plist
I figure out that problem might be here in this part of the code. there is a menu in my uinavigationbar as well which is loading xml menu. wait i will post my code.
- (void) makeMenu{
#try {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0),^{
NSURL *url=[NSURL URLWithString:#"http://url/xml-menu.php"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:url];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
NSError *error = nil;
NSHTTPURLResponse *response = nil;
NSData *urlData=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
if ([response statusCode] >=200 && [response statusCode] <300)
{
rssOutputData = [[NSMutableArray alloc]init];
xmlParserObject =[[NSXMLParser alloc]initWithData:urlData];
[xmlParserObject setDelegate:self];
[xmlParserObject parse];
}
dispatch_async(dispatch_get_main_queue(), ^{
sideMenu.delegate = self;
NSInteger count;
NSMutableArray *itemsArry = [[NSMutableArray alloc] init];
count = [rssOutputData count];
for (int i = 0; i < count; i++){
BTSimpleMenuItem *item = [[BTSimpleMenuItem alloc]initWithTitle:[[rssOutputData objectAtIndex:i]xmltitle] image:[UIImage imageNamed:#"arrow.png"]
onCompletion:^(BOOL success, BTSimpleMenuItem *item) {
[webPage loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[[rssOutputData objectAtIndex:i]xmllink]]]];
}];
[itemsArry addObject:item];
}
NSArray *itemSarry=[[NSArray alloc] initWithArray:itemsArry];
sideMenu = [[BTSimpleSideMenu alloc]initWithItem:itemSarry addToViewController:self];
});
});
}
#catch (NSException * e) {
NSLog(#"Exception: %#", e);
}
}
when i call this method in viewdidload uiwebview stop responding. And if i do not call this part uiwebview works just fine. please help me i need this menu as well.
Try this:
webPage.userInteractionEnabled = YES;

delegate method cannot callback

delegate method cannot callback
This is my .h file
#protocol ServiceAPIDelegate <NSObject>
#optional
- (void) onRequestLoginFinish:(NSDictionary*) dict;
#end
#interface ServiceAPI : NSObject
+ (id)shareAPI;
#property (nonatomic, weak) id <ServiceAPIDelegate> delegate_service;
;
#end
and this is .m file, i use ASIFORMData request and it is callback to requestFinished after get a response from server. but ater that, i want to send data to myviewcontroler use [self.delegate_service onRequestLoginFinish:result]; after this line. my program run normaly not bugs, not callback to function. I cannot see where errors are.
- (void) requestLoginWithUserName:(NSString*) username andPassWord:(NSString*) password {
NSString* urlString = [PublicInstance API_LOGIN];
NSArray *keys = PARAMS_ARRAY;
NSArray *objects = [NSArray arrayWithObjects:username, password, [#((int)En) stringValue], APPID, [PublicInstance getDevideID], DEVIDEOS, nil];
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjects:objects forKeys:keys];
NSString* signData = [PublicInstance signData:dict];
[dict setObject:signData forKey:signKey];
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:urlString]];
[request addRequestHeader:#"Content-Type" value:#"application/json"];
[request setValidatesSecureCertificate:NO];
[request setRequestMethod:#"POST"];
NSData *jsonDataToPost = [NSJSONSerialization dataWithJSONObject:dict options:0 error:nil];
[request appendPostData:jsonDataToPost];
[request startAsynchronous];
[request setDelegate:self];
}
- (void)requestFinished:(ASIHTTPRequest *)request
{
NSError* error = [request error];
if(!error) {
NSString* responseString = [request responseString];
NSDictionary *result = [NSJSONSerialization JSONObjectWithData;
[self.delegate_service onRequestLoginFinish:result];
}else{
NSLog(#"%#", [error description]);
}
}
ServiceAPI *ShareServiceAPI;
- (id) init {
if ([self init]) {
ShareServiceAPI = [ServiceAPI shareAPI];
ShareServiceAPI.delegate_service =self;
}
}
- (void) requestLoginWithUserInfor:(UserInfor*) _userinfor {
[ShareServiceAPI requestLoginWithUserName:_userinfor.username andPassWord:_userinfor.password];
}
#####################
and this is delegate method - but never callback (O^-oO)
- (void) onRequestLoginFinish:(NSDictionary *)dict {
if ([[dict objectForKey:Key_Status] intValue] == 1) {
NSLog(#"login successful");
}
else {
NSLog(#"login fail....");
}
}
Could anyone please help me?. Thank you for your time
You should always check if the delegate is nil or not and also check if the delegate responds to the selector as :
if(self.delegate_service){
if([self.delegate_service repondsToSelector:#selector(onRequestLoginFinish:)]){
[self.delegate_service onRequestLoginFinish:result];
}
}
Through the way you can make your program safe and found the reason that the method is not called.

IOS Creating Utility Class For NSURL

I have a project I am working on that has a number of different news feeds and announcement boards that displays post from various sources. Currently I have the code for the like, delete and flag buttons in methods contained in each class file for the views that display the feeds. I have been trying to craft a utility class that allows me to place the code for the three functionalities listed above in one object to be used throughout the project. I have done the exact same type of thing in C++ or Java, but am having issues reproducing it in objective-c. The like, delete and flag buttons use the NSURL libraries to interact with the web service. Bellow is an example of one of the methods I am trying to implement in the utility class, and is the code used to be implemented in the like buttons:
+ (void)btnLikeAction:(UIButton *)btnLike userIdString:(NSString *)userId contentSource:(NSString *)sourceId cellUsed:(NewsfeedCell *)cell dataForCell:(Post *)post
{
BOOL hasLiked = post.hasLiked;
UILabel *likeLabel = cell.likeCount;
NSString *pId = post.postId;
if (hasLiked)
{
[btnLike setEnabled:NO];
NSString *url = [NSString stringWithFormat: #"http://192.155.94.183/api/v1/likes/unlike/%#/%#/%#/", sourceId, pId, userId];
NSURL *URL = [NSURL URLWithString:url];
NSURLRequest *request = [NSURLRequest requestWithURL:URL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:30.0];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[connection start];
int localCount = [likeLabel.text intValue];
localCount--;
likeLabel.text = [NSString stringWithFormat:#"%i", localCount];
post.likeCount = [NSString stringWithFormat:#"%i", localCount];
post.hasLiked = NO;
[btnLike setEnabled:YES];
}
else
{
[btnLike setEnabled:NO];
NSError *err = nil;
NSDictionary *likeData = [NSDictionary dictionaryWithObjectsAndKeys:
userId, #"user_id",
pId, #"source_id",
sourceId, #"source",
nil];
NSData *JSONLike = [NSJSONSerialization dataWithJSONObject:likeData options:NSJSONWritingPrettyPrinted error:&err];
NSString *url = #"http://192.155.94.183/api/v1/likes.json";
NSURL *URL = [NSURL URLWithString:url];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:30.0];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setValue:[NSString stringWithFormat:#"%d", [JSONLike length]] forHTTPHeaderField:#"Content-Length"];
[request setHTTPBody:JSONLike];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[connection start];
int localCount = [likeLabel.text intValue];
localCount++;
likeLabel.text = [NSString stringWithFormat:#"%i", localCount];
post.likeCount = [NSString stringWithFormat:#"%i", localCount];
post.hasLiked = YES;
[btnLike setEnabled:YES];
}
}
This code uses a web service to update the number of likes for a specific piece of content. It works when the method is placed into the individual ViewController class files, but when I try to make a utility class with the individual methods I run into issues with the didReceiveAuthenticationChallenge, didReceiveResponse, didReceiveData and connectionDidFinishLoading methods not being called. Originally, I assumed that the delegate methods would be called in the file that that the utility methods were called in. But that was not the case. When I implemented the method definitions in the actual utility class, the methods still weren't called. I did some research on the topic and looked into this article but found I was unable to find substantial resources that helped my specific situation. How do I set up my utility class? I can post the full code of the utility if needed.
As #serrrgi already said, the problem is that btnLikeAction:... is a class method, so that self is the class itself. You have the following options:
Make all delegate methods class methods, e.g.
+ (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSLog(#"didReceiveResponse");
}
Create an instance of your Utility class and use that as delegate:
YourClass *u = [[self alloc] init];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:u];
Use sendAsynchronousRequest:..., which does not need a delegate:
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (data != nil) {
NSLog(#"success");
} else {
NSLog(#"error: %#", error);
}
}];

iOS: EXC_BAD_ACCESS Error NSString length and setHTTPBody

When I start the simulator and the application starts and I click on the UI I get EXC_BAD_ACCESS for NSString *strLength = [NSString stringWithFormat:#"%d", [postStr length]]; and for [req setHTTPBody:[_postStr dataUsingEncoding:NSUTF8StringEncoding. I dont know why this happens. If I uninstall the app but keep the simulator open and run it again I get no errors. Any help would be great. Code is below.
#import "LocavoreRetroAPIAdapter.h"
//Class extention declares a method that is private to the class
#interface LocavoreRetroAPIAdapter ()
-(NSMutableURLRequest *)initRequest:(NSURL *)url method:(NSString *)method;
#end
#implementation LocavoreRetroAPIAdapter
//Called when this class is first initialized
-(id) initWithName:(NSString *)postStr webService:(NSString *)webService spinner: (UIActivityIndicatorView *)spinner{
if(self = [super init]){
_postStr = postStr;
_baseURL = #"http://base/api/";
_webService = webService;
_spinner = spinner;
_result = nil;
}
return self;
}
//Request to Locavore API restful web services
-(void) conn:(NSString *)method{
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(concurrentQueue, ^{
__block NSDictionary *resultBlock = nil;
dispatch_sync(concurrentQueue, ^{
/* Download the json here */
//Create webservice address
NSString *webService = [_baseURL stringByAppendingString:_webService];
//Create the url
NSURL *url = [NSURL URLWithString:webService];
//Create error object
NSError *downloadError = nil;
//Create the request
NSMutableURLRequest *req = [self initRequest:url method:method];
if(req != nil){
//Request the json data from the server
NSData *jsonData = [NSURLConnection
sendSynchronousRequest:req
returningResponse:nil
error:&downloadError];
NSError *error = nil;
id jsonObject = nil;
if(jsonData !=nil){
/* Now try to deserialize the JSON object into a dictionary */
jsonObject = [NSJSONSerialization
JSONObjectWithData:jsonData
options:NSJSONReadingAllowFragments
error:&error];
}
//Handel the deserialized object data
if (jsonObject != nil && error == nil){
NSLog(#"Successfully deserialized...");
if ([jsonObject isKindOfClass:[NSDictionary class]]){
resultBlock = (NSDictionary *)jsonObject;
//NSLog(#"Deserialized JSON Dictionary = %#", resultBlock);
}
else if ([jsonObject isKindOfClass:[NSArray class]]){
NSArray *deserializedArray = (NSArray *)jsonObject;
NSLog(#"Deserialized JSON Array = %#", deserializedArray);
} else {
/* Some other object was returned. We don't know how to deal
with this situation, as the deserializer returns only dictionaries
or arrays */
}
}
else if (error != nil){
NSLog(#"An error happened while deserializing the JSON data.");
}else{
NSLog(#"No data could get downloaded from the URL.");
[self conn:method];
}
}
});
dispatch_sync(dispatch_get_main_queue(), ^{
/* Check if the resultBlock is not nil*/
if(resultBlock != nil){
/*Set the value of result. This will notify the observer*/
[self setResult:resultBlock];
[_spinner stopAnimating];
}
});
});
}
//Configure the request for a post/get method
- (NSMutableURLRequest *)initRequest:(NSURL *)url method:(NSString *)method{
//Create the request
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
//Get the string length
NSString *strLength = [NSString stringWithFormat:#"%d", [_postStr length]];
//Specific to requests that use method post/get
//Configure the request
if([method isEqualToString:#"POST"]){
[req addValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content- Type"];
[req addValue:strLength forHTTPHeaderField:#"Content-Length"];
[req setHTTPMethod:#"POST"];
}else if([method isEqualToString:#"GET"]){
[req addValue:#"text/xml; charset=utf-8" forHTTPHeaderField:#"Content-Type"];
[req addValue:strLength forHTTPHeaderField:#"Content-Length"];
[req setHTTPMethod:#"GET"];
}else{
return nil;
}
//Set the HTTP Body
[req setHTTPBody:[_postStr dataUsingEncoding:NSUTF8StringEncoding]];
//Return the request
return req;
}
//Called when this object is destroyed
- (void)dealloc {
NSLog(#"DEALLOC LocavoreRetroAPIAdapter");
[super dealloc];
[_baseURL release];
[_result release];
}
#end
Familiarize yourself the with memory management and object lifetime rules. Your code is crashing because you do not retain (or copy) the arguments within your init... method, and they are being deallocated. Change your init... method to:
-(id) initWithName:(NSString *)postStr webService:(NSString *)webService spinner: (UIActivityIndicatorView *)spinner{
if(self = [super init]){
_postStr = [postStr copy];
_baseURL = #"http://base/api/";
_webService = [webService copy];
_spinner = [spinner retain];
}
return self;
}
Be sure to release the three instance variables you are now copying or retaining in your dealloc method. Also call [super dealloc] as the last step in that method but that's not the source of your problem right now.
//Called when this object is destroyed
- (void)dealloc {
NSLog(#"DEALLOC LocavoreRetroAPIAdapter");
[_postStr release];
[_webService release];
[_spinner release];
[_result release];
[super dealloc];
}
Notice I removed the call to [_baseURL release] from your dealloc as you did not retain it so you do not own the object. If you didn't create an object with alloc or new, and didn't call retain or copy on it, then you don't own the object, so you must not release it.

How to remove the associated UIProgressview with the right request?

I use asihttprequest to download multiple files and I'm wondering how I can remove the associated UIProgressview with the right request when the download is done.
NSMutableArray *contentArray contains the ASIHTTPRequest and NSMutableArray *progArray contains my custom UIProgressview.
-(void)addDownload:(NSString *)theURL withName:(NSString *)fileName
{
theProgress = [[PDColoredProgressView alloc] initWithFrame:CGRectMake(3, 17, 314, 14)];
//...
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:theURL]];
//..
[request setDelegate:self];
[request setDownloadProgressDelegate:theProgress];
request.allowResumeForFileDownloads = YES;
[request startAsynchronous];
[request setShouldContinueWhenAppEntersBackground:YES];
[contentArray addObject:request];
[progArray addObject:theProgress];
[theProgress retain];
[self.tableView reloadData];
}
- (void)requestFinished:(ASIHTTPRequest *)request{
[contentArray removeObject:request];
[progArray removeObject:theProgress];
NSLog(#"%#",progArray);
NSLog(#"%#",contentArray);
[self reloadMyData];
[self.tableView reloadData];
}
The problem is that this code remove the last progressview even if the there are 3 downloads in contentArray and the second one finish first.
Can you help me with this ?
If you need to remove progress view that's associated with finished request you can get it from request's downloadProgressDelegate property:
- (void)requestFinished:(ASIHTTPRequest *)request{
PDColoredProgressView *progress = (PDColoredProgressView*)request.downloadProgressDelegate;
[contentArray removeObject:request];
if (progress)
[progArray removeObject:progress];
[self reloadMyData];
[self.tableView reloadData];
}

Resources