31
Kill the Controllers

Kill the Controllers, and an introduction to MotionKit

Embed Size (px)

DESCRIPTION

This presentation is in two parts. The first part goes over how I refactored the Jukely app to have better separation of concerns by breaking up the controller into a "Storage" (aka Adapter, Repository) class and a "Layout" class. This technique worked very well for me, and resulted in cleaner code, more testable classes, and controllers that were easy to understand. I also present MotionKit, which is a gem that is designed to fulfill that "Layout" role. You can design and update your views very easily using MotionKit, and it can be used on iOS, OS X, and we already have Android support on the way! MotionKit was written by me and Jamon Holmgren.

Citation preview

Page 1: Kill the Controllers, and an introduction to MotionKit

Kill the Controllers

Page 2: Kill the Controllers, and an introduction to MotionKit

Kill the ControllersA terrible great way refactor large applications.

Page 3: Kill the Controllers, and an introduction to MotionKit

Mod

el Yes, there are models, fetched and created by the controller

View

Yes, views, too. The controller makes em!

Contro

ller

API Queries, UI, animations, network availability, login status, UI events,ticket purchasing, Facebook login, caching, scrolling updates, image downloading and processing,search filters, recommendations, notifications, sharing…

Page 4: Kill the Controllers, and an introduction to MotionKit

State

APIModels

Views

Layout

Storage

Controller

API

Models

Views

Controller

Page 5: Kill the Controllers, and an introduction to MotionKit

MVC?

Page 6: Kill the Controllers, and an introduction to MotionKit

MVC?❌

Page 7: Kill the Controllers, and an introduction to MotionKit

MQVLCS

Page 8: Kill the Controllers, and an introduction to MotionKit

StorageController

LayoutView

QueryModel

Page 9: Kill the Controllers, and an introduction to MotionKit

wat

credit: LimeCoconutShake from www.skyblock.net

Page 10: Kill the Controllers, and an introduction to MotionKit

The benefits are many!

• Classes focus on one task

• Controllers don’t “do it all”

• Less code bloat all around

• Testable controllers

• Easier to add new features

• Easier to read and understand

Page 11: Kill the Controllers, and an introduction to MotionKit

– Colin T.A. Gray

“Don’t quote me on any of that.”

Page 12: Kill the Controllers, and an introduction to MotionKit

• Layout?

• Storage? WTF is that?

• Models & Queries?It’s your call.

Again, up to you.

There’s a gem for that. ;-)

Page 13: Kill the Controllers, and an introduction to MotionKit

credit: Gant Laborde

Page 14: Kill the Controllers, and an introduction to MotionKit

• Animations

• Events

• Styles

• Imagine a controller and a view got together and had an adorable love child

• Move your UI code out of your controller:

Page 15: Kill the Controllers, and an introduction to MotionKit

• Support for iOS and OS X

• Support for CALayer, NSMenu

• Frame and AutoLayout DSLs

• Add your own helpers just bydefining a class

Page 16: Kill the Controllers, and an introduction to MotionKit

class LoginLayout < MK::Layout # or < MK::WindowLayout # or < MK::MenuLayout view :username_field view :submit_button # @layout.submit_button

def layout background_color UIColor.blackColor

add UILabel, :login_label add UIView, :login_container do add UITextField, :username_field add UIButton, :submit_button end end

end

Page 17: Kill the Controllers, and an introduction to MotionKit

def login_label_style initial do # only apply when views are created text 'Login' backgroundColor UIColor.clearColor font UIFont.boldSystemFontOfSize(40) textAlignment UITextAlignmentCenter layer do shadowRadius 20 shadowOpacity 0.5 masksToBounds false end

constraints do width('100%') center_x.equals(:superview) end endend

Page 18: Kill the Controllers, and an introduction to MotionKit

# separate method to work with controller.topLayoutGuidedef add_constraints(controller) constraints(self.view) do origin [0, 0] width.equals(:superview) height.equals(:superview) end

