PDA

View Full Version : [Resolved] Help needed in solving this mess.




troop231
Feb 6, 2012, 09:36 PM
Hello, I need some input on possible ways I can solve a huge issue in one of my current apps.

Basically, my app (ORR&S) on the app store ships with about ~700MB of PDFs stored in the mainbundle. I've had a few people get annoyed with having to redownload the app update only when I update a state or two's regulations. I agree with them, it is annoying, and I therefore want to put a significant update into my app that will allow me to basically never have to submit an app update again.

Here's what I want to do: 1.) Move all mainbundle "shipped" PDFs into a Documents folder which will tie in with point #2.) Connect to a website's folder and view the PDFs I put in there as updates. 3.) Before displaying the updates, check the modified date of the PDF files on the server against the "shipped" PDFs, then display only the ones that need updated each in a table cell and alloc an Update All button; if no new PDFs are found, display a UIAlert "No updates found" 4.) If there are updates, and the user clicks the Update All button, then begin download (with a progress indicator) and overwrite the "shipped" PDFs with the newer ones.

Sorry for the long post, I'm sure all of this is doable, I just need some help/input on where to get started.

Thank you in advance :)



jnoxx
Feb 7, 2012, 02:45 AM
Create a webservice which sends out a JSON call which holds objects like this
PDF with int, timestamp updated, and some others if u want to, like size, name, etc.
Then it's your time to code like, make a grid view with all the PDF's, check the timestamp of the timestamp of last updated vs the one you have (download them over NSObjects which u created yourself, i guess you can do that).
If timestamp of last updated differs, then allow them to click download or something.
I think that should be a small way to send you.

xStep
Feb 7, 2012, 04:01 AM
Some random thoughts...

Looks like you may want to look at "URL Loading System Programming Guide (https://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/URLLoadingSystem/URLLoadingSystem.html%23//apple_ref/doc/uid/10000165i)". NSURLConnection looks interesting. I can't say I know much about this.

Unless there is an "Update this file" option, I don't see a need for an "Update All" button. Just initiate the update in the background one at a time. You could place one of those animated circular timing icons somewhere obvious to indicate a background process is occurring.

If there are no updates, don't disturb the user with an alert. Don't give any feedback in this case, or perhaps have a status line for such minor information. It could also post that the user has all updates.

If the user wants to review a PDF that is known to have an update, then give them the option to immediately update that file.

You may think timestamps are a way to indicate a newer file, but I wonder if something simpler, like an integer value might be a better choice. Simply put, if the local PDF integer doesn't match the server value, then fetch the PDF from the server.

Oh, I'd still ship the updates with the current PDFs. That way new users wouldn't have to initiate a "Update All" for the 700MB content. As updates occur, you'd ignore the older version of the file in app bundle and use the newer one in the document folder. If you don't update your app frequently for other reasons, this shouldn't annoy current users.

BTW, I couldn't find your app on the app store. What's the link?

troop231
Feb 7, 2012, 08:55 AM
Create a webservice which sends out a JSON call which holds objects like this
PDF with int, timestamp updated, and some others if u want to, like size, name, etc.
Then it's your time to code like, make a grid view with all the PDF's, check the timestamp of the timestamp of last updated vs the one you have (download them over NSObjects which u created yourself, i guess you can do that).
If timestamp of last updated differs, then allow them to click download or something.
I think that should be a small way to send you.

Is this a server side method or can it all be done from the app? Basically I just want to FTP new PDFs as I get them from each state, for example the folder would be like this: http://www.sitesnamehere.com/updates/A-Z.pdf And the app should read the contents of this folder, and compare what it finds with the modified date of the shipped PDF, then display the results in a table view.

I'm approaching the one year mark of getting into apps, but I'm not sure where to begin, are there any good guides on how to do something like this?

Some random thoughts...

Looks like you may want to look at "URL Loading System Programming Guide (https://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/URLLoadingSystem/URLLoadingSystem.html%23//apple_ref/doc/uid/10000165i)". NSURLConnection looks interesting. I can't say I know much about this.

Unless there is an "Update this file" option, I don't see a need for an "Update All" button. Just initiate the update in the background one at a time. You could place one of those animated circular timing icons somewhere obvious to indicate a background process is occurring.

If there are no updates, don't disturb the user with an alert. Don't give any feedback in this case, or perhaps have a status line for such minor information. It could also post that the user has all updates.

If the user wants to review a PDF that is known to have an update, then give them the option to immediately update that file.

You may think timestamps are a way to indicate a newer file, but I wonder if something simpler, like an integer value might be a better choice. Simply put, if the local PDF integer doesn't match the server value, then fetch the PDF from the server.

Oh, I'd still ship the updates with the current PDFs. That way new users wouldn't have to initiate a "Update All" for the 700MB content. As updates occur, you'd ignore the older version of the file in app bundle and use the newer one in the document folder. If you don't update your app frequently for other reasons, this shouldn't annoy current users.

BTW, I couldn't find your app on the app store. What's the link?

Check the first link in my signature, if you want I can PM you a promo code perhaps.

So shouldn't the first thing I should do is put all of the mainbundle PDFs into a documents folder? How would I do this? (I want to be able to overwrite the shipped PDFs, so they need to be in a documents folder)

Here is the current code that I use to read PDFs:


