How To Create an App Like Instagram With a Web Service Backend – Part 1/2

Marin Todorov

This post is also available in: Chinese (Simplified)

Learn how to make a fun app with a web service backend!

Learn how to make a fun app with a web service backend!

This is a blog post by iOS Tutorial Team member Marin Todorov, a software developer with 12+ years of experience, an independent iOS developer and the creator of Touch Code Magazine.

There’s no doubt photography apps have huge momentum on the App Store. With the iPhone 4’s awesome camera and fast processor, taking photos and applying various effects is a blast.

You guys requested a tutorial on how to create a photo application that pairs with a web service as a backend, and your wish is our command! :]

In this tutorial, you’ll learn how to make a simple photo sharing app, like an extremely simple version of Instagram. In particular, you’ll learn:

Using a blank startup project that has all the UI already set up, this tutorial will cover how to:

  • How to connect to a JSON-based web API in Objective-C
  • How to create a simple JSON API in PHP
  • How to implement user authorization for the API
  • How to take photos, apply effects and send them over to the JSON service.

Quite a lot of cool stuff, eh? ;]

This tutorial assumes you have prior familiarity with iOS 5 technologies like Storyboards and ARC. If you are not yet familiar with these, now’s the time – check out this tutorial or this tutorial.

You will also need access to a web server with MySQL running. If that sounds intimidating, check out this project, which makes it pretty easy to run a local test server on your Mac.

If you are still feeling insecure about the process of setting up a web server on your Mac, have a look at this great tutorial, which covers all the basics of setting up your development environment.

Without further ado, strike a pose for the camera – and let’s get started!

Getting Started

Download the startup project and extract the contents of the ZIP file to a convenient location on your hard drive. It includes quite few things, so I’ll briefly go over what’s inside.

The project you’re going to develop in this tutorial is called iReporter. It’s an app that gives you the ability to see a stream of all users’ photos, and if you’d like to, register to upload your own photos to the stream.

iReporter has in total four view controllers. Have a look at Storyboard.storyboard in the project file list to get a feeling of what the workflow looks like:

The startup project storyboard

The initial view controller is a navigation controller that shows a screen called StreamScreen after the app launches. This is where you are going to show the stream of all user-uploaded photos.

If you follow the segue going to the screen below, you get to a controller called StreamPhotoScreen. This screen will show up when the user taps a photo thumbnail, and will show a larger-size photo and the photo title.

From StreamScreen there’s a second segue going to the right to a screen called PhotoScreen. On PhotoScreen, you’ll show an action sheet and allow the user to take a photo, apply effects to it and finally post it to the web API.

To be able to do the above, the user will have to authorize themselves to the API. That means when the PhotoScreen appears, in case the user hasn’t logged in yet, you will show a modal screen with the LoginScreen view controller asking the user to either login or register.

Lucky for you, the startup project already includes classes for all of those screens, including connected IBOutlets and IBActions (empty bodies which you will implement during the tutorial).

There are a few more files in the startup project. Inside “Categories,” you’ll find the UIImage categories from Trevor Harmon. You’ll need these to easily resize and crop images. As a side benefit, Trevor’s categories fix the faulty image orientation that sometimes comes from iPhone’s camera! So thanks again Trevor!

I also included a small category which facilitates showing UIAlertView on the screen with a simple message.

As you can see, you’re off to a good start :] Next, you’ll add a third-party library to the project that will handle all the networking for you.

Outsourcing Your App’s Social Life

You can use the good old NSURLConnection class provided by iOS to handle the communication with the web API. But it’s 2012… let’s do something sexier :]

Of the few up-and-coming networking libraries out there for Objective-C, it seems AFNetworking has the most momentum right now, so you’re going to use this one as well. Head over to the AFNetworking Git and download the current version. (Short docs are included on the page, too.)

After downloading, inside the AFNetworking folder you’ll find a subfolder called “AFNetworking.” Just drag and drop it into your project file list. You should get a prompt similar to the following:

Instagram1

Make sure that “Copy items into destination group’s folder (if needed)” is checked so that the AFNetworking files will be copied into your project folder. Click Finish, and you should see the AFNetworking files included in your files list.

AFNetworking files

AFNetworking is not ARC-compatible, but your skeleton project is set to use Automatic Reference Counting (ARC). So you’ll have to set the AFNetworking classes to be non-ARC so that the compiler knows how to handle them.

