These notes introduce Objective-C and iOS application development in the context of an iPhone application that allows the user to decrypt and encrypt messages with Caesar cipher.

In order to develop iOS applications, you need a Mac and Xcode. These notes cover iOS 8 SDK, for which you’ll need Xcode 6 and OS X 10.9.4 or later.
Build or compile your code frequently to detect any errors early! Don’t proceed if you have warnings or errors.
iphone 3

Objective-C

iOS applications are usually written in Objective-C and more recently in Swift. Swift was developed by Apple over the course of the past several years and released to public in summer 2014. It “adopts safe programming patterns and adds modern features to make programming easier, more flexible and more fun,” according to Apple. Objective-C is an older language, designed in the earliy 1980s and based on a language called SmallTalk-80. Objective-C is a strict superset of C, a language on which C++ was based. Similarly to C++, Objective-C added extensions to C to create a new programming language that enabled objects to be created and manipulated.

Command Line Application

  • Let’s start by creating a command line application where we can write and test some code. Launch Xcode, select Create a new Xcode project, choose OS X and Application on the left, and then select Command Line Tool. You can give your project any name, but here, we’ll call it CaesarTests. Be sure to select Objective-C as the language.

    xcode project options
  • Notice how Xcode has automatically create a file called main.m for you. .m is the extension in Objective-C that’s equivalent to .cpp in C++ and m comes from implementation. If you compile and run the project, you’ll see a message that includes Hello, World! in the console on the bottom of Xcode’s widow:

    xcode hello world
  • Let’s look at the code that Xcode has given us, top to bottom.

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
    // // main.m // CaesarTests // // Created by Maxim Aleksa on 12/20/14. // Copyright (c) 2014 Maxim Aleksa. All rights reserved. // #import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { @autoreleasepool { // insert code here... NSLog(@"Hello, World!"); } return 0; }
  • Notice how on lines 1 through 7 comments are started with two forward slashes (//). And just like in C++, one can have multi-line comments between /* and */.

  • Like 9 has a #import statement. It’s similar to a #include, but smarter. It will only import a file once when the program compiles, even if multiple files reference it.

    Foundation is a library that gives you many classes and functions, including NSLog(). In fact, you’ll find that many identifiers begin with NS, as NeXT Software licensed the Objective-C language in 1988 and developed its libraries and a development environment called NEXTSTEP.

  • Line 11 begins the main function, which is the entry point of the program. Notice that it takes to arguments: an integer and an array of char *s (more on this later). This is a way to pass command line arguments to a program, so that it can receive input even before it enters main. You can do the same in C++, and Xcode has been giving you a main with argc and argv every time you create a new C++ project.

  • Line 12 reads as follows: @autoreleasepool {. Any program statements between the @autoreleasepool { and the matching closing } are executed within a context known an autorelease pool. The autorelease pool is a mechanism that allows the system to efficiently manage the memory your application uses as it creates new objects (again, more on this later). This is a fairly recent addition to the language and you might think that it’s tedius to do these few extra steps in the beginning and at the end of main, but it fact it takes care of many headaches of dynamic memory that you’d have to deal with in C++ (more on these ones in EECS 280!).

  • Line 14 calls NSLog(). Like cout, you can use it to print messages to the console, though its usage is more similar to C’s printf(), if familiar. It takes a string object (which begins with @" and ends in " in Objective-C) and prints (logs) it along with some other information, such as the current time.

  • Finally, main returns 0 on line 16, which means all went well.

  • Hopefully, Objective-C is making sense to you thus far. If not, step back and try to understand this small program.

Pointers

  • To write Objective-C code you need to understand what pointers are and how they relate to computer’s memory. Let’s try to explain them in the context of C++. First, remember that code and data for your program are stored in random-access memory (RAM).

  • So far when you have been declaring variables (either of privitive types, e.g. ints, or objects, e.g. strings), you’ve been doing it like this:

    int courseNumber = 203;
    string courseTitle = "Discrete Mathematics";
  • And if you declared them inside of a function, they disappeared, unless you returned their copies. These variables live in the part of computer’s memory known as the stack. You can think of the memory devided in two main parts: the stack, where the local variables in functions are stored and deleted as the functions return, and the heap, where memory can be dynamically allocated to store values. The stack and the heap grow toward each other as more memory is allocated to hold variables, as illustrated by this diagram from Cornell:

memory layout
  • You can also think of computer’s memory as a huge array of 1 byte (8 bits) blocks. Each block is associated with a hexadecimal number that represents its memory address.

    memory array
  • Just as ints are variables that store integers and doubles are variables that store numbers with a decimal point, a pointer is a variable that stores memory addresses. On modern Macs (a 64-bit computers), a memory address is 8 bytes, so it makes sense that a pointer is also 8 bytes. Pointers thus allow you to keep references to other variables.

  • Let’s look at how to declare a pointer in C++:

    <type> *<variable name>;

    Note that * can either be next to the type and have a space on the right size, be next to the variable name and have a space on the left side, or have a space on each side.

  • Let’s look at a few examples:

    int *p0;
    double *p1;
    char *p2;
    string *p3;
  • Remember that a pointer’s value is a memory address. Its type then describes the data located at that address. So in the first example, int *p0 declares a pointer to 4 bytes of memory that will contain an integer. Similarly, p1 is a pointer to (can hold the memory address of) a double, p2 is a pointer to a char and p3 is a pointer to a string.

  • Remember char * from earlier? It turns out that it was simply a pointer to a character. So argv (short for argument vector), the second argument of main, was simply an array of pointers to characters. An array can also be though of as a pointer, since both the variable that represents an array and a pointer refer to a memory address. So argv is just a two dimensional array of chars, or simply an array of C-style strings!

  • Anyway, by default, pointers will have invalid memory addresses, so we should only use them after we make them pointer to either an existing variable or to a dynamically-allocated chunk of memory on the heap. Here is how to allocate memory on the heap in C++ and make a pointer point to that memory:

    int *myDynamicNumber = new int
    Since new is a feature of C++ and not of Objective-C or C, the above code will not work in the project we created earlier.

    This will allocate 4 bytes of memory on the heap (remember that other part of computer’s memory?) and myDynamicNumber will be a pointer to that memory address. And so the only way to reference that memory or the integer value that is stored in that part of memory is with the pointer myDynamicNumber. Here’s how to store a value:

    *myDynamicNumber = 5;

    Here the is the *dereference operator that allows us to manimulate the memory that myDynamicNumber points to instead of the pointer itself (which would mean that we modify the memory address it holds and it would point to a different address). We can also create an integer on the stack (just like the usual) and assign to it the value of the dynamically created integer:

    int x = *myDynamicNumber;
  • Whenever we finish using that chunck of memory, we have to deallocate it, so that the operating system can give it to other programs and processes that might need it.

    Not deallocating dynamically-allocated memory can really slow down the user’s computer. Whenever you see the spinning beach ball and your computer is very slow, it’s running low on memory, which was probably caused by a programmer who was not careful enough to deallocate the memory he claimed.

    To deallocate the memory to which myDynamicNumber points (a.k.a. tell teh operating system we no longer need that memory), use the delete keyword:

    delete myDynamicNumber;

    Note that this is not doing anything with myDynamicNumber, which still points to the same memory address. All we did was tell the operating system that we no longer wanted that memory.

  • Finally, curl up with Binky for some pointer fun: http://www.cs.stanford.edu/cslibrary/PointerFunCppBig.avi. (Yes, that’s computer science at Stanford.)

Stop! Don’t proceed further until you have a basic understanding of pointers or until you appreciate this comic from http://xkcd.com/138/:
pointers
  • Remember that since large objects can take a really long time to copy if we pass or return them by value, and since we want to be able to create objects inside of functions, Objective-C uses exclusively pointers to objects, and this is why knowing pointers is important. But don’t be discouraged if pointers don’t quite make sense to you. Just know that whenever you deal with objects in Objective-C, you’ll be dealing with pointers, so their type will be <ClassName> *.

  • Similarly, don’t be worried about having to deallocate memory. You might have had to a few years ago, but no more! Objective-C has introduced automatic reference counting (ARC), which keeps track of all the strong pointers and deallocates memory whenever an object on the heap loses all strong pointers to it. This is what @autoreleasepool is for.

Syntax

Let’s get back to objective-C and our Xcode project! Even though you’re about to dive into a new programming language, you are already familiar with some of Objective-C syntax, since just like C++ it derives from C.

Variables

  • Objective-C provides four basic data types: int, float, double and char. You can declare, initialize and manipulate them just like you can in C++:

    int thisCourse = 183;
    int nextCourse = 203;
    int diff = nextCourse - thisCourse;
  • There is also BOOL, which is either YES or NO (though it’s really a signed char underneath the hood).

Strings

  • When you created a command line tool application in Xcode, it gave you this line of code:

    NSLog(@"Hello, World!");

    @" and " enclose a string object. The Foundation framework supports a class called NSString for working with character string objects. Whereas C-style strings consist of char characters, NSString objects consist of unichar characters. A unichar character is a multibyte character according to the Unicode standard. This enables you to work with character sets that can contain literally millions of characters. There are also some other types, like unichar, a multibyte character according to the Unicode standard.

  • Let’s modify our program by creating an NSString object (and a pointer thereto!) and then passing it to NSLog:

    #import <Foundation/Foundation.h>
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
    
            NSString *str0 = @"Hello from EECS 183.";
            NSLog(str0);
        }
        return 0;
    }

    Notice that you get a warning from Xcode: Format string is not a string literal (potentially insecure). NSLog() likes to take literal strings, so we’ll replace it with a literal format string—a string where whose parts come from values of other variables and objects. To fix it, we just need to replace

    NSLog(str0);

    with

    NSLog(@"%@", str0);
  • %@ just means that there you will insert an object. That object (str0) comes after the string literal.

  • If you wanted to insert an integer instead of an object, you could do something like this with %d, which is in fact used for inserting decimal integers:

    #import <Foundation/Foundation.h>
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
    
            NSString *subject = @"EECS";
            int courseNumber = 183;
            NSLog(@"Hello from %@ %d!", subject, courseNumber);
        }
        return 0;
    }

    As you might guess, this would print

    Hello from EECS 183!
  • %f is used to insert doubles and %c is for characters.

