J'ai besoin d'utiliser un NSTimer pour annuler mon NSURLRequest avant 75 secondes (le temps que j'ai mesuré quel que soit le timeout que j'ai défini). J'utilise les classes XMLRPC d'Eric Czarny. Le XMLRPCConnection est essentiellement une image de la classe NSURLConnection.iphone NSURLConnection NSTimer fonctionne dans initWithRequest mais provoque une erreur de sélecteur non reconnue dans sendSynchronousRequest
est ici l'interface et le fichier de mise en œuvre:
#import <Foundation/Foundation.h>
@class XMLRPCRequest, XMLRPCResponse;
/* XML-RPC Connecion Notifications */
extern NSString *XMLRPCRequestFailedNotification;
extern NSString *XMLRPCSentRequestNotification;
extern NSString *XMLRPCReceivedResponseNotification;
@interface XMLRPCConnection : NSObject {
NSURLConnection *_connection;
NSString *_method;
NSMutableData *_data;
id _delegate;
UIViewController* _requester;
}
@property(nonatomic, retain) NSMutableData* _data;
- (id)initWithXMLRPCRequest: (XMLRPCRequest *)request delegate: (id)delegate;
- (id)initWithXMLRPCRequest: (XMLRPCRequest *)request delegate: (id)delegate requester:(UIViewController*)requester;
- (void) timedOut;
#pragma mark -
+ (XMLRPCResponse *)sendSynchronousXMLRPCRequest: (XMLRPCRequest *)request;
#pragma mark -
- (void)cancel;
@end
#pragma mark -
@interface NSObject (XMLRPCConnectionDelegate)
- (void)connection: (XMLRPCConnection *)connection didReceiveResponse: (XMLRPCResponse *)response
forMethod: (NSString *)method;
- (void)connection: (XMLRPCConnection *)connection didFailWithError: (NSError *)error
forMethod: (NSString *)method;
@end
fichier de mise en œuvre:
#import "XMLRPCConnection.h"
#import "XMLRPCRequest.h"
#import "XMLRPCResponse.h"
NSString *XMLRPCRequestFailedNotification = @"XML-RPC Failed Receiving Response";
NSString *XMLRPCSentRequestNotification = @"XML-RPC Sent Request";
NSString *XMLRPCReceivedResponseNotification = @"XML-RPC Successfully Received Response";
@interface XMLRPCConnection (XMLRPCConnectionPrivate)
- (void)connection: (NSURLConnection *)connection didReceiveData: (NSData *)data;
- (void)connection: (NSURLConnection *)connection didFailWithError: (NSError *)error;
- (void)connectionDidFinishLoading: (NSURLConnection *)connection;
- (void) timedOut;
@end
#pragma mark -
@implementation XMLRPCConnection
@synthesize _data;
- (id)initWithXMLRPCRequest: (XMLRPCRequest *)request delegate: (id)delegate requester:(UIViewController*)requester {
if (self = [super init])
{
_connection = [[NSURLConnection alloc] initWithRequest: [request request] delegate: self];
_delegate = delegate;
_requester = requester;
// set the timer for timed out requests here
NSTimer* connectionTimer = [NSTimer timerWithTimeInterval:10.0f target:self selector:@selector(timedOut) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:connectionTimer forMode:NSDefaultRunLoopMode];
if (_connection != nil)
{
_method = [[NSString alloc] initWithString: [request method]];
_data = [[NSMutableData alloc] init];
[[NSNotificationCenter defaultCenter] postNotificationName:
XMLRPCSentRequestNotification object: nil];
}
else
{
if ([_delegate respondsToSelector: @selector(connection:didFailWithError:forMethod:)])
{
[_delegate connection: self didFailWithError: nil forMethod: [request method]];
}
return nil;
}
}
return self;
}
- (void) timedOut {
NSLog(@"connection timed out now!");
}
#pragma mark -
+ (XMLRPCResponse *)sendSynchronousXMLRPCRequest: (XMLRPCRequest *)request
{
NSURLResponse *urlres;
//NSHTTPURLResponse *urlres;
NSError *err = NULL;
// set the timer for timed out requests here
NSTimer* connectionTimer = [NSTimer timerWithTimeInterval:10.0f target:self selector:@selector(timedOut) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:connectionTimer forMode:NSDefaultRunLoopMode];
NSData *data = [NSURLConnection sendSynchronousRequest: [request request]
returningResponse: &urlres error: &err];
if ([urlres isKindOfClass:[NSHTTPURLResponse class]]) {
NSLog(@"Received status code: %d %@", [(NSHTTPURLResponse *) urlres statusCode],
[NSHTTPURLResponse localizedStringForStatusCode:[(NSHTTPURLResponse *) urlres statusCode]]) ;
NSDictionary* headerFields = [(NSHTTPURLResponse*)urlres allHeaderFields];
NSArray* cookie = [NSHTTPCookie cookiesWithResponseHeaderFields:headerFields forURL:[request host]];
if ([cookie count] != 0) {
NSString* cookieName = [[cookie objectAtIndex:0] name];
NSString* cookieValue = [[cookie objectAtIndex:0] value];
NSLog(@"cookie name=value: %@=%@", cookieName, cookieValue);
[[User getInstance] setCookieID:[NSString stringWithFormat:@"%@=%@", cookieName, cookieValue] ];
} else {
NSLog(@"cookie array empty!");
}
}
// if an error occured while processing the request, this variable will be set
if(err != NULL)
{
//TODO: we may need to create a XMLRPCResponse with the error. and return
return (id) err;
}
if (data != nil)
{
NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"response is: %@",str);
if (! str) {
str = [[NSString alloc] initWithData:data encoding:[NSString defaultCStringEncoding]];
data = [str dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];
}
//Check for HTML code 400 or greater in response statusCode (from header) and throw error if so
if ([urlres isKindOfClass:[NSHTTPURLResponse class]]) {
// HTTP codes equal or higher than 400 signifies an error
if ([(NSHTTPURLResponse *) urlres statusCode] >= 400) {
// NSLog(@"Received status code: %d %@", [(NSHTTPURLResponse *) urlres statusCode],
// [NSHTTPURLResponse localizedStringForStatusCode:[(NSHTTPURLResponse *) urlres statusCode]]) ;
NSString *errorIntString = [NSString stringWithFormat:@"%d", [(NSHTTPURLResponse *) urlres statusCode]];
NSString *stringForStatusCode = [NSHTTPURLResponse localizedStringForStatusCode:[(NSHTTPURLResponse *) urlres statusCode]];
NSString *errorString = [[errorIntString stringByAppendingString:@" "] stringByAppendingString:stringForStatusCode];
NSInteger code = -1; //This is not significant, just a number with no meaning
NSDictionary *usrInfo = [NSDictionary dictionaryWithObject:errorString forKey:NSLocalizedDescriptionKey];
err = [NSError errorWithDomain:@"org.wordpress.iphone" code:code userInfo:usrInfo];
return (id) err;
}
}
//[str release];
return [[[XMLRPCResponse alloc] initWithData: data] autorelease];
}
return nil;
}
#pragma mark -
- (void)cancel
{
[_connection cancel];
[_connection autorelease];
}
#pragma mark -
- (void)dealloc
{
[_method autorelease];
[_data autorelease];
[super dealloc];
}
@end
#pragma mark -
@implementation XMLRPCConnection (XMLRPCConnectionPrivate)
....
@end
La minuterie est réglée dans la méthode initWithXMLRPCRequest fonctionne très bien, mais si elle est définie dans la méthode sendSycnhronousXMLRPCRequest, je reçois l'erreur suivante:
2010-01-29 11:36:40.442 ActiveE[1071:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** +[XMLRPCConnection timedOut]: unrecognized selector sent to class 0x32610'
2010-01-29 11:36:40.443 ActiveE[1071:207] Stack: (
31044699,
2497855305,
31426811,
30996086,
30848706,
609709,
30829248,
30825544,
39135117,
39135314,
3100675
)
Je ne sais pas comprendre, j'ai déclaré la méthode timeOut dans le fichier d'implémentation?
Cela est également vrai - je viens de le répara. Les interfaces sont mélangées avec l'implémentation et je me suis complètement perdu dans ça. – Leonard