Select the Xcode project root (as in the picture above), and then switch to the “Build Phases” tab in the right-hand pane. Find and open the “Compile Sources” strip and you’ll see the classes to be compiled within your project.

Scroll down. At the bottom you’ll see all the files for AFNetworking (from AFHTTPClient.m to UIImageView+AFNetworking.m). Select them all (as in the image below), press Enter on the keyboard and in the popup enter “-fno-objc-arc” and click Done. All the AFNetworking files should now be marked as not supporting ARC.

Instagram2

Press Cmd+B now to build the project. If everything is set up correctly, the project should compile successfully. (Except, that is, for a few warnings in the UIImage category classes from Trevor. You can disregard these.)

Note: When you include AFNetworking in your project, you have to go to your project’s .pch and add “#import <SystemConfiguration/SystemConfiguration.h>.” Otherwise, AFNetworking won’t compile. This is already done for you in the startup project, but it’s good to know for the future.

Setting Up the Web API

Now for the other side of the project – the web API. For this, you will need an HTTP server and FTP access to it, and also access to a MySQL server. If you don’t already have those on your Mac, check the link at the top of the article for more details on setting up a local test server on your Mac.

I’ve also prepared a startup project for the web API, so once you have the web server running, go ahead and download the API startup project. Extract the contents of the ZIP file to a location on your hard drive.

The extracted ZIP should contain a few PHP files and a folder named “upload.” The main API file is index.php, and it’s the one you’re going to call from the iPhone app. In api.php, you’re going to add few simple functions to process the user requests, and lib.php contains a couple of functions that will help you with this.

One of them, a function called query, will help you avoid writing too much code. It takes an SQL query and a list of parameters as input, and returns an array containing the results of the SQL query. There’s also a function called thumb that scales down an image and saves the thumbnail.

Upload the folder with the api files to a location on your web server that can be accessed via a browser. Rename the folder to “iReporter,” since that will make things easier to identify. (If you’re doing this on your own machine at home, you can of course just copy the files in Finder to your web folder.) Make sure that the “upload” folder is writable via your PHP scripts, as you’ll be using the folder to save uploaded photos.

Note: You’ll be modifying some of the PHP files for the web service as you proceed through the tutorial. Do note that if you don’t have the web server set up on your Mac and are using a remote server, you’ll need to re-upload the modified files to the server each time you modify them.

Finally, set up the database for the web API. You need two simple tables – one to hold the username and passwords and one to hold the photos. The database table structures are shown below (it’s pretty simple):

SQL database structure

Here’s the SQL file to create the database tables (name the database “iReport”).

Now take one extra step: open up lib.php in your text editor of choice, and have a look at the first couple of lines of code. Depending on how you set up your MySQL server (or how it’s set on your hosting server), you need to edit this code so that PHP knows how to connect to your database server.

The first function, mysqli_connect, takes three parameters. Fill in this information: the first parameter is the name of the database server (leave “localhost” if you’re connecting to a server on your own computer), the second is the MySQL username, and the third is the password. I’ve input some default values working with a blank local installation of MySQL, but as mentioned, you might need to edit those to work with your setup.

On the second line of lib.php, there’s the mysqli_select_db function that takes two parameters: the already-active link to the database server, and the name of the database. If you called your database something other than iReport, change the database name here.

Awesome! You now have skeleton iPhone and web service projects. All that’s left is to write some code to get them to work together.

Registering Users to the Web Service

The plan for the app is to let users upload photos only after they have registered with the app. So the app workflow will go like this:

App workflow

Let’s start with implementing the registration and login to the server. Open up index.php in your favorite text editor. On the line where it says “//API”, add a check for all the possible commands that the API will handle.

Start with register and login. Replace the comment “//API” with:

switch ($_POST['command']) {
	case "login": 
		login($_POST['username'], $_POST['password']); break;
 
	case "register":
		register($_POST['username'], $_POST['password']); break;
 
}

If the POST parameter named “command” holds the login value, you pass the username and password POST parameters to a function called login. The register parameter is treated pretty much the same way, except that the function called is named register. If the client asks for a command the API doesn’t expect, the code execution will simply reach the exit() command at the end of the source file, and no response will be sent to the client.

Note: Don’t panic, we won’t be passing or storing the password as cleartext. (And neither should you!) More on this later :]