Loops

  • Loops in Objective-C are very similar to those is C++. Here’s what a for-loop that prints Hello World! ten times might look like:

    for (int i = 0; i < 10; i++)
    {
        NSLog(@"Hello world!");
    }

Conditions

  • If/else statements are also similar to C++:

    if (x < 0)
    {
        NSLog(@"x is negative");
    }
    else if (x > 0)
    {
        NSLog(@"x is positive");
    }
    else
    {
        NSLog(@"x is zero");
    }

Classes

  • Classes and objects are a very important part of Objective-C. They are use for mostly everything and it’s vital that you understand the principles of object-oriented programming before you begin building applications in Objective-C.

  • So let’s create a class to represent a message that can be encrypted or decrypted using a Caesar cipher. We’ll put it in two separate files, Message.h and Message.m. Message.h will be the header file and will hold the public interface of the class. This is the only file that the user’s of the class should know about. Message.m will hold the implementation of the class, as well as the private interface.

    In the menu bar, select File > New > File…. Choose OS X and Source on the left (since we’re still working on the command line application). Then choose Cocoa Class. Name the class Message. By default, most classes (including NSString) derive from NSObject, so leave it as Message’s superclass, but be sure that Objective-C is selected as the language.

    If you’re not familiar with subclasses, superclasses and polymorphism in general, don’t worry too much, though you’ll probably want to learn about it if you want to make larger applications.
    xcode new message class
  • Once you create this class, you’ll see both Message.h and Message.m in the Navigator area on the left side of Xcode. Click on Message.h and you’ll see that Xcode has given you some code to start with.

  • It’s much easier to work with class files when you have the header file on the left side of the window and the implementation file on the right side of the window. Simply click on assistant-editor-button in the top-right corner to enable assistant editor and you shoud get a window that looks like this, with Message.h on the left and Message.m on the right:

    xcode message class 0

