如何从objective-c(iPhone)中的Web服务返回数据?

时间:2012-01-14 19:16:46

标签: iphone objective-c ios

这可能是一个愚蠢的问题。对不起,如果是。

但我正在开发一个消耗Web服务的项目。我可以连接到Web服务并获取我需要的数据。

我想有一个方法将从Web服务获得的数据返回给调用者。唯一的问题是数据只在ConnectionDidFinishLoading方法中获得,我无法从我的方法访问这些数据。

这是我的代码,工作正常:

- (NSData *) dataForMethod:(NSString *)webMethod withPostString:(NSString *)postString
{
    NSURL *url = [NSURL URLWithString:[SigameWebServiceAddress stringByAppendingFormat:@"%@%@", @"/", webMethod]];
    NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
    NSString *msgLength = [NSString stringWithFormat:@"%d", [postString length]];

    [req addValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
    [req addValue:msgLength forHTTPHeaderField:@"Content-Length"];
    [req setHTTPMethod:@"POST"];
    [req setHTTPBody: [postString dataUsingEncoding:NSUTF8StringEncoding]];

    conn = [[NSURLConnection alloc] initWithRequest:req delegate:self];
    if (conn) 
    {
        webData = [NSMutableData data];
    }   

    // I WOULD LIKE TO RETURN WEBDATA TO THE CALLER HERE, BUT WEBDATA IS EMPTY NOW, THE  
    //connectionDidFinishLoading ONLY GETS CALLED WITH THE DATA I WANT AFTER THE COMPILER
    //IS DONE EXECUTING MY METHOD.
}

-(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(@"FATAL ERROR");
}

-(void) connectionDidFinishLoading:(NSURLConnection *) connection 
{
    NSLog(@"DONE. Received Bytes: %d", [webData length]);

    NSString *theXML = [[NSString alloc] initWithBytes: [webData mutableBytes] length:[webData length] encoding:NSUTF8StringEncoding];

    //---shows the XML---
    NSLog(@"%@", theXML);  //NOW, THIS IS THE DATA I WANT. BUT HOW CAN I RETURN THIS TO 
                           //THE CALLER. I MEAN, THE CALLER THAT CALLED MY METHOD 
                           //+ (NSData *) dataForMethod: withPostString:
}

感谢任何帮助! 感谢

6 个答案:

答案 0 :(得分:11)

实际上有两种方法。

  1. 创建委托界面
  2. 使用块
  3. 我强烈建议不要使用同步方法 - 除非你已经创建了自己的异步框架(即你手动启动另一个线程并在该线程上执行同步请求)。从长远来看,您将意识到您需要异步请求,并且您必须重新处理它们的所有内容。

    快速概述我给出的两个选项:

    1。创建委托界面

    这里的想法是创建一个执行请求的类,并创建调用者必须实现的协议。请求完成后,您将使用数据调用委托上的指定方法:

    协议可能如下所示:

    @protocol RequestClassDelegate <NSObject>
    
    - (void)requestCompleted:(ResponseClass *)data;
    - (void)requestError:(NSError *)error;
    
    @end
    

    发出请求的类可能如下所示:

    @interface RequestClass : NSObject
    
    - (void)makeRequest:(id<RequestClassDelegate>)delegate;
    
    @end
    

    除了连接逻辑之外,请求类实现可能包含以下一些内容:

    @implementation RequestClass
    {
        __weak id<RequestClassDelegate> _delegate;
    }
    
    // Connection Logic, etc.
    
    - (void)makeRequest:(id<RequestClassDelegate>)delegate
    {
        _delegate = delegate;
        // Initiate the request...
    }
    
    -(void) connectionDidFinishLoading:(NSURLConnection *) connection 
    {
        NSString *theXML = [[NSString alloc] initWithBytes: [webData mutableBytes] length:[webData length] encoding:NSUTF8StringEncoding];
    
        // Processing, etc.
    
        // Here we'll call the delegate with the result:
        [_delegate requestCompleted:theResult];
    }
    
    @end
    

    2。使用块

    这个解决方案与第一个解决方案大致相同 - 但是,在我看来,更优雅一点。在这里,我们将RequestClass更改为使用块而不是委托:

    typedef void (^requestCompletedBlock)(id);
    typedef void (^requestErrorBlock)(NSError *);
    @interface RequestClass : NSObject
    
    @property (nonatomic, copy) requestCompletedBlock completed;
    @property (nonatomic, copy) requestErrorBlock errored;
    
    - (void)makeRequest:(requestCompletedBlock)completed error:(requestErrorBlock)error;
    
    @end
    

    其实现可能如下所示:

    @implementation RequestClass
    
    @synthesize completed = _completed;
    @synthesize errored = _errored;
    
    // Connection Logic, etc.
    
    - (void)makeRequest:(requestCompletedBlock)completed error:(requestErrorBlock)error
    {
        self.completed = completed;
        self.errored = error;
        // Initiate the request...
    }
    
    -(void) connectionDidFinishLoading:(NSURLConnection *) connection 
    {
        NSString *theXML = [[NSString alloc] initWithBytes: [webData mutableBytes] length:[webData length] encoding:NSUTF8StringEncoding];
    
        // Processing, etc.
    
        // Here we'll call the delegate with the result:
        self.completed(theResult);
    }
    
    @end
    

答案 1 :(得分:2)

听起来您正在尝试使用从您的方法同步返回数据,但您正在使用异步方法(使用NSURLConnection并可能调用其start方法)来开始检索数据。如果您真的希望您的方法同步返回其结果,请继续阅读。正如@Steve在另一个答案中所说的那样,您也可以重新考虑您的界面设计,而是使用异步方法实现它,并将其建议用于委托或基于块的界面。

如果要从方法同步返回数据,请使用同步请求。因此,请更改代码的这一部分:

conn = [[NSURLConnection alloc] initWithRequest:req delegate:self];
[conn start]; // I presume you have this somewhere
if (conn) 
{
    webData = [NSMutableData data];
} 

有更像这样的东西:

 NSURLResponse *response = nil;
 NSError *error = nil;
 webdata = [NSURLConnection sendSynchronousRequest:req returningResponse:&response error:&error];
 if (webdata) {
     return webdata;
 }
 else {
     // Handle error by looking at response and/or error values
     return nil;
 }

如果您使用此方法,则不再需要任何代理代码。但是你会在某些方面受到限制。例如,如果您的Web服务需要通过URL参数以外的其他方式进行身份验证,则无法使用此方法。

答案 2 :(得分:2)

史蒂夫的答案很棒,我只能建议使用积木的方式。实际上,由于我是Objective-C的新手,我实施了steve概述的方法。它运作得很好。

帖子了解更多详情和我自己的观点,你可以在这里找到: http://kerkermeister.net/how-to-build-an-cocos2d-ios-app-communicating-with-a-restful-api-the-sequence/

The Post包含了您需要遵循的所有微小步骤,以便通过块工作来获得Steve的解决方案。那包括: - 一个可更新的视图,只要异步从Web API检索就会呈现信息 - 调用Web API的HTTP请求的控制器 - 使用iOS标准NSURLConnections的实际HttpRequest类 - 使用块作为回调来更新其数据的模型类

答案 3 :(得分:0)

您必须实现一个单独的方法,在connectionDidFinishLoading方法返回数据后使用数据或同步发出请求。我认为上述方法不起作用的原因是因为请求发生在一个单独的线程上,所以主线程继续,但实际上没有数据。

如果你想要同步,这是一个很好的方法:

Does all NSURLConnections connect asynchronously? iOs

答案 4 :(得分:0)

为了从webserivce下载数据-使用 NSURLSession - URL会话任务,可将下载的数据直接返回到内存中的应用程序。

// 1. create NSURL link to your webservice
NSString *dataUrl = @"DATA_LINK_TO_WEBSERVICE";
NSURL *url = [NSURL URLWithString:dataUrl];

// 2. create a NSURLSessionDataTask
NSURLSessionDataTask *downloadTask = [[NSURLSession sharedSession]
  dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
  //Handle response here
}];

