chris1
Code is Art
Profile
Posts: 3862
Reg: Aug 10, 2012
Austin, TX
50,120
12/14/12 10:57 AM (13 years ago)

Issuing update does not override btconfig.txt

Okay, so I am using php on my webserver to generate the btconfig.txt files for some of my apps. The problem is, I accidentally messed up my code by sending out the same btconfig.txt file to several apps. Once I was notified of the error, I immediately rebuilt the app and issued it as an update to the Apple App Store. However, that didn't seem to override the config.txt. And since it's pointing to the wrong one now, I can't issue an update from the control panel or my website - it thinks its a completely different app. I was under the impression that app store updates would override btconfig.txt files - is that not true? Is the only recourse to instruct all users to delete the app and re-download the file from the app store?
 
David @ buzztouch
buzztouch Evangelist
Profile
Posts: 6866
Reg: Jan 01, 2010
Monterey, CA
78,840
like
12/14/12 04:08 PM (13 years ago)
I too thought the config.txt file included in your binary would "overwrite" the config.txt file that a user may already have on their device. I wonder if the current implementation of iCloud (or some other change) is backing up cached data on the device then re-using this data after an app update? I say this because it coud be that.. a) User gets app b) User uses "refresh" and gets your most recent config.txt data, which is "wrong" of course. c) User installs new app update from App Store. App uses previously cached data from "refresh" that happened before they installed the most recent update from the App Store. I'll see what I can learn about this behavior but I'm not sure what I'll find.... yet.
 
chris1
Code is Art
Profile
Posts: 3862
Reg: Aug 10, 2012
Austin, TX
50,120
like
12/14/12 07:38 PM (13 years ago)
Thanks David...I had 2 apps get messed up by this. The first seemed to work with the update on my device, the 2nd did not. The 2nd was for a client, who notified me that the update didnt do anything for their device either. I instructed to delete and reinstall, which obviously did the trick. But I have no idea how many users got the bad config and are out there stuck. I'm just glad I happened upon this early in my development career and didn't have a lot of apps "in the wild"! My php has been updated to make sure it never happens again. But it was certainly embarrassing! (also issued a discount for the trouble) I would think this would also be an issue for anyone having an "offline" app that later needs to take online...
 
chris1
Code is Art
Profile
Posts: 3862
Reg: Aug 10, 2012
Austin, TX
50,120
like
12/15/12 06:08 AM (13 years ago)
Okay - oddly enough, the same thing just happened to me on one of my Android apps (for the same client, actually). Here the issue isn't quite as serious. In an attempt to speed up the app (based on what we've been discussing in the thread "Android apps running much slower than iOS apps"), I reduced the size of header images and icon images for the "Menu Screen with Image" plugin screens and loaded them locally. Since users that wouldn't have installed my update might refresh their data, I realized I needed 2 different btconfig.txt files. Otherwise, users without the update would get reference to local files that weren't actually there. So, I created a second one (being sure to include reference to the second one in the dataurl line!), loaded it in the app, and pushed the update through last night. When I updated this morning, though, I found that it's still using the old btconfig.txt data. Fortunately, it's a lot easier to test this on Android, since updates don't go through a lengthy review process.
 
chris1
Code is Art
Profile
Posts: 3862
Reg: Aug 10, 2012
Austin, TX
50,120
like
12/17/12 02:17 PM (13 years ago)
Looking through the appDelegate.m file, I think I see what's going on. It does seem to be as you suspected, which is even noted in the comments: /* loadAppData method, where is the app's configuration data? ------------------------------------------------------------ a) If a cached version of the app's configuration data is available, use that (then check for updates) b) If no cached version is available, use the data in the bundle (then check for updates) c) If no cached version is available, and no dataURL is provided in the bundle config file, use the bundle config data. */ Since the cached version already exists on the device (and has a different file name than BT_config.txt - cachedAppConfig.txt or appModified.txt??), the app will check for this and never even look at changes to the BT_config.txt file. I assume it's a similar process in Android. If that's true, I can think of one possible solution. What if there was a file that was included with the build that the app could check for. If it exists, it takes the config data from the BT_config.txt file. If not, it takes it from the cached data. And if the app downloads config data from a dataURL, it checks to see if the temporary file exists and if so, delete it. That way, the file only exists if the app is either an offline app or opened from a fresh build/update, and therefore can serve as a check.
 
