Slide 1

Slide 1 text

Andrew Nesbitt github.com/andrew @teabass

Slide 2

Slide 2 text

forwardtechnology.co.uk

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

410m iOS devices As of June 2012 http://en.wikipedia.org/wiki/IOS That is a market that is hard to ignore

Slide 6

Slide 6 text

http://www.textfromxcode.com That is a market that is hard to ignore

Slide 7

Slide 7 text

http://macruby.org Macruby Rubymotion is based on Macruby, which implements ruby natively in the objective c runtime Rubymotion has a number of internal differences which allow it to run within the sandboxed iOS Including memory management code as iOS lacks proper garbage collection

Slide 8

Slide 8 text

twitter.com/lrz Laurent Sansonetti Build by Laurent Sansonetti, formerly of the iOS team Apple Core contributor to Macruby Left Apple to be able to release Rubymotion from his new company, Hipbyte

Slide 9

Slide 9 text

£129 ($199) Proprietary software is pretty unusual in the ruby community. Concerns about what long term support So far so good, lots of regular updates and good customer support About the same cost as the iOS Developer Program

Slide 10

Slide 10 text

// Objective C - (NSInteger)pickerView:(UIPickerView *)pickerView forComponent:(NSInteger)component { ... } # RubyMotion def pickerView(pickerView, titleForRow:row, forComponent:component) ... end Code The are quite similar, syntactic sugar aside, except for the static typing The main difference between Rubymotion and normal ruby motions are the keyword arguments

Slide 11

Slide 11 text

String => NSMutableString => NSString => NSObject Integer => Numeric => NSNumber => NSValue => NSObject Hash => NSMutableDictionary => NSDictionary => NSObject Array => NSMutableArray => NSArray => NSObject Symbol => NSString => NSObject Time => NSDate => NSObject Inheritance Ruby classes are implemented on top of their Cocoa equivalents

Slide 12

Slide 12 text

[:!, :!=, :!~, :%, :*, :+, :<, :<<, :<=, :<=>, :==, :===, :=~, :>, :>=, :[], : []=, :__id__, :__send__, :ascii_only?, :between?, :bytes, :bytesize, :byteslice, :capitalize, :capitalize!, :casecmp, :center, :chars, :chomp, :chomp!, :chop, :chop!, :chr, :class, :clear, :clone, :codepoints, :concat, :count, :crypt, :define_singleton_method, :delete, :delete!, :display, :downcase, :downcase!, :dump, :dup, :each_byte, :each_char, :each_codepoint, :each_line, :empty?, :encode, :encode!, :encoding, :end_with?, :enum_for, :eql?, :equal?, :extend, :force_encoding, :freeze, :frozen?, :getbyte, :gsub, :gsub!, :hash, :hex, :include?, :index, :initialize_clone, :initialize_dup, :insert, :inspect, :instance_eval, :instance_exec, :instance_of?, :instance_variable_defined?, :instance_variable_get, :instance_variable_set, :instance_variables, :intern, :is_a?, :kind_of?, :length, :lines, :ljust, :local_methods, :lstrip, :lstrip!, :match, :method, :methods, :next, :next!, :nil?, :object_id, :oct, :ord, :partition, :prepend, :pretty_inspect, :pretty_print, :pretty_print_cycle, :pretty_print_inspect, :pretty_print_instance_variables, :private_methods, :protected_methods, :public_method, :public_methods, :public_send, :replace, :respond_to?, :respond_to_missing?, :reverse, :reverse!, :rindex, :rjust, :rpartition, :rstrip, :rstrip!, :scan, :send, :setbyte, :singleton_class, :singleton_methods, :size, :slice, :slice!, :split, :squeeze, :squeeze!, :start_with?, :strip, :strip!, :sub, :sub!, :succ, :succ!, :sum, :swapcase, :swapcase!, :taint, :tainted?, :tap, :to_c, :to_enum, :to_f, :to_i, :to_r, :to_s, :to_str, :to_sym, :tr, :tr!, :tr_s, :tr_s!, :trust, :unpack, :untaint, :untrust, :untrusted?, :upcase, :upcase!, :upto, :valid_encoding?] "ruby".methods.sort Here we can see the methods available on a string in ruby including methods for Object, which string inherits from

Slide 13

Slide 13 text

