iOS客戶端網絡框架設計(一)

咱們的客戶端網絡框架至少要解決三個問題:實現通訊協議、帳戶系統、簡化服務端接口調用。後端

實現通訊協議 根據與服務端制定的通訊協議,實現請求的組裝,序列化,發送,以及響應的接收和解析等。
帳戶系統 簡而言之就是實現註冊、登錄、註銷等功能,並維護登錄狀態等。
簡化服務端接口調用 客戶代碼只須要提供業務參數和回調函數就能夠實現與服務器通訊,網絡框架負責封裝掉其他全部細節。設計模式

我想對架構比較敏感的讀者會馬上有這樣的共鳴,首先上述的帳戶系統顯然是一個獨立的模塊,最好單獨設計實現。另外一方面,帳戶系統的功能又是以服務端接口調用爲基礎的,在形式上登錄操做也是調用服務端接口,那麼把登錄相關操做與其餘服務端接口調用實現於一處就是天然的。若是再做一些考慮,咱們還會想到的一個問題是,網絡框架暴露給客戶代碼的接口應當儘量單一,若是咱們用一個類維護帳戶系統,用另外一個類作服務端業務接口調用,會嫌不夠簡潔。達成這幾點共識以後,咱們就能夠繼續探討一些設計細節了。先看下面的代碼。服務器

//SFClient.h
@interface SFClient

@property (nonatomic,readonly) NSString* account;
@property (nonatomic,readonly) NSString* password;
@property (nonatomic,readonly) BOOL isLoggedIn;
@property (nonatomic,readonly) BOOL pendingLogin;
@property (nonatomic,readonly) NSString* sessionId;

-(NSURLSessionTask*)loginWithAccount:(NSString*)account password:(NSString*)password;

-(NSURLSessionTask*)logout;

-(NSURLSessionTask*)someNetworkingTaskWithCompletionHandler:(SFNetworkingTaskCompletionHandler)completionHandler;

//...

@end
有的同行習慣於爲每個後端接口單獨開一個類,這固然也不失爲一種設計風格,筆者也曾嘗試過,我的感受嫌繁。

這裏的SFClient類做爲帳戶系統,又兼具服務端業務接口調用功能,實現了使接口儘量簡潔的設計目標,卻違背了帳戶系統應當單獨實現的架構設計直覺。
如何解決這一矛盾呢?能夠採用dynamic proxy設計模式。網絡

圖片描述

定義一個protocol假設叫SFBackendInterfaces,和一個實現類假設叫SFBackendInterfacesImpl。讓SFClientSFBackendInterfacesImpl都實現這個協議。session

@protocol SFBackendInterfaces<NSObject>

-(NSURLSessionTask*)loginWithAccount:(NSString*)account password:(NSString*)password completionHandler:(SFNetworkingTaskCompletionHandler);

-(NSURLSessionTask*)someNetworkingTaskWithCompletionHandler:(SFNetworkingTaskCompletionHandler)completionHandler;

@end

@interface SFClient:NSObject<SFBackendInterfaces>

@interface SFBackendInterfacesImpl:NSObject<SFBackendInterfaces>

這樣作的目的是什麼呢,就是讓SFClient類繼續提供服務端接口調用功能,同時把這些接口調用的實現代碼交給SFBackendInterfacesImpl。這樣就既知足網絡框架接口簡潔的需求,又保持了SFClient類做爲帳戶系統的純淨,-(NSURLSessionTask*)someNetworkingTaskWithCompletionHandler:(SFNetworkingTaskCompletionHandler)completionHandler;這行代碼能夠從SFClient的interface中拿掉了,而且相關代碼也不須要出如今它的implementation文件裏了。咱們來看implementation。架構

@implementation SFClient

-(void)forwardInvocation:(NSInvocation *)anInvocation
{
    if([self.backendInterfacesImpl respondsToSelector:anInvocation.selector]){
        [anInvocation invokeWithTarget:self.backendInterfacesImpl];
    }else{
        [super forwardInvocation:anInvocation];
    }
}

-(void)loginWithAccount:(NSString*)account password:(NSString*)password
{
    NSURLSessionTask* task=[self loginWithAccount:account password:(NSString*)password completionHandler:^(SFResponse* response){
        [[NSNotificationCenter defaultCenter] postNotificationNamed:SFLoginCompletionNotification object:response];
        _pendingLogin=NO;
        if(response.status==SFResponseStatusSuccess){
            _loggedIn=YES;
        }
    }];
    [task resume];
    _pendingLogin=YES;
}

@end

@implementation SFBackendInterfacesImpl

-(NSURLSessionTask*)loginWithAccount:(NSString*)account password:(NSString*)password completionHandler:(SFNetworkingTaskCompletionHandler)
{
    //...
}

-(NSURLSessionTask*)someNetworkingTaskWithCompletionHandler:(SFNetworkingTaskCompletionHandler)completionHandler
{
    //...
}

@end

這樣客戶代碼就能夠經過SFClient這一單一接口使用網絡框架了。框架

[[SFClient sharedClient] loginWithAccount:xxxx password:xxxx];
//...
NSURLSessionTask* task=[[SFClient sharedClient] someNetworkingTaskWithPara::param completionHandler:^(SFResponse* response){
    //...
}];
[task resume];

而在框架內部實現上,帳戶系統和業務接口調用的實現仍然是分離的。函數

相關文章
相關標籤/搜索