chris1
Code is Art
Profile
Posts: 3862
Reg: Aug 10, 2012
Austin, TX
50,120
like
12/17/12 05:17 PM (13 years ago)
On second thought, that suggestion won't work - bundled files are read-only.
 
chris1
Code is Art
Profile
Posts: 3862
Reg: Aug 10, 2012
Austin, TX
50,120
like
12/17/12 06:47 PM (13 years ago)
Okay - sorry for the flurry of responses - just using the forums to help think this little bugger out! Here's another thought. Might not work well for apps with their config.txt on buzztouch.com, but for my case where I'm hosting the config.txt and needing to host a different config.txt for new versions of the app (based on changes to image files, plugin files, document files): What if I told XCode to get the version number of the app like so: NSString *myVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"]; And then grab the config.txt from the url by passing in the version number: //the dataURL may contain merge fields... NSString *tmpURL = [BT_strings mergeBTVariablesInString:[self.rootApp dataURL]]; tmpURL = [[tmpURL stringByAppendingString:@"?version="] stringByAppendingString:myVersion]; Then, my php script would just have to look at the "version" variable using the $_GET method and distribute the appropriate config.txt. Right?
 
chris1
Code is Art
Profile
Posts: 3862
Reg: Aug 10, 2012
Austin, TX
50,120
like
12/17/12 07:12 PM (13 years ago)
Actually - that gives me another thought. What if the app writes the version number to a string whenever the config.txt is updated via dataURL. But before it does an update, it checks to see if the previously recorded version number is the same as the actual version number. If so, go ahead and refresh. If not, it knows we have a new version on our hands and so takes the config.txt from the bundled BT_Config.txt file. If I'm correct in my thinking, that should work for all users - whether their config.txt is located at buzztouch.com or not.
 
