Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

An introduction to RubyMotion

An introduction to RubyMotion

Presentation made at the Bristol Ruby User Group about Rubymotion

Andrew Nesbitt

October 26, 2012
Tweet

More Decks by Andrew Nesbitt

Other Decks in Technology

Transcript

  1. 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
  2. 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
  3. £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
  4. // 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
  5. 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
  6. [:!, :!=, :!~, :%, :*, :+, :<, :<<, :<=, :<=>,

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

    :"<=>:", :"==:", :"===:", :"=~:", :">:", :">=:", :"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
  8. 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
  9. 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
  10. Interface Builder Can be hooked up to IB nib files

    and also supports storyboarding for iOS 5 onwards
  11. # 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"}
  12. In The App Store There have been quite a few

    Rubymotion apps approved on the App store, let’s check out a few
  13. Command line driven (no xcode) Bring your own editor Memory

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

    to learn Cocoa Provisioning still a pain Proprietary software camelCaseMethods Cons
  15. 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