Now start with the register command. Open up api.php and add a little helper function for returning errors to the client (the iPhone app). Add this function to api.php within the PHP script tags (“<?” and “?>”):

function errorJson($msg){
	print json_encode(array('error'=>$msg));
	exit();
}

Since the iPhone app expects only JSON responses, you’ll use this little function, which takes a text message and turns it into a JSON response. json_encode is the PHP function that converts PHP objects to JSON – you just feed it an array with the error message, and that’s all.

Now proceed with the register function. First, check if the username exists already. Add the following code to api.php:

function register($user, $pass) {
	//check if username exists
	$login = query("SELECT username FROM login WHERE username='%s' limit 1", $user);
	if (count($login['result'])>0) {
		errorJson('Username already exists');
	}
 
}

On the first line, you create an SQL query to check if there’s a record in the login table with the username given. If there are results, you call errorJson, since you cannot register a user if the username already exists. And that pretty much ends the script, as errorJson dumps the error message and exits the program execution.

Note how the count function in PHP was used to check if there were any results – count returns the number of elements in an array.

OK, so things aren’t so difficult. Add the following to the end of the register method (before the closing curly bracket):

//try to register the user
$result = query("INSERT INTO login(username, pass) VALUES('%s','%s')", $user, $pass);
if (!$result['error']) {
	//success
	login($user, $pass);
} else {
	//error
	errorJson('Registration failed');
}

You use the query helper function again with an SQL command trying to insert a new record to the database. If the query doesn’t return an error (AKA, big success for you) then you just call login.

Why do you call login? Well, you’d like the user to be logged in after successful registration, so why not combine both into one call to the API, right? OK. Time to move to the login function. Wow, you’re moving ahead quite quickly :]

Here’s the whole login function. Add it to api.php, immediately after the register function:

function login($user, $pass) {
	$result = query("SELECT IdUser, username FROM login WHERE username='%s' AND pass='%s' limit 1", $user, $pass);
 
	if (count($result['result'])>0) {
		//authorized
		$_SESSION['IdUser'] = $result['result'][0]['IdUser'];
		print json_encode($result);
	} else {
		//not authorized
		errorJson('Authorization failed');
	}
}

That looks pretty similar to what you already did for register, right?

You run a SELECT SQL command on the login table, and then you check the count of the returned results. If any were found, you have a successful login. Then to create a server session for the logged user, you store their IdUser to the PHP session.

$_SESSION is a special array – everything you store inside will be persisted between different script executions – but only when they were made by the same client. So what you store in the SESSION array is the value of “$result['result'][0]['IdUser']“, which in PHP reads “the value of the IdUser key of the first element of the array stored in the result key.” Or to put it simply, you store the user’s ID, so you know who they are in the following API calls.

Believe it or not, that’s all the PHP code you need to have users register and login to your web service.

Let’s move to the iPhone – because I know just how you’re feeling! ;]

Finally Objective-C!!!

Making Friendly with the Server

Woohoo! You can finally switch to Xcode and do some Objective-C! (Your favorite programming language, right?)

First of all, you need to create a class to talk to the API for you. After a lot of thinking, I decided you should call it “API” :]

So choose from Xcode’s menu “File/New/File…” and choose iOS\Cocoa Touch\Objective-C class as the file type. Click Next, enter “API” as the class name, make it subclass of AFHTTPClient and save it to your project folder.

Since this will be a class that talks to the server over the network, you’re directly subclassing the AFHTTPClient, and you’ll use its capabilities to make POST requests and other goodies.

Open API.h and add this import at the top to use all of the AFNetworking functionality:

#import "AFNetworking.h"

Then above the @interface line, add a define:

//API call completion block with result as json
typedef void (^JSONResponseBlock)(NSDictionary* json);

The above adds a definition of a block, which receives an NSDictionary containing JSON information as a parameter. You use this kind of block for all calls to the API, since the server always returns a JSON response.

Inside the interface definition add the following property to hold the user data, once the user has been authenticated:

//the authorized user
@property (strong, nonatomic) NSDictionary* user;

Then… you’d like to be able to use this class from all the screens in the app. The easiest way to manage this is to make it a Singleton class, so you need a method to access the static instance of the class. Add this method definition above the @end line:

+(API*)sharedInstance;

Finally, you need to add two more methods to get past the user authorization phase (still in API.h). One will be a method to check whether the user is authorized, and the other will be a generic method you will have in the API class to execute commands on the web server.