// 3.resume the task
[downloadTask resume];

引用:

  1. apple文档参考: https://developer.apple.com/documentation/foundation/nsurlsessiondatatask?language=objc
  2. Raywanderlich很棒的食谱: https://www.raywenderlich.com/2392-cookbook-using-nsurlsession

答案 5 :(得分:-1)

您需要解析返回的XML。那里有一些很好的Objective C XML解析器。其中一个特别是为了易于使用....

http://nfarina.com/post/2843708636/a-lightweight-xml-parser-for-ios

它是一个非常轻量级的解析器,用于从XML中提取所需的值。我已经多次使用并取得了巨大成功并且没有什么麻烦。以下是我查询网址并将其转换为数据的方法。

 NSString *query = [NSString stringWithFormat:@"http://WEB_ADDRESS_FOR_XML];
    NSURL *URL = [NSURL URLWithString:query];
    NSData *data = [NSData dataWithContentsOfURL:URL];

或者使用NSURLConnection,在确实收到数据:

-(void) connection:(NSURLConnection *) connection didReceiveData:(NSData *) data 
{
    //USE THE DATA RETURNED HERE....
}

然后使用我链接中的Parser获取内容:

 SMXMLDocument *document = [SMXMLDocument documentWithData:data error:NULL];

    NSLog("\nXML Returned:%@",document);