Not sure how to best explain this - If I use blocks to load images for UITableViewCells, how best can I ensure that when an image actually finishes loading - it is the correct image for the cell. Say I'm making an app with user icons - so cell 5 is for John and it starts loading John's image. Say that request gets hung up and by the time it returns, that cell has been reused for Bill at cell 23. We obviously don't want to load John's icon - we just want Bill's.
This is a simplified explanation. I know that we can check if a cell is visible in the block before setting the image - but say in a different scenario I have a method that makes a remote request when called. When the remote request is done, it calls a block defined in that method that updates the UI. If I call it and before it returns I call it again and only want the most recent call's block executed. Is there a good pattern for doing this?
Currently, I try to store a variable that has some state in the method so that when it returns I can check if that state is still valid. Just thought I'd see if there was a better way. Thanks.
Just checking if any updates on this.
Related
There is no done handler in removePendingNotificationRequests huh? How are you supposed to know when a request has been removed?
Suppose you have a table view or something showing these requests. And you remove one - you need to remove that cell row to reflect that. But as removePendingNotificationRequests is async, according to the docs, it sounds like it would be incorrect to assume that requests would always be removed instantly.
I have an API code, which loads a data necessary for my application.
It's as simple as:
- (void) getDataForKey:(NSString*) key onSuccess:(id (^)())completionBlock
I cache data returned from server, so next calls of that functions should not do network request, until there is some data missing for given key, then I need to load it again from server side.
Everything was okey as long as I had one request per screen, but right now I have a case where I need to do that for every cell on one screen.
Problem is my caching doesn't work because before the response comes in from the first one, 5-6 more are created at the same time.
What could be a solution here to not create multiple network request and make other calls waiting for the first one ?
You can try to make a RequestManager class. Use dictionary to cache the requesting request.
If the next request is the same type as first one, don't make a new request but return the first one. If you choose this solution, you need to manager a completionBlock list then you will be able to send result to all requesters.
If the next request is the same type as first one, waiting in another thread until the first one done. Then make a new request, you API will read cache automatically. Your must make sure your codes are thread-safe.
Or you can use operation queues to do this. Some documents:
Apple: Operation Queues
Soheil Azarpour: How To Use NSOperations and NSOperationQueues
May be there will be so many time consuming solutions for this. I have a trick. Create a BOOL in AppDelegate, its default is FALSE. When you receive first response, then set it TRUE. So when you go to other screen and before making request just check value of your BOOL variable in if condition. If its TRUE means response received so go for it otherwise in else don't do anything.
I'm currently going through Bloc bootcamp for ios development and I'm looking for a solution that will reload "new images" upon launch. Essentially it needs to automatically refresh and fetch new images without the user having to pull to refresh. We are building out a replica of instagram if that helps.
It sounds like you've already got the method created to accomplish your task, so it's just a question of calling that method.
I don't know what your code looks like, so I'll attempt a "physics for poets" example:
- (void)pullToRefreshMethod {
// there might be code for configuring the view, etc. in here
// your code to refresh
}
The simplest way to do it when it loads is in your viewDidLoad on the initial ViewController. Let's say your pullToRefreshMethod does other things in addition to refreshing. What you could do is take the code that refreshes and put it in its own method.
- (void)refreshInstagram {
// your code to refresh
}
Then, within your pullToRefreshMethod, you'd call it with:
[self refreshInstagram];
and in your viewDidLoad, you could call it there, too.
Without specific code, I can't give you a specific answer, though.
Similar to the iPhone Facebook app search function, I am implementing search as you type functionality into my application although I have a problem when decoding the data into JSON format.
Basically what happens is because some searches take longer than others, they return at different intervals and this causes some small visual issues when the data is presenting on the screen.
I have set an NSLOG after each decode using NSJSONSerialization for the keyword 'industry'
2013-04-09 23:38:18.941 Project Name [42836:1d03] http://fooWebAddress/json/?method=search&limit=10&q=indus
2013-04-09 23:38:19.776 Project Name [42836:3e07] http://fooWebAddress/json/?method=search&limit=10&q=indu
2013-04-09 23:38:20.352 Project Name [42836:8803] http://fooWebAddress/json/?method=search&limit=10&q=indust
2013-04-09 23:38:21.814 Project Name [42836:4e03] http://fooWebAddress/json/?method=search&limit=10&q=industr
2013-04-09 23:38:23.434 Project Name [42836:8803] http://fooWebAddress/json/?method=search&limit=10&q=ind
2013-04-09 23:38:24.070 Project Name [42836:7503] http://fooWebAddress/json/?method=search&limit=10&q=industry
As you can see it is all out of order.
Does anyone have any way of stopping NSJSONSerialization for the previous connection.
Or possibly any other way to go about this problem?
Steps up to NSJSONSerialization...
NSURLRequest (initwithURL)
NSOperationQueue
NSURLConnection (asynchronous)
NSJSONSerialization
Thanks in advance.
When the user starts typing more text, you could cancel your previous connections and ignore any further delegate callbacks you receive from them. Then make the new request for the current text.
You can do this by maintaining some sort of lastRequest or lastOperation reference. When the user starts typing, call [self.lastRequestOrOperation cancel] and ignore any further notifications from that request with a check like if (request != self.lastRequest) { return; } in whatever callbacks you have.
However this has the problem that if the user keeps typing for a while you are constantly cancelling requests and they may not see any results until they have stopped typing.
A better solution would be to add sequencing so that each request is associated with an increasing sequence ID. You then only parse the result and update the UI when the sequence of the response is higher than the last one you received. If you receive any out-of-band responses from earlier, you just ignore them.
This is a much more complex issue than just being able to cancel the NSJSONSerialization. My suggestion is to use NSFetchedResultsController to populate your table view that shows the search results. Use the search term as one of the predicate variable in the NSFetchRequest attached to NSFetchedResultsController. And then, when you parse the results using NSJSONSerialization, store the results with the search term associated with that request. As soon as the search term changed (which you can detect when the user types more characters), re-create the NSFetchedResultsController and reload your table view. In addition, you can also try to cancel the call to parse the previous results if you launched it using performSelector:withObject:afterDelay. Beware that this cannot be always relied upon as the call may have been initiated by the time you are trying to cancel.
Kinda basic, but you could always maintain an nsdictionary of sub-classed NSURLRequests (sub-classed to provide a tag).
Start request - add request to dicationary with tag = array.count - 1, with key matching tag
Connection returns - is the request the most recent request, if so, parse json
Parse JSON - is the request the most recent request, if so, show results, if not, only display if there are no previous results displayed
Request handling - remove key from dictionary
most recent request = does the dictionary contain an object with a higher key value
Currently what you are doing is, you type each character and calling web-service. Why to call web-service for each letter you type. If user is type continuously, then it will increase the load, so call the web-service only when user stops for a particular interval of time. and then pass that string to call web-service or what ever method you are calling.
[NSObject cancelPerformSelectorsWithTarget:self]; // This will cancel your all req which is going to make when user typing without stopping
[self performSelector:#selector(sendSearchRequest) withObject:searchText afterDelay:0.1f]; // This will pass the string to call a web-service method, on which user hold for some time.
Following up with the post: Can AFNetworking return data synchronously (inside a block)?
One of the comments in that post was:
The trick to asynchronous programming is to break the procedural,
synchronous assumption that data is there when you ask for it.
Instead, with async, when you ask for something, you give it a
callback to perform when the data finally is ready. In this case, you
would call the block in the success block of the JSON operation.
Rather than the method returning data, it's told what to do when the
data is finished downloaded.
Although I'm using GCD and asynchronous downloading on iOS, I do not really understand how to implement this "procedural break" when programming with async.
For example, assume I need to download some JSON data, which includes lots of data including an image URL. I will have to download the actual image afterwards.
Each cell in the table takes in the data from the JSON/images downloaded. How would I implement this procedural break in this case?
While your data has not arrived, your table view dataSource tells its table view that it has zero rows, and displays a spinner. When the callback is fired, you store the data somewhere, remove the spinner, and call [tableView reloadData]. Basically, that's all there is to it.