0

我正在为一个 iOS 项目编写一个带有 AFNetworking 的 REST API 层。我对到目前为止所写的内容有一些疑问,所以我转向 stackoverflow 寻求一些指导/答案。

以下是我想要实现的指导方针:

  • 一个基类(DRAPI:AFHTTPClient),它将初始化一个单例客户端,就像 AFHTTPClient 的 cocoadocs 推荐的那样。
  • DRAPI 的“基础”委托:DRAPIDelegate,它包含以统一格式显示错误的方法。
  • 处理我的 REST API 的某些路由的 DRAPI 子类。例如,对用户的 CRUD 操作是 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 子类中调用的方法。

这是一个好主意吗?

谢谢!

4

2 回答 2

1

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

我会在 DRAPI 中注入 AFHTTPClient,在 DRUserAPI 中注入这个,使它们都成为 NSObject 的简单子类。扁平化设计在 IMO 上更简洁,并且可以更轻松地对类进行单元测试。

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

为此,您应该使用基于块的 API 而不是委托,因为您将只有一个 DRAPI 实例,并且您不想在对其进行任何调用之前设置其委托(您可以在注入 DRAPI 实例的地方使用另一个类,例如 DRUserAPI )。

于 2013-09-14T01:39:23.990 回答
0

它并不完美,但它有效。为什么这么多代表?看来您正在进入单例的无限循环。我觉得你应该停下来...

于 2013-09-12T22:06:05.917 回答