Unlike controllers and views, there’s no default superclass that models inherit from; they’re just plain-old Ruby objects. We can use the standard attr_accessor, reader, and writer functions to declare getter and setter methods, which sometimes are all you need.
Many apps have users and profiles, so let’s work through a portion of that sort of app and use some nice models. Let’s create a new project (such as motion create UserProfile) and two subdirectories within ./app: models and controllers. We’re not only going to cover models; we’re going to keep building on what we already know.
Our app will let us create, view, and edit users. Since we’re doing an awful lot of work with users, this sounds like a good place to begin with our first model. To get started, we first create user.rb in ./app/models. For now, each user will have a name, email, and ID. This is a pretty basic implementation of User:
| class User |
| attr_accessor :id |
| attr_accessor :name |
| attr_accessor :email |
| end |
That’s all we need for now. Let’s add our first controller, which we can use to view a user. Create user_controller.rb in app/controllers, which will be a subclass of UIViewController. We’ll use a custom initializer that takes a User and fills the UI appropriately. Sound good? Let’s start with that initializer.
| class UserController < UIViewController |
| attr_accessor :user |
| |
| def initWithUser(user) |
| initWithNibName(nil, bundle:nil) |
| self.user = user |
| self.edgesForExtendedLayout = UIRectEdgeNone |
| self |
| end |
Pretty typical initializer, isn’t it? We’re going to assume the user passed is a User object, but we’ll worry about that later. Next, we need to set up the view. This will be kind of lengthy, but that’s nothing new for us at this point.
For each of our User properties, we’ll create two labels: one to tell us what value we’re looking at (“Email”) and one right beside it that presents the value of that property for our user (“[email protected]”). Since we are using Ruby’s nifty send, we can do this in one loop.
| def viewDidLoad |
| super |
| |
| self.view.backgroundColor = UIColor.whiteColor |
| |
| last_label = nil |
| ["id", "name", "email"].each do |prop| |
| label = UILabel.alloc.initWithFrame(CGRectZero) |
| label.text = "#{prop.capitalize}:" |
| |
| label.sizeToFit |
| if last_label |
| label.frame = [ |
| [last_label.frame.origin.x, |
| last_label.frame.origin.y + last_label.frame.size.height], |
| label.frame.size] |
| else |
| label.frame = [[10, 10], label.frame.size] |
| end |
| last_label = label |
| |
| self.view.addSubview(label) |
| |
| value = UILabel.alloc.initWithFrame(CGRectZero) |
| value.text = self.user.send(prop) |
| value.sizeToFit |
| value.frame = [ |
| [label.frame.origin.x + label.frame.size.width + 10, label.frame.origin.y], |
| value.frame.size] |
| self.view.addSubview(value) |
| end |
| self.title = self.user.name |
| end |
| end |
Don’t get overwhelmed! We just created two UILabels for each property and laid them out nicely.
Before we run our app, we need to set up our AppDelegate to get the controller on the screen. First, we create a new User in app_delegate.rb and use it to initialize a UserController. We’re going to wrap it in a UINavigationController so we get the nice effect with self.title.
| class AppDelegate |
| def application(application, didFinishLaunchingWithOptions:launchOptions) |
| @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds) |
| |
| @user = User.new |
| @user.id = "123" |
| @user.name = "Clay" |
| @user.email = "[email protected]" |
| @user_controller = UserController.alloc.initWithUser(@user) |
| @nav_controller = |
| UINavigationController.alloc.initWithRootViewController(@user_controller) |
| @window.rootViewController = @nav_controller |
| @window.makeKeyAndVisible |
| true |
| end |
| end |
Let’s give it a rake and see what happens! Your app should look something like Figure 7, A controller for our User model. Our view isn’t the prettiest, but a good designer could dress up even this limited information into something shippable.
This is great, but this User class didn’t help out all that much. We’re going to give it some abilities that will make our code much more flexible while showing off what smart models can do.