//check whether there's an authorized user
-(BOOL)isAuthorized;
 
//send an API command to the server
-(void)commandWithParams:(NSMutableDictionary*)params onCompletion:(JSONResponseBlock)completionBlock;

Next comes the API class implementation! Open API.m, and just above the @implementation directive add these defines:

//the web location of the service
#define kAPIHost @"http://localhost"
#define kAPIPath @"iReporter/"

For the above defines, you will have to change the domain and path if you have any setup other than calling the API from your local machine. The code above will talk to the API if it’s set up exactly as I explained in the text above – local machine web server and subfolder iReporter in the root of the default web site.

Otherwise, you’ll need to change the host and path according to your setup. If you set up a custom domain for this tutorial, and the API files are in the root of the domain instead of being inside a sub-folder, set the path to an empty string.

Just below the @implementation directive, add the user property synthesizer and the method to get the static class instance:

@synthesize user;
 
#pragma mark - Singleton methods
/**
 * Singleton methods
 */
+(API*)sharedInstance
{
    static API *sharedInstance = nil;
    static dispatch_once_t oncePredicate;
    dispatch_once(&oncePredicate, ^{
        sharedInstance = [[self alloc] initWithBaseURL:[NSURL URLWithString:kAPIHost]];
    });
 
    return sharedInstance;
}

sharedInstance creates an instance of the API class the first time it’s called, and any subsequent call to the method will just return that instance. There are many different ways to create a singleton class in Objective-C, but since iOS 4.0 (when Grand Central Dispatch or GCD was introduced), this is one of the most straightforward ways to do it. The call to dispatch_once ensures that the shared instance creation is executed only once.

You’ll need a custom init, but it’s gonna be pretty easy. You need to register a default HTTP operation class (you’ll use the default) and instruct the API class to accept only JSON as response. Add this to the end of the file (but before the @end):

#pragma mark - init
//intialize the API class with the destination host name
 
-(API*)init
{
    //call super init
    self = [super init];
 
    if (self != nil) {
        //initialize the object
        user = nil;
 
        [self registerHTTPOperationClass:[AFJSONRequestOperation class]];
 
        // Accept HTTP Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
        [self setDefaultHeader:@"Accept" value:@"application/json"];
    }
 
    return self;
}

The user property just holds the response from the login/register API calls, so that’s a dictionary with all the database columns for the current user (the ones you get back from the API). Here’s a simple check to see if the user is authorized:

-(BOOL)isAuthorized
{
    return [[user objectForKey:@"IdUser"] intValue]>0;
}

Just checking if the login/register call returned anything, and if so whether the IdUser column is a number greater than zero – then the authorization was successful.

All that’s left is the implementation of the call to the server.

Add the following code to the end of API.m:

-(void)commandWithParams:(NSMutableDictionary*)params onCompletion:(JSONResponseBlock)completionBlock
{
    NSMutableURLRequest *apiRequest = 
        [self multipartFormRequestWithMethod:@"POST" 
                                        path:kAPIPath 
                                  parameters:params 
                   constructingBodyWithBlock: ^(id <AFMultipartFormData>formData) {
                       //TODO: attach file if needed
    }];
 
    AFJSONRequestOperation* operation = [[AFJSONRequestOperation alloc] initWithRequest: apiRequest];
    [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
        //success!
        completionBlock(responseObject);
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        //failure :(
        completionBlock([NSDictionary dictionaryWithObject:[error localizedDescription] forKey:@"error"]);
    }];
 
    [operation start];
}
  1. First you create an NSMutableURLRequest instance using the parameters you want to send via POST. Note that it’s a mutable instance – you don’t need that just now, but later on when you are adding file upload capability to the API, you’ll need to adjust the request after it has been created; so you just have it ready for the future.
  2. Next you create an operation to handle the network communication in the background and initialize it with the POST request you’ve already prepared.
  3. After the operation is initialized, you set the two blocks to execute on success and failure. When the call is successful, you just pass in the JSON response.
  4. If there is an error and the failure block gets called, you construct a new dictionary holding the message of the network error, and you pass it back – pretty easy.
  5. Finally, you call the start method on the operation, and at that point AFNetworking starts performing its magic in the background.

This is almost the complete API class you’re going to need in your project. As you can see, it’s pretty simple. You’ll make few little adjustments later on, but for now – you’re good to go!

