Official Developer Program
For a standard iPhone you'll need to pay the US$99/yr to be a member of the developer program. You can then use the adhoc system to install your application onto up to 100 devices. The developer program has the details but it involves adding UUIDs for each of the devices to your application package. UUIDs can be easiest retrieved using Ad Hoc Helper available from the App Store. For further details on this method, see Craig Hockenberry's Beta testing on iPhone 2.0 article
Jailbroken iPhone
For jailbroken iPhones, you can use the following method which I have personally tested using the AccelerometerGraph sample app on iPhone OS 3.0.
Create Self-Signed Certificate
First you'll need to create a self signed certificate and patch your iPhone SDK to allow the use of this certificate:
Launch Keychain Access.app. With no items selected, from the Keychain menu select Certificate Assistant, then Create a Certificate.
Name: iPhone Developer
Certificate Type: Code Signing
Let me override defaults: Yes
Click Continue
Validity: 3650 days
Click Continue
Blank out the Email address field.
Click Continue until complete.
You should see "This root certificate is not trusted". This is expected.
Set the iPhone SDK to allow the self-signed certificate to be used:
sudo /usr/bin/sed -i .bak 's/XCiPhoneOSCodeSignContext/XCCodeSignContext/' /Developer/Platforms/iPhoneOS.platform/Info.plist
If you have Xcode open, restart it for this change to take effect.
Manual Deployment over WiFi
The following steps require openssh
, and uikittools
to be installed first. Replace jasoniphone.local
with the hostname of the target device. Be sure to set your own password on both the mobile
and root
users after installing SSH.
To manually compile and install your application on the phone as a system app (bypassing Apple's installation system):
Project, Set Active SDK, Device and Set Active Build Configuration, Release.
Compile your project normally (using Build, not Build & Go).
In the build/Release-iphoneos
directory you will have an app bundle. Use your preferred method to transfer this to /Applications on the device.
scp -r AccelerometerGraph.app root@jasoniphone:/Applications/
Let SpringBoard know the new application has been installed:
ssh mobile@jasoniphone.local uicache
This only has to be done when you add or remove applications. Updated applications just need to be relaunched.
To make life easier for yourself during development, you can setup SSH key authentication and add these extra steps as a custom build step in your project.
Note that if you wish to remove the application later you cannot do so via the standard SpringBoard interface and you'll need to use SSH and update the SpringBoard:
ssh root@jasoniphone.local rm -r /Applications/AccelerometerGraph.app &&
ssh mobile@jasoniphone.local uicache
These are good questions, and its great to see that you're doing this research and seem concerned with learning how to "do it right" instead of just hacking it together.
First, I agree with the previous answers which focus on the importance of putting data in model objects when appropriate (per the MVC design pattern). Usually you want to avoid putting state information inside a controller, unless it's strictly "presentation" data.
Second, see page 10 of the Stanford presentation for an example of how to programmatically push a controller onto the navigation controller. For an example of how to do this "visually" using Interface Builder, take a look at this tutorial.
Third, and perhaps most importantly, note that the "best practices" mentioned in the Stanford presentation are much easier to understand if you think about them in the context of the "dependency injection" design pattern. In a nutshell, this means that your controller shouldn't "look up" the objects it needs to do its job (e.g., reference a global variable). Instead, you should always "inject" those dependencies into the controller (i.e., pass in the objects it needs via methods).
If you follow the dependency injection pattern, your controller will be modular and reusable. And if you think about where the Stanford presenters are coming from (i.e., as Apple employees their job is to build classes that can easily be reused), reusability and modularity are high priorities. All of the best practices they mention for sharing data are part of dependency injection.
That's the gist of my response. I'll include an example of using the dependency injection pattern with a controller below in case it's helpful.
Example of Using Dependency Injection with a View Controller
Let's say you're building a screen in which several books are listed. The user can pick books he/she wants to buy, and then tap a "checkout" button to go to the checkout screen.
To build this, you might create a BookPickerViewController class that controlls and displays the GUI/view objects. Where will it get all the book data? Let's say it depends on a BookWarehouse object for that. So now your controller is basically brokering data between a model object (BookWarehouse) and the GUI/view objects. In other words, BookPickerViewController DEPENDS on the BookWarehouse object.
Don't do this:
@implementation BookPickerViewController
-(void) doSomething {
// I need to do something with the BookWarehouse so I'm going to look it up
// using the BookWarehouse class method (comparable to a global variable)
BookWarehouse *warehouse = [BookWarehouse getSingleton];
...
}
Instead, the dependencies should be injected like this:
@implementation BookPickerViewController
-(void) initWithWarehouse: (BookWarehouse*)warehouse {
// myBookWarehouse is an instance variable
myBookWarehouse = warehouse;
[myBookWarehouse retain];
}
-(void) doSomething {
// I need to do something with the BookWarehouse object which was
// injected for me
[myBookWarehouse listBooks];
...
}
When the Apple guys are talking about using the delegation pattern to "communicate back up the hierarchy," they're still talking about dependency injection. In this example, what should the BookPickerViewController do once the user has picked his/her books and is ready to check out? Well, that's not really its job. It should DELEGATE that work to some other object, which means that it DEPENDS on another object. So we might modify our BookPickerViewController init method as follows:
@implementation BookPickerViewController
-(void) initWithWarehouse: (BookWarehouse*)warehouse
andCheckoutController:(CheckoutController*)checkoutController
{
myBookWarehouse = warehouse;
myCheckoutController = checkoutController;
}
-(void) handleCheckout {
// We've collected the user's book picks in a "bookPicks" variable
[myCheckoutController handleCheckout: bookPicks];
...
}
The net result of all this is that you can give me your BookPickerViewController class (and related GUI/view objects) and I can easily use it in my own application, assuming BookWarehouse and CheckoutController are generic interfaces (i.e., protocols) that I can implement:
@interface MyBookWarehouse : NSObject <BookWarehouse> { ... } @end
@implementation MyBookWarehouse { ... } @end
@interface MyCheckoutController : NSObject <CheckoutController> { ... } @end
@implementation MyCheckoutController { ... } @end
...
-(void) applicationDidFinishLoading {
MyBookWarehouse *myWarehouse = [[MyBookWarehouse alloc]init];
MyCheckoutController *myCheckout = [[MyCheckoutController alloc]init];
BookPickerViewController *bookPicker = [[BookPickerViewController alloc]
initWithWarehouse:myWarehouse
andCheckoutController:myCheckout];
...
[window addSubview:[bookPicker view]];
[window makeKeyAndVisible];
}
Finally, not only is your BookPickerController reusable but also easier to test.
-(void) testBookPickerController {
MockBookWarehouse *myWarehouse = [[MockBookWarehouse alloc]init];
MockCheckoutController *myCheckout = [[MockCheckoutController alloc]init];
BookPickerViewController *bookPicker = [[BookPickerViewController alloc] initWithWarehouse:myWarehouse andCheckoutController:myCheckout];
...
[bookPicker handleCheckout];
// Do stuff to verify that BookPickerViewController correctly called
// MockCheckoutController's handleCheckout: method and passed it a valid
// list of books
...
}
Best Answer
You can only use plist if the amount of data is relatively small. Plist are entirely loaded into memory so you can only really use them if you can sustain all the objects created by the plist in memory at once for as long as you need them.
Core Data has a learning curve but in use it is usually less complex than SQL. In most cases the "simpler" SQL leads to more coding because you end up having to duplicate much of the functionality of Core Data to shoehorn the procedural SQL into the object-oriented API. You have to manually manage the memory use of all the data by tracking retention. You've write a lot of SQL code every time you want data. I've updated several apps from SQL to Core Data and in all cases the Core Data implementation was smaller and cleaner than the SQL.
Neither is the memory or processor "overhead" any larger. Core Data is highly optimized. In most cases, off the shelf Core Data is more efficient than hand tuned SQL. One minor sub optimization in SQL usually destroys any theoretical advantage it might have.
Of course, if you're already highly skilled at managing SQL in C then you personally might get the app to market more quickly by using SQL. However, if you're wondering what you should plan to use in general on on Apple Platforms, Core Data is almost always the answer and you should take the time to learn it.