- (void)viewDidLoad {
[super viewDidLoad];
[webView addSubview: activityIndicator];
self.title = @"Florida Saltwater";

NSString *urlAddress = [[NSBundle mainBundle] pathForResource:@"FloridaSalt" ofType:@"pdf"];

NSURL *url = [NSURL fileURLWithPath:urlAddress]; NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
[webView loadRequest:requestObj];

Edit: It appears that you can't have a documents directory when submitting your app, but it has to be created by the user? I don't think there's anyway I can do what I want then :(

jnoxx
Feb 8, 2012, 02:31 AM
hmm, it will be more then that to be honest..
You will need a webservice who will send out an API which your App can listen to, for example, on your iPhone, in a gridview or whatever, you want to fill that up right, so you want to do a check, so at your iPhone app, you do a call "http://yourCall.com/GIEFINFOZONFOLDER?=az"
i don't know, just messing around, it will give u back a JSON/XML. compare those JSON/XML versus the objects u already have. on the device itself, a compare method from object vs object, and I think you're set, don't know if i'm explaining it right, but i'm sure some will understand, might care to explain better :p

troop231
Feb 11, 2012, 07:19 PM
I guess the question I need to ask is, is it better to ship the app with no out of the box functionality at all? requiring the users to download the state pdfs they want ahead of time (This would allow the app to be purchased on 3G <20MB rule too), Or just leave it as is, shipping it with ~700MB of pdfs that are readily available even if you don't have access to an internet connection. (Downside to this is that the app can only be purchased on WiFi, and updates take about a week to get into the app store.) (Advantage is not every hunting and fishing spot has cellular service or good enough service, plus data caps for users nowdays.)

What are your opinions? Thanks again! :cool:

xStep
Feb 12, 2012, 04:59 AM
After trying your app I came to the conclusion that you should only have the Knots page installed. That is a hefty download otherwise, and the time between uploading an update and the user downloading it could leave the user with old information.

Read Cleaning (http://www.marco.org/2011/10/13/ios5-caches-cleaning) regarding backups and Apple's change in opinion. There is an update at the regarding iOS 5.0.1. This could affect your design decisions for local device storage.

Here are some more thoughts. I don't have experience in this realm of service. ;)

I'd have to user select the state(s) of interest and download only those state documents. As part of that process, I'd first try downloading from the official state site if the URL is reliable. This would save you possible bandwidth charges. Upon failure of that URL, I'd then check a server that I have set up for the latest document copy that I'd have to manually or automatically keep up to date.

How to manage that could be tricky considering you are dealing with many states. On your part, create a database on your server that you keep up to date. A copy of that would be placed onto the iOS device. I'd automatically 'call home' to check if the local copy is out of date from the server copy, and if so, download the latest version. With the local information up to date, you could check other local information against that database to confirm the user has the latest document for the states they have. If not, then ask them if they want to update them all.

The database might have a single table with the following fields in it. Currently I'm not thinking it as to be fancy.

State
Document Type (fishing, hunting, etc)
Document Title
Document Filename
Document File Size
Document Date
Document Version
Document ISBN??
Document Identifier Code
State URL
App Server URL
App Server URL Timestamp
Record Update Timestamp


So how would this work?

Well, on the server side,you'd prime the database and the downloaded documents.

When a user request comes in for the database, part of the request would be a timestamp of the last update. That timestamp would have originated from the server and stored on the device with the database copy.

If an update is needed, you'd download the database and timestamp to the device, and compare it the current database on the device. The database on the device would only contain records for documents download to it therefore allowing you to ignore states your customer wasn't interested in. For documents found to be newer on the server compared to the local copy, you would ask the user if they want to download the newer version.

For the download sequence, I'd first try to download from the state URL. Only if that failed, would I use the server URL. If that fails, I would't remove the local copy, but instead let the user know that the download could not be done at this time.

If your app gets very popular and the state URLs are not reliable or otherwise workable, you'll have to be concerned about bandwidth issues. That is something you don't have to worry about by doing the app update method you are doing right now because Apple is paying that price, for now.

troop231
Feb 12, 2012, 11:33 AM
snip

Thanks for downloading the app!

The only reason I won't connect to the state's url is for two reasons: One being the state servers are extremely slow and Two: the states don't post process the PDFs making them larger than necessary, when I download the PDFs, I run them through quartz filters in preview and the exported file is much smaller in size while still maintaining quality.

So it looks like I should mark the documents directory as do not backup for iCloud?

nehalvpatel
Feb 12, 2012, 12:31 PM
I suggest using php and mySQL to pump out a plist with the documents' links and info in it. I did that for my app, here's an example: http://painkilleralready.tv/podcasts/plist.php

All you need to do to grab that information is:

NSMutableArray *pdfArray = [[NSMutableArray alloc] initWithContentsOfURL:[NSURL URLWithString:@"http://yourdomain.com/pdf.php"]];

You can then use objectAtIndex to grab the current pdf, and objectForKey to grab a certain attribute, so you can load it in a tableview.

I'm not sure if this is exactly what you wanted, but here you go.

EDIT: I'm assuming you're talking about your Outdoor Rules app, and if that's the case, then you'll have to nest the pdfs under categories and states. Again, plist is the way to go. You don't have to bother with parsing, it does it for you.

xStep
Feb 12, 2012, 02:06 PM
So it looks like I should mark the documents directory as do not backup for iCloud?

I'm not sure as I haven't looked at it closely. The concern Marco had was the the new system would remove files from iDevice when memory was low under certain conditions. Perhaps only when using the cache and tmp areas. I just thought I should mention it as a concern to watch out for.

troop231
Feb 12, 2012, 02:32 PM
I'm not sure as I haven't looked at it closely. The concern Marco had was the the new system would remove files from iDevice when memory was low under certain conditions. Perhaps only when using the cache and tmp areas. I just thought I should mention it as a concern to watch out for.

Thank you for the information.

So hear me out, I'll need to implement these methods:

User taps on a state cell, we then check to see if the file has already been saved locally, if not, we download the PDF from the server (we ask first with a UIAlert due to data caps) with a UIprogressView. If we do have a local file already, we load the PDF as normally in the current version that's on the app store.

Next, we add a check for update button on each state's view toolbar, we then connect to the server, check the file with the same name as the local one, and compare the two dates of the file, if the server contains the newer file, then lets download it and overwrite the existing file (first prompt with UIAlert saying there is a update available, would you like to download?) with a UIprogressView.

How does this sound in comparison to the version that's already on the app store?

xStep
Feb 12, 2012, 07:09 PM
In Apple's Clock app, in the World and Alarm screens, they have an Edit and "+" button on the top in the navigation bar. You might want to copy that idea or do something similar.

When the user chooses the "+" button, you accept that they don't need to be queried again for permission to download the request. This 'add' button would bring up a list of all the states. I mention that, because there isn't a need to display all states for files already downloaded.

I'm not sure why you mentioned data caps. I suppose if there is a programatic way to tell a user is going to be using their cell service for download, it is worth asking. The thing is, if they have initiated a download, shouldn't they already know this? I'm not sure of the correct answer, or practice here. :confused:

I'd do each download in a background thread sequentially. That way the user can continue to add items to the list.

As for the update request, either an "Check for Updates" button would work, or simply do a check in a background thread when the user starts up your app. If you keep the information limited, the bandwidth would be very small. You could limit this to occur not more than once a day also. I like the second option.

As I was here typing all of this I was thinking about your list of states. If want to keep them all listed even when the user has no document for a state, you could use the dimming technique to indicate that a state does not have data.

Layout your possible workflows on paper and try to understand which ones are more elegant than the others.


Some constructive criticism and suggestions...

I doubt all users want both the fishing and hunting regulations, so let them download only what they are interest in. That will help reduce your bandwidth needs & costs, and their wait time. I noticed Alaska has several documents for Hunting but I don't know if you want to insist they choose each of those. Perhaps a preselected check list for those would do.

Your 'App Feedback' has no good reason to go into another screen, so don't do that. In fact, I don't know why the front screen needs to be using a table view. The front screen is a short menu, so it might be nice to place semi-transparent buttons on it with a dimmed wild life background image that you have taken or get the right to. Oh, and if your purpose for 'App Feedback' is to encourage users to write a review, then change the title to 'Review App' or 'Review ORR&S' and send them to the page App Store page that has 'Write Review' on the top of it. I use this code for that. Replace AppID with your apps AppID.

- (IBAction) appReviewPage:(id)sender;
{
NSString * urlString = [NSString stringWithString: @"itms-apps://ax.itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=AppID"];

[[UIApplication sharedApplication] openURL:[NSURL URLWithString: urlString]];
}

You can remove or abbreviate the wording on your return arrows. For instance, instead of the one that says "Outdoor Reg...", you might consider using ORR&S as the text. Instead of "Hunting Regulations", perhaps just use "Hunting Reg." This will also give you more room the main title text.

Wow, I was on a run there. :D

troop231
Feb 12, 2012, 07:43 PM
snip

Wow, I was on a run there. :D

Thanks for all of the tips and info! I really have been wanting to get rid of the main screen table view and replace it with nice icons. The app currently is so much cleaner and better than version 1.0. It initially was created using RedFoundry, which is a joke and ran horribly slow. I then bought some iOS programming books and began to try my hand at real coding. Ever since, it's been minor upgrades that were possible based on what I had as a backbone.

I think if you saw the way this app is setup and coded, you'll see why all of these mods will be hard to implement. Basically, I have a .plist containing a title and a view number for each state and its children. Here's the shocker: I have over 160 cases in the rootviewcontroller; when the respective case happens for the row in the plist, it alloc inits (another shocker here) a respective main file for each state, which in turn contains the correct URL for the webview or pdfview to load.

I found no other way to do it from the rootviewcontroller, so I created a class for EACH state and for some states multiple classes, and the only difference in this classes is JUST the url for each state! Honestly it seems like a pain and overkill to create a class and header for every PDF, and it was, but I could find no other way to do it at the time.

If you want, I can send my source without all of the PDFs so you can see how I did everything, just let me know. The app still supports iOS 3.0 also. :)

xStep
Feb 13, 2012, 06:40 AM
We need to know if you understand the difference between a class and an object. I wonder because you mention that you 'created a class for EACH state'. There should only be one class for states, perhaps named State. You instantiate an object for each state that you have.

Each state has one or more documents. For each document there should be an object instantiated from a Document class. Those might go into an array in the state object.

Your outer most class seems to be a parent regulation type, which currently has two types, 'Fishing' and 'Hunting'. Those contain states.

I'm basing the above on your app layout. It could be something different and for that we'd need to see the plist layout. Just enough to understand it.

I've come up with my own plist. It is a dictionary of documents that are dictionaries that contain key-value pairs for the different fields. This looks simpler than an alternative I came up with based on your app layout. I like this data structure because it encapsulates the information by document. From there you can easily create other dictionaries and lists as needed.

Here is what my plist looks like. Remember that this is only a hack. Notice that DOCID00005 has an empty LocalFileURL string, indicating that there isn't a local copy.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Documents</key>
<dict>
<key>DOCID00001</key>
<dict>
<key>ParentType</key>
<string>Hunting</string>
<key>SubType</key>
<string>Big Game</string>
<key>State</key>
<string>Alaska</string>
<key>LocalFileURL</key>
<string>file://path/filename</string>
<key>ORRS_Server_URL</key>
<string>http://orrs_server/path/filename</string>
<key>StateURL</key>
<string>http://state.gov/path/filename</string>
<key>LocalDocDateTimeStamp</key>
<date>2012-02-13T09:39:33Z</date>
<key>ServerDocDateTimeStamp</key>
<date>2012-02-13T09:39:30Z</date>
<key>Title</key>
<string>2011-2012 Alaska Hunting Regulations</string>
</dict>
<key>DOCID00002</key>
<dict>
<key>ParentType</key>
<string>Hunting</string>
<key>SubType</key>
<string>Small Game</string>
<key>State</key>
<string>Alaska</string>
<key>LocalFileURL</key>
<string>file://path/filename</string>
<key>ORRS_Server_URL</key>
<string>http://orrs_server/path/filename</string>
<key>StateURL</key>
<string>http://state.gov/path/filename</string>
<key>LocalDocDateTimeStamp</key>
<date>2012-02-13T09:39:33Z</date>
<key>ServerDocDateTimeStamp</key>
<date>2012-02-13T09:39:30Z</date>
<key>Title</key>
<string>Fur Animals, Small Game, Unclassified Game and Deleterious Exotic Wildlife</string>
</dict>
<key>DOCID00005</key>
<dict>
<key>ParentType</key>
<string>Fishing</string>
<key>SubType</key>
<string>Salt Water</string>
<key>State</key>
<string>Florida</string>
<key>LocalFileURL</key>
<string></string>
<key>ORRS_Server_URL</key>
<string>http://orrs_server/path/filename</string>
<key>StateURL</key>
<string>http://state.gov/path/filename</string>
<key>LocalDocDateTimeStamp</key>
<date>2012-02-13T09:39:33Z</date>
<key>ServerDocDateTimeStamp</key>
<date>2012-02-13T09:39:30Z</date>
<key>Title</key>
<string>Florida Saltwater Recreational 2012 Fishing Regulations</string>
</dict>
</dict>
</dict>
</plist>


I could use this to drill down from a top of say ParentType down to State, then SubType, and then display the document. The order isn't critical. Based on your current system, only one view controller would be needed for the listings. You'd use that for each drill downed list. In fact it might be useable for the very top, but I'd have to give that some thought.

Time for sleep.

troop231
Feb 13, 2012, 10:02 AM
<Snip>

Time for sleep.

Wow, you're sample .plist looks amazing compared to what I have. (Again, willing to provide .zip archive of source without all the PDF resources)

Here's a sample of the way my crappy .plist is structured (you will notice from here and my rootviewcontroller why I made a class for each state):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Rows</key>
<array>
<dict>
<key>Title</key>
<string>Fishing Regulations</string>
<key>Children</key>
<array>
<dict>
<key>Title</key>
<string>Arkansas</string>
<key>View</key>
<integer>5</integer>
</dict>
<dict>
<key>Title</key>
<string>California</string>
<key>Children</key>
<array>
<dict>
<key>Title</key>
<string>Freshwater</string>
<key>View</key>
<integer>6</integer>
</dict>
<dict>
<key>Title</key>
<string>Saltwater</string>
<key>View</key>
<integer>7</integer>
</dict>
</array>
</dict>
</array>
</dict>
</plist>

and here's only PART of my rootviewcontroller, but you will see how it works hopefully.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

//Get the dictionary of the selected data source.
NSDictionary *dictionary = [self.tableDataSource objectAtIndex:indexPath.row];

//Get the children of the present item.
NSArray *Children = [dictionary objectForKey:@"Children"];

if([Children count] == 0) {
NSInteger ViewNumber = [[dictionary objectForKey:@"View"] integerValue];
switch (ViewNumber) {
case 1: {
RootViewController *rvc = [[RootViewController alloc] initWithNibName:@"RootViewController" bundle:[NSBundle mainBundle]];
//Switch the view here
rvc.view = tbController.view;
[self.navigationController pushViewController:rvc animated:YES];
[rvc release];
}
break;
case 2: {
AlabamaFish *ivc2 = [[AlabamaFish alloc] initWithNibName:@"PDFView" bundle:[NSBundle mainBundle]];

[self.navigationController pushViewController:ivc2 animated:YES];
[ivc2 release];
}
break;

case 3: {
AlaskaFish *ivc3 = [[AlaskaFish alloc] initWithNibName:@"Web" bundle:[NSBundle mainBundle]];

[self.navigationController pushViewController:ivc3 animated:YES];
[ivc3 release];
}
break;

Thank you again.

xStep
Feb 14, 2012, 12:51 AM
Wow, you're sample .plist looks amazing compared to what I have.

The difference is I think you are trying to reflect your user view of the data in your plist. I've chosen another path because I know the data structure doesn't have to match the UI layout.

I've done a basic quick & dirty app to read the plist I created and drill down it until the user displays some content. In my case, the content is just the title I've placed in the plist for each document. Here is the directory URL (http://www.reely.com/darren/SampleCode/). The zipped file is named; DJR Recursive VC Sample Code.zip

The first thing I wanted to demonstrate is that you don't need many UIViewControllers for drilling down these lists, where as little as one will work in this case. (I also considered putting much of the logic into a subclass of UITableView.) It didn't sound like you were doing this.

I used one UIVIewController for displaying each list as you drill down. To keep track of where I am, I use two variables. If they are both nil, then I'm at the top. If the first is not nil, then I'm displaying states. If both are filled in, I'm displaying document titles, or a document if there was just one. It is a bit hacky, but works for the limited levels available.

Second, I wanted to demonstrate that a different plist layout could simplify coding. There maybe reasons for other formatting, but I like this layout for this type of app. The focus is the document which contains all the relevant information.

troop231
Feb 14, 2012, 10:46 AM
Second, I wanted to demonstrate that a different plist layout could simplify coding. There maybe reasons for other formatting, but I like this layout for this type of app. The focus is the document which contains all the relevant information.

Thank you again for everything, I was able to play around with it and load a URL to a webpage that was stored from a key I added in the plist "URL"

NSString *urlAddress = [doc objectForKey: @"URL"];
NSURL *url = [NSURL URLWithString:urlAddress];
NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
[webView loadRequest:requestObj];

I have a few questions, self.window.rootViewController is showing as only being introduced in iOS 4.0, is there a way to fix that to be compatible with iOS 3? Also, How would I set the rootView's navigation title (ORRS) without it being in the further drill downs.

One more question, If I change the order of the plist so that Fishing is before hunting on the table, it doesn't work, the rootView always shows hunting on the top for some reason, how can I fix this?

Thank you again :)

xStep
Feb 14, 2012, 03:40 PM
I have a few questions, self.window.rootViewController is showing as only being introduced in iOS 4.0, is there a way to fix that to be compatible with iOS 3? Also, How would I set the rootView's navigation title (ORRS) without it being in the further drill downs.

Two more questions, If i change the order of the plist so that Fishing is before hunting on the table, it doesn't work, the rootView always shows hunting on the top for some reason. Last question, how can I add a string titled Review App (last on the rootView table) and have it to where the user taps it it doesn't drill down, but launches the review page on the app store instead?

Thank you again :)

Looks like I ran into this problem in the past. Try changing self.window.rootViewController to
[self.window addSubview:[viewController view]];

For the titling, lookup the navigationItem in the UIViewController class. It is a UINavigationItem class with title properties. It is a one line fix within each of those if statements within the viewDidLoad method.

For adding the 'Review App' text, at the end of the viewDidLoad do another setValue: forKey: to the parents dictionary. Then in the didSelectRowAtIndexPath delegate method, you test for the value early and if you find it, go do what you want.

I have written up the changes in my source, but for now I'll leave it to you to figure out the details as an exercise. Hopefully I've explained it well enough to lead you on the right path.


Sorting is a sore point of this code. I'll have to look at it when I have time because I'm sure it will affect some logic in the current code. To get you started, look at the NSArray and NSDictionary class documentation, you'll see methods for returning sorted content. Right now I'm thinking using a sorted array of the keys would be a way to go. You could use that to get the values to populate the table, and to take actions based on selection index.

troop231
Feb 14, 2012, 09:36 PM
Sorting is a sore point of this code. I'll have to look at it when I have time because I'm sure it will affect some logic in the current code. To get you started, look at the NSArray and NSDictionary class documentation, you'll see methods for returning sorted content. Right now I'm thinking using a sorted array of the keys would be a way to go. You could use that to get the values to populate the table, and to take actions based on selection index.

Your effort is greatly appreciated, once again I thank you so much for everything; it gives me something to munch on and maybe focus on for a 2-3 month rollout of implementing and tidying up everything I want.

I was able to implement about 3 of the 5 or so features I've been wanting for awhile, and submitted an update to Apple today. The only things left that I want to implement are tackling PDF updates, and tidying up the plist/loading local PDFs and server URLs via one .h and .m files and delete the hundred or so .h and .m files I have for each state.

xStep
Feb 15, 2012, 06:40 AM
Your effort is greatly appreciated, once again I thank you so much for everything; it gives me something to munch on and maybe focus on for a 2-3 month rollout of implementing and tidying up everything I want.

You are welcome. I'm glad this is helping out. Yea, I figured this is a lot. It doesn't answer your original question, but simplifying up front should make that process easier too.


I was able to implement about 3 of the 5 or so features I've been wanting for awhile, and submitted an update to Apple today. The only things left that I want to implement are tackling PDF updates, and tidying up the plist/loading local PDFs and server URLs via one .h and .m files and delete the hundred or so .h and .m files I have for each state.

Were some of those 3 things related to this thread?

Hundreds of .h & .m files! Holy cow! Well then, my sample will save you tons of hassle.

I've added a version 2 project file to that directory. It handles most of the items I previously mentioned and the sorting which got a little weird when I realized I was using a real key in the final stage where a list of documents can be. I added a little logic to remove the disclosure indicated for the 'Review App' selection. I even added a goto in the new version. :D

The more I think about the server side, the more I think it should be database driven. That's a whole other bag of tricks and security headaches.

troop231
Feb 15, 2012, 09:28 AM
You are welcome. I'm glad this is helping out. Yea, I figured this is a lot. It doesn't answer your original question, but simplifying up front should make that process easier too.

Were some of those 3 things related to this thread?

Hundreds of .h & .m files! Holy cow! Well then, my sample will save you tons of hassle.

I've added a version 2 project file to that directory. It handles most of the items I previously mentioned and the sorting which got a little weird when I realized I was using a real key in the final stage where a list of documents can be. I added a little logic to remove the disclosure indicated for the 'Review App' selection. I even added a goto in the new version. :D

The more I think about the server side, the more I think it should be database driven. That's a whole other bag of tricks and security headaches.

I will look at Version 2 here soon. The things I wanted to do were pretty basic to most people probably, move network status activityindicator to the status bar, make the review app link on the table go directly to the app's review page, and I also coded in a uialert if you have no internet connection. It's a start at least lol.

Right now I'm having two issues: the first one is when I go to load a webpage, then touch the "back" button immediately before the webpage finishes loading, the network activity indicator in the status bar continues to move, I've tried to fix this, but it's not working. Another issue is that I can cause my app to crash replicating it in a certain way, [toolbarRefresh release]; is where the problem occurs, it's really confusing me. Let me see if I can get a screenshot of what Xcode says again.

Edit: I was able to replicate the crash easily by doing this: Tap on a state that connects to a website (Alaska), push back button immediately, retap that state again, push back again, etc. etc. (it seems like its going into overload or something lol) It didn't take but a few taps to cause it to crash and here's a screenshot:

http://i.imgur.com/2e81h.png

Am I not releasing some stuff in the right order or something not at all perhaps? (Maybe this is also why the activityindicator continues to move while pressing back, perhaps this would solve both issues.) Thanks for your help.

xStep
Feb 15, 2012, 02:50 PM
It's a start at least lol.

Forward steps in coding is always a good thing. :)



Right now I'm having two issues: ...

You might want to open a new thread on these ones since it isn't related to this one except in the app name. I think you haven't supplied enough detail either. For instance, when and where are you sending the stop code to the indicator? And what class is toolbarRefresh and is it subview to something you have already release?

I had a similar 'push back immediately' problem before. I found that I had to disable the push back button until the view controller had actually finished initialization. Specifically, the networking setup. I found this comment in my viewDidLoad method.
// We do this because if the user hits the back button before the networking and service browser is set up, we get a crash.
// See netServiceBrowser delegates netServiceBrowserWillSearch: and netServiceBrowser:didNotSearch: for where we turn the button back on.
[self.navigationItem setHidesBackButton: YES animated: NO];

troop231
Feb 15, 2012, 03:26 PM
For instance, when and where are you sending the stop code to the indicator? And what class is toolbarRefresh and is it subview to something you have already release?

Here is the indicator stop (and start) code (it's in three (void) places)



- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
NSLog(@"ERROR");
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
toolbarStop.hidden = YES;
toolbarRefresh.hidden = NO;

}

- (void)webViewDidFinishLoad:(UIWebView *)webView {
NSLog(@"FINISH LOAD");
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
toolbarStop.hidden = YES;
toolbarRefresh.hidden = NO;
[self actualizeButtons];
}

- (void)webViewDidStartLoad:(UIWebView *)webView {
NSString *onlineCheck = [[NSData dataWithContentsOfURL: [NSURL URLWithString:@"http://www.google.com/favicon.ico"]] retain];
if (onlineCheck == nil) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error" message:@"Can't connect to the internet, check your connection" delegate:self cancelButtonTitle:@"OK" otherButtonTitles: nil];
[alert show];
[alert release];

NSLog(@"UIAlert2");
}
[onlineCheck release];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
toolbarStop.hidden = NO;
toolbarRefresh.hidden = YES;

NSLog(@"START LOAD");

}