Interface

  • Let’s begin working on the public interface of the Message class. First notice that Xcode automatically decided to #import <Foundation/Foundation.h> for you. It will provide everything we’ll need to create our class. And remeber that #import ensures that the library will be imported just once when we compile.

  • Then we have

    @interface Message : NSObject
    
    @end

    @interface Message : NSObject begins the public interface of a class called Message that derives from the class NSObject. This would be equivalents to these lines of C++ code:

    class Message : public NSObject
    {
    public:
    
    };

    All the public declarations will go between @interface Message : NSObject and @end. Note that only public interface is defined in an Objective-C header file.

  • Fundamentally, our Message class needs to know just one piece of information: the message itself. The class should then be able to encrypt or decrypt with a key that the user of the class provides.

  • In C++ we would declare a private member variable of type string and we would create public setter and getter functions. In Objective-C, by contrast, we create a @property, which automatically generates setter and getter methods for us:

    @property (strong, nonatomic) NSString *message;

    Notice that message is a pointer to an NSString object. Also within the paretheses there is a list of attributes for the propery. The strong attribute is used for dynamic memory management and says to make an additional reference to the object whenever the setter method is used. This also helps the operating system free memory when a dynamically-allocated object is no longer being referenced. The nonatomic attribute says that you don’t need to worry about race conditions that could occur from multiple threads trying to access the instance variable at the same time. Unless you copy objects, you shouldn’t be worrying about this.

    The name of the instance (member) variable created by the @property is actually _message and underneath the hood Objective-C is creating these setter and getter methods:

    // setter
    - (void)setMessage:NSString *message {
        _message = message;
    }
    
    // getter
    - (NSString *)message {
        return _message;
    }
  • You might find Objective-C syntax for methods (member functions) a bit stange, but it doesn’t take that long to get used to. - means an instance method, a method that’s applied to a particular object upon which the method is called (just like all member functions work in C++). This is in constrast with class methods that begin with a + are called on a class. Class methods cannot access instance variables created with properties and are usually utility and helper functions. We’ll see an example later.

    Then comes the return type in parentheses, followed by the method’s name. As you’ll find later, arguments are interspersed throughout the method name. So if you had a method that took two arguments, its signature could look something like this:

    - (void)aMethodThatTakesAWord:NSString *word andANumber:int number;

    Note that the method’s name in this case would be aMethodThatTakesAWord: andANumber:.

  • But now, let’s declare two public methods that will work with the Message class. Remember that we need a way to encrypt a message with some key and decrypt a message with some key. The message stored inside the Message object will ultimately remain the same, rather the methods will return the encrypted and decrypted versions thereof, so the are the methods we’ll need are these:

    - (NSString *)encryptWithKey:(int)key;
    - (NSString *)decryptWithKey:(int)key;

    They both take an integer argument, a key, that is applied to the object’s message instance variable and an encrypted or a decrypted version of the message is returned.

  • So this is the entire public interface of Message:

    Message.h
    1 2 3 4 5 6 7 8 9 10
    #import <Foundation/Foundation.h> @interface Message : NSObject @property (strong, nonatomic) NSString* message; - (NSString *)encryptWithKey:(int)key; - (NSString *)decryptWithKey:(int)key; @end
  • Before we proceed, let’s take a look at how we access properties and call methods in Objective-C. Let’s go back to main.m and write some tests for the Message class.

    First, let’s #import "Message.h" (remember to use double quotes for local files) and then let’s create a Message object, which we’ll call secretMessage.

    Although Message class has an instance variable of type NSString, Message itself is not a string, so we can’t just do

    Message *secretMessage = @"hi";

    Since we’re creating a pointer to a Message, we need to first ask the operating system for some memory. We’ll call alloc method on Message class. Notice that alloc is a class method and we’ve never implemented it. It’s inherited from NSObject from which Message derives. Then we need to initialize the newly-created object by calling init instance method, which is also inherited from NSObject. Unlike constructors in C++, you have to call initializers yourself, and the only place you should call them is right after allocating memory (see example below). The syntax for calling methods in Objective-C is the following and method calls can be nested:

    [<object/class> <method name and arguments>];

    And so we create a Message object, allocate memory for it and initialize it all in one line. This is very common in Objective-C.

    Message *secretMessage = [[Message alloc] init];
  • Now let’s create a string that will have our initial plaintext:

    NSString *plaintext = @"Hello world!";

    As a sanity check, let’s make a call to NSLog() and print plaintext.

    NSLog(@"Original: %@", plaintext);

    Run the project and make sure that Original: Hello world! is indeed printed to the console.

  • Now let’s assign that plaintext string to the message instance variable of our Message object secretMessage. Recall that message is a property of Message. Properties are accessed with a dot notation on Objective-C and setters and getters are called automatically:

    secretMessage.message = plaintext;
  • Now let’s create another string, ciphertext, to which we’ll assign the message encrypted with a key of 5. To do that, we’ll declare an int and initialize it to 5 and then we’ll call encryptWithKey: method on secretMessage, passing in th integer:

    int key = 5;
    NSString *ciphertext = [secretMessage encryptWithKey:key];

    Let’s follow that line by another call to NSLog() to test the encryption:

    NSLog(@"Encrypted with a key of %d: %@", key, ciphertext);
  • Time to decrypt! Reset the message instance variable of secretMessage, create a new string deciphertext and log the result:

    secretMessage.message = ciphertext;
    NSString *deciphertext = [secretMessage decryptWithKey:key];
    NSLog(@"Decrypted with a key of %d: %@", key, deciphertext);
  • Let’s add a few more tests. A good idea would be to outsource the testing to a function, and here’s what that would look like:

    main.m
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
    #import <Foundation/Foundation.h> #import "Message.h" void testMessageWithKey(NSString *text, int key); int main(int argc, const char * argv[]) { @autoreleasepool { testMessageWithKey(@"Hello world!", -26); testMessageWithKey(@"Hello world!", -13); testMessageWithKey(@"Hello world!", -1); testMessageWithKey(@"Hello world!", 0); testMessageWithKey(@"Hello world!", 1); testMessageWithKey(@"Hello world!", 2); testMessageWithKey(@"Hello world!", 3); testMessageWithKey(@"Hello world!", 13); testMessageWithKey(@"Hello world!", 25); testMessageWithKey(@"Hello world!", 26); testMessageWithKey(@"Hello world!", 27); testMessageWithKey(@"Hello world!", 52); } return 0; } void testMessageWithKey(NSString *text, int key) { Message *secretMessage = [[Message alloc] init]; NSString *plaintext = text; NSLog(@"Original: %@", plaintext); secretMessage.message = plaintext; NSString *ciphertext = [secretMessage encryptWithKey:key]; NSLog(@"Encrypted with a key of %d: %@", key, ciphertext); secretMessage.message = ciphertext; NSString *deciphertext = [secretMessage decryptWithKey:key]; NSLog(@"Decrypted with a key of %d: %@", key, deciphertext); }
  • Ideally, you’ll have many tests, more that what’s above. And while our program won’t yet work correctly (in fact, it will crash when we run it), writing tests before writing the implementations is a very good practice.