chris1
Code is Art
Profile
Posts: 3862
Reg: Aug 10, 2012
Austin, TX
50,120
like
12/17/12 08:56 PM (13 years ago)
Okay - I verified that this does the trick in XCode. Haven't gotten to Android yet. But here's what I modified in the appDelegate.m file: Under the section: -(void)loadAppData{ ****code truncated**** //get the name of the configuration file NSString *bundleFileName = [self configurationFileName]; if([bundleFileName length] < 4){ [BT_debugger showIt:self:@"There is no config.txt file configured in FUMCBixby_appDelegate.m?"]; bundleFileName = @"thereIsNoFileName.txt"; } //check for cached version of configuration data if([BT_fileManager doesLocalFileExist:self.saveAsFileName]){ ****code truncated**** I changed that to read: -(void)loadAppData{ ****code truncated**** //get the name of the configuration file NSString *bundleFileName = [self configurationFileName]; if([bundleFileName length] < 4){ [BT_debugger showIt:self:@"There is no config.txt file configured in FUMCBixby_appDelegate.m?"]; bundleFileName = @"thereIsNoFileName.txt"; } //look to see if this is a new version. NSString *savedVersionNumber = [BT_strings getPrefString:[NSString stringWithFormat:@"version"]]; NSString *myVersion = @""; [BT_debugger showIt:self:[[NSString stringWithFormat:@"Saved version number is: "] stringByAppendingString:savedVersionNumber]]; if([savedVersionNumber length] > 1){ myVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"]; [BT_debugger showIt:self:[[NSString stringWithFormat:@"Actual version number is: "] stringByAppendingString:myVersion]]; } //check for cached version of configuration data if(([BT_fileManager doesLocalFileExist:self.saveAsFileName]) && ([myVersion isEqualToString:savedVersionNumber])){ ****code truncated**** Later in the appDelegate.m file: -(void)reportToCloud{ ****code truncated**** if([useURL length] > 3){ //the dataURL may contain merge fields... [BT_debugger showIt:self:[NSString stringWithFormat:@"reporting to cloud at : %@", useURL]]; NSString *tmpURL = [BT_strings mergeBTVariablesInString:useURL]; //clean-up URL, encode as UTF8 NSURL *escapedURL = [NSURL URLWithString:[tmpURL stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; //make the http request NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:escapedURL cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:10.0]; [theRequest setHTTPMethod:@"GET"]; NSURLConnection *theConnection; if((theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self])){ //prepare to accept data receivedData = [[NSMutableData data] retain]; }else{ [BT_debugger showIt:self:[NSString stringWithFormat:@"reportToCloud error? Could not init request%@", @""]]; } } ****code truncated**** That was changed to: -(void)reportToCloud{ ****code truncated**** if([useURL length] > 3){ //the dataURL may contain merge fields... [BT_debugger showIt:self:[NSString stringWithFormat:@"reporting to cloud at : %@", useURL]]; NSString *tmpURL = [BT_strings mergeBTVariablesInString:useURL]; //clean-up URL, encode as UTF8 NSURL *escapedURL = [NSURL URLWithString:[tmpURL stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; //make the http request NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:escapedURL cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:10.0]; [theRequest setHTTPMethod:@"GET"]; NSURLConnection *theConnection; if((theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self])){ //prepare to accept data receivedData = [[NSMutableData data] retain]; NSString *myVersion = @""; myVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"]; [BT_debugger showIt:self:[[NSString stringWithFormat:@"saving actual version number as: "] stringByAppendingString:myVersion]]; [BT_strings setPrefString:[NSString stringWithFormat:@"version"]:myVersion]; }else{ [BT_debugger showIt:self:[NSString stringWithFormat:@"reportToCloud error? Could not init request%@", @""]]; } } ****code truncated****
 
chris1
Code is Art
Profile
Posts: 3862
Reg: Aug 10, 2012
Austin, TX
50,120
like
12/17/12 09:17 PM (13 years ago)
Okay ... close but not quite actually. It only works if the user refreshes on initial load. Otherwise it will save the new version number to the preference string when the cloudURL has been requested, even though the user chose not to refresh to get the cloud data. Better to stick it here: appDelegate.m file: -(void)downloadFileCompleted:(NSString *)message{ /* *****new code here****** */ //first, save version number NSString *myVersion = @""; myVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"]; [BT_debugger showIt:self:[[NSString stringWithFormat:@"saving actual version number as: "] stringByAppendingString:myVersion]]; [BT_strings setPrefString:[NSString stringWithFormat:@"version"]:myVersion]; /* *****end new code****** */ [BT_debugger showIt:self:[NSString stringWithFormat:@"downloadFileCompleted%@", @""]]; [self hideProgress];
 
chris1
Code is Art
Profile
Posts: 3862
Reg: Aug 10, 2012
Austin, TX
50,120
like
12/17/12 09:18 PM (13 years ago)
whew! I'm on my way to actually figuring this stuff out :)
 
David @ buzztouch
buzztouch Evangelist
Profile
Posts: 6866
Reg: Jan 01, 2010
Monterey, CA
78,840
like
12/18/12 01:20 AM (13 years ago)
Hi again. Figured if you worked this long enough you'de engineer something amazing by the end of the saga. LOL. Kidding of course but for your work here is impressive. I've spent a few days tinkering with the iCloud, Backup, Update, File Overwrite logic. I'm finding that depending on the version of the device (the hardware) and the version of iOS running on that device, the behavior is different. This is bad news and not something I expected to find. Forcing Xcode to look for a new config.txt file, or to hit a URL, or not, or whatever is for sure something you could do. I can think of several different ways to force it to refresh. Heck, you could even ask it to NOT look for external data until a certain amount of time lapsed (like a few days after install), x number of launches....whatever. The key here is understanding exactly what you're running into with your fixed .php script and exactly what you're trying to do. I thought I had a good understanding but I'm not so sure now. Your forum posts are fun to read but for sure hard to keep track of. LOL. Maybe we should "re set" a little and start fresh. If you explain what your trying to do, without the details about what you were doing, or what happened, it may be easier to figure out the easiest approach. Dunno...just trying to understand where it stands now :-)
 
chris1
Code is Art
Profile
Posts: 3862
Reg: Aug 10, 2012
Austin, TX
50,120
like
12/18/12 02:51 AM (13 years ago)
Okay bluhblahblahbluhbra .... let's try that again! So my issue started out with sending out the wrong config.txt to a couple apps. That was bad, but totally my fault. I was just about to go into the details of how that happened, but I won't - suffice it to say it happened and won't happen again. The second issue I came across that was related to this was in changing bundled files. Previously, my menu screens were loading icons from a dataurl instead of a local file. To speed things up, I included the local files and repackaged the app. However, I knew I had a problem. Users that hadn't updated their apps but had refreshed them would have an app point to a file that wasn't there. The app would look worse for them and they'd have no idea why until they checked the app store to see if there were any updates. My solution was to simply create a second config.txt file, so that anyone with the new version would grab data from it, while users that hadn't updated would keep the config.txt that works for them. A third issue (that I haven't mention in this thread yet) should relate to all Buzztouch users. When testing on the emulator, if I refresh the data in my app, the only way I can continue testing (assuming I want to make changes to the config.txt) is to delete the app from the emulator before starting again. Kind of a minor pain, really. But my latest solution above gets around this. Now, I just change the version number on the app and it resets back to the bundled config.txt, just as it would when a user updated the app from the app store. Issue one should be nothing more than a hypothetical mind exercise at this point. For the vast majority of Buzztouch users, it would (could) never become an issue. And for me, it's no longer an issue. I made sure of that! Issue two and three, though, might be worth considering for the broader community. I've only been using Buzztouch for a few months, but in that time I've seen a few instances of people complaining about issue #2. My solution was to host a second config.txt on my webserver - not knowing that it was doing no good since app updates would never point there due to this problem. Other people have said "well, if you are really concerned about it, you should just keep your app offline and issue all updates through the app store". I don't really consider that a buzztouch solution, though. It seems that the plugin market is fixing to explode in the new year. When that happens, I predict issue #2 will become a big problem. How do you point to a new plugin screen when people who haven't updated their apps don't have that plugin to load? Everytime we want to purchase a new plugin and incorporate it into an existing app, we will have to consider whether it's worth the burden of explaining to our users of what they need to do to get their apps working again, and putting up with some possible negative reviews. That and/or trying to catch the majority of users by waiting until updates have been in the app store for XX days/weeks before changing our Buzztouch control panels to use the new plugin. There you're simply playing the odds and hoping for the best. Incorporating my solution above won't fix that, of course. Unless, that is, we have an option to do versioning on the control panels as well. As for where this stands now...I think the best solution for me is to manually update my appDelegate for each of my apps, keeping in mind I will need to do that every time I download the source code package again. I still need to figure out how to do this in Java for Android, but I assume it won't be that difficult. Then I'll need to host a new version of the config.txt on my server for each app version. Hopefully that clears some things up. If I just made them muddier, I'm sorry! It's almost 4am and I'm restless. If you have a fix for that, I'm all ears! :)
 