Surely there is another way to tap the back button without making the app crash?

It probably doesn't help that I'm not using ARC yet either.

Edit: Is it possible to have the back button act as a stop button in my Web enabled views? (not the pdf ones)

Basically, if a webpage is loading and the user taps the back button, its the equivalent of a stop button and a back button for the previous tableView.

jnoxx
Feb 15, 2012, 04:16 PM
viewWillDisappear --> make it stop there :)

xStep
Feb 15, 2012, 04:39 PM
Surely there is another way to tap the back button without making the app crash?

It probably doesn't help that I'm not using ARC yet either.

Edit: Is it possible to have the back button act as a stop button in my Web enabled views? (not the pdf ones)

Basically, if a webpage is loading and the user taps the back button, its the equivalent of a stop button and a back button for the previous tableView.

The networking code goes off and does it's own thing during the setup. That takes time. Exiting before that finished cause my app to crash as you described. Perhaps there is a better way, but I didn't find it. I doubt ARC will help here.

jnoxx's suggestion is the likely solution, but...

You can connect the back button to a local method, do the things you need done there, and then send the view controller the exit message. I think the final line in that method would be this; [self.navigationController popViewControllerAnimated: YES];

troop231
Feb 15, 2012, 09:10 PM
The networking code goes off and does it's own thing during the setup. That takes time. Exiting before that finished cause my app to crash as you described. Perhaps there is a better way, but I didn't find it. I doubt ARC will help here.

