我对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方法将同时接收并发回它所获得的内容(我将其留给调用代码以确保它返回预期的内容)。我使用APIKey作为额外的安全性,以确保只有我的应用程序可以访问Web服务资源(我在发送数据之前检查的第一件事是APIKey与我在数据库中对该应用程序的匹配)。
这是一种有效的方法来做这种事情吗?有什么办法让它变得更好?
答案 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应该阻止屏幕(因为你等到操作完成后等待),如果用户决定移动到另一个屏幕或取消查询怎么办?
我投票决定关闭此问题,因为它属于代码审核。