Getting a Little Salty

Select Storyboard.storyboard, and notice how the Photo Stream presents the modal Login dialogue. Next you’re going to authorize the user on this screen, by asking them to login or register with the application.

Photo!

Switch to PhotoScreen.m (it’s inside the Screens folder) and import the API class by adding the following code under the existing #import statement:

#import "API.h"

The above allows you to refer to the API class from within PhotoScreen.

Next, at the end of viewDidLoad, add this code to check whether the user is already authorized:

if (![[API sharedInstance] isAuthorized]) {
    [self performSegueWithIdentifier:@"ShowLogin" sender:nil];
}

Here you just call isAuthorized, which you added just moments ago to the API class. If the user is not authorized, you call the segue called ShowLogin to show the Login screen modally.

If you want to have some fun, run the project and tap on the top right button – this should show the login screen! Fun!

Login or register screen

Of course, if you did the above, then you noticed that neither the Login nor Register buttons work. So switch your focus to the Login screen and add some functionality to it!

First, add a quick usability enhancement. Add a viewDidLoad method like this one to LoginScreen.m just below the @implementation line:

-(void)viewDidLoad {
    [super viewDidLoad];
 
    //focus on the username field / show keyboard
    [fldUsername becomeFirstResponder];
}

Nice! The user doesn’t need to tap on the username field; the keyboard just pops up by itself. That was easy – let’s go on!

At the top of the source file, just below the two import directives add:

#import "API.h"
#include <CommonCrypto/CommonDigest.h>
 
#define kSalt @"adlfu3489tyh2jnkLIUGI&%EV(&0982cbgrykxjnk8855"

Whoa there! What’s that?!

First you import the API class. Then you include the built-in iOS cryptographic library. Okay, makes sense…

The string that looks like rubbish is a salt string that you’re going to use to make the user password a wee bit more difficult to hack. Instead of passing the plaintext password to the server for anyone who dumps the database to easily see, we’ll munge it a bit instead using a technique called hashing with salt.

This is not bulletproofing your server-client model at all, but it’s a good start. If you want to know more about salting the passwords you’re storing, have a look at salting passwords and rainbow tables.

Note: The focus of this tutorial is not on security, so hashing the password with salt is as far as we’re going to go here. Sometime soon, we hope to write another tutorial specifically focused on username/password security with respect to web services ;]

Look for a moment at the login screen. You always have the same text fields: username and password. The only difference between the login and register use cases is the actual call to the API: otherwise the data sent over is the same.

This means you only need one method to handle taps on both the login and register buttons. To determine whether to send a login or register command to the server, you’ll just check which button was tapped.

Both the login and register buttons send a btnLoginRegisterTapped: message to the view controller class. This is going to be a rather long method, so you’ll go through each step carefully. Inside the empty btnLoginRegisterTapped: in LoginScreen.m, add this code:

//form fields validation
if (fldUsername.text.length < 4 || fldPassword.text.length < 4) {
    [UIAlertView error:@"Enter username and password over 4 chars each."];
    return;
}
 
//salt the password
NSString* saltedPassword = [NSString stringWithFormat:@"%@%@", fldPassword.text, kSalt];

First you do a simple check on the entered username and password to determine whether they’re long enough (you could implement more checks if you wish). Then you add the salt to the password string. The next step is hashing the salted password. Add this code to the method:

//prepare the hashed storage
NSString* hashedPassword = nil;
unsigned char hashedPasswordData[CC_SHA1_DIGEST_LENGTH];
 
//hash the pass
NSData *data = [saltedPassword dataUsingEncoding: NSUTF8StringEncoding];
if (CC_SHA1([data bytes], [data length], hashedPasswordData)) {
    hashedPassword = [[NSString alloc] initWithBytes:hashedPasswordData length:sizeof(hashedPasswordData) encoding:NSASCIIStringEncoding];
} else {
    [UIAlertView error:@"Password can't be sent"];
    return;
}

You declare two variables: hashedPassword will hold the string of the hashed and salted password, while hashedPasswordData is a plain C array, which you will use as an intermediate storage for the hashed data.

Using dataUsingEncoding: you get the data bytes from the salted password. Here’s where the magic happens: CC_SHA1() gets the bytes of the salted password, performs SHA1 hashing on them and stores the result in hashedPasswordData. Next, you quite easily create an NSString out of the data bytes. This is the hashed password you will send to the server.