jnoxx's suggestion is the likely solution, but...

You can connect the back button to a local method, do the things you need done there, and then send the view controller the exit message. I think the final line in that method would be this; [self.navigationController popViewControllerAnimated: YES];

Here is what I did to stop the activity indicator:

-(void)viewWillDisappear:(BOOL)animated { [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[webView stopLoading];

}

That also takes care of stopping the webView too.

Edit: I solved the crashing just now, here's what I had to do:

I moved [toolbarStop release];
[toolbarRefresh release]; from the -(void)dealloc method to the -(void)viewDidUnload method, it works perfect now!

xStep
Feb 16, 2012, 02:08 AM
Edit: I solved the crashing just now, here's what I had to do:

I moved [toolbarStop release];
[toolbarRefresh release]; from the -(void)dealloc method to the -(void)viewDidUnload method, it works perfect now!

That's interesting. I wonder if you've just delayed the issue somehow, or fixed it by accident. There is just something about it that is nagging me. ;)

jnoxx
Feb 16, 2012, 02:25 AM
viewDidUnload is only getting called normally when an Memory Warning level 1 is sended to your controllers, so you normally need to release & set to nil in the viewDidUnload and the release is when the controller gets released.
But, if you are autoreleasing objects, know that you should NOT release them again. Like xStep said, i think you are just delaying the issue :) and not actually fixed it. Have u tried memory leaking against it?
Command+Shift+B or run it through analyzer vs Leaks. I thinnk you're going to have some issues.
If you can't figure it out, i'll try to find my code when I last dealt with webview :)

