2013-10-30 68 views
6

在我的應用程序委託中,我創建了一個數據模型和inject it into a root view controller我從故事板while requesting user's credentials if needed from the start中獲得。稍後,在訪問某些數據模型方法時,我需要驗證用戶的密碼並重試一個觸發密碼重新驗證的請求。誰應該負責用登錄表單顯示模態對話框?

最明顯的是將此功能構建到可能需要此信息的每個視圖控制器中,但我希望儘可能避免這種情況,因爲它使得控制器不那麼通用,也使測試變得更加困難。在我看來,管制員一定不知道他們給出的模型的內部運作。

將此功能添加到模型中也不適合我:管理用戶交互完全超出了MVC模型的責任。

誰應該負責顯示與相應的視圖控制器模式對話框,讓用戶輸入他的憑據?

+0

不能錯過一個有趣的答案:Q「誰應該負責以登錄形式顯示模態對話框?」答:「開發者」 - 不是? :) – 2013-10-31 17:48:02

+2

@matheszabi呃哦,突然我想象每個iPhone都附加了數以萬計的「開發人員」 – sanmai

回答

3

它可以通過回調使用很少的代碼行來完成。回調API將在模型層定義(所以它是可重用的),但用戶交互是在控制器級別實現的(因爲它屬於它的位置)。

根據您的描述,我不完全確定您的架構是什麼樣子,我假定該應用程序意識到您沒有僅在失敗的請求中進行身份驗證(您可能希望存儲令牌到期日期並利用它, 如果可能的話)。

基本思想:

在你的模型,你有一個回調塊屬性(例如,在客戶端類或任何其它方式使用)。

@property (nonatomic, copy) void (^onNonauthenticatedRequest)(NSURLRequest *failedRequest, NSError *error); 

當您的請求因用戶未通過身份驗證而失敗時,您可以在模型圖層中執行此塊。

在控制器級別上,您有控制器提示用戶輸入憑據(並且具有類似的回調模式)。

client.onNonauthenticatedRequest = ^(NSURLRequest *failedRequest, NSError *error) { 

    ABCredentialsViewController *credentialsViewController = [ABCredentialsViewController new]; 
    credentialsViewController.onAuthenticationSuccess = ^{ 
     // This gets called after the authentication request succeeded 
     // You want to refire failedRequest here 
     // Make sure you use a weak reference when using the object that owns onAuthenticationFailure 
    }; 

    credentialsViewController.onAuthenticationFailure = ^(NSError *) { 
     // You might want to do something if the user is not authenticated and failed to provide credentials 
    } 

    [[UIApplication sharedApplication].delegate.window.topViewController presentViewController:credentialsViewController animated:YES]; 
    // or you could just have a method on UIViewController/your subclass to present the credentials prompt instead 
}; 

的邏輯是在正確的地方,如果你想在不同的情況進行不同的處理未通過認證的請求,就可以了。

+0

看起來不錯。以爲有很多灰色的地方,但你不應該爲我做作業,對吧?謝謝! – sanmai

0

你說得對。將這個功能構建到視圖控制器中是不必要的,而且封裝很差

在MVC範例中,模型通常具有數據上下文。數據上下文管理與後端存儲(在iOS中,這往往是一個Web服務或本地文件)的通信來填充和存檔模型對象。對於經過身份驗證的數據上下文,您擁有用戶名,密碼和身份驗證狀態的屬性。

@interface DataContext : NSObject 
    //Authentication 
    @property (nonatomic, strong) NSString * username; 
    @property (nonatomic, strong) NSString * password; 
    @property (nonatomic, assign) NSInteger authenticationState; 
    -(void)login; 
    //Data object methods from authenticated data source (web service, etc) 
    -(NSArray *)foos; 
    -(NSArray *)bars; 
@end 

認證狀態可以是一個簡單的布爾或整數,如果你想跟蹤許多國家(認證,未經認證,試圖與存儲的證書認證未認證後)。 您現在可以觀察到authenticationState屬性,以允許您的控制器層對身份驗證狀態的更改採取操作。

請求時從Web服務數據,更改時,服務器拒絕請求的認證狀態由於憑據無效

-(NSArray *)foos 
{ 
    NSArray * foos = nil; 
    //Here you would make a web service request to populate the foos array from your web service. 
    //Here you would inspect the status code returned to capture authentication errors 
    //I make my web services return status 403 unauthorized when credentials are invalid 
    int statusCode = 403; 
    if (statusCode == 403) 
    { 
     self.authenticationState = 0;//Unauthorized 
    } 
    return foos; 
} 

該控制器是應用程序委託。它存儲我們的DataContext的實例。它會觀察對該authenticated屬性的更改,並在適當時顯示視圖或重新嘗試身份驗證。

- (void)observeAuthenticatedState:(NSNotification *)notification 
{ 
    DataContext * context = [notification object]; 
    if (context.authenticatedState == 0)//You should have constants for state values if using NSIntegers. Assume 0 = unauthenticated. 
    { 
     [self.context login]; 
    } 
    if (context.authenticatedState == -1)//You should have constants for state values if using NSIntegers. Assume -1 = unauthenticated after attempting authentication with stored credentials 
    { 
     UIViewController * loginController = nil;//Instantiate or use existing view controller to display username/password to user. 
     [[[self window] rootViewController] presentViewController:loginController 
                 animated:YES 
                 completion:nil]; 
    } 
    if (context.authenticatedState == 1)//authenticated. 
    { 
     [[[self window] rootViewController] dismissViewControllerAnimated:YES 
                   completion:nil]; 
    } 
} 

在你的故事板,你基本上可以假裝認證不存在,因爲應用程序委託中插言的用戶界面認證每當數據上下文通信需要它的。

+0

問題是我沒有經過身份驗證的狀態。例如,用戶憑證可能會在服務器上過期,並可能拒絕請求。然後,我需要使用密碼刷新服務器上的用戶會話,並重試失敗的請求。我沒有看到通知可以解決這個問題,而不是強制意大利式肉丸類代碼。 – sanmai

+0

這不是意大利麪代碼。 DataContext封裝與您的身份驗證數據源進行通信。當服務器拒絕請求時(記住,你的DataContext是唯一的請求),你設置'authenticatedState'爲未認證的。添加了要演示的請求代碼。 –

+0

你的代碼不是,但如果我向呼叫控制器添加反向通知,我的代碼將很可能變成肉丸。非常感謝您的意見。 – sanmai

1

聽起來對我來說,這裏的一個重要要求是您有幾個控制器可能需要提供相同的模態對話框。對我來說,這聽起來像一個委託模式將工作得很好。這裏的想法是保持每個控制器可以根據需要使用的一組模式對話框處理功能。它也是UIKit內部用於UITableViews和日期選取器等模式的模式。 https://developer.apple.com/library/ios/documentation/general/conceptual/CocoaEncyclopedia/DelegatesandDataSources/DelegatesandDataSources.html提供了一個概述。

+0

代表團很好,但如果希望所有東西都是通用的,他將不得不維持現場所有人之間的代表團關係。模型會將應用程序委託作爲其代表。應用程序委託將他作爲登錄視圖控制器的代表。最後還會保留數據模型作爲他的代表......我想知道是否有更簡潔的方式來做到這一點。 – sanmai