constraints(:login_label) do below(controller.topLayoutGuide).plus(50) # define top_margin: 50 # below(controller.topLayoutGuide).plus(:top_margin) endend

Page 19: Kill the Controllers, and an introduction to MotionKit

MotionKit Extensions

def label_style frame from_top(:container, width: 200) frame below(:button) frame left_of(:other_label)

x 0 y 0 right 320 bottom '100% - 8'end

Frames

Page 20: Kill the Controllers, and an introduction to MotionKit

MotionKit ExtensionsConstraints

def label_style constraints do left.equals(:superview, :left).plus(8) right.equals(:button, :left).minus(8) top_left.equals(:superview) height.is <= 100 endend

Page 21: Kill the Controllers, and an introduction to MotionKit

MotionKit ExtensionsNSMenu

class MainMenu < MK::Menu def layout add 'File' do add 'Open', action: 'open:', key: 'o' add 'Close', action: 'close:', key: 'w' add 'Foo', :foo_menu add 'Bar', :bar_menu do add 'Baz' end end endend

Page 22: Kill the Controllers, and an introduction to MotionKit

MotionKit Extensionsdef label_style autoresizing_mask :pin_to_top layer do backgroundColor UIColor.blackColor.CGColor end

title 'Send' title 'Done', state: UIControlStateDisabled

portrait do frame [[0, 0], [100, 100]] end landscape do frame [[20, 0], [140, 100]] endend

Page 23: Kill the Controllers, and an introduction to MotionKit

shadow_color UIColor.colorWithRed(0.133, green: 0.133, blue: 0.133)text_color UIColor.colorWithRed(0.6, green: 0.6, blue: 0.6)

font UIFont.fontWithName(‘Avenir-Medium’, size: 20)

line_break_mode UILineBreakModeWordWraptext_alignment NSTextAlignmentCenterbaseline_adjustment UIBaselineAdjustmentNone

SweetKitUses SugarCube methods to turn this…

Page 24: Kill the Controllers, and an introduction to MotionKit

shadow_color '#222222'text_color '#999999'

font 'Avenir-Medium', size: 20

line_break_mode :word_wraptext_alignment :centerbaseline_adjustment :none

SweetKit…into this!

Page 25: Kill the Controllers, and an introduction to MotionKit

MotionKit::EventsDecouple your UI events

class LoginLayout < MK::Layout def login_button_style target.on :touch do trigger(:login, username_field.text.to_s, @password_field.text.to_s) end endend

class LoginController < UIViewController def viewDidLoad super @layout = LoginLayout.new(root: self.view)

@layout.on :login do |username, password| handle_login(username, password) end endend

Page 26: Kill the Controllers, and an introduction to MotionKit

Roadmap

• View Layout helpers

• Spec helpers

• Layouts (e.g. Linear flow)

• Tutorials

• Screencasts

• Documentation

Page 27: Kill the Controllers, and an introduction to MotionKit

– Colin T.A. Gray

“Oh yeah, Jamon I forgot to tell you.”

Page 28: Kill the Controllers, and an introduction to MotionKit

class LoginLayout < MK::Layout view :username_field view :submit_button

def layout add Android::Widget::LinearLayout, :login_container do orientation VERTICAL

add Android::View::TextView, :username_field add Android::View::Button, :submit_button end end

def submit_button_style text 'Login' end

end

MotionKit on Android

Page 29: Kill the Controllers, and an introduction to MotionKit

Kill the Controllers!

• Controllers should do the minimum amount of “paper pushing”

• UI and animations → Layout

• Talking to the API → Storage

• etc. (Misc Task → Misc Class)

Page 30: Kill the Controllers, and an introduction to MotionKit

DEMOExample of a LoginController written with

Storage and Layout classes.

You can see some similar code here:github.com/rubymotion/motion-kit-events

Page 31: Kill the Controllers, and an introduction to MotionKit

Colin T.A. GrayCommunity Manager at HipByte

Engineer at Jukelygithub.com/colinta

github.com/rubymotion/motion-kitgem install motion-kit

github.com/colinta/motion-xraygem install motion-xray