troop231
Feb 16, 2012, 06:24 PM
viewDidUnload is only getting called normally when an Memory Warning level 1 is sended to your controllers, so you normally need to release & set to nil in the viewDidUnload and the release is when the controller gets released.
But, if you are autoreleasing objects, know that you should NOT release them again. Like xStep said, i think you are just delaying the issue :) and not actually fixed it. Have u tried memory leaking against it?
Command+Shift+B or run it through analyzer vs Leaks. I thinnk you're going to have some issues.
If you can't figure it out, i'll try to find my code when I last dealt with webview :)

I was not able to run the analyzer, Xcode says: "The scheme 'OutdoorRules' is not configured for Analyzing." Tried to fix it to no avail. If it helps, this project is an older non ARC one (based on OS 3.0 code)

Any help is appreciated. :)

xStep
Feb 16, 2012, 11:05 PM
I was not able to run the analyzer, Xcode says: "The scheme 'OutdoorRules' is not configured for Analyzing."

A Google search came up with suggestions on Stackoverflow. Seems your scheme messed up.

jnoxx
Feb 17, 2012, 03:18 AM
I never used ARC, so I'm in the same boat, so all I can say is, try to recreate your schemes, if you know how to go to your schemes (otherwise google it), there is a button to REMOVE, and then autocreate. Try again ;)
Gl

troop231
Mar 1, 2012, 08:11 PM
I made this flowchart to describe what I'm after, doesn't seem so bad right?

[Click for larger]

http://i.imgur.com/d02jz.png

xStep
Mar 2, 2012, 03:14 AM
I understand what you're trying to do. Perhaps title them if you are going to show them to others.

Flowchart 1 suggests the action is from a list of states. So I'm guessing that you will download all PDFs for a state and fishing/hunting combination when the user requests a download.

Flowchart 2 suggests to me that you will display the update request button when the user has a document open, allowing single PDF updates. You might also want a general update request button on the main screen.

In the first chart, just a nit-pick on wording. The "(I.E. first app store download)" is not correct. They may not have the state they are looking for, but may have others already downloaded. Also, don't display a blank webView. In that case, either keep the user at the level they were at when they initiated an action, or back up a level if needed.

Some constructive criticism/suggestions...

I do have a suggestion, that I think I already mentioned. Instead of having the user press a button to do a check, why not do the check when they first start up the app. You could do that on a background thread. If a change is detected, then put up a update request button where you wish. Otherwise it isn't needed, except as perhaps an aid to user satisfaction. Use of a notification (perhaps UIApplicationDidBecomeActiveNotification) in the app delegate to trigger a (successful) check say once a day, GCD (Grand Central Dispatch) to kick off the thread, and another notification or KVO (key value observing) to observe a flag or object of updates which would display the update request button if needed.

If you are thinking of listing all the states even when some states do not have documents, I think that is a mistake. If you insist, then do some coloring trick to distinguish those from states that have documents. If it were me, I'd only list the states that have local documents, and I'd have a + button on the right side of the navigation bar when in the states listing so users could request applicable documents for a missing state. I'd also implement a method to allow users to delete states, and documents within states.

Of course those suggestions change your flow chart. :D


Just curious, how will you get the UIProgressIndicator to work. I haven't used the web download tools yet so I'm wondering how that works.

BTW, what app did you use to create the flowchart?

Did you sort out your scheme issue? If so, what was the solution?

troop231
Mar 2, 2012, 10:24 PM
I understand what you're trying to do. Perhaps title them if you are going to show them to others.

Flowchart 1 suggests the action is from a list of states. So I'm guessing that you will download all PDFs for a state and fishing/hunting combination when the user requests a download.

I wasn't planning on a combination at all, if the user wants fishing for Alabama, then that's what he/she will get, not hunting with it.

Flowchart 2 suggests to me that you will display the update request button when the user has a document open, allowing single PDF updates. You might also want a general update request button on the main screen.

I was just wanting to always have a refresh button on the navigation bar permanently, maybe in future it could be conditional (focusing on baby steps after all lol)

