0

私は、Objective-C と iOS の開発にやや慣れていません (約 1.5 年間使用しており、実際には過去 8 か月程度で深く関与しています)。すべての Web サービス要求を処理するカスタム クラスを作成しました。私はこれらのリクエストにAFNetworkingを使用しています (そして気に入っています) が、自分が行っていることが効率的であり、後で問題が発生しないようにしたいと考えています。

Instruments とアプリのパフォーマンスから見ると、これは良い方法のように思えますが、私は専門家にはほど遠いので、フィードバックやアドバイスを求めています。

これが私のNetworkClientクラスです:

NetworkClient.h:

#import <Foundation/Foundation.h>

extern NSString * const APIKey;

@interface NetworkClient : NSObject

+(void)processURLRequestWithURL:(NSString *)url 
                      andParams:(NSDictionary *)params 
                          block:(void (^)(id obj))block;

+(void)processURLRequestWithURL:(NSString *)url 
                      andParams:(NSDictionary *)params 
                    syncRequest:(BOOL)syncRequest
                          block:(void (^)(id obj))block;

+(void)processURLRequestWithURL:(NSString *)url 
                      andParams:(NSDictionary *)params 
                    syncRequest:(BOOL)syncRequest
             alertUserOnFailure:(BOOL)alertUserOnFailure
                          block:(void (^)(id obj))block;

+(void)handleNetworkErrorWithError:(NSError *)error;

+(void)handleNoAccessWithReason:(NSString *)reason;

@end

NetworkClient.m:

#import "NetworkClient.h"
#import "AFHTTPClient.h"
#import "AFHTTPRequestOperation.h"
#import "SBJson.h"

NSString * const APIKey = @"MyAPIKeyThatIsDefinedInDatabasePerApplication";

@implementation NetworkClient

+(void)processURLRequestWithURL:(NSString *)url 
                      andParams:(NSDictionary *)params 
                          block:(void (^)(id obj))block {

    [self processURLRequestWithURL:url andParams:params syncRequest:NO alertUserOnFailure:NO block:^(id obj) {
        block(obj);
    }];
}

+(void)processURLRequestWithURL:(NSString *)url 
                      andParams:(NSDictionary *)params 
                    syncRequest:(BOOL)syncRequest
                          block:(void (^)(id obj))block {
    [self processURLRequestWithURL:url andParams:params syncRequest:syncRequest alertUserOnFailure:NO block:^(id obj) {
        block(obj);
    }];
}


+(void)processURLRequestWithURL:(NSString *)url 
                      andParams:(NSDictionary *)params 
                    syncRequest:(BOOL)syncRequest
             alertUserOnFailure:(BOOL)alertUserOnFailure
                          block:(void (^)(id obj))block {

    // Default url goes here, pass in a nil to use it
    if (url == nil) {
        url = @"https://MyURLToWebService";
    }

    // Add in our API Key
    NSMutableDictionary *newParams = [[NSMutableDictionary alloc] initWithDictionary:params];
    [newParams setValue:APIKey forKey:@"APIKey"];

    NSURL *requestURL;
    AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:requestURL];

    NSMutableURLRequest *theRequest = [httpClient requestWithMethod:@"POST" path:url parameters:newParams];

    __block NSString *responseString = @"";

    AFHTTPRequestOperation *_operation = [[AFHTTPRequestOperation alloc] initWithRequest:theRequest];
    __weak AFHTTPRequestOperation *operation = _operation;

    [operation  setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
        responseString = [operation responseString];

        id retObj = [responseString JSONValue];

        // Check for invalid response (No Access)
        if ([retObj isKindOfClass:[NSDictionary class]]) {
            if ([[(NSDictionary *)retObj valueForKey:@"Message"] isEqualToString:@"No Access"]) {
                block(nil);
                [self handleNoAccessWithReason:[(NSDictionary *)retObj valueForKey:@"Reason"]];
            }
        } else if ([retObj isKindOfClass:[NSArray class]]) {
            if ([(NSArray *)retObj count] > 0) {
                NSDictionary *dict = [(NSArray *)retObj objectAtIndex:0];
                if ([[dict valueForKey:@"Message"] isEqualToString:@"No Access"]) {
                    block(nil);
                    [self handleNoAccessWithReason:[(NSDictionary *)retObj valueForKey:@"Reason"]];
                }
            }
        }
        block(retObj);
    } 
                                      failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                                          NSLog(@"Failed with error = %@", [NSString stringWithFormat:@"[Error]:%@",error]);
                                          block(nil);
                                          if (alertUserOnFailure) {
                                              // Let the user know something went wrong
                                              [self handleNetworkErrorWithError:operation.error];
                                          }

                                      }];

    [operation start];

    if (syncRequest) {
        // Process the request syncronously
        [operation waitUntilFinished];
    } 


}


+(void)handleNetworkErrorWithError:(NSError *)error {
    NSString *errorString = [NSString stringWithFormat:@"[Error]:%@",error];

    // Standard UIAlert Syntax
    UIAlertView *myAlert = [[UIAlertView alloc] 
                            initWithTitle:@"Connection Error" 
                            message:errorString 
                            delegate:nil 
                            cancelButtonTitle:@"OK" 
                            otherButtonTitles:nil, nil];

    [myAlert show];

}