Implementation

  • Now the only task left for us in order to finish the Message class is to implement encryptWithKey: and decryptWithKey:. Well, that’s not quite true. Take a look at Message.m, which should be on the right side of the Xcode window if you enabled Assistant Editor earlier. By default, you’ll get the following lines of code from Xcode, after the header comments:

    #import "Message.h"
    
    @implementation Message
    
    @end
  • As you might guess, the methods' implementation will go between @implementation Message and @end. To declare any private properties or methods (which we’ll do later), you’ll need to add the following lines before @implementation that will extend the Method class:

    @interface Message ()
    
    @end

    All the private properties and methods of Message will go between @interface Message () and @end.

  • Now, let’s start implementing encryptWithKey: and decryptWithKey:. Remember that implementations will go between @implementation Message and @end. We’ll begin with decryptWithKey:, which is quite easy. To decrypt, we’ll just encrypt with the opposite key. In other words, if the user of the class wants to decrypt a message with a key of 5, we’ll just encrypt the message with a key of -5. And this is the implementation:

    - (NSString *)decryptWithKey:(int)key {
        return [self encryptWithKey:-1 * key];
    }

    Notice that when calling methods on the current object, we have to use the keyword self (unlike C++ where we just called the method without an object).

  • Let’s now “stub” encryptWithKey:, so that at least our class doesn’t crash when we try to run our program. We’ll return the actual message stored inside the object, which means that the current implementation will neither encrypt nor decrypt anything.

    - (NSString *)encryptWithKey:(int)key {
        return self.message;
    }
  • We can the program without crashing and all original, encrypted and decrypted messages you see should be just “Hello world!,” since our program isn’t doing any encryption or decryption.

  • Now, let’s work on encryption. It will be a bit different from what you did in the project. It will not convert all lowercase letters to uppercase, nor will it strip non-alphabetical letters. It will just shift all lowercase letters and all upper-case letters, leaving the other characters as they are.

  • First, we need to simplify the key. The user of the Message class can pass in any integer, but the key that’s applied to the characters should only be between 0 and 25. Also make sure that the key is not negative. We’ll leave that to you:

    // TODO: simplify key and put it in range [0, 25]
  • Because NSString is an immutable object, we cannot modify its contents. Nor should we modify the contents of the instance variable, since we’ll be just returning the encrypted string. So let’s put all characters from the message into an array. It will be an array of unichars, since NSString is a string of Unicode characters (recall the discussion earlier).

    We first find out the length of the message. Remember that it’s an NSString, which has a length method. So we first access the message property of the current object (using the self keyword) and then call the method length. We store the length in messageLength, a variable of type NSUInteger, which is similar to an int, but is unsigned. Notice that it’s not an object, so we don’t need a pointer to it.

    NSUInteger messageLength = [self.message length];

    We then create an array of unichars of size messageLength. Unlike C++, Objective-C and have arrays whose size is not known at compile time, so messageLength doesn’t have to be a constant.

    unichar messageArray[messageLength];
  • We then have a for loop that gets a character from the message string using NSString’s characterAtIndex: method and put that character in the corresponding index of the messageArray.

    for (int i = 0; i < messageLength; i++) {
        unichar currentCharacter = [self.message characterAtIndex:i];
        messageArray[i] = currentCharacter;
    }
  • So now we have an array of characters. Note that the characters haven’t been shifted, and so if we create a new string using characters from that array, we’ll essentially get the original string. Let’s do exactly that. It turns out that NSString class has a class method called stringWithCharacters: length: that takes an array of unichars and an NSUInteger and returns an NSString object that represents a string of that length with the characters from the array. So let’s use that method to get an “encrypted” string:

    NSString *encryptedMessage = [NSString stringWithCharacters:messageArray
                                                         length:messageLength];

    Note that it’s very common to line up the semicolons in method names, which tend to be quite long and descriptive.

  • And we can return this “encrypted” message:

    return encryptedMessage;
  • Try running the project again, and you should still get only just “Hello world!” printed to the console, since our program still isn’t doing any encryption or decryption.

  • We now need to add the part that actually does the encryption. Go back to the for loop you wrote earlier that put the characters in the message array. Instead of putting the same characters in the array, we need to shift the letters before inserting them in the array. To keep our code clean, let’s write a helper function that will shift a character by a given key, so it will take to arguments: a unichar and an int. Because we’re inside of a class, we’ll actually write a private class method and this is how we’ll use it in the for loop:

    for (int i = 0; i < messageLength; i++) {
        unichar currentCharacter = [self.message characterAtIndex:i];
        messageArray[i] = [Message shiftCharacter:currentCharacter byKey:key];
    }

    Notice that the method is sent to Message. We could instead write an instance method and send it to self, but a class method probably makes more sense, since we’re not dealing with any instance variables.

  • Let’s declare shiftCharacter: byKey: between @interface Message () and @end in Message.m, as we don’t want it to be in the public interface of Message,

    @interface Message ()
    
    + (unichar)shiftCharacter:(unichar)character byKey:(int)key;
    
    @end
  • Now, write the definition of shiftCharacter: byKey: between @implementation and @end. We’ll leave it as an exercise. Be sure to only shift lowercase and uppercase letters, and leave the other characters as they are. Also be sure to take care of situations where the letters wrap around the alphabet (e.g. z goes back to a).

    + (unichar)shiftCharacter:(unichar)character byKey:(int)key {
    
        // TODO: shift lowercase and uppercase letters, leave other chars unchanged
    
        return character;
    }
  • So that’s it for the implementation of the Message class! Your code should now be similar to the one that appears below, and you should have figured out how to simplify the key and how to shift just alphabetical characters.

    Message.m
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
    #import "Message.h" @interface Message () + (unichar)shiftCharacter:(unichar)character byKey:(int)key; @end @implementation Message - (NSString *)decryptWithKey:(int)key { return [self encryptWithKey:-1 * key]; } - (NSString *)encryptWithKey:(int)key { // TODO: simplify key and put it in range [0, 25] // create empty array to store characters NSUInteger messageLength = [self.message length]; unichar messageArray[messageLength]; // put characters into array for (int i = 0; i < messageLength; i++) { unichar currentCharacter = [self.message characterAtIndex:i]; messageArray[i] = [Message shiftCharacter:currentCharacter byKey:key]; } NSString *encryptedMessage = [NSString stringWithCharacters:messageArray length:messageLength]; return encryptedMessage; } + (unichar)shiftCharacter:(unichar)character byKey:(int)key { // TODO: shift lowercase and uppercase letters, leave other chars unchanged return character; } @end
  • If you run the project now and your implementation is indeed correct, “Hello world!” should be encrypted with keys -1 and 25 to “Gdkkn vnqkc!”, should be encrypted with keys -13 and 13 to “Uryyb jbeyq!” and should stay they same for keys -26, 0, 26 and 52. You’re now ready to build an iPhone app!