In the first chart, just a nit-pick on wording. The "(I.E. first app store download)" is not correct. They may not have the state they are looking for, but may have others already downloaded. Also, don't display a blank webView. In that case, either keep the user at the level they were at when they initiated an action, or back up a level if needed.

I mean that it will only check for the selected table cell/ animal category, the other states will be downloaded individually and not in bulk.

Some constructive criticism/suggestions...

I do have a suggestion, that I think I already mentioned. Instead of having the user press a button to do a check, why not do the check when they first start up the app. You could do that on a background thread. If a change is detected, then put up a update request button where you wish. Otherwise it isn't needed, except as perhaps an aid to user satisfaction. Use of a notification (perhaps UIApplicationDidBecomeActiveNotification) in the app delegate to trigger a (successful) check say once a day, GCD (Grand Central Dispatch) to kick off the thread, and another notification or KVO (key value observing) to observe a flag or object of updates which would display the update request button if needed.

If you are thinking of listing all the states even when some states do not have documents, I think that is a mistake. If you insist, then do some coloring trick to distinguish those from states that have documents. If it were me, I'd only list the states that have local documents, and I'd have a + button on the right side of the navigation bar when in the states listing so users could request applicable documents for a missing state. I'd also implement a method to allow users to delete states, and documents within states.

Of course those suggestions change your flow chart. :D

You're right, but I don't think my knowledge is that great at the moment, and it would aid user satisfaction.

Coloring sounds neat, just not sure how to implement it at the moment.

Just curious, how will you get the UIProgressIndicator to work. I haven't used the web download tools yet so I'm wondering how that works.

Shouldn't be too hard, it checks for a certain amount of bytes expected and changes the progress accordingly. One thing I'll need to be careful of is to limit the download rate so Apple doesn't reject the app. Isn't it 5MB a minute max? Another thing, if the user minimizes the app to the home screen, I'll need a way to background the download pdf task so it doesn't stop every time the user multitasks.

BTW, what app did you use to create the flowchart?

Gliffy online based flowchart tool :) http://www.gliffy.com/gliffy/#templateId=blank&signup=1

Did you sort out your scheme issue? If so, what was the solution?

I didn't yet :(

Thanks for your thoughts again! I enjoy the collaboration and brainstorming :)

xStep
Mar 3, 2012, 12:17 AM
You're right, but I don't think my knowledge is that great at the moment, and it would aid user satisfaction.

Coloring sounds neat, just not sure how to implement it at the moment.

Here is some pseudo code for an idea how I think it could be done. My thought here is to change the background color. You could also change the text color. A more advanced method would be to add a custom background view to make it look exceptional.


- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell;
NSString *cellIdentifier;

someObject = [data objectAtIndex: indexPath.row];

if ([someObject hasPDFs]) cellIdentifier = @"cellWithData";
else cellIdentifier = @"cellWithNOData";

cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle: UITableViewCellStyleSubtitle reuseIdentifier: cellIdentifier] autorelease];

if (![someObject hasPDFs]) {
[[cell backgroundView] setBackgroundColor: [UIColor lightGrayColor]];
}
}

<what ever you do here>

return cell;
}



Thanks for your thoughts again! I enjoy the collaboration and brainstorming :)

It's fun and gives me a chance to think about how something might work and look.

Those baby steps your taking will get you to a more advanced product, just a little slower. That isn't a bad thing. It is more important to keep your product stable.

troop231
Mar 19, 2012, 06:32 PM
Is there any way I can check to see if a documents directory exists? I know I can check to see if there is a file in the mainbundle, but what I'm wanting to do is check to see if the user has already downloaded and stored a pdf in the NSDocuments directory. Also, if no NSDirectory exists, I hope that the function would be able to determine that as well.

xStep
Mar 20, 2012, 05:31 AM
NSFileManager and other calls are available. Perhaps these two samples, ripped from my own code, will lead you in the right direction.



+ (NSString *) appRootPathString
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString * documentsDirectory = [paths objectAtIndex: 0];
return documentsDirectory;
}


if ([[NSFileManager defaultManager] fileExistsAtPath: videoPath] ) {

troop231
Mar 21, 2012, 02:29 PM
NSFileManager and other calls are available. Perhaps these two samples, ripped from my own code, will lead you in the right direction.



+ (NSString *) appRootPathString
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString * documentsDirectory = [paths objectAtIndex: 0];
return documentsDirectory;
}


if ([[NSFileManager defaultManager] fileExistsAtPath: videoPath] ) {


I tried this in my code:

NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:url2];
if(![fileManager fileExistsAtPath:path])
{ UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Woops!" message:@"You haven't downloaded this regulation yet, would you like to?" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"OK", nil];
[alert show];
[alert release];

}

else { NSURL *url = [NSURL fileURLWithPath:path]; NSURLRequest *requestObj = [NSURLRequest requestWithURL:url]; [webView loadRequest:requestObj];
timer = [NSTimer scheduledTimerWithTimeInterval:(1.0/2.0) target:self selector:@selector(loading) userInfo:nil repeats:YES];
}

But I don't think it's working correctly. url2 is set in the rootviewcontroller to read a value in the plist which indicates the file name:

NSString *url2 = [dictionary objectForKey:@"URL"]

plist screenshot: http://i.imgur.com/Gfa5F.png

What I don't understand is, I have Alabama.pdf in the main bundle, and it's not being loaded into the webView, but rather the UIAlert gets shown. This is actually what I want in the long term, but why isn't NSFileManager detecting the files that are currently in my main bundle?

I'm getting alot closer to what I want :) Thanks in advance again!

http://i.imgur.com/Klyfe.png

xStep
Mar 21, 2012, 03:11 PM
I haven't looked into the details yet, but as I understand it, the main bundle is separate from the apps document path. For all I know, the document folder is just another folder within the main bundle. Like I said, I haven't reviewed the details. Your code is looking into the apps document path which upon first startup has no data in it.

Upon first startup, you would want to copy the appropriate resources to their appropriate places in the apps document folder. You'll need a method to avoid overwriting newer versions of the documents that have been later downloaded into the document folder structure.

Based on the information you provided, the fileExistsAtPath: statement is trying to confirm a folder exists within the document folder. Something like "~/Alabama", where the tilde represents the document folder. Don't use a tilde on iOS.

Also, it doesn't look like you are building your url properly in the area you are trying to load the existing file. You are only pointing to the folder, so what would the request load. At best, you'd see a listing of the folder. I haven't used webViews so don't really know.

Put some NSLog statements in to see the results of your path and url variables. That will help you understand what the paths actually look like.

troop231
Mar 22, 2012, 01:01 PM
I haven't looked into the details yet, but as I understand it, the main bundle is separate from the apps document path. For all I know, the document folder is just another folder within the main bundle. Like I said, I haven't reviewed the details. Your code is looking into the apps document path which upon first startup has no data in it.

Upon first startup, you would want to copy the appropriate resources to their appropriate places in the apps document folder. You'll need a method to avoid overwriting newer versions of the documents that have been later downloaded into the document folder structure.

Based on the information you provided, the fileExistsAtPath: statement is trying to confirm a folder exists within the document folder. Something like "~/Alabama", where the tilde represents the document folder. Don't use a tilde on iOS.

Also, it doesn't look like you are building your url properly in the area you are trying to load the existing file. You are only pointing to the folder, so what would the request load. At best, you'd see a listing of the folder. I haven't used webViews so don't really know.

Put some NSLog statements in to see the results of your path and url variables. That will help you understand what the paths actually look like.