Phew! The heavy work is done. Now you just need to make the call to the API.

First you should detect whether the user tapped the login or register button, and prepare the correct parameters for the API call. Since the register button has a tag of “1″ (this was set via Interface Builder when the skeleton project was originally created), this is quite easy:

//check whether it's a login or register
NSString* command = (sender.tag==1)?@"register":@"login";
NSMutableDictionary* params =[NSMutableDictionary dictionaryWithObjectsAndKeys: 
 command, @"command",
 fldUsername.text, @"username",
 hashedPassword, @"password",
 nil];

Now that you have all the parameters for the call prepared, it’s really easy to make the actual call:

//make the call to the web API
[[API sharedInstance] commandWithParams:params
                           onCompletion:^(NSDictionary *json) {
                           //handle the response
 
                           }];

The result from the server (or an error if there was a communication problem) will be passed as an NSDictionary to the block above. Let’s dive into handling that response. Replace the “//handle the response” comment with:

//result returned
NSDictionary* res = [[json objectForKey:@"result"] objectAtIndex:0];
 
if ([json objectForKey:@"error"]==nil && [[res objectForKey:@"IdUser"] intValue]>0) {
   //success
 
} else {
   //error
   [UIAlertView error:[json objectForKey:@"error"]];
}

json is an NSDictionary and, as per the PHP API code you wrote earlier, it should hold a result key. You fetch another dictionary from within this key – hopefully that’s the info of the logged user!

You then check whether an error has been returned, or if the user info is invalid, and if so, you display an error alert. Otherwise, you log in the user and take him/her to the Photo screen. To do that, replace the “//success” line with:

[[API sharedInstance] setUser: res];
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
 
//show message to the user
[[[UIAlertView alloc] initWithTitle:@"Logged in" 
                           message:[NSString stringWithFormat:@"Welcome %@",[res objectForKey:@"username"] ]
                         delegate:nil 
                cancelButtonTitle:@"Close" 
                 otherButtonTitles: nil] show];

You store the user data to the API class and dismiss the Login dialogue. A nice alert also pops up to congratulate the user by their name. Sweet!

To recap: if the user wants to register with the system, this method will register and log them in at the same time. Alternatively, the user can use an existing user account and password to simply log in to the system. If the login was unsuccessful, you show the error message from the server. It’s all so very easy :]

That’s an awesome job so far! Everything should work right now, so fire up the project and go to the login screen. Choose a username and a password and tap on register. If you have a working server environment, you should see the Login screen disappear and the Photo screen should be now accessible! Awesome!

Logged in the app

I’m Ready for Some Files, Baby

Switching back to the web service, you’re going to go on with the part of the web service that accepts photos submitted by the users and stores them on the server. First you need to add the code to handle the upload command.

Open up index.php and add a new case in the switch command:

case "upload":
	upload($_SESSION['IdUser'], $_FILES['file'], $_POST['title']);break;

In case the upload command of the API is being called, you take the user ID from the user session, and you send it to the upload function along with the submitted file (from the $_FILES array in PHP) and the title the user provided for that photo.

Ah! The upload function. No problem at all.

Open up api.php. Here’s where you’re going to add the aforementioned function. At the bottom of the file add this code:

function upload($id, $photoData, $title) {
	//check if a user ID is passed
	if (!$id) errorJson('Authorization required');
 
	//check if there was no error during the file upload
	if ($photoData['error']==0) {
		$result = query("INSERT INTO photos(IdUser,title) VALUES('%d','%s')", $id, $title);
		if (!$result['error']) {
 
			//inserted in the database, go on with file storage
 
		} else {
			errorJson('Upload database problem.'.$result['error']);
		}
	} else {
		errorJson('Upload malfunction');
	}
}

This is the first part of the code you need in order to do the photo upload. Let’s see where you are:

  1. Since this is a protected method (i.e. accessible only to logged in users) you check if there is a user ID stored in the session (the $id parameter passed to the function).
  2. $photoData (an array) is being populated by PHP with the file upload details. You check the error key to see if the upload succeeded. If there was an error, there’s no sense in going on with inserting data in the database.
  3. If the upload is OK, you go on with inserting the title of the photo and the user ID in the database.
  4. You’re going to use the automatically-created ID in the database table “photos” as an identifier for the photo file itself. You’ll see this in a moment.
  5. If the database insert was successful, you go on with saving the file – you see the comment where the code will be.