Stop! Do not proceed further until your implementation of the Message class is correct.

Intro to iOS Development

Requirements

  • In order to develop iOS applications, you need a Mac and Xcode. These notes cover iOS 8 SDK, for which you’ll need Xcode 6 and OS X 10.9.4 or later.

MVC

  • We’ll introduce a new concept, MVC (Model-View-Controller). MVC is a design paradigm used not only for iOS development, but also for creating many websites[1] and other software applications. More simply, it’s a way of organizing and thinking about code. Take a look at it’s representation in the diagram below:

    mvc diagram
  • Within the MVC design paradigm, we can divide an application into three major components: the model, the view and the controller.

    Component Function Examples

    Model

    Managing and organizing data, main logic of the application

    Databases (e.g. of songs), data files, logic of a game

    View

    Presentation of information to user, user interface

    Buttons, sliders, text views, table views

    Controller

    Handles user’s interaction with the application, gets information from the model

    Validating user input (e.g. does the email field actually contain a valid email address?)

  • From the above diagram, notice that the controller is “in charge” of the application. Only the controller is allowed to directly communicate with the model and with the view. The controller will usually get the data from the model and then display it on the view. The view can also communicate with the view (e.g. when the user taps on a button or scrolls through the list), as can the model (e.g. the database updates), but these communication are not as direct. Usually the controller decides to “listen” to the updates from the view and the model. But the view and the model are not allowed to talk to each other.

  • The good news is that we’ve already written our model. Message class is the brains of our application that will take care of encrypting and decrypting text. So now we need to build the view (i.e. user interface) and the controller, that will connect the view to the model.

Single View Application

  • Let’s start creating our iPhone application by creating a new Xcode Project, this time, select iOS and Application on the left and then choose Single View Application. A single-view application is one with just one screen. This is all we need for our app.

    xcode project new ios
  • Next, name the project Caesar, since our app will ultimately encrypt and decrypt messages using a Caesar cipher, choose Objective-C as the language, select iPhone from the list of devices (so as to keep our app simple) and make sure that Core Data is not checked.

    xcode project options ios
  • Once you create the project, you’ll see a window with lots of settings, but leave them as they are for now. Instead, turn your attention to the Project navigator on the left side.

    xcode navigator 0
  • Among the files are two that correspond to the AppDelegate class. An application delegate subclass is created for every new iOS application you create. In general, in this class are methods that control the application’s execution. This includes such things as what to do when the application starts, when it enters and leaves the background, when the system tells it it’s using too much memory, and when it terminates. We won’t make any changes to this class, so feel free to drag AppDelegate.h and AppDelegate.m into Supporting Files group. If you do take a look at Supporting Files group itself, you’ll find main.m, which contains the main function for our app. But we won’t touch it either.

  • Next, turn your attention to the ViewController class. This is our application’s controller. In general, apps have many models, views and controllers, but ours will be very simple, with just one model, one view and one controller.

  • Speaking of the model, let’s bring our Message class from the CeasarTest project we worked on earlier.

    In the file menu, choose File > Add Files to “Caesar”…, then navigate to Message.h and Message.m that you worked on earlier. Make sure that both of them are selected and that the checkbox next to Copy items if needed is checked. After you add the two files that correspond to the model, select them both in the Project navigator on the left, right-click on them and choose New Group from Selection. Name that group Model, so as to end up with the organization of files like in the screenshot below. It’s not strictly necessary to have our files organized in such a way, but it’ll make working on our app a bit easier.

    xcode navigator 1
  • You can actually compile run the project right now by pressing the Run button. Xcode with open the iOS Simulator, but all you’ll see is a blank screen:

    iphone 0
    You can actually switch devices in the iOS Simulator (e.g. between iPhone 5, iPhone 6 and iPad). Just choose Hardware > Device and select the device you want to test on. You can also test on a real device, but you’ll have to enroll in the iOS Developer Program to do that.