Thanks again! I was able to figure it out, as I've now got a bulk of what I needed to do accomplished now. Wow! I can't believe everything I got done in 24 hours, as indicated by the Red X's:

http://i.imgur.com/IqujR.png


Now another question, is there a way I can check to see when a file was last modified so I can compare that date to the date in the documents directory?

The reason I ask, is this would be easier than the other way I have planned, which would be to create a plist and add a key called 'Number' and set it to an integer. The app would then read the locally saved plist and if local 'Number' is less than server 'Number' key, show an update.

Edit: Would something like this work well? NSDictionary *dictionary = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:&error];
NSDate *fileDate =[dictionary objectForKey:NSFileModificationDate];


Side note, I'm so happy that my app is only going to be about a 1MB download from the App Store after I get this update rolled out, that sure beats the current 843MB app download size :)

xStep
Mar 22, 2012, 03:34 PM
Using any of the dates for a file entry is not the right thing to do. The reason is that devices have different time zone or potentially could be out of sync with accepted reality. The differences could cause your code to suggest a change is always present. Also, your server may not have the date you expect. Without being able to control these factors, those dates would be considered unreliable.

The 'proper' way to do this is something independent from file time stamps, such as your number sequence technique. Frankly, coming from a corporate database background and having maintained a document control system, I'd track each document by a date and perhaps the time too. Since you probably can't get accurate dates from your all of your sources, I'd use the date you downloaded or processed the downloaded file to your server. Just the date would likely be fine as I doubt it common for a state to update one of those documents more than once a day, even if they find errors. Also if your check is only once daily, that would be enough resolution of a noticed change.

If you know each document has a definite date or other document tracking number, you could use that as your tracking key. The real thing to note will be a change from the previous key to the new key. What that key actually is, is likely irrelevant.

The number of documents involved is likely small enough that a plist will be fine performance wise. The next step up would be to use Core Data.

Oh, forgot to add. I don't trust the system modification. Seems I recall that OS X, and I'd assume it is possible for iOS, sometimes alters that date even when I think it shouldn't.

troop231
Mar 22, 2012, 05:47 PM
Using any of the dates for a file entry is not the right thing to do. The reason is that devices have different time zone or potentially could be out of sync with accepted reality. The differences could cause your code to suggest a change is always present. Also, your server may not have the date you expect. Without being able to control these factors, those dates would be considered unreliable.

The 'proper' way to do this is something independent from file time stamps, such as your number sequence technique. Frankly, coming from a corporate database background and having maintained a document control system, I'd track each document by a date and perhaps the time too. Since you probably can't get accurate dates from your all of your sources, I'd use the date you downloaded or processed the downloaded file to your server. Just the date would likely be fine as I doubt it common for a state to update one of those documents more than once a day, even if they find errors. Also if your check is only once daily, that would be enough resolution of a noticed change.

If you know each document has a definite date or other document tracking number, you could use that as your tracking key. The real thing to note will be a change from the previous key to the new key. What that key actually is, is likely irrelevant.

The number of documents involved is likely small enough that a plist will be fine performance wise. The next step up would be to use Core Data.

Oh, forgot to add. I don't trust the system modification. Seems I recall that OS X, and I'd assume it is possible for iOS, sometimes alters that date even when I think it shouldn't.

Thanks for your input!

I'm deciding that I will allow the user to check for updates whenever they want, because they are the ones who wanted this feature in the first place. My blue "Updates" button that's in the navigation bar will be shown only after the first PDF is downloaded, because if they haven't downloaded anything yet there's no reason to show it (already got that part figured out yesterday)

So here is how I'm thinking about going about the checking for updates procedure:

This will be the plist that resides on the server, and will download along with the associated PDF file as well.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>UpdateNumber</key>
<integer>1</integer>
</dict>
</plist>

So, whenever I want to add a newer PDF to the server, I simply edit and add +1 to the integer in the plist, ex: <integer>2</integer>

The app will compare the server's integer in the plist to the integer of the plist stored locally for the associated PDF, and if the local plist integer is not equal to the server's integer, then there is a new PDF (update available)

This is totally doable correct?

xStep
Mar 23, 2012, 01:17 PM
Doable? Yes.

One thing I thought of is that you have cases where there are several documents for a category. Have you considered how you are handling that? How about cases where a category has a new document added?

It might be redundant but I think I would put the document file name in the plist also. Just a thought.

troop231
Mar 23, 2012, 05:26 PM
Doable? Yes.

One thing I thought of is that you have cases where there are several documents for a category. Have you considered how you are handling that? How about cases where a category has a new document added?

It might be redundant but I think I would put the document file name in the plist also. Just a thought.

Yes, some categories will have several documents. The way I'm going to do it is like this: (url scheme)

server.com/PDFS/Alabama/AlabamaHunt.pdf
server.com/PDFS/Alabama/AlabamaFish.pdf

and I will have a .plist for each pdf file in the same state directory, ex:

server.com/PDFS/Alabama/AlabamaHunt.plist
server.com/PDFS/Alabama/AlabamaFish.plist

xStep
Mar 23, 2012, 08:58 PM
Ah, but California has two fishing documents, freshwater and saltwater, and Alaska has five hunting guides. So you might need another directory level to distinguish hunting and fishing, I suppose this would work too;


server.com/PDFS/California/CaliforniaFishingFreshWater.pdf
server.com/PDFS/California/CaliforniaFishingSaltWater.pdf
server.com/PDFS/California/CaliforniaHunt.pdf
server.com/PDFS/Alaska/AlaskaHuntBigGame.pdf
server.com/PDFS/Alaska/AlaskaHuntSmallGame.pdf
server.com/PDFS/Alaska/AlaskaHuntTrapping.pdf
server.com/PDFS/Alaska/AlaskaHuntWaterfowl.pdf
server.com/PDFS/Alaska/AlaskaHuntFalconyManual.pdf
server.com/PDFS/Alaska/AlaskaHuntFish.pdf


I'm not convinced keeping plists for every PDF is a great idea, but it does keep the implementation simple enough for now. It likely keeps the bandwidth lower because you aren't exchanging a large dataset every time someone checks in.

I can't wait to try it out!

chown33
Mar 24, 2012, 03:20 PM
Ah, but California has two fishing documents, freshwater and saltwater, and Alaska has five hunting guides.

There are also boundary conditions. Literally. The Colorado River is a boundary river and has specific rules, regs, and licenses. Enforceable in the states of Arizona, California, and Nevada, on the river, its lakes (dammed), and some tributaries.

troop231
Mar 25, 2012, 05:36 PM
Ah, but California has two fishing documents, freshwater and saltwater, and Alaska has five hunting guides. So you might need another directory level to distinguish hunting and fishing, I suppose this would work too;


server.com/PDFS/California/CaliforniaFishingFreshWater.pdf
server.com/PDFS/California/CaliforniaFishingSaltWater.pdf
server.com/PDFS/California/CaliforniaHunt.pdf
server.com/PDFS/Alaska/AlaskaHuntBigGame.pdf
server.com/PDFS/Alaska/AlaskaHuntSmallGame.pdf
server.com/PDFS/Alaska/AlaskaHuntTrapping.pdf
server.com/PDFS/Alaska/AlaskaHuntWaterfowl.pdf
server.com/PDFS/Alaska/AlaskaHuntFalconyManual.pdf
server.com/PDFS/Alaska/AlaskaHuntFish.pdf