+(void)handleNoAccessWithReason:(NSString *)reason {
    // Standard UIAlert Syntax
    UIAlertView *myAlert = [[UIAlertView alloc] 
                            initWithTitle:@"No Access" 
                            message:reason 
                            delegate:nil 
                            cancelButtonTitle:@"OK" 
                            otherButtonTitles:nil, nil];

    [myAlert show];

}

@end

そして、これが私がそれを呼び出す方法です:

NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:
                            @"GetApplications", @"Command",
                            userInfo.networkID, @"NetworkID",
                            nil];

    [NetworkClient processURLRequestWithURL:nil andParams:params block:^(id obj) {
        [MBProgressHUD hideHUDForView:self.view animated:YES];

        if ([obj isKindOfClass:[NSArray class]]) {
            myTableViewData = (NSArray *)obj;        
            [self.myTableView reloadData];  
        }
    }];

したがって、私の Web サービスは、辞書構造の JSON 応答と配列形式の JSON 応答の両方を返すことができます。NetworkClient メソッドは両方を取得し、取得したものを返します (期待されるものを確実に返すように、呼び出し元のコードに任せます)。APIKey を追加のセキュリティとして使用して、自分のアプリケーションだけが Web サービス リソースにアクセスできるようにします (データを返送する前に最初に確認することは、APIKey が DB 内のそのアプリケーション用のものと一致することです)。

これは、この種のことを行うための効率的な方法ですか? それを改善する方法はありますか?

4

1 に答える 1

3

processURLRequestWithURL:nilこれは特定のサービスを扱うために作られているので、なぜ使用するのかわかりません。それはまた紛らわしいです、それはそれが全くそこにないかのように同じである他の場所にいくつかの魔法があることを私に伝えるだけです。シングルトンを使用します:

extern NSString * const kBaseURL;

@interface NetworkClient : AFHTTPClient
+ (NetworkClient *) sharedClient;
@end

NSString* const kNodeApiURL = BASE_URL;

@implementation NetworkClient

+ (NetworkClient*) sharedClient
{
    static NetworkClient *_sharedClient = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _sharedClient = [[NetworkClient alloc] initWithBaseURL:[NSURL URLWithString:kBaseURL]];
    });
    return _sharedClient;
}

- (id)initWithBaseURL:(NSURL*)url 
{
    if (self = [super initWithBaseURL:url]) {
        [self registerHTTPOperationClass:[AFJSONRequestOperation class]];
        [self setDefaultHeader:@"Accept" value:@"application/json"];
    }
    return self;
}

-(id) init {
    error(@"Use initWithBaseURL: instead.");
    [super doesNotRecognizeSelector:_cmd];
    return nil;
}

@end

そしてPCHで

#define BASE_URL  @"https://MyURLToWebService"

また、ポップアップを表示するパラメーターを追加するときに、GUIとサーバーAPIを混在させています。サーバーAPIがスレッドをブロックすることはないと思います。純粋に非同期のコードを記述し、呼び出し元が自分の側からGUIをブロックできるようにします。

handleNoAccessWithReasonと同じです。APIは何も処理せず、入力を取得して出力を生成します。あなたが書くコードのすべての部分は1つのことをするべきです。テスト、理解、再利用がはるかに簡単になります。

なぜあなたが__weakで操作を修飾したのかわかりません。

渡すパラメータは、ユーザーやコマンドなどのドメインオブジェクトを使用すると、はるかに理解しやすくなります。さて、「コマンド」は悪臭を放ちます。使用法コードの後ろに意味のある名前のメソッドが本当にありますか?何が起こっているのかを伝えるためにパラメーターを出力しなければならないコードをデバッグするとき、私は激怒するからです。サーバーAPIを作成している場合(そして、クリーンなコードが必要な場合はデフォルトで作成する必要があります)、意味のある名前を公開する必要があります。

別の方法でコードを記述します。たとえば、特定のユーザーのサーバーから牛が欲しい場合です。

typedef void (^AFJSONSuccess)(NSURLRequest *request, NSHTTPURLResponse *response, id JSON);
typedef void (^AFJSONFailure)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON);

+(void) cowForUser:(User*)user callback:(void(^)(Cow *cow, NSError *error))callback {

    AFJSONSuccess success = ^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
        // turn JSON into a cow
        callback(cow,nil);
    };

    AFJSONFailure failure = ^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
        // create a custom NSError
        callback(nil,error);
    };

    NetworkClient *client = [NetworkClient sharedClient];
    NSMutableURLRequest *request = [client requestWithMethod:@"GET" path:kCowPath parameters:jsonDic];
    AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:sucess failure:failure];
    [client enqueueHTTPRequestOperation:operation];
}

使用中はhudを開始し、コールバックブロックでhudを停止して、牛がゼロかどうかを確認します。(操作が完了するまで待っているように)hudが画面をブロックするべきではないと思います。ユーザーが別の画面に移動するか、クエリをキャンセルすることにした場合はどうなりますか?

この質問はコードレビューに属しているため、私はこの質問を閉じることに投票します。

于 2012-08-21T22:45:01.550 に答える