Interface Builder

  • Now let’s work on the view. Select Main.storyboard from the Project navigator and you should see a window that looks something like this:

    xcode storyboard 0
  • Building the user interface of our application will be much easier than writing the model. It mostly consists of dragging and dropping UI elements on the canvas. Literally.

  • This view is the representation of the device’s screen when the application runs. In the past, this would actually be a rectangle of the same dimensions as an iPhone’s screen. But know an iPhone can have four different screen sizes, so Apple has introduced Auto Layout mechanism, whereby we can add constraints to objects, so that our app looks right on any device.

  • Let’s begin by changing the background of our view to light gray. Click in the middle of the white square to select the view. Then click on the dropdown menue next to Background in the Attributes inspector of the Utilities area on the right side.

    xcode utilities 0

    Choose Other and select a very light gray color (about 5%) from the color palette.

  • Our application will have these UI elements:

    • Two text views: one for inputting the original message, and another one for diplaying the encrypted or decrypted message.

    • A slider to select the key from 0 to 26.

    • A label to display the current value of the key.

    • A segmented control to let the user choose between encryption and decryption.

  • Let’s start with the slider. Click on xcode-object-library-button in the lower part of the Utilities area to switch to the Object library. You’ll see a selection of many different UI elements. To quickly find the slider, type slider in the text field on the bottom of the Utilities area. Drag it and drop it somewhere in the middle of the view, then stretch it horizontally to match the below screenshot. Then click on the slider and set its maximum value to 26 in the Attributes inspector of the Utilities area on the right side.

    xcode view slider
  • Now let’s get two text views. Go back to the Object library in the lower part of the Utilities area and search for a text view. Place two text views in the view: one above the slider (for the original message) and one below the slider (for the encrypted/decrypted message). Then strech them, leaving some space between the upper text view and the slider for a segmented control and a label.

    xcode view text views 0

    Double-click on each text view and type Hello World! to set their default text. Then click on the lower text view and uncheck Editable next to Behavior in the Attributes inspector of the Utilities area on the right side. This will prevent the user from being able to edit the resulting message.

    xcode view text views 1
  • We also need a segmented control that will allow the user to switch between encryption and decryption. Search for the segmeted control in the Object library and place it above the slider toward the left. Stretch it to make it take the left half of the remaining space. Then double-click on First and type Encrypt and double-click on Second and type Decrypt.

    xcode segmented control
  • Finally, search for a label in the Object library and place it to the right of the segmented control. Also stretch it to take the remaining space. Double-click on it and type Key: 0 to set its default value.

    xcode label
  • If we now try to run the app in the simulator, we’ll see the UI elements we’ve added, but they will most likely not be properly layed out:

    iphone 1
  • So let’s add some constraints:

    • Click on the upper text view. Then click on the Pin icon that looks like xcode-pin. In the Add New Constraints dialog, click on the four icons that look like xcode-constraint to set the constraints around the text view. Then set a fixed values for the leading (left), trailing (right), top and bottom distances (0, 0, 0 and 20 points, respectively). To finish, click Add 4 Constraints.

      xcode constraints 0
    • Click on the segmented control. Just like you did for the text view, click on the Pin icon that looks like xcode-pin and click on the four icons that look like xcode-constraint to set the constraints around the text view. But this time, set a values of 20 above and below, 0 for the left and 40 for the right, then click Add 4 Constraints.

      xcode constraints 1
    • Click on the label and click on the Pin icon (xcode-pin). In the Add New Constraints dialog, again set the four constraints around the text view. They should be 20 for above and below, 40 for the left and 0 for the right. Then check th checkbox next to Height and set it to 30 to match that of the segmented control and click Add 5 Constraints.

      xcode constraints 2
    • Now the Shift key and click on the segmented control and then on the label to select both of them. Click on the Pin icon and check the checkbox next to Equal Widths, then click Add 1 Constraint.

      xcode constraints 3
    • Click on the slider and then click on the Pin icon. Add four constraints, 20 for above and below and 0 for the left and the right. Click Add 4 Constraints.

      xcode constraints 4
    • Click on the lower text view. Again add four constraints: 20 above and below, 0 for the left and for the right. Click Add 4 Constraints.

      xcode constraints 5
    • Finally, hold shift and click on both text fields to select them. Add the last constraint: Equal Height.

      xcode constraints 6
  • Notice that the constraints are now highlighted in orange, but the UI elements are still positioned the same way. Generally you want to see user interface elements where they’ll be at runtime, so you should update the canvas to reflect that.

  • Click on the Resolve Autolayout Issues icon on the bottom of the Xcode window that looks like xcode-resolve and select All Views in View Controller > Update Frames.

    xcode update frames
  • The objects should reposition themselves and the view should look like this:

xcode view
  • If you run the app now, it should look correct, but nothings will happen if you slide the slider or change the text in the upper text view. This is where the controller comes in.

    iphone 2

