An introduction to RubyMotion

An introduction to RubyMotion

Presentation made at the Bristol Ruby User Group about Rubymotion

8ddbf811da78bb0daeeb3cacd7cf743f?s=128

Andrew Nesbitt

October 26, 2012
Tweet

Transcript

  1. Andrew Nesbitt github.com/andrew @teabass

  2. forwardtechnology.co.uk

  3. None
  4. None
  5. 410m iOS devices As of June 2012 http://en.wikipedia.org/wiki/IOS That is

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

  7. 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
  8. 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
  9. £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
  10. // 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
  11. 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
  12. [:!, :!=, :!~, :%, :*, :+, :<, :<<, :<=, :<=>,

    :==, :===, :=~, :>, :>=, :[], : []=, :__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
  13. [:!, :"!=:", :"! ~:", :"%:", :"*:", :"+:", :"<:", :"<<:", :"<=:",

    :"<=>:", :"==:", :"===:", :"=~:", :">:", :">=:", :"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
  14. Things you can’t do Kernal.anything require ‘foo’ eval ‘foo’ Remember

    it’s not “real” ruby
  15. 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
  16. Demo http://github.com/andrew/lightning

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

    with `rake test`
  18. 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
  19. Interface Builder Can be hooked up to IB nib files

    and also supports storyboarding for iOS 5 onwards
  20. 3rd Party Libraries http://rubymotion-wrappers.com Rubygems CocoaPods http://rubymotion-wrappers.com

  21. BubbleWrap http://bubblewrap.io

  22. Notification Center Color conversion Gestures Camera Timers Audio HTTP JSON

  23. # colour '#FF8A19'.to_color # => #<UIDeviceRGBColor:0x8d54110> # 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"}
  24. In The App Store There have been quite a few

    Rubymotion apps approved on the App store, let’s check out a few
  25. Cabify http://blog.rubymotion.com/post/30514580062/rubymotion-success-story-cabify

  26. Ultra Reaction http://ultrareaction.sausyn.com

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

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

  29. Summary

  30. Command line driven (no xcode) Bring your own editor Memory

    Management Interactive Console Fast, native apps It’s Ruby! Pros
  31. Not an offical Apple project Lacking in documentation Still have

    to learn Cocoa Provisioning still a pain Proprietary software camelCaseMethods Cons
  32. 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
  33. MobiRuby http://mobiruby.org There may soon be an open source alternative

  34. Questions?