David @ buzztouch
buzztouch Evangelist
Profile
Posts: 6866
Reg: Jan 01, 2010
Monterey, CA
78,840
like
12/18/12 04:31 AM (13 years ago)
Yes it's late here too. 3:32 on my clock. LOL. This is a wonderful post and for sure isolates some of the downsides of developing apps using the plugin / external data approach. Aside from the data file being "out of sync" when new things are added and changed...the Plugins also need compiled in the new project which introduces additional problems. You're well aware of all of these. We've been working really hard on a Live Mode and Design Mode feature and hope to release it on Jan. 1. The idea here is that you can make changes in the control panel that the app "won't notice" until you're ready to roll them out. It addresses some gotchas but not all. One of the biggest challenges is the shear number of things you can change, approaches you can take (in terms of handling the config.txt data's location) and options you may or may not take advantage of. This would all be much easier if we said "hey, put this JSON.txt file in your project, it powers your app" But, what fun would that be! Remotely hosting this file, connecting it to the control panel, adjust it...that's were all the fun comes in. But, this fun comes at an expense for sure. Broken apps are bogus for everyone. I think ultimately the best solution will revolve around simplifying the "connection" between the control panel and the app (somehow) and doing a better job at showing / explaining what's happening so app owners don't run into the confusion. That sort of thinking should help the config.txt data trouble tons. We'll still need to work out how to best communicate what a "Plugin" is and how to take advantage of somebody else's' work and code. Like you said, it's not always a good idea :-) I'll ponder this discussion a bit more and for sure draw on it as we move towards Jan. 1. Thanks again, I always enjoy your thoughtful comments. d.
 