As you can see, every step in the upload process so far can fail in a different way, so you already have three if-else statements that use errorJson to return different fail responses. In decoupled systems (like your client-server app), much more can go wrong in the workflow compared to ordinary software, so having good error-handling logic is essential if you want to build even a moderately complicated application.

Now add the code to store the uploaded files on the server.

First you need to get hold of a link to the database, so you can get the automatically generated ID in the photos table. Luckily, lib.php already grabs a link to the database for you, so just use that. The link is called “$link” and is found in the global variable namespace.

Add the code and see how it works. Replace the “//inserted in the database…” comment with:

	//database link
	global $link;
 
	//get the last automatically generated ID
	$IdPhoto = mysqli_insert_id($link);
 
	//move the temporarily stored file to a convenient location
	if (move_uploaded_file($photoData['tmp_name'], "upload/".$IdPhoto.".jpg")) {
		//file moved, all good, generate thumbnail
		thumb("upload/".$IdPhoto.".jpg", 180);
		print json_encode(array('successful'=>1));
	} else {
		errorJson('Upload on server problem');
	};

You call mysqli_insert_id, which returns the last auto-generated ID in the database – namely, the ID of the newly saved photo record.

When you upload a file to the server using PHP, the file is saved to a temporary location on the server, and the path to that location is passed to the PHP script handling the upload via the $_FILES array. You then call move_uploaded_file, which (as the name describes pretty well) moves the newly uploaded photo file from its temporary location to the folder where you want it to be.

You rename the file, following the [database id].jpg format so that the file name always corresponds to the database ID for the file record. Then you call the nifty thumb function to generate a nice thumbnail of the full-size photo.

Finally, if all went well, you return a JSON response with the key “successful.” That lets the iPhone app know that all is well, the photo was uploaded successfully, and that it was saved to the system.

Where To Go From Here?

You have the app talking to the server and that’s pretty cool, so you can play around a bit with sending data over and maybe adjusting the server response.

But, you are far from finished! When you’re ready, Part 2 of this tutorial awaits, where you will develop all the cool functionality of the iPhone, allowing the user to take photos, apply effects and send them over to the server.

In the meantime, if you have any questions or comments please join the forum discussion below!

This is a blog post by iOS Tutorial Team member Marin Todorov, a software developer with 12+ years of experience, an independant iOS developer and the creator of Touch Code Magazine. You can also find me on

Marin Todorov

Marin Todorov is an independent iOS developer and publisher with background in various platforms and languages. 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 and Native Instruments, has lived in 4 different countries, and (more recently) has worked on 10+ titles on the App Store.

Besides crafting code, Marin also enjoys teaching and training others. He sometimes speaks at iOS conferences.

Throughout his career he has authored or contributed to 7 great book titles, including "iOS7 by Tutorials", "iOS6 by Tutorials", "iOS Games by Tutorials", and O'Reilly's "Adobe AIR Cookbook". He maintains Touch Code Magazine, his tech blog about iOS development and also has a successful video course on game programming.

User Comments

107 Comments

[ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ]
  • I

    @property (nonatomic,strong) dispatch_queue_t completionQueue;
    thewildcloud
  • i am stuck at AFHTTPRequestOperationManager.h

    error message: property with strong(retain) must be object.
    thewildcloud
[ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ]

Other Items of Interest

Ray's Monthly Newsletter

Sign up to receive a monthly newsletter with my favorite dev links, and receive a free epic-length tutorial as a bonus!

Advertise with Us!

Hang Out With Us!

Every month, we have a free live Tech Talk - come hang out with us!


Coming up in September: iOS 8 App Extensions!

Sign Up - September

RWDevCon Conference?

We are considering having an official raywenderlich.com conference called RWDevCon in DC in early 2015.

The conference would be focused on high quality Swift/iOS 8 technical content, and connecting as a community.

Would this be something you'd be interested in?

    Loading ... Loading ...

Our Books

Our Team

Tutorial Team

  • Sam Davies
  • Jake Gundersen

... 49 total!

Update Team

Editorial Team

... 23 total!

Code Team

  • Orta Therox

... 1 total!

Translation Team

  • Wilson Lin

... 33 total!

Subject Matter Experts

  • Richard Casey

... 4 total!