I'm not convinced keeping plists for every PDF is a great idea, but it does keep the implementation simple enough for now. It likely keeps the bandwidth lower because you aren't exchanging a large dataset every time someone checks in.

I can't wait to try it out!

Yeah, at first I thought about using just one plist, but it quickly grew to almost 100KB in size, so now I'm using individual plists which are only about 200 bytes. This cuts down on the latency and makes it almost instantaneous.

Right now all I have left to do is try to figure out how to migrate everything to download asynchronously, as right now everything is synchronous :(

xStep
Mar 26, 2012, 12:52 AM
Regarding the downloads. Your getting out of my area of experience. One thing to keep in mind is, if you are want to download several documents simultaneously, don't over do it as too many would give bad performance.

I have thought of one consideration. What will users do when they next update and find they don't have their documents because the update doesn't come with them?

An idea might be to post an update that keeps track of what documents they look at. Immediately upon startup of the 'new' non-preloaded document version, let the user know that they need to download those tracked documents.

At the very least, in your app changes entry, make it as clear as you can. Maybe even in the first description lines, for those that look at that kind of thing. I doubt though many current customers will see either though as they will just update. A large group of people don't even do an update.

troop231
Mar 28, 2012, 02:18 PM
Regarding the downloads. Your getting out of my area of experience. One thing to keep in mind is, if you are want to download several documents simultaneously, don't over do it as too many would give bad performance.

I have thought of one consideration. What will users do when they next update and find they don't have their documents because the update doesn't come with them?

An idea might be to post an update that keeps track of what documents they look at. Immediately upon startup of the 'new' non-preloaded document version, let the user know that they need to download those tracked documents.

At the very least, in your app changes entry, make it as clear as you can. Maybe even in the first description lines, for those that look at that kind of thing. I doubt though many current customers will see either though as they will just update. A large group of people don't even do an update.

I just submitted the app for approval :) I can't believe what all I accomplished in the last week, it should make for a fine upgrade for existing users, and attract new customers more now.

The app's download size should now be 1.4MB or so :)

I think I made my app changes entry pretty clear. Whether people read it or not is a whole different matter :(

Thank you again for all of the wisdom and information you've provided me; I hope you enjoy the update also!

xStep
Mar 29, 2012, 02:06 AM
I just submitted the app for approval :) I can't believe what all I accomplished in the last week, it should make for a fine upgrade for existing users, and attract new customers more now.

The app's download size should now be 1.4MB or so :)

I think I made my app changes entry pretty clear. Whether people read it or not is a whole different matter :(

Thank you again for all of the wisdom and information you've provided me; I hope you enjoy the update also!

Congratulations! :D

I'll try to remember to check it out. I'm on the road for a few days and starting a new job, so I'll be pretty busy for the next week or three. :eek:

troop231
Mar 30, 2012, 12:06 AM
Congratulations! :D

I'll try to remember to check it out. I'm on the road for a few days and starting a new job, so I'll be pretty busy for the next week or three. :eek:

I had to reject the binary, I found a nasty bug I can't figure out, it's so nasty that you have to delete the app on the device/simulator to get it to work again.

I setup a url shortener on my server, that way the links in the app's plist aren't hard coded, and this will allow me to change redirects to a different server without submitting an app update.

The url shortener just redirects traffic. It works perfectly on all urls, except my pdf file's url, and when I change the redirect url on my server, the changes won't take effect until you delete the app and reinstall. Example: shorturl.com/pdf redirects to url.com/file.pdf and that works fine, but if I change it to redirect to url.com/filetwo.pdf it still loads the url.com/file.pdf This is not good! It sounds as if something is being retained through the app's lifecycle, but I don't see how.

What I don't understand is my other dynamic (redirect urls) work fine, and the changes on the server are reflected immediatelyin the app. (I use one of these urls to check to see if a server is online.)

Do you think you could help me fix this bug? :)

xStep
Mar 30, 2012, 03:54 AM
What I don't understand is my other dynamic (redirect urls) work fine, and the changes on the server are reflected immediatelyin the app. (I use one of these urls to check to see if a server is online.)

Do you think you could help me fix this bug? :)

It may be something that you have to follow through with the debugger and/or some NSLog statements. Search your code to see where the variable is used and assigned to.

Unfortunately I can't commit to helping. I start a new iOS job Monday and I'm currently driving 1800 miles to get there. I have 1400 to go, and hope to visit Mount Rushmore on the way!

----------

I also think I have thought of one consideration. What will users do when they next update and find they don't have their documents because the update doesn't come with them?

An idea might be to post an update that keeps track of what documents they look at. Immediately upon startup of the 'new' non-preloaded document version, let the user know that they need to download those tracked documents.

I'm glad you also mentioned this problem. I can imagine users being surprised at an in-opportune time.

troop231
Mar 30, 2012, 11:35 AM
It may be something that you have to follow through with the debugger and/or some NSLog statements. Search your code to see where the variable is used and assigned to.

Unfortunately I can't commit to helping. I start a new iOS job Monday and I'm currently driving 1800 miles to get there. I have 1400 to go, and hope to visit Mount Rushmore on the way![COLOR="#808080"]


I solved it! I had to change this line: NSURLRequestUseProtocolCachePolicy

to this line:

NSURLRequestReloadIgnoringLocalAndRemoteCacheData

Cannot believe something as simple as that required the app to be deleted :O

Have fun on your trip by the way! :)

dejo
Mar 30, 2012, 11:56 AM
Unfortunately I can't commit to helping. I start a new iOS job Monday and I'm currently driving 1800 miles to get there. I have 1400 to go, and hope to visit Mount Rushmore on the way!

Congratulations on the new job! Care to spill any beans about it? :) And don't forget to stop by The Devils Tower (http://www.nps.gov/deto/index.htm), since you'll kinda be in the area!

xStep
Mar 30, 2012, 12:47 PM
Congratulations on the new job! Care to spill any beans about it? :) And don't forget to stop by The Devils Tower (http://www.nps.gov/deto/index.htm), since you'll kinda be in the area!

Thanks for the attraction tip. I hadn't a clue where that was.

I'm working for a large consultancy group who has a contract for porting/creating medical related software to the iPad. The location is Minneapolis MN.

troop231
Mar 30, 2012, 11:41 PM
Thanks for the attraction tip. I hadn't a clue where that was.

I'm working for a large consultancy group who has a contract for porting/creating medical related software to the iPad. The location is Minneapolis MN.

Yes, congrats on your new job! :)

By the way, I worked up an even lighter way to check for updates, before, I was checking a 233 byte plist file, but even then I was not pleased, so I'm now comparing two .txt files strings, and each .txt file is only 8 bits (1 byte) lol this will save on bandwidth more too :) I tested this many times by editing the .txt integer by adding +1 to the current, and sure enough it said update available, score!

http://i.imgur.com/ZCCNR.png



NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:url4];


NSString *server = [NSString stringWithContentsOfURL:[NSURL URLWithString:url5] encoding:NSASCIIStringEncoding error:nil];
NSString *local = [NSString stringWithContentsOfFile:path encoding:NSASCIIStringEncoding error:nil];


if ( ![local isEqualToString:server] ) { UIAlertView *alertView3 = [[UIAlertView alloc] initWithTitle:@"Update Available!" message:@"Would you like to download the new regulations?" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"OK",nil];
[alertView3 show]; alertView3.tag = 2;
[alertView3 release];
}
else { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"You're up to date!" message:@"There are no new regulations available at this time." delegate:self cancelButtonTitle:@"OK" otherButtonTitles: nil];
[alert show];
[alert release];