Controller

  • Our application will have only one controller and it will be represented by the ViewController class. We won’t have to modify it’s public interface for now and most of the remaining code will go in ViewController.m

  • Will again be working with Assistant Editor, but this time we’ll have the Main.storyboard on the left and ViewController.m on the right.

    xcode controller 0
  • Xcode has automatically given us two methods, viewDidLoad and didReceiveMemoryWarning. From their name, you can guess when they will be called, but our applications is simple enough, so we don’t need them. You can just delete their implementations from ViewController.m.

  • Before connecting the view, let’s connect the model. First we need to

    #import "Message.h"
  • Then we need to add a property for the message (still remember what a property is?). Add

    @property (strong, nonatomic) Message *originalMessage;

    to the private interface of ViewController between @interface ViewController () and @end.

  • Now, remember that originalMessage is a pointer to a Message object (still remember what a pointer is?). This means that it points to some memory address on the heap. In order for originalMessage to be valid, we need to allocate memory for a Message object, initialize that object and set originalMessage to point to that object before we ever use originalMessage. But when should this be done? A very good time is when we first try to access originalMessage. Remember that when we acces properties in Objective-C, the getter method is called. Objective-C automatically generates setters and getters for us when we create properties, and the default getter just returns the instance variable. What we want the getter to do instead is allocate and initialize a Message object the first time we access it, and then always return the instance variable. By default, all pointers are set to nil in Objective-C, which just means that they do not point to anything. So let’s write our own getter for originalMessage where we do a simple check to see if originalMessage has been set. Let’s add this to the implementation of ViewController:

    - (Message *)originalMessage {
        if (!_originalMessage) {
            _originalMessage = [[Message alloc] init];
        }
    
        return _originalMessage;
    }

    This is known as lazy instantiation, i.e. we don’t instantiate an object until we need to use it. It’s a very common pattern in Objective-C.

  • So this takes care of connecting the controller to the model. The controller now has originalMessage, a Message object that will store a string and will be able to encrypt and decrypt it with a key.

  • Now let’s connect the view to the controller. We need to add a property for each of the UI elements we’ve added (so we’ll need five more properties). These properties are known as outlets. Let’s first add an outlet for the text view that will have the original message. Connecting outlets is extremely easy (and fun!) in Xcode. Just hold the Control key and drag the text view between @interface ViewController () and @end.

    xcode controller 1

    Name the outlet originalMessageView. Notice that it’s of type UITextView and that it’s a weak pointer (as opposed to a strong pointer.). The view will have a strong pointer to it, controller does not need to.

    xcode controller 2

    Click Connect and you’ll see this line of code appear in the private interface:

    @property (weak, nonatomic) IBOutlet UITextView *originalMessageView;

    IBOutlet just lets Xcode that this is an outlet.

  • We now need to do the same for the rest of the UI elements that we’ve added. Connect the other text view and name the outlet resultingMessageView. Then connect the segmented control and name its outlet encryptionModeControl, then connect the slider, naming it keySlider and the label, naming it keyLabel.

  • Notice the circles to the right of @property for the outlets. If you hover over them with your mouse, Xcode will highlight the view to which that outlet is connected.

    xcode controller 3
  • As a reminder, we’ve added these outlets so that our controller can talk to our view.

  • Let’s add a method called updateUI that we’ll call whenever we need to update the view (this can happen when the user updates the original message, changes encryption mode or slides the slider). We’ll put the implementation between @implementation ViewController and @end.

    - (void)updateUI {
    
    }

    First, we need to update the text value of the label, in case the user has slid the slider. We first need to access the keySlider property of self and then we need to access the text property of that slider, so we’ll begin with

    self.keyLabel.text

    We need to assign to it a string that we’ll be of format Key: X, where X is the value of the slider. Remember the format strings from earlier? We need to create one using NSString’s stringWithFormat: class method. The value of the key will come from the keySlider and we need to cast it to an int to make the compiler happy. So we’ll ultimately need this line of code in updateUI:

    self.keyLabel.text = [NSString stringWithFormat:@"Key: %d",
                                                    (int)self.keySlider.value];

    Then, we need to update the message in our model to match the one typed by the user in originalMessageView:

    self.originalMessage.message = self.originalMessageView.text;
  • Finally, we have to update the text in the resultingMessageView. We first have to check the value of the segmented control (encryptionModeControl). Segmeted control has a property called selectedSegmentIndex, whose value is the index of the selected segment. So it will be 0 if the user has selected Encrypt and 1 if the user has selected Decrypt. We then set the text of the resultingMessageView according to the sected mode. (Rember that we encypt and decrypt by calling methods on our model and that the key is the value of the slider).

    if (self.encryptionModeControl.selectedSegmentIndex == 0) {
        self.resultingMessageView.text = [self.originalMessage
                                          encryptWithKey:self.keySlider.value];
    } else if (self.encryptionModeControl.selectedSegmentIndex == 1) {
        self.resultingMessageView.text = [self.originalMessage
                                          decryptWithKey:self.keySlider.value];
    }

    So that’s all we had to do to update the UI: update the key label, update the model (which is not part of the UI, but updateUI is a good place to put this since we might want to update the model when the message changes) and encrypt or decrypt.

  • The control will need to call updateUI when something changes with the view (when the user updates the original message, changes encryption mode or slides the slider). But since the view can’t talk directly to the controller, the controller has to “listen” to those changes. This is known as an action.

  • You can add an action just like you add an outlet, by dragging and dropping a UI element from the view to the controller, but this time it has to go between @implementation ViewController and @end.

  • First, let’s add an action for when the user slides the slider. Hold the Control key and drag the line between @implementation ViewController and @end.

    xcode controller 4
  • Name the method changeKey and change type to UISlider. Notice that the event is Value Changed, which means that this method will be called when the value of the slider changes. Also note that the argument is Sender, which means that a pointer to UISlider will be passed in.

    xcode controller 5

    Click Connect and you’ll see this code appear in the implementation section:

    - (IBAction)changeKey:(UISlider *)sender {
    }

    IBAction return type is the same as void, but it tell Xcode that this is indeed an action. Again, you can hover the mouse over the circle to the left of - (IBAction) and Xcode will highlight the view with which the action is associated.

    When the user slides the slider we must do two things: ensure that the value of the slider is an integer (because a slider doesn’t step its values by 1) and update UI. So we fist store the value in an int and then assign that integer back to the value of the slider. And then we call the updateUI method.

    int key = (int)sender.value;
    sender.value = key;
    [self updateUI];
  • Now we need to add action for when the user switches between encryption and decryption. Hold the Control key and drag the slider in an empty space betwen @implementation ViewController and @end. Name the method changeEncryptionMode and change type to UISegnementedControl. You’ll these lines of code added to the implementation:

    - (IBAction)changeEncryptionMode:(UISegmentedControl *)sender {
    }

    This method is very easy. All we need to do when the user switches the mode is just update the UI:

    [self updateUI];
  • Finally, the controller has to know when the user finishes editing text in originalMessageView so that it can update the text in resultingMesageView right away. Unfortunately, UITextView does not support the actions mechanism to communicate with the controller. Another mechanism that allows the view to talk to the controller is delegates. We can make ViewController to be the delegate of the text view that contains the original message. This means that ViewController will conform to UITextViewDelegate protocol, i.e. it will implement all of the required methods declared in that protocol and some of the optional ones. It turns out that the UITextViewDelegate protocol does not have any required methods, but an optional one includes textViewDidEndEditing:, which is called when the user finishes editing a text view. This sounds like a solution to our problem. First, switch to ViewController.h by clicking

    xcode controller 6

    and change

    @interface ViewController : UIViewController

    to

    @interface ViewController : UIViewController <UITextViewDelegate>

    This is all we need to do to say that ViewController conforms to the UITextViewDelegate protocol. Now we need to set ViewController to be the delegate of text view where the user will be typing messages. Control-drag the text view to the yellow circle above it that represents the View Controller.

    xcode controller 7

    Select ViewController to be the delegate.

    xcode delegate

    Now switch back to ViewController.m and implement textViewDidEndEditing:. What we need to do when the user finishes typing the message is again just update the UI.

    - (void)textViewDidEndEditing:(UITextView *)textView {
        [self updateUI];
    }
    Alternatively, you could update the model here instead of in updateUI, but this would require that you also do so in viewDidLoad that we deleted earlier.
  • One last touch. In order for the user to actually end editing a text view, he or she must dismiss the keyboard. Let’s add some code that dismisses it when the user taps on an area outside of the keyboard. We’ll skip the explanation of this code and assume thtat it works.

    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
        // dismiss keyboard on touch
        [self.view endEditing:YES];
        [super touchesBegan:touches withEvent:event];
    }
  • As a summary, this is what your ViewController.h should look like:

    ViewController.h
    1 2 3 4 5 6
    #import <UIKit/UIKit.h> @interface ViewController : UIViewController <UITextViewDelegate> @end

    And this should resemble your ViewController.m:

    ViewController.m
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
    #import "ViewController.h" #import "Message.h" @interface ViewController () @property (strong, nonatomic) Message *originalMessage; @property (weak, nonatomic) IBOutlet UITextView *originalMessageView; @property (weak, nonatomic) IBOutlet UITextView *resultingMessageView; @property (weak, nonatomic) IBOutlet UISegmentedControl *encryptionModeControl; @property (weak, nonatomic) IBOutlet UISlider *keySlider; @property (weak, nonatomic) IBOutlet UILabel *keyLabel; @end @implementation ViewController // lazy instantiation - (Message *)originalMessage { if (!_originalMessage) { _originalMessage = [[Message alloc] init]; } return _originalMessage; } - (void)updateUI { // update key label self.keyLabel.text = [NSString stringWithFormat:@"Key: %d", (int)self.keySlider.value]; // update model self.originalMessage.message = self.originalMessageView.text; // encrypt or decrypt if (self.encryptionModeControl.selectedSegmentIndex == 0) { self.resultingMessageView.text = [self.originalMessage encryptWithKey:self.keySlider.value]; } else if (self.encryptionModeControl.selectedSegmentIndex == 1) { self.resultingMessageView.text = [self.originalMessage decryptWithKey:self.keySlider.value]; } } - (IBAction)changeKey:(UISlider *)sender { int key = (int)sender.value; sender.value = key; [self updateUI]; } - (IBAction)changeEncryptionMode:(UISegmentedControl *)sender { [self updateUI]; } - (void)textViewDidEndEditing:(UITextView *)textView { [self updateUI]; } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { // dismiss keyboard on touch [self.view endEditing:YES]; [super touchesBegan:touches withEvent:event]; } @end
  • Congratulations, you’ve now finised building your first iPhone application! Run it to make sure once more that it doesn’t have any bugs. Feel free to play with your new app and procrastinate.

    iphone 3

Further Resources

There are many resources available that will teach you iOS development, ranging from Apple’s own official documentation to video tutorials on YouTube. But allow me to recommend the following:

  • Programming in Objective-C, Sixth Edition
    Stephen G. Kochan
    Addison-Wesley Professional, 2013
    ISBN 0-321-96760-7
    (This is the book with which I first taught myself programming.)

  • Objective-C Programming: The Big Nerd Ranch Guide, Second Edition
    Aaron Hillegass
    Big Nerd Ranch Guides, 2013
    ISBN 0-321-94206-X

  • iOS Programming: The Big Nerd Ranch Guide, Fourth Edition
    Joe Conway, Aaron Hillegass, Christian Keur
    Big Nerd Ranch Guides, 2014
    ISBN 0-321-94205-1

  • CS193p: Developing iOS 7 Apps for iPhone and iPad
    Paul Hegarty
    Stanford, 2013
    https://itunes.apple.com/us/course/developing-ios-7-apps-for/id733644550

  • iOS Developer Library Documentation
    https://developer.apple.com/library/ios/navigation/

And here are some resources for starting with Swift:


1. Take a look at http://symfony.com/legacy/doc/jobeet/1_2/en/04?orm=Propel for an article on how to use MVC and PHP for designing websites.