Slide 1

Slide 1 text

BUILDING(BUILD(TOOLS TO#BUILD#A#TOOL THAT$BUILDS$APPS

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

how?

Slide 4

Slide 4 text

New$toys$today!

Slide 5

Slide 5 text

Clay%Allsopp @clayallsopp

Slide 6

Slide 6 text

Propeller UsePropeller.com @UsePropeller

Slide 7

Slide 7 text

We#deliver#apps @"SCALE

Slide 8

Slide 8 text

>150%New%Apps/Month

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

GUIs don't scale :(

Slide 11

Slide 11 text

Hard%Problem

Slide 12

Slide 12 text

Solu%on in#two#acts

Slide 13

Slide 13 text

Act$1 Do#things#that#don't#scale !"Paul"Graham

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

#"steps ✕!"me ✕!#!apps ✕!HUMAN_ERR

Slide 16

Slide 16 text

#"steps ✕!"me ✕!#!apps ✕!HUMAN_ERR

Slide 17

Slide 17 text

Act$2 Automate

Slide 18

Slide 18 text

1. Code'the'app'(Xcode) 2. Take'screenshots'(Simulator) 3. Test'the'app'(Xcode.&.Simulator) 4. Create'a'bundle'iden;fier'&'profile'(ADC) 5. Archive'the'app'(Xcode) 6. Create'an'entry'on'iTunes'Connect'(ITC) 7. Upload'(Applica6on.Loader) 8. Do'something'when'it's'released'(Email)

Slide 19

Slide 19 text

"Code&the&app"

Slide 20

Slide 20 text

RubyMo'on! rake rake spec rake archive

Slide 21

Slide 21 text

&"AppMarkup Control'app'from'the'server Na#ve&UI

Slide 22

Slide 22 text

"Take&screenshots"

Slide 23

Slide 23 text

motion-screenshots github.com/usepropeller/mo1on3screenshots

Slide 24

Slide 24 text

class AppScreenshots < Motion::Screenshots::Base async_screenshot "profile" do ready_delay 3 before do App.delegate.table_view_controller.selectRowAtIndexPath( NSIndexPath.indexPathForRow(0, inSection: 1), animated: false, scrollPosition: UITableViewScrollPositionNone ) ready! end end end

Slide 25

Slide 25 text

class ViewController def viewDidAppear(animated) super Dispatch::Queue.main.after(1) { AppScreenshots.start! } end end

Slide 26

Slide 26 text

rake screenshots ./screenshots/**/*.png

Slide 27

Slide 27 text

$ gem install motion-screenshots

Slide 28

Slide 28 text

"Test&the&app"

Slide 29

Slide 29 text

motion-screenspecs github.com/usepropeller/mo1on3screenspecs

Slide 30

Slide 30 text

class AppScreenshots < Motion::Screenshots::Base async_screenshot "profile" do ready_delay 3 before do App.delegate.table_view_controller.selectRowAtIndexPath( NSIndexPath.indexPathForRow(0, inSection: 1), animated: false, scrollPosition: UITableViewScrollPositionNone ) ready! end end end

Slide 31

Slide 31 text

describe AppScreenshots do tests_screenshots AppScreenshots end

Slide 32

Slide 32 text

describe SomeComponentScreenshots do tests_screenshots SomeComponentScreenshots end

Slide 33

Slide 33 text

spec/ screenshots/ AppScreenshots/ expectations/ profile.png results/ # where the last run goes failures/ # where the diffs go

Slide 34

Slide 34 text

No content

Slide 35

Slide 35 text

$ rake spec AppScreenshots - should take screenshots AppScreenshots.profile - should be <= 5.0% difference [FAILED - was 10.75%] Bacon::Error: was 10.75% spec.rb:698:in `satisfy:': AppScreenshots.profile - should be <= 5.0% difference spec.rb:438:in `execute_block' spec.rb:402:in `run_postponed_block:' spec.rb:397:in `resume' 2 specifications (2 requirements), 1 failures, 0 errors

Slide 36

Slide 36 text

$ gem install motion-screenspecs

Slide 37

Slide 37 text

"Create'a'bundle'iden.fier'&'profile" ... "Create'an'entry'on'iTunes'Connect"

Slide 38

Slide 38 text

No content

Slide 39

Slide 39 text

AppleBot Friendly)ADC)&)ITC)robot github.com/usepropeller/applebot

Slide 40

Slide 40 text

Ruby%Library%&%CLI

Slide 41

Slide 41 text

Apple%Developer%Center and!iTunes!Connect

Slide 42

Slide 42 text

Uses$CasperJS Eas(ier))to)debug)&)maintain

Slide 43

Slide 43 text

Ruby require 'applebot' AppleBot.app.create(title: "My Awesome App") CLI $ applebot app:create --title "My Awesome App"

Slide 44

Slide 44 text

app:create Create App app:reject_binary Reject the binary of an pending release app:remove_from_sale Remove app from sale app:update Update App app_id:create Create App ID app_id:delete Delete App ID app_id:list List App IDs profile:create Create Provisioning Profile profile:download Download a Provisioning Profile profile:list List Provisioning Profiles

Slide 45

Slide 45 text

$ applebot app:create -h Options: --title VALUE REQUIRED App's title --sku VALUE REQUIRED App's SKU --id VALUE REQUIRED App's bundle identifier --app_version VALUE REQUIRED App version number --copyright VALUE REQUIRED App copyright info --first_name VALUE REQUIRED Contact Info first name --last_name VALUE REQUIRED Contact Info last name --email VALUE REQUIRED Contact Info email --phone VALUE REQUIRED Contact Info phone number ...

Slide 46

Slide 46 text

JSON%Manifest%File applebot command --manifest .json AppleBot.command(manifest: ".json")

Slide 47

Slide 47 text

ITC$In$Your$VCS $ applebot app:update --manifest ./itc/update_110.json

Slide 48

Slide 48 text

$ gem install applebot $ brew install casperjs --devel

Slide 49

Slide 49 text

"Archive)the)app"

Slide 50

Slide 50 text

Github'API'+ TravisCI'+ RubyMo6on tl;dr

Slide 51

Slide 51 text

"Upload"

Slide 52

Slide 52 text

AppleLoad github.com/usepropeller/appleload

Slide 53

Slide 53 text

Applica'on*Loader*via*Ruby*&*CLI

Slide 54

Slide 54 text

caveat: AppleScript

Slide 55

Slide 55 text

CLI $ appleload upload 'My Awesome App' './ipas/app1000.ipa' Ruby AppleLoad.upload('My Awesome App', './ipas/app1000.ipa')

Slide 56

Slide 56 text

$ gem install appleload

Slide 57

Slide 57 text

"Do$something$when$it's$released"

Slide 58

Slide 58 text

AppleSlice Parse&iTunes&Connect&emails github.com/usepropeller/appleslice

Slide 59

Slide 59 text

require "appleslice" email_body = "..." slice = AppleSlice::Email.new(email_body) slice.review_status # => :ready_for_sale slice.app_version_number # => "1.0.5" slice.app_name # => "Awesome App"

Slide 60

Slide 60 text

$ gem install appleslice

Slide 61

Slide 61 text

Act$2: ✓!Automate

Slide 62

Slide 62 text

1. Code'the'app'(Xcode) 2. Take'screenshots'(Simulator) 3. Test'the'app'(Xcode.&.Simulator) 4. Create'a'bundle'iden;fier'&'profile'(ADC) 5. Archive'the'app'(Xcode) 6. Create'an'entry'on'iTunes'Connect'(ITC) 7. Upload'(Applica6on.Loader) 8. Do'something'when'it's'released'(Email)

Slide 63

Slide 63 text

1. Code'the'app'(AppMarkup) 2. Take'screenshots'(motion-screenshots) 3. Test'the'app'(motion-screenspecs) 4. Create'a'bundle'iden;fier'&'profile'(AppleBot) 5. Archive'the'app'(TravisCI) 6. Create'an'entry'on'iTunes'Connect'(AppleBot) 7. Upload'(AppleLoad) 8. Do'something'when'it's'released'(AppleSlice)

Slide 64

Slide 64 text

github.com/usepropeller

Slide 65

Slide 65 text

end$of$line