chris1
Code is Art
Profile
Posts: 3862
Reg: Aug 10, 2012
Austin, TX
50,120
like
12/18/12 10:41 AM (13 years ago)
Thanks David. I think the Live Mode and Design Mode will help out with this. It would be nice if there were an easy way to see how many users are on an old version vs a new version of the app. Maybe there's a way to add the device version as a tag onto the report to cloud url, and then have a screen in the control panel to show screen load analytics for each version? I'm just thinking each app will have a different lag time before a sufficient number (however that is defined) of people have upgraded to the latest version. I still think the most helpful solution for developers would be to have the ability to do versioning in the control panel. Perhaps if we told the control panel that we're on, say, version 1.1 of the app, it would archive the config.txt and save it with a tag of 1.0, and all changes going forward would be to the 1.1 config.txt. Then, the app just needs to append a url variable to the dataURL to tell the control panel what version it is working with. (See my post above starting with the words "Okay - sorry for the flurry of responses"). You may have good reasons for not wanting to do that. It's just what makes sense to me. Thanks again for everything you do! As I said in my email to you, before Buzztouch I was very limited in my programming ability. In fact, the extent of it was pretty much limited to HTML and Javascript. Now I've taught myself php and the basics of objective-C, and gotten 4 apps approved in the app store on my first try!
 
chadh0130
Apple Fan
Profile
Posts: 352
Reg: Nov 05, 2011
Rhode Island
4,020
like
12/20/12 09:38 PM (13 years ago)
@chris1- about two months ago, I had a client inquire about version control. I have since found and utilized this https://github.com/nicklockwood/iVersion I think this may help with what your last post was about. Instructions are found on this page as well :)
 
chris1
Code is Art
Profile
Posts: 3862
Reg: Aug 10, 2012
Austin, TX
50,120
like
12/21/12 08:43 AM (13 years ago)
Thanks WaYsTiiD TIME - that looks like an interesting iOS control to have, and could certainly be a great alternative to the solution I mentioned above. Perhaps someone could turn it into a plugin for Buzztouch - looks like the configuration isn't all that hard. Would also need an Android version, though.
 
WebNevees
Code is Art
Profile
Posts: 206
Reg: Oct 28, 2012
KL
11,660
like
04/20/13 03:24 AM (12 years ago)
So Chris, did anything come out of this discussion. I mean did you test or extend the code you put up here? Or is the new Live and Debug mode functionality the resulted outcome? I'm having problems getting the auto refresh to work the first instance of running my app. It's acting funny though...So I wound up here...
 

Login + Screen Name Required to Post

pointerLogin to participate so you can start earning points. Once you're logged in (and have a screen name entered in your profile), you can subscribe to topics, follow users, and start learning how to make apps like the pros.