4

コードよりも設計上の考慮事項である問題がある。

私の iOS アプリは、json Web サービスとインターフェイスします。私は AFNetworking を使用していますが、私の問題は基本的に、追加の要求 (上記のトークンを必要とする) を行う前に、init 関数 (AFHTTPClient を認証してトークンを取得する) を完全に完了する必要があることです。

以下のコードから、これを達成するための設計アプローチを聞くことに興味があります。すべてのリクエストを非同期に保つことをお勧めします。別の解決策は、リクエストを initWithHost:port:user:pass 同期 (AFNetworking を使用しない) にすることです。悪い習慣であることを認識しており、避けたいと思っています。

DCWebServiceManager.h

#import <Foundation/Foundation.h>
#import "AFHTTPClient.h"

@interface DCWebServiceManager : NSObject
{
    NSString *hostServer;
    NSString *hostPort;
    NSString *hostUser;
    NSString *hostPass;
    NSString *hostToken;
    AFHTTPClient *httpClient;
}

// Designated Initialiser
- (id)initWithHost:(NSString *)host port:(NSString *)port user:(NSString *)user pass:(NSString *)pass;

// Instance Methods
- (void)getFileList;
@end

DCWebServiceManager.m

#import "DCWebServiceManager.h"
#import "AFHTTPClient.h"
#import "AFHTTPRequestOperation.h"
#import "AFJSONRequestOperation.h"

@implementation DCWebServiceManager

- (id)initWithHost:(NSString *)host port:(NSString *)port user:(NSString *)user pass:(NSString *)pass
{
    self = [super init];
    if (self)
    {
        hostServer = host;
        hostPort = port;
        hostUser = user;
        hostPass = pass;

        NSString *apiPath = [NSString stringWithFormat:@"http://%@:%@/", hostServer, hostPort];

        httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:apiPath]];
        [httpClient setAuthorizationHeaderWithUsername:hostUser password:hostPass];

        NSMutableURLRequest *request = [httpClient requestWithMethod:@"GET" path:@"authenticate.php" parameters:nil];
        AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];

        [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject){

        // Do operations to parse request token to be used in
        // all requests going forward...
        // ...
        // ...
        // Results in setting: hostToken = '<PARSED_TOKEN>'        
            NSLog(@"HostToken: >>%@<<",  hostToken);

        } failure:^(AFHTTPRequestOperation *operation, NSError *error){
            NSLog(@"ERROR: %@",  operation.responseString);
        }];

        [operation start];
    }

    return self;
}

- (void)getFileList
{
    // *************************
    // The issue is here, getFileList gets called before the hostToken is retrieved..
    // Make the authenticate request in initWithHost:port:user:pass a synchronous request perhaps??
    // *************************
    NSLog(@"IN GETFILELIST: %@", hostToken); // Results in host token being nil!!!

    NSString *queryString = [NSString stringWithFormat:@"?list&token=%s", hostToken];
    NSMutableURLRequest *listRequest = [httpClient requestWithMethod:@"GET" path:queryString parameters:nil];

    AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:listRequest success:^(NSURLRequest *request,    NSHTTPURLResponse *response, id JSON){
        NSLog(@"SUCCESS!");
    } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON){
        NSLog(@"ERROR!: %@", error);
    }];

    [operation start];
}
@end

ViewController.m

....
DCWebServiceManager *manager = [[DCWebServiceManager alloc] initWithHost:@"localhost" port:@"23312" user:@"FOO" pass:@"BAR"];
[manager getFileList];

// OUTPUTS
IN GETFILELIST: (nil)
HostToken: >>sdf5fdsfs46a6cawca6<<
....
...
4

2 に答える 2

0

@patric.schenke が言っAFHTTPClientたように、コードの一部をクリーンアップしたい場合はサブクラス化できますが、実際の問題は、へのリクエストを行う前に、(トークンが nil の場合) 認証するために別のリクエストを行う必要があることですgetFileList

AFNetworkingブロックを使用して非同期を維持するのと同じ方法でブロックを使用することをお勧めします。HTTP 呼び出しを独自のメソッドに移動し、hostTokennilの場合にのみ呼び出します。

- (void)getFileList
{
  if (self.token == nil) {
    [self updateTokenThenWhenComplete:^(void){
      // make HTTP call to get file list
    }];
  } else {
    // make HTTP call to get file list
  }
}

- (void)updateTokenThenWhenComplete:(void (^))callback
{
  //... make HTTP request
  [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject){
      self.token = responseObject.token;
      callback();
    } failure:^(AFHTTPRequestOperation *operation, NSError *error){
        //...
    }];

}
于 2013-04-12T22:21:57.997 に答える
0

AFHTTPClient をサブクラス化+sharedInstanceし、トークンに and プロパティを追加することをお勧めします。

+ (MyGClient *)sharedInstanceWithHost:(NSString *)host port:(NSString *)port user:(NSString *)user pass:(NSString *)pass {
    static MyClient *sharedInstance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [... your code from the init ...]
    });
    return sharedInstance;
}

enqueueHTTPRequestOperationWithRequest:success:failureその後、さらに操作をエンキューする前にトークンを確認するようにオーバーライドできます。

さらに、プロパティのセッターをオーバーライドすることで、トークンが設定されるとすぐに操作を収集してキューに入れることができます。

于 2013-02-22T13:34:32.587 に答える