Touch Code Magazine

Posts about Swift, Apple, iOS, conferences, and more

Update dynamically your iPhone app – Part 1

This a Part 1 of a 2 parts tutorial, if you are returning visitor you should definitely check out Part 2.
If you’re a newcomer have a look at this one and the attached demo application and then head up to Part 2.

Introduction

Building an iPhone app is great, but building a dynamic iPhone app is even greater – providing new content to your users will make them enjoy your application more and will keep them interested longer. You can always go for rare updates trough new versions of your app (if you don’t mind the tedious approval process), but when you need to push new content in real time that’s not a solution at all.

What content would like to dynamically update in your app? Well that might be virtually anything; although Apple reviewers will be harsh on apps which even suggest including new functionality trough updates from the Internet. So, I would say – if you have an app with cooking recipes – provide new recipes from your web server as they come, if you have an image gallery, provide new images, etc. etc.

The problem

When you create an application the resources of that application are bundled with it, and you don’t really have a way to add new resources to the application bundle at run-time. What you would like to do is to keep your content in a directory you have access to – like your app Documents directory and add new content there as it comes.

Another issue is downloading files – if you have quite a big list of text files it really makes much more sense to download 1 zip file of 5Kb in one single shot instead of 45 files text files in total of 300Kb and 45 http requests.

Our example application and the necessary libraries

In this tutorial I’ll outline the process of creating a small image gallery which can be updated with new images from a web hosting server.

I’ll use 2 images bundled with the application and 3 additional images, which I’ll put in a zip file, which the application will be able to download, unzip and show together with the initial images.

First of all a note to the mac users: If you are trying this yourselves it’s a good idea to use archiver like YemuZip, because the default OS X archiver will include all the hidden metadata files found along your own files. YemuZip will include in the zip archive only the files you actually want to add.

The iPhone OS has a zlyb library, but you need a wrapper around it. I’ll be using ZipArchive, so that I won’t need to write extra code for the unzipping of the new app content. You have to also add the zlyb library to your project:

Adding libz library to xcode project

I also will be using NSInvocatinOperation, so be sure to check Apple’s documentation on this one.

Now let’s get coding.

The solution

To be able to merge your initial content and any new content coming from your web server, I’ll advice you to keep everything in the Documents folder. I use a small function to preload all initial images into the Documents directory and I call this function in viewDidLoad. For your app, you should add some extra code so you do that only the very first time your app is started:

Now that my images are in Documents, I show the them on the screen (those are the 2 images included in the app bundle)

When the user hits the “Update” button, a new thread will be created and it will do the hard work, while the main thread will lightheartedly rotate the activity indicator which will appear on the place of the button.

The separate thread loads the contents of a remotely located zip file into a NSData object, then writes it to a file, and tries to extract the contained images:

Now when already in a separate thread we can do the heavy work – download the update file from a web server and unzip all the contents to the documents directory:

After the images are extracted and the temporary zip files is deleted, the main thread reloads again all images from the Documents directory and shows the result (both bundled and downloaded ones):

Wrap up

This is the basic setup of an app that updates its content dynamically from the web. Further you will want to look into checking for Internet connectivity, error handling, incremental updates, etc. etc.

The example application you can download here, it’s fully functional and will demonstrate everything I wrote about in the article (and also will well demonstrate the use of NSInvocationOperation).

I really believe it become a nice demo – use of ZipArchive, simple demo of NSInvocationOperation, providing demo source code, so if you liked what you red please leave a comment or ping me on Twitter.

Cheers, Marin

Tagged under: , , , , , , , ,

Pin it

Marin Todorov is an independent iOS consultant and publisher with complimentary background in server and desktop development. He started developing on an Apple ][ more than 20 years ago and keeps rocking till today. Meanwhile he has worked in great companies like Monster Technologies and Native Instruments, has lived in 4 different countries, and (more recently) is one the founding memebers of the raywenderlich.com tutorial team. Besides crafting code, Marin also enjoys bloging, writing books, teaching and training others, and speaking at mobile conferences. He sometimes open sources his code. He walked the way to Santiago.

Website: http://www.touch-code-magazine.com

47 Comments

  1. Pingback: Tweets that mention Update dynamically your iPhone app with new content | Touch Code Magazine -- Topsy.com

  2. savior

    Can we use the same way to replace existing images? Like replace image1 & image2 with image3 & image4?

  3. Marin

    Hi Savior,

    you can definitely replace images, but there’s not so easy way to do it as one would hope for :)

    The problem is that the content of your app bundle is off limits – meaning, everything that comes in the .app bundle can’t be changed. So the real answer to your question is that you cannot physically replace resources inside your bundle, but you can do other stuff …

    let’s say you are showing in your UI image1 and you have in your Documents folder a folder called updates. When you load your UI you need to do something like this (pseudo code):

    if (fileExists updates/image1.jpg ) load updates/image1.jpg in the UI
    else load appBundleFolder/image1.jpg in the UI

    Or second way coming instantly to my mind is on your very launch of the app to copy all images from your applicationBundle folder into Documents/Images and always load your images from Documents/Images. This way whenever there’s an update you will just overwrite the images in the Documents/Images folder and you won’t have the “if” from the first example every time you load the UI.

    So … first way to do it – have an “if” on every UI load, second way to do it – have the images doubled in the Documents folder.

    That’s what comes immediately to my mind, if I came up with something else, will definitely write about it here … cheers

  4. Hyper programmer

    In ur tutorial u are using

    NSString *pathLocal = [self.documentsDir stringByAppendingPathComponent:fileName];
    NSString *pathBundle = [[[NSBundle mainBundle] resourcePath]
    stringByAppendingPathComponent:fileName];
    [self.fileManager copyItemAtPath:pathBundle toPath:pathLocal error:nil];

    to get the documents folder. Is there any way to see what images are updated to the documents folder directly?

  5. Marin

    Hi Hyper programmer :)

    I am not really sure what are looking to do … can you give me an example what exactly you’d want the app to do? I can then put together some code for you if possible :)

    What the code you highlighted does is that it just reads files from the bundle directory (as you cannot updated them directly inside the application bundle – look at the answer I posted to Savior) and copies them into the sandboxed Documents folder for the give iPhone app – in this location they can be overwritten with new ones whenever needed.

    Marin

  6. savior

    Hi Marin,

    Can I know how to download all the images in the URL without knowing their names?

  7. Marin

    Hi Savior,

    I see you’re hard working on your app :) Let me know when it’s out – I’d like to see if my tips were helpful

    Here’s 1 possible solution (I use that in the app I’m currently developing) : in that zip file which you are downloading include a file which gives you the names of the images – easy isn’t it?

    So … in your zip file let’s say you have : image1.png, image2.png and image3.png. Include also a file called contents.plist :


    image1.png
    image2.png
    image3.png

    There you go – download the zip file, extract the files inside a temp folder and load contents.plist into an NSArray :

    NSArray* contents = [NSArray arrayWithContentsOfFile:@"PATH TO PLIST FILE HERE"];

    and you have the names of the images in “contents”

    Marin

  8. Marin

    Hm .. WordPress is stripping my markup … Let’s try again – the contents.plist file should be :

    <plist version=”1.0″>
    <array>
    <string>image1.png</string>
    <string>image2.png</string>
    <string>image3.png</string>
    </array>
    </plist>

  9. Douglas Schmidt

    Hi, I’m having trouble with getting the file from my server.

    Your file downloads and creates the NSData ok.
    But with my file on localhost the NSData always create the object with 0 bytes :(

    The zip was created with the Yemuzip and there’s no restrictions on file privileges.
    Also I can access, download and unzip the file in Finder.

    Any clues?
    Tks!

  10. Marin

    Hi Douglas,

    yeah – that’s sounds like a nasty little problem :)))

    However … I have a pretty good guess – what URL are you using to access your file? You know – if you are trying to download something like “http://localhost/myFile.zip” that just won’t work inside the phone or the simulator.

    If that’s the case you’ve got a problem – why? Well … you are not allowed and you have now way to change the hosts file for your device, so you are not able to really test between your device and your local web server on your computer.

    Now … still there are solutions – try to access your computer’s web server by IP. If you’ve got an IP like “212.1.2.0” (for example … you must know your web server’s IP) try an address like “http://212.1.2.0/myFile.zip” – that should work.

    I hope that helps you! Let me know how it goes

    Marin

  11. Douglas Schmidt

    Ow, thanks for the fast response :)

    Well, I tried to use my IP address directly, but it didn’t work either, probably because I’m inside a proxy network.

    Well, I’ll try to contact the network admin to grant me rights to surpass the proxy, but I’ll be grateful to you if you point me some solution that don’t involve the IT dep. long waiting ticket list :)

    Tks!

  12. Marin

    Well :)))) You need to have a web server visible from your device for the downloading exercise

    Depending on the urgency – I’d say use one of those free hosting sites or a service like MobileMe to make your test file available in Internet (desperate times – desperate measures)

    I know how is with IT – my advice – escalate and make them do their job :)

  13. Savior

    Hi Marin,

    Thank you for the idea. But, I don’t want to have a zipped file. Is there any way to copy a folder(which contains all the images) from the internet to the documents directory?

  14. Marin

    Hi Saviour,

    the web is not a filesystem – so there is no entity like a “folder”. If I get correctly your drift you might want to have an FTP server to update from – there you’ll have a filesystem exposed and you can iterate trough folder contents… Have a look at the FTP example at Apple :

    http://developer.apple.com/iphone/library/documentation/Networking/Conceptual/CFNetwork/CFFTPTasks/CFFTPTasks.html

    best, Marin

  15. Douglas Schmidt

    Marin, I downloaded your Crowberries app and loved! So beautiful and simple, good job :)

    I saw that you added the possibility of getting new paid content throw the Internet. How does you charge for it? Its delegated to Apple charge system?

    I’ll buy you app to check how the process goes.

    I’m creating a magazine app and want to have similar features. I read that Apple doesn’t accept apps that charges for paid content? How did you done that? :)

    Tks in advance!

  16. Marin

    Hi Douglas,

    glad you liked the Digital Crowberries app, it’s simple and beautiful – that was our goal :)

    Now on your questions – Apple does allow purchases of paid content, it’s not really easy, but there’s a good bunch of tutorials out there.
    Look in Google for “In app purchases” this should give you a good overview, but anyway you should start with the guide from Apple here :

    http://developer.apple.com/iphone/library/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Introduction/Introduction.html

    How it works in few words is that you create 2 types of products in your iTunes connect account :

    1st type are applications – aka your iPhone app
    2nd type are virtual products which are purchasable from inside your app …

    so Apple takes care for you to charge the client’s iTunes account with whatever amount you set to be the price of your products, and then you take care to either unlock some features or content; OR to provide a download of your product from your own server…

    You have also different types of products – subscriptions, consumable products … I can go for hours, it’s really powerful, you should check out their guide – it’s the best as a starting point…

    I’m submitting to the App store tomorrow and I’ll be working on some purchasing tutorials, so stay tuned !

    best, Marin

  17. Pingback: Update your iPhone app with new content – Part 2 | Touch Code Magazine

  18. Marin

    Hi everyone who left a comment and watching the comments stream :)
    After the discussion around various additions to the Update your iPhone app with new content tutorial on Touch Code Magazine, I posted also a second part which enhances with several cool features the demo app – do check it out and let me know what you think :)

    http://www.touch-code-magazine.com/update-your-iphone-app-with-new-content-part-2/

    Marin

  19. ringoapp

    Thank you for the nice post!

    I have a question on in App Purchase. With your implementation above, I assume that I can let my users purchase and download new contents from my own server to their apps without being recognized by appstore.

    So, can I do things like below?

    1. Build a my own web purchasing system and list new contents there.
    2. User can visit the site, and buy some new contents. (The payment is being done in my site, not in the appstore.)
    3. After payment, the user open their iPhone app, and ask to retrieve new contents.
    4. Request from the user includes account info, and the server already has the user’s purchasing records.
    5. The server now allow the user’s app to download the purchased contents.

    Technically, I think above is possible. But is it allowed? Anybody doing this?
    (Yes, You can say that ask it to Apple, but I just wondered if you have an answer.)

  20. Marin

    As you said it is technically possible. What I think you should do is read trough the Developer Terms & Conditions very carefully. I think Apple will rarely reject an app just because they don’t like it, but otherwise is there any point in the Terms that you don’t comply to, they’ll be on your back.

    My opinion : I really don’t know. I think the only allowed payment method for a native apps is trough iTunes, because Apple do want 30% tax on the income you make from your app.

  21. muthiah

    hi marin,
    If i want to add new images often in the zipped folder which is in my server,how i should do it effectively and how i can retrieve them dynamically.

  22. Marin

    Hi muthiah,

    if you want to update regularly the zip file on the server I guess you’d need some kind of a script maybe Perl or PHP.
    Or maybe lookup in Google how can you record an Automator script – maybe that’s a good idea – have a macro that’d zip your images and upload them on the server. Unfortunately can’t give you a ready solution right away – I release seldom updates for my apps and didn’t have a look for such an automation.

    Have a look at the second part of this tutorial too – will provide you more useful tips on the topic:
    http://www.touch-code-magazine.com/update-your-iphone-app-with-new-content-part-2/

    best, Marin

  23. muthiah

    i want to get the file attributes of the ftp server and also i want to download the files based on the file count …i saw ur second app its really wondering me..,,. if u have any idea about my app please share with me..
    thanks in advance…

  24. Mark

    You saved my hide, now I just need to know if I can implement this just using a JavaScript Framework ie JQuery Mobile and HTML5/CSS

    and I need to know if Android has the same capabilities.

    thanks again!

  25. Pingback: Expanding beyond the App Store

  26. marshad13

    This code will access data from your resources and unzip it in your app document directory

    self.fileManager = [NSFileManager defaultManager];

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

    NSLog(@”document directory path:%@”,paths);

    self.documentDirectory = [paths objectAtIndex:0];

    NSString *filePath = [NSString stringWithFormat:@"%@/abc", self.documentDirectory];

    NSLog(@”file path is:%@”,filePath);

    NSString *fileContent = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@”data.zip”];

    NSData *unzipData = [NSData dataWithContentsOfFile:fileContent];

    [self.fileManager createFileAtPath:filePath contents:unzipData attributes:nil];

    // here we go, unzipping code

    ZipArchive *zipArchive = [[ZipArchive alloc] init];

    if ([zipArchive UnzipOpenFile:filePath])
    {
    if ([zipArchive UnzipFileTo:self.documentDirectory overWrite:NO])
    {
    NSLog(@”Archive unzip success”);
    [self.fileManager removeItemAtPath:filePath error:NULL];
    }
    else
    {
    NSLog(@”Failure to unzip archive”);
    }
    }
    else
    {
    NSLog(@”Failure to open archive”);
    }
    [zipArchive release];

  27. David

    Hi Marin,

    I am extremely new to IOS development, and pretty much any programming really.

    I am building an app for my church that would have a few pages that update weekly. e.g. a home page that features “What’s happening this week at youth” or something similar.

    The way i am picturing it to work would be to have an image banner (or placer or whatever it is called) that is linked to an image file that is on my server, that when I update the image file it updates the image banner on the home page.

    Am I along the right track of thought on how it works???

    Is your example above what I need to do???

  28. Marin

    Hey David, for what you are describing I think it might be a better idea to use a UIWebView to just load a web page from your web server…
    Have a look at the RSS reader tutorial on this web site and see how reading rss and showing web pages inside an app works – it might be an easier solution for your case. Good luck – Marin

  29. Lil' J

    This is perfect for what I need. Any idea how to implement this is on javascript, or any equivalent codes that does the same job?

  30. Marin

    hey! I’m not really sure what you are asking about – you can create iPhone apps in Objective-C, C++ or C

  31. facebook_TRGRichie

    hi, probably being very stupid – I’m very new to this but the demo will not run, am getting a Linker command failed with exit code 1 error! library not found for -iz.1.2.3

  32. facebook_TRGRichie

    Hi could the app write the files to camera roll or DCIM folder? this would be very helpful for an app I’m working on. Many Thanks.

  33. Marin

    You could do that, check my post here http://www.touch-code-magazine.com/ios5-saving-photos-in-custom-photo-album-category-for-download/

  34. Richard James Molloy

    Hi Marin, I’m sorry but I’m a complete beginner with xcode – help! basically I already have an app that is html then phone gapped – this app has video content but it has made the app very large – my customer doesn’t want to have the apps pull from the web either – they want to have 2 apps one that is the stand alone presentation(this is all built and I can get the app to pull the videos in from either the camera roll or DCIM folder) the problem I have is I need a separate app to download the zip and unzip to a folder the other app can access – your app is nearly perfect! it just needs to either download and unzip to DCIM or camera roll or move the unzipped files from the apps documents folder after down load – hope this makes sense – any help is really appreciated. Cheers Richie

  35. Iman

    I didn’t practice your code yet but i know that it will work :-)
    You saved my day dude! Thanks a lot!

  36. Prasanna

    Hello Marin,

    I downloaded this app and I liked it very much.In my Iphone application I am using this type of function.In my Iphone app there is a module called wardrobe in that module by default we have to place 2 folders and also we have to include a feature like when the user clicks on a plus button the user can able to create new folders dynamically and the user will save data(photos) to these folders dynamically from their iphone device. If you know how to implement this functionality, can you please send me sample iphone projects similar to this functionality??

  37. spoolup

    excellent post! i’ve followed the post and your source code and managed to go online download the file and extract the zip (i used html files instead of png).
    my problem is, as in another post, that I downloaded files that are duplicates of files in the bundle.
    normally i call my html files with the following:
    what can i do to open a downloaded html file (if downloaded) instead of the bundle one?
    Thanks

    – (IBAction)loadLocalFile:(id)sender
    {
    UIButton *tmp = sender;
    int tag = tmp.tag;
    NSString *fileName = [NSString stringWithFormat:@"dossier%i", tag];
    NSURL *url = [NSURL URLWithString:@""];
    NSString *filePath = @””;

    switch (tag) {
    case 1:
    {
    filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:@”html”];
    break;
    }
    case 2:
    {
    filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:@”html”];
    break;
    }
    case 3:
    {
    filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:@”html”];
    break;
    }
    case 4:
    {
    filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:@”html”];
    break;
    }
    case 5:
    {
    filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:@”html”];
    break;
    }
    case 6:
    {
    filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:@”html”];
    break;
    }
    case 7:
    {
    filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:@”html”];
    break;
    }
    case 8:
    {
    filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:@”html”];
    break;
    }
    case 9:
    {
    filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:@”html”];
    break;
    }
    case 10:
    {
    filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:@”html”];
    break;
    }
    case 11:
    {
    filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:@”html”];
    break;
    }
    case 12:
    {
    filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:@”html”];
    break;
    }
    case 13:
    {
    filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:@”html”];
    break;
    }
    case 14:
    {
    filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:@”html”];
    break;
    }
    case 15:
    {
    filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:@”html”];
    break;
    }
    case 16:
    {
    filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:@”html”];
    break;
    }
    case 17:
    {
    filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:@”html”];
    break;
    }
    case 18:
    {
    filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:@”html”];
    break;
    }
    case 19:
    {
    filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:@”html”];
    break;
    }
    case 20:
    {
    filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:@”html”];
    break;
    }

    default:
    break;
    }

    url = [NSURL fileURLWithPath:filePath];
    LocalViewController *vc = [[LocalViewController alloc] initWithNibName:@”LocalViewController” bundle:nil url:url];
    [self.navigationController pushViewController:vc animated:YES];

    }

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

Back to top