How to setup asynchronous image loading using VIPER artechiture? - viper-architecture

The nature of the app is a UITableView that displays UIImages on every row as the user scrolls down the tableView. How should i load the image into the cell without breaking VIPER architecture?
--VC code--
func cellForRowAtIndexPath...... {
var cell = dequeueCellWithIdentifier.....
//Method 1
cell.image = outputInteracter.loadImage("someHttpImageLink")
//Method 2. - using a completion block
outputInteracter.loadImage(anImage -> {
cell.image = anImage
}
}
Both methods above seem to break VIP, since the presenter is not involved and no structures are passed between boundaries of V-I-P
How would you suggest getting the UIImages of the TableView to make asynchronous calls to get the images downloaded (without blocking main thread) using VIP? I can make the call pattern be like this, but I am confused on step #4:
VC --> interacterOutput.loadImage("someLink")
Interacter --> does the work to get the image, and send UIimage to presenter
Presenter --> UIImage is already in correct format for VC, so pass the information back to VC
VC --> How should i best setup the code so that the cell knows that its image is ready? I would not want to reload the whole tableView
Any help would be greatly appreciated.

The scenario you described isn't so much VIPER. In VIPER, the presenter is in the middle between the view and interactor. Your description is more closely to Clean Swift with its unidirectional flow of control from V to I to P.
Assuming your question is for Clean Swift, you first need to be clear about what the business logic is. Without knowing the details of your app, I can only assume you first ask the interactor to fetch a list of objects in JSON. Each object has a link to an image somewhere which you then have to fetch separately.
If my assumptions are correct, fetching these individual images isn't the business logic. It is simply one part of the business logic. The business logic is to fetch the list of objects in the first place.
Once the list of objects is received in JSON, your presenter can assemble a view model object to represent these objects. The image URLs can be represented simply as String instead of UIImage.
If you wait to fetch all these images before you pass the view model object back to the view controller, your UI, although not technically being blocked, will have a long wait before the user sees content in the table view. Therefore, I would simply pass the image URLs to the view controller.
The view controller can then fetch the actual images all in itself without involving the interactor or the presenter. It can use something like SDWebImage to fetch and cache the images for performance reason.
Not everything has to go through the VIP cycle. The following may help.
Think of the business logic as fetching the list of objects. Once that's done, the business logic is fulfilled. The view controller can choose to display the image URL strings, not the actual images, to the user. That's certainly one representation, although not common. But your view controller chooses to display the images as UIImages. This displaying is only internal to the view controller. It isn't really your business logic.
Your view controller can fetch the images itself. It can even asks a custom subview to do the fetching.
A subquestion in your question is about not reloading the table view if you were to fetch the images individually going through the VIP cycle. I wouldn't recommend doing this. But if your list of objects is actually just a list of images, as in a photo album collection view, this may be necessary. I would embed the image URL, the row/column data in a request object to the interactor. When an image is returned asynchronously, I would already know which row and column this image is to be displayed at. Just pass that along to the presenter then view controller. The view controller can then just reload a single cell instead of the whole table or collection view.
I hope this helps clarify VIPER, Clean Swift, VIP cycle, and what business logic is.

Related

Two views displaying live data at the same time

I have two view controllers which should work like described below:
First view controller displays a view which has a table in it. The table contains data which is constantly changing. I have a delegate method for reloading the data when a change occurs. So that is taken care of. When the user selects a row in the table I would like to display a second view which would also contain live data in text format (one UITextView which would constantly change).
I would like to allow the user to access view 1 while view 2 would still be monitoring and displaying live data and vice versa. While user is on view 2, view 1 should still be monitoring and displaying any changes in the table content.
I guess its like having two view controllers present at the same time and switching between them.
What is the easiest or the most standard way to accomplish this? I dont mind if its all done programatically.
It's a little hard to tell what you're asking here. First, views shouldn't be processing data at all -- they should only be displaying it. It sounds like what you're describing is having a background process that updates to your second view. That process could certainly be running while your table is on screen, and when you switch to the second view, you can update it using this running process. Exactly how you would do this depends on the details of what you're trying to do. So, I think we could help you better if you provide more context on what kind of process you want going on that updates your second view, and how choosing a row in your table affects that process.
After Edit:
You can certainly do what you want to do. I think the main thing is that you need to have a property (typed strong) in your first view controller that points to your second view controller, so that when you go back to the second controller a second time, it goes back to the same instance. Based on the selection from the table, you can start whatever process you need to populate your text view, and that process can keep running even after you go back to the table view, since that controller won't get deallocated when it's view goes off screen (due to the strong reference you have). You would just have to have some sort of if clause in your second view controller to know whether the user has selected the same row again, and if so, just show the updated text view, rather than starting a new process.
That's about as specific as I can be without more detail from you.
what you have is a problem where you need a custom parentviewcontroller
look into splitviews as one example :: SplitView like Facebook app on iPhone
cool intros of how to do custom container view controllers :: Container View Controller Examples

Re-initialize view controller, or create separate view controllers for image capturing process?

I have created a custom camera overlay to capture an item, but my app requires that I go through the capturing process twice, so basically, once the user has taken the picture of the front of an item, they need to take the picture of the back of that same item, back to back. At the moment I am re-initializing the same view controller after the first picture has been taken, but I have read that it would be better practice to create a separate view controllers for each "step".
Which would be the better option, separate view controllers, or one view controller that gets re-used, as and when is needed?
I am quite new to Objective-C, so I am not sure if I have phrased everything correctly, I hope that I was able to adequately convey my problem.

Retain View Controller when popped from Navigation Controller using ARC

I have two table views and a detail view being managed by a UINavigationController. When a row is selected on the second table, it pops to a detailed view allowing changes specific data represented by the row. I need to be able to switch between the UITableView and the UIViewController without losing any of the data that might have been changed in the UIViewControllers (button selections, text values entered in fields, etc).
Basically, once the UIViewController is presented, one should be able to switch back and forth between the table and detail view without losing any data.
The problem is, when a UIViewController is popped from the navigation controller, that memory is automatically released, and since I'm using ARC, I can't just keep a pointer to that with a retain command in the UITableView.
I know that I could manually rebuild the view each time its corresponding table row is selected, but I feel that might be a messy solution. Another option I've thought of is keeping an array of pushed UIViewControllers and checking if the selected table row corresponds with an existing value before a new one is created, but that might also get messy.
Any suggestions?
Your final wish in this back and forth of view is what you stated in your question: you want to switch between view controllers without losing any information of the actions performed in these controllers.
But if you think at how MVC pattern works, you should consider a view controller as the glue logic between the view and the model. The view is not persistent, that's why it is legitimate for ARC to get rid of the owning view controller when the view is no more needed. Instead what you should persist while your app is working is the model data only: the model data will be shared between the involved view controllers, the view controllers will be recreated each time and the corresponding views will be updated based on the model data. The only reason why the view controller should be kept alive is when its alloc-init-loadView takes too much (e.g.: the view is OpenGL backed) but in such case I would suggest you to keep a strong reference to it in the AppDelegate and ask it to refresh the content when the model data is replaced.
So basically what you should do is:
- select the table
- extract the model data associated to the table, including all information relevant for the view controller
- push the view controller; save all view modifications to the model
- when the view controller is popped, the model data will be returned only
- next time, when you push the view controller again, you will restore the model and re-init the view controller.
This approach is not complicated and gives you the possibility to structure the app in a clean way. Tomorrow you can change your view controller structure (that its view and the logic) without any impact in the communication with the other view controllers as this managed by the model passing only.
There are a couple of solutions to this, just like you suggested.
The array solution is highly inefficient because of memory issues.
The second solution you proposed is a lot more elegant. Just write your own init method in that view controller and init the view controller with data from a plist file
If I'm reading the question correctly, you've got a tableView and a detailView that are driven by the same model data. When changes to the model data are made in the detail view, you want those changes to persist.
If you update the model based on the state of the controls when the detail view is popped, then those changes will persist and the changes will be visible the next time you drill back down into the detail view.
You don't mention what form the table data takes, but let's assume it's an NSArray of NSMutableDictionaries. When you tap the row, the didSelectRowAtIndexPath: method will need to hand the dictionary from that array index to the detail view controller through a property on the detail controller. The detail view controller will update the dictionary values in the method that dismisses it.
The way to think of this is using the model-view-controller pattern. The table and detail view data is stored in the model; the views present the data; and the controllers are responsible for updating the model and navigating between views.

UIPageViewController optimization and possible solution

I am using a UIPageViewController in my app to display number of images (1024x768 size). Is it ok to make array of UIViewControllers each with his picture and in set appropriate for each page? Maybe if you get like 50 pictures it will crash? At the moment I store images in Documents folder so I can remove images from view controllers that are not on screen
This wouldn't be very efficient, you'd be much better off doing the same thing with UIImageViews. Even then you have to be careful. Probably the best way to handle this would be to using image views and deallocating them when they go more than one image width out of the screens bounds. Then of course reallocing when the image in question is the next image in line to be displayed in either direction.
EDIT: It looks like you can use the following UIPageViewController Datasource methods to set which view controller do basically have queued and waiting to the right/left. Using these you should be able to only allocate 3 view controllers at a time.
– pageViewController:viewControllerBeforeViewController:
– pageViewController:viewControllerAfterViewController:
Then you can use this to set the initial controller:
Set the initial view controller using UIPageViewController's
-setViewControllers:direction:animated:completion:
The sentence below is quoted from Apple's documentation and is leading me to believe that the controller may be able to only load the necessary view controllers into memory on its own.
View controllers are either provided one at a time (or two at a time,
depending upon the spine position and double-sided state) via the
setViewControllers:direction:animated:completion: method, or provided
as-needed by the data source. Gesture-based navigation is enabled only
when a data source is provided.

KVO and MVC Question

Simple question here... Is it alright in terms of MVC design to observe a value in the model from a view (using Key-Value observing), and update the view's current location on the screen based on when the model's variable changes?
Is it okay to observe from the view object and have that object move itself when the Location variable inside of the model object changes?
Or is this against MVC because the view and the model are communicating in a sense?
You should tie it through a C, controller, item. Even if it means you're pulling in the state data from the model, and then having the controller set the view or the view read that data from the Controller.
The view and model should always be separated by a controller. That's MVC according to Apple. The reason is that even right now it may be straightforward for you to have the view reference the model's state - but the model could change in the future, and then you'd be stuck updating the view, when there's really no reason the view should be impacted. And the model should never update the view's position - it should really not have any idea of any details of display. That's the job of a controller, to control your views and move them around based on model data.
Think about it this way: the view should only know how to display things or interact with user I/O, a model should only know about the business logic with data that comes in over an interface of inputs and outputs. You should be able to run a model without a view even existing, instead you should be able to just have unit test type code that feeds those inputs and outputs. So something like moving a view around is completely out of the responsibilities of a model.