Who should be responsible for displaying a modal dialog with a journal form?

In my application, I create a data model and embed it in the root view controller. I got it from the storyboard when I asked for user credentials, if necessary, from the very beginning . At some point later, when accessing some methods of the data model, I will need to check the user password and repeat the request that caused the password to be verified again.

The most obvious is the creation of this functionality in every view controller, which may need to request this information, but I want to avoid this as much as possible because it makes the controllers less universal and also makes testing more difficult. In my opinion, controllers should not know anything about the internal workings of the model that they are given.

Adding this functionality to the model is also not suitable for me: user interaction management is completely dependent on the model of the model in MVC.

Who should be responsible for displaying a modal dialog with the appropriate view controller so that the user can enter their credentials?

+6
source share
3 answers

This can be done using very few lines of code through callbacks. The callback API will be defined at the model level (therefore, it can be reused), but user interaction is carried out at the controller level (because where it belongs).

I'm not quite sure what your architecture looks like based on your description, I believe that the application understands that you are not authenticated only on a failed request (you may want to save the expiration date of the token and use it if possible).

Main idea:

In your model, you have a callback block property (for example, on the client class or any other template that you use).

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

You execute this block at the model level when your request fails due to the user not being authenticated.

At the controller level, you have a controller that asks the user for credentials (and has a similar callback pattern).

 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 }; 

The logic is in the right place, and if you want to handle unauthenticated requests differently in another case, you can.

+3
source

It seems to me that one of the big requirements is that you have several controllers that may need to present the same modal dialog. It seems to me that the delegate pattern will work well. The idea here is to maintain a single set of modal dialog processing functions that each controller can use if necessary. It is also the same template that is used in UIKit internals for things like UITableViews and date pickers. https://developer.apple.com/library/ios/documentation/general/conceptual/CocoaEncyclopedia/DelegatesandDataSources/DelegatesandDataSources.html offers an overview.

+1
source

You're right. The construction of this function in view controllers is not necessary and poor encapsulation.

In the MVC paradigm, models often have a data context. Data contexts control communication with the backend repository (in iOS, it is usually a web service or a local file) to populate and archive model objects. For an authenticated Data Context, you have a property for username, password, and authentication status.

 @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 

The authenticated state can be simple logical or integer if you want to monitor many states (authenticated, not authenticated, not authenticated after trying to authenticate with the credentials stored). Now you can observe the authenticationState property so that your controller level can take action when the authentication state changes.

When requesting data from a web service, you change the authentication state when the server refuses the request due to invalid credentials

 -(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; } 

The controller is your application delegate. It stores an instance of our DataContext. It tracks changes to this authenticated property and displays a view or re-authentication when necessary.

 - (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]; } } 

In your storyboard, you can basically pretend that authentication does not exist, because your application delegate inserts a user interface for authentication whenever the data context communicates with it.

0
source

Source: https://habr.com/ru/post/956996/


All Articles