这是一个很好的编程风格。使用像这样的类方法是否有效?

时间:2012-08-21 21:07:53

标签: iphone objective-c ios afnetworking

我对Objective-C和iOS开发有点新手(使用它大约花了1。5年,而且在过去8个月左右的时间里,大量参与其中)。我编写了一个自定义类来处理我的所有Web服务请求。我使用AFNetworking来表示这些请求(并且喜欢它),但我想确保我所做的是有效的,而不会在以后引起问题。

从我可以看到的仪器以及应用程序的运行方式来看,这似乎是一种很好的方法,但我真的不是专家,而是希望得到一些反馈和/或建议。

这是我的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方法将同时接收并发回它所获得的内容(我将其留给调用代码以确保它返回预期的内容)。我使用API​​Key作为额外的安全性,以确保只有我的应用程序可以访问Web服务资源(我在发送数据之前检查的第一件事是APIKey与我在数据库中对该应用程序的匹配)。

这是一种有效的方法来做这种事情吗?有什么办法让它变得更好?

1 个答案:

答案 0 :(得分: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来限定操作。

您传递的参数,如果您使用像User和Command之类的域对象,那么它将更容易理解。那么,“命令”很臭。在您的使用代码背后是否真的有一个名义有意义的方法?因为当我调试代码时,我必须打印参数来告诉我发生了什么事情。如果您正在编写服务器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,并在回调块中调用stop hud并检查cow是否为零。我不认为hud应该阻止屏幕(因为你等到操作完成后等待),如果用户决定移动到另一个屏幕或取消查询怎么办?

我投票决定关闭此问题,因为它属于代码审核。