AFNetworking AFHTTPClient子类,单例客户端和代表

时间:2013-09-12 20:08:47

标签: objective-c delegates afnetworking afhttpclient

我正在为iOS项目编写带有AFNetworking的REST API层。关于我到目前为止所写的内容,我有一些问题,所以我转向stackoverflow获取一些指导/答案。

以下是我正在努力实现的指导原则:

  • 将初始化单例客户端的基类(DRAPI:AFHTTPClient),就像AFHTTPClient推荐的cocoadocs一样。
  • DRAPI:DRAPIDelegate的“基础”委托,其中包含以统一格式显示错误的方法。
  • 处理我的REST API的某些路由的DRAPI的子类。例如,对用户的CRUD操作是DRUserAPI的责任,DRUserAPI是DRAPI的子类。
  • DRAPI的每个子类都有自己的委托。对于DRUserAPI,有DRUserAPIDelegate,它扩展了DRAPIDelegate。

以下是迄今为止编写内容的快速示例:

DRAPI.h

@interface DRAPI : AFHTTPClient

- (void) apiGetCallWithRoute:(NSString*)route
                  parameters:(NSDictionary*)parameters
                   onSuccess:(void(^)(id))successBlock
                     onError:(void(^)(NSArray* errors))errorBlock;

@end

@protocol DRAPIDelegate <NSObject>

-(void) DRAPIErrorFromServer:(NSArray*)errors;

@end

DRAPI.m

@implementation DRAPI

+(DRAPI*) sharedClient
{
  static DRAPI *aSharedClient = nil;
  static dispatch_once_t onceToken;
  dispatch_once(&once_token, ^{
    _sharedClient = [DRAPI alloc] initWithBaseURL:[NSURL URLWithString:@"http://127.0.0.1:3000/api"];
  });
  return aSharedClient;
}

-(id) initWithBaseURL:(NSURL *)url
{
  self = [super initWithBaseURL:url];
  if (self) {
     // configuration goes here... skipping because it is not important.
  }
  return self;
}

#pragma mark - Helper methods for Server Calls

- (void) apiGetCallWithRoute:(NSString*)route
                  parameters:(NSDictionary*)parameters
                   onSuccess:(void(^)(id))successBlock
                     onError:(void(^)(NSArray* errors))errorBlock 
{
  [[DRAPI sharedClient] getPath:route
                    parameters:addAuthenticationParametersTo(parameters)
                       success:^(AFHTTPRequestOperation *operation, id responseObject) {
                         successBlock(responseObject);
                       }
                       failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                         errorBlock( processError() );
                       }];
}

@end

DRUserAPI.h

@interface DRUserAPI: DRAPI

@property (weak, nonatomic) id<DRUserAPIDelegate>delegate;

+(DRUserAPI*) APIWithDelegate:(id<DRUserAPIDelegate>)delegate;

-(void) createUser:(NSString*)username password:(NSString*)password;

// ... more methods would be declared here...

@end

@protocol DRUserAPIDelegate <NSObject, DRAPIDelegate>

-(void) DRUserAPIOnUserCreated:(MyUserModel*)newUser;

// more delegate methods would be here...

@end

DRUserAPI.m

@implementation DRUserAPI

@synthesize delegate;

+(DRUserAPI*) APIWithDelegate:(id<DRUserAPIDelegate>)delegate 
{
  DRUserAPI * client = [DRUserAPI new];
  client.delegate = delegate;
  return client;
}

-(void) createUser:(NSString*)username password:(NSString*)password
{
  [self apiGetCallWithRoute:@"users/create"
                 parameters:@{@"username" : username, @"password": password}
                  onSuccess:^(id response) {
                    NSDictionary *parsedJSON = response;
                    [delegate DRUserAPIOnUserCreated:[MyUserModel newModelFromDictionary:parsedJSON];
                  }
                  onError:^(NSArray *errors) {
                    [delegate DRAPIErrorFromServer:errors];
                  }];
}

@end

一位同事提请我注意,代表和单身人士不要混在一起。不过,我仍然想管理代表。我认为好的解决方案是将委托的单例实例传递给我在API子类中调用的方法。

这是个好主意吗?

谢谢!

2 个答案:

答案 0 :(得分:1)

我更喜欢基于合成而不是子类化的实现,即使AFNetworking文档建议将子类化为AFHTTPClient。

我会在DRAPI中注入AFHTTPClient,在DRUserAPI中注入这个,使它们都成为NSObject的简单子类。平面设计更清洁IMO,它可以更容易地对您的课程进行单元测试。

您可以创建一个负责创建整个对象图的注入类,而不是单例,只需在应用程序委托中调用它。

为此,您应该使用基于块的API而不是委托,因为您只有一个DRAPI实例,并且您不希望在调用它之前设置其委托(您可以使用另一个类,如DRUserAPI,您注入DRAPI实例)。

答案 1 :(得分:0)

它并不完美,但它确实有效。为什么这么多代表?你似乎转向无限循环的单身人士。我想你应该停止......