This is part two of the GGT Tutorial. Due to a heavy and frustrating part 1 and a need to ensure that material gets covered, there is no Git repo for this section. Please complete part 1 before moving to part 2.
Here, we will examine a few problems in the existing code and modify it as needed. Again, you won't receive explicit directions on the steps to take, instead all of the tools and steps needed will be outlined here– you must synthesize the solution yourself.
The app has a problem.
Open the Xcode project and run it a few times. Each time you run it, pay attention to the console log, and only run it again once you see the API call log a success.
Notice anything? The same grant is duplicated over and over as the API calls come in. As calls complete, the grant is cached, and then cached grants are loaded back into memory upon view controller loads.
We have to check to make sure the grant does not already exist in the array before adding it, and then clear the simulator's memory to get rid of all of the already-duplicated grants.
The NSUserDefaults variable will still be stored on the simulator between launches, however, so even if we start checking for duplicates before adding now the old duplicates will still be present. In order to clear this data structure, you must reset the simulator.
To check if your fix worked, run the simulator a few times again (making sure the connection logs a success to the console before re-running) and ensure only one grant appears.
Currently the download method is hard-coded to download one file over and over. In order to get all of the spreadsheets stored there, we have to add another API call type, the “mod” call, to fetch a list of them.
Check the contents of urlString within the download method, making sure that you understand what each argument within the URL represents. Specifically, examine the type and fname variables passed along with the URL.
stringWithFormat is NSString's method of building strings. This is very similar to C strings– when the method sees a %@ in the literal string portion of the argument, it checks for NSObjects, seperated by commas, after the string and subtitutes them in. In this case, type, “samplefile.enc” and key are paseed in to create the URL. When the PHP server checks the keys passed in, it looks for the type=[string] portion to determine what method it should execute, in this case “download.”
If you look closely at the other argument you will see that the download type call requires the name of the file requested with the key fname=[name]. There are other files in the directory with the server– the code right now is “hardcoded” to only download “samplefile.enc.”
The other API method that interests us is called mod. It returns all of the excel spreadsheets in the directory as a dictionary, each a key to a time value that represents the last time the file was modified. The purpose of this is simple– only update grants that have a later modified time than the last time the app downloaded them.
For the purposes of this tutorial we will not consider this time-sensitive downloading. Your task is as follows:
You could make a second method and copy over the code from downloadGrants. The shorter, cleaner approach would be to edit the method, adding parameters to change the type of the API call, branching conditionally based on the type before the call and the type after the call (i.e. you'll have to check what kind of API call you will issue AND what kind was returned in the completion block, see below for details). You should use an NSMutableArray as an instance variable to store the names, using objectAtIndex:0 and removeObjectAtIndex:0 to retrieve and dequeue objects. You'll have to recursively call downloadGrants at the end of the completion block (unless you're terribly clever.)
The implementation details are left to you. Creating another method should run you about 20 lines total changes (most of which is just a copy/paste); modifying downloadGrants will require about 6 lines of code in total changes.
How can you tell which kind of API call returns? You'll run into this problem if you go the one-function route detailed above; in this situation you will only have one place in your code where you call NSURLSession, and thus one completion block that has to deal with potentially two different calls returning.
The useful key in question is type. Although not strictly required, it is good form as a backend developer to include a lot of extra detail within your API responses. You can check this field by setting a debug point after the JSON is converted once you've created and sent the mod api call. You'll need to branch based on this value if you go the one-method route.
The importance and power of StackOverflow and Google as far as learning a new language is concerned cannot be overstated.
On the next and final lecture we will put the “Graphic” in Graphic Grant Tracker– graphs, animations, and more.