[:!, :"!=:", :"! ~:", :"%:", :"*:", :"+:", :"<:", :"<<:", :"<=:", :"<=>:", :"==:", :"===:", :"=~:", :">:", :">=:", :"CA_addValue:multipliedBy:", :CA_copyRenderValue, :"CA_distanceToValue:", :"CA_interpolateValue:byFraction:", :"CA_interpolateValues:::interpolator:", :CA_prepareRenderValue, :Complex, :"Complex:", :"LS_hasCaseInsensitivePrefix:", :LS_unescapedQueryValue, :MCAppendDeviceName, :MCAppendGreenteaSuffix, :MCSHA256DigestWithSalt, :MCSafeFilenameHash, :"MCSafeFilenameHashWithExtension:", :"MLBindToSQLiteStatement:atPosition:", :MLSortString, :MPMediaLibraryDataProviderSystemML3CoercedString, :Rational, :"Rational:", :UTF8String, :[], :"[]:", : []=, :"[]=:", :__callee__, :__id__, :__method__, :__send__, :"__send__:", :__type__, :accessibilityActivate, :accessibilityActivationPoint, :"accessibilityArrayOfTextForTextMarkers:", :"accessibilityAttributeValue:", :"accessibilityAttributeValue:forParameter:", :"accessibilityBoundsForTextMarkers:", :"accessibilityCandidateWordDescription:", :accessibilityColumnRange, :"accessibilityCompareGeometry:", :accessibilityContainer, :accessibilityContainerElements, :"accessibilityContentForLineNumber:", :accessibilityCustomActions, :"accessibilityCustomRotorItemsAtIndex:", :accessibilityCustomRotorTitles, :accessibilityDecrement, :"accessibilityEditOperationAction:", :"accessibilityElementAtIndex:", :accessibilityElementCount, :accessibilityElementDidBecomeFocused, :accessibilityElementDidLoseFocus, :"accessibilityElementForRow:andColumn:", :accessibilityElementIsFocused, :accessibilityElementsHidden, :"accessibilityEnumerateContainerElementsUsingBlock:", :"accessibilityEnumerateContainerElementsWithOptions:usingBlock:", :accessibilityFlowToElements, :accessibilityFrame, :"accessibilityFrameForLineNumber:", :accessibilityHeaderElements, :accessibilityHint, :"accessibilityHitTest:", :accessibilityIdentification, :accessibilityIdentifier, :accessibilityIncrement, :accessibilityInvalidStatus, :accessibilityIsComboBox, :accessibilityLabel, :"accessibilityLabelForRange:", :accessibilityLanguage, :"accessibilityLineNumberForPoint:", :accessibilityLinkedElement, :accessibilityMathEquation, :accessibilityMathMLSource, :accessibilityMenuActions, :accessibilityPageContent, :accessibilityPaths, :"accessibilityPerformAction:withValue:", :"accessibilityPerformCustomAction:", :accessibilityPerformEscape, :accessibilityPerformMagicTap, :accessibilityPlaceholderValue, :"accessibilityPostNotification:withObject:afterDelay:", :accessibilityRequired, :accessibilityRootElement, :accessibilityRowRange, :"accessibilityScroll:", :accessibilityScrollDownPageSupported, :accessibilityScrollLeftPageSupported, :accessibilityScrollRightPageSupported, :accessibilityScrollUpPageSupported, :accessibilitySecondaryLabel, :"accessibilitySetIdentification:", :accessibilityShouldEnumerateContainerElementsArrayDirectly, :accessibilitySpeechHint, :accessibilityStartStopToggle, :"accessibilityStringForTextMarkers:", :accessibilityTitleElement, :accessibilityTraits, :accessibilityURL, :accessibilityUserDefinedActivationPoint, :accessibilityUserDefinedFrame, :accessibilityUserDefinedHint, :accessibilityUserDefinedIdentifier, :accessibilityUserDefinedIsMainWindow, :accessibilityUserDefinedLabel, :accessibilityUserDefinedLanguage, :accessibilityUserDefinedNotFirstElement, :accessibilityUserDefinedServesAsFirstElement, :accessibilityUserDefinedSize, :accessibilityUserDefinedTraits, :accessibilityUserDefinedValue, :accessibilityUserDefinedWindowVisible, :accessibilityValue, :accessibilityViewIsModal, :"accessibilityViewWithIdentifier:", :"accessibilityZoomInAtPoint:", :"accessibilityZoomOutAtPoint:", :"addObserver:forKeyPath:options:context:", :allowSafePerformSelector, :allowsWeakReference, :"appendCharacters:length:", :"appendFormat:", :"appendString:", :ascii_only?, :autoContentAccessingProxy, :autorelease, :"awakeAfterUsingCoder:", :awakeFromNib, :becomeActive, :becomeInactive, :"between?:", :blockingMainThreadProxy, :boolValue, :bytes, :bytesize, :cString, :cStringLength, :"cStringUsingEncoding:", :camelize, :"camelize:", :"canBeConvertedToEncoding:", :capitalize, :capitalize!, :capitalizedString, :"capitalizedStringWithLocale:", :"caseInsensitiveCompare:", :"casecmp:", :center, :"center:", :"characterAtIndex:", :chars, :chomp, :chomp!, :"chomp!:", :"chomp:", :chop, :chop!, :"chop!:", :"chop:", :chr, :class, :classForArchiver, :classForCoder, :classForKeyedArchiver, :classForPortCoder, :className, :clear, :clone, :codepoints, :"commonNonWordBreakingPrefixWithString:options:", :"commonPrefixWithString:options:", :"compare:", :"compare:options:", :"compare:options:range:", :"compare:options:range:locale:", :"completePathIntoString:caseSensitive:matchesIntoArray:filterTypes:", :"componentsSeparatedByCharactersInSet:", :"componentsSeparatedByString:", :"concat:", :"conformsToProtocol:", :"containsSubstring:", :copy, :"copyUTF8StringOfLength:", :"copyWithZone:", :copyWithoutInsignificantCharacters, :copyWithoutInsignificantPrefixAndCharacters, :count, :"count:", :countOfComposedCharacterSequences, :"crypt:", :"cts_boundingRectWithSize:options:attributes:", :"cts_doBoundingRectWithSize:options:attributes:", :"cts_drawAtPoint:withAttributes:", :"cts_drawInRect:withAttributes:", :"cts_drawWithRect:options:attributes:", :"cts_sizeWithAttributes:", :cutStringByResolvingAndStandardizingPath, :"dataUsingEncoding:", :"dataUsingEncoding:allowLossyConversion:", :"dd_appendSpaces:", :dealloc, :debugDescription, :decimalValue, :decomposedStringWithCanonicalMapping, :decomposedStringWithCompatibilityMapping, :defaultAccessibilityTraits, :define_singleton_method, :"define_singleton_method:", :"delayedProxy:", :delete, :delete!, :"delete!:", :"delete:", :"deleteCharactersInRange:", :description, :destinationIdIsEmailAddress, :destinationIdIsPhoneNumber, :"dictionaryWithValuesForKeys:", :"didChange:valuesAtIndexes:forKey:", :"didChangeValueForKey:", :"didChangeValueForKey:withSetMutation:usingObjects:", :disallowSafePerformSelector, :displayableString, :"displayableStringByStrippingOffCommonPrefixWithString:", :"displayableStringByTrimmingPrefixString:", :"doesNotRecognizeSelector:", :doubleValue, :downcase, :downcase!, :"drawAtPoint:forWidth:withAttributes:", :"drawAtPoint:forWidth:withFont:fontSize:lineBreakMode:baselineAdjustment:", :"drawAtPoint:forWidth:withFont:fontSize:lineBreakMode:baselineAdjustment:includeEmoji:", :"drawAtPoint:forWidth:withFont:lineBreakMode:", :"drawAtPoint:forWidth:withFont:lineBreakMode:letterSpacing:", :"drawAtPoint:forWidth:withFont:lineBreakMode:letterSpacing:includeEmoji:", :"drawAtPoint:forWidth:withFont:minFontSize:actualFontSize:lineBreakMode:baselineAdjustment:", :"drawAtPoint:forWidth:withFont:minFontSize:actualFontSize:lineBreakMode:baselineAdjustment:includeEmoji:", :"drawAtPoint:withFont:", :"drawInRect:withAttributes:", :"drawInRect:withFont:", :"drawInRect:withFont:lineBreakMode:", :"drawInRect:withFont:lineBreakMode:alignment:", :"drawInRect:withFont:lineBreakMode:alignment:lineSpacing:", :"drawInRect:withFont:lineBreakMode:alignment:lineSpacing:includeEmoji:", :dump, :dup, :each_byte, :each_char, :each_codepoint, :each_line, :"each_line:", :"editDistanceFrom:", :empty?, :encode, :encode!, :"encode!:", :"encode:", :"encodeWithCoder:", :encoding, :end_with?, :"end_with?:", :endsSentence, :endsWord, :enum_for, :"enum_for:", :"enumerateLinesUsingBlock:", :"enumerateLinguisticTagsInRange:scheme:options:orthography:usingBlock:", :"enumerateSubstringsInRange:options:usingBlock:", :"eql?:", :"equal?:", :extend, :"extend:", :fastestEncoding, :fileSystemRepresentation, :finalize, :floatValue, :"force_encoding:", :formatConfiguration, :"forwardInvocation:", :"forwardingTargetForSelector:", :freeze, :"fromMainThreadPostNotificationName:object:userInfo:", :"fromNotifySafeThreadPerformSelector:withObject:", :"fromNotifySafeThreadPostNotificationName:object:userInfo:", :frozen?, :"getBytes:maxLength:filledLength:encoding:allowLossyConversion:range:remainingRange:", :"getBytes:maxLength:usedLength:encoding:options:range:remainingRange:", :"getCString:", :"getCString:maxLength:", :"getCString:maxLength:encoding:", :"getCString:m "iOS".methods.sort And here is the methods available on a string in Rubymotion These include all Cocoa and Obecjtive C methods inherited from NSString and NSObjectas well as ruby’s string methods

Slide 14

Slide 14 text

Things you can’t do Kernal.anything require ‘foo’ eval ‘foo’ Remember it’s not “real” ruby

Slide 15

Slide 15 text

Building and Debugging Building and running apps is all done on the command line using rake Debugging is done with the rubymotion repl, which integrates with the iOS simulator A debugger was released a few weeks ago

Slide 16

Slide 16 text

Demo http://github.com/andrew/lightning

Slide 17

Slide 17 text

Testing http://www.rubymotion.com/developer-center/articles/testing/ testing using bacon, an rspec clone test ran with `rake test`

Slide 18

Slide 18 text

Ship it! rake archive generates an .ipa file which can be uploaded to the app store, used for adhoc distribution or sent out to testers via testflight

Slide 19

Slide 19 text

Interface Builder Can be hooked up to IB nib files and also supports storyboarding for iOS 5 onwards

Slide 20

Slide 20 text

3rd Party Libraries http://rubymotion-wrappers.com Rubygems CocoaPods http://rubymotion-wrappers.com

Slide 21

Slide 21 text

BubbleWrap http://bubblewrap.io

Slide 22

Slide 22 text

Notification Center Color conversion Gestures Camera Timers Audio HTTP JSON

Slide 23

Slide 23 text

# colour '#FF8A19'.to_color # => # # Notification Center @reload_observer = App.notification_center.observe ReloadNotification do | notification| loadAndRefresh end # Gestures view.whenTapped do UIView.animateWithDuration(1, animations:lambda { # animate # @view.transform = ... }) end # Camera BW::Device.camera.front.picture(media_types: [:movie, :image]) do |result| image_view = UIImageView.alloc.initWithImage(result[:original_image]) end # JSON BW::JSON.parse "{\"foo\":1,\"bar\":[1,2,3],\"baz\":\"awesome\"}" # => {"foo"=>1, "bar"=>[1, 2, 3], "baz"=>"awesome"}

Slide 24

Slide 24 text

In The App Store There have been quite a few Rubymotion apps approved on the App store, let’s check out a few

Slide 25

Slide 25 text

Cabify http://blog.rubymotion.com/post/30514580062/rubymotion-success-story-cabify

Slide 26

Slide 26 text

Ultra Reaction http://ultrareaction.sausyn.com

Slide 27

Slide 27 text

Everclip http://blog.rubymotion.com/post/27906866028/rubymotion-success-story-everclip

Slide 28

Slide 28 text

Open source example apps http://github.com/hipByte/RubyMotionSamples

Slide 29

Slide 29 text

Summary

Slide 30

Slide 30 text

Command line driven (no xcode) Bring your own editor Memory Management Interactive Console Fast, native apps It’s Ruby! Pros

Slide 31

Slide 31 text

Not an offical Apple project Lacking in documentation Still have to learn Cocoa Provisioning still a pain Proprietary software camelCaseMethods Cons

Slide 32

Slide 32 text

Playing Hacking Internal Tools Small apps on the store Use cases Right now it feels risky to invest a lot of time and money into building a large, complex rubymotion app That being said, we have started building a number of medium size apps within Forward that we plan on putting in the App Store

Slide 33

Slide 33 text

MobiRuby http://mobiruby.org There may soon be an open source alternative

Slide 34

Slide 34 text

Questions?