Scala on Your Phone:
    Making Mobile
Development Suck Less
      Michael Galpin, eBay
          @michaelg
Example: Event Handlers
Just a Click
iPhone
Android
Objective-C
@synthesize textInput;
@synthesize label;
@synthesize name;

- (IBAction)changeGreeting:(id)sender {
  self.name = textInput.text;

    NSString *nameString = name;
    if([nameString length] == 0) {
      nameString = @"Code Camp";
    }
    NSString *greeting = [[NSString alloc]
                           initWithFormat:@"Hello %@!", nameString];
    label.text = greeting;
    [greeting release];
}

- (BOOL)textFieldShouldReturn:(UITextField *)theTextField {
  if(theTextField == textInput) {
    [textInput resignFirstResponder];
  }
  return YES;
}

- (void)dealloc {
  [textInput release];
  [label release];
  [name release];
  [super dealloc];
}
Java
@Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            Button sayButton = (Button) findViewById(R.id.say_button);
            sayButton.setOnClickListener(new OnClickListener(){

	   	    	    public void onClick(View v) {
	   	    	    	 EditText nameBox = (EditText) findViewById(R.id.name);
	   	              String name = nameBox.getText().toString();
	   	              TextView greeting = (TextView) findViewById(R.id.greeting);
	   	              greeting.setText("Hello " + name);
	   	    	    }
             	
             });
     }
Scala
override def onCreate(savedInstanceState:Bundle){
    import R.id._
    super.onCreate(savedInstanceState)
    setContentView(R.layout.main)
    val sayButton = findViewById(say_button).asInstanceOf[Button]
    sayButton.setOnClickListener( () => {
        val user = findViewById(name).asInstanceOf[EditText].text
        findViewById(greeting).asInstanceOf[TextView].text = "Hello " + user
    })
}
Advantages
Closures
•   Strongly typed handlers   •   Forget one-method
                                  interfaces
•   Lexical scoping
Closures
          •   Strongly typed handlers           •   Forget one-method
                                                    interfaces
          •   Lexical scoping

val computedValue = parseInt(findViewById(foo).asInstanceOf[EditText].text)
this.post( () => {
    val url = new URL("http://foo.com?qs=" + computedValue)
    Source.fromStream(url.openStream).getLines.foreach(findViewById(results).text += _)
})

mainMenu.onClick = (item:MenuItem) => { startActivity(item.activity) }
Getters, Setters, Updates

mapImage.alpha = if (topLevelGroup.searchButton.clickable) BLENDED else OPAQUE

allTiles(selectedTileIndex) = throbberImageView
Operators

resultsLabelView += "Read more..."

keywordInput -= eventListener

mainMenu ++ fileMenuItems
Traits

class SearchDetailsActivity extends Activty with JsonParser, Trackable
XML
val results = for (suggested <- responseXml"Suggested"){
    yield Term(sugested"Term" text, suggested"Priority" text)
}

nextNode   match {
    case   <code>{txt}</code> => this.status = parseInt(txt)
    case   <state>{txt}</state> => this.state = txt
    case   _ =>
}
Trade Secrets
*    Compiler   Dex Compiler         Compress




Source Code Class files        Dex File              APK
Implicits
object Activity{

    implicit def funcToClicker1(f:_root_.android.view.View => Unit):OnClickListener =
      new OnClickListener(){ def onClick(v:_root_.android.view.View)=f.apply(v)}

    implicit def funcToClicker0(f:() => Unit):OnClickListener =
      new OnClickListener() { def onClick(v:_root_.android.view.View)=f.apply}

    implicit def funcToLongClicker0(f:() => Boolean):OnLongClickListener =
      new OnLongClickListener() { def onLongClick(v:_root_.android.view.View) = f.apply}

    implicit def funcToLongClicker1(f:_root_.android.view.View => Boolean):OnLongClickListener =
      new OnLongClickListener() { def onLongClick(v:_root_.android.view.View) = f.apply(v)}

  implicit def viewToRichView(v:_root_.android.view.View):scala.android.view.View = new
scala.android.view.View(v)

    implicit def richViewToView(view:scala.android.view.View):_root_.android.view.View = view.base
}
Bag o’ Tricks
class ViewGroup(base:_root_.android.view.ViewGroup) extends View(base){
   object views{
     def +=(v:_root_.android.view.View){
       base.addView(v)
     }

      def apply(index:Int) = new {
          def update(v:_root_.android.view.View) = base.addView(index,v)
      }
   }
...
}
Bag o’ Tricks
class ViewGroup(base:_root_.android.view.ViewGroup) extends View(base){
   object views{
     def +=(v:_root_.android.view.View){
       base.addView(v)
     }

      def apply(index:Int) = new {
          def update(v:_root_.android.view.View) = base.addView(index,v)
      }
   }
...
}

myGroup.views += someOtherView
myGroup.views(3) = yetAnotherView
Scala on Android

• More Correct
• More Concise
• Simpler
• Perfect Fit
Challenges
2500+ Classes
Hey, What About?

Scala on Your Phone

  • 1.
    Scala on YourPhone: Making Mobile Development Suck Less Michael Galpin, eBay @michaelg
  • 2.
  • 3.
  • 4.
  • 6.
  • 8.
  • 9.
    @synthesize textInput; @synthesize label; @synthesizename; - (IBAction)changeGreeting:(id)sender { self.name = textInput.text; NSString *nameString = name; if([nameString length] == 0) { nameString = @"Code Camp"; } NSString *greeting = [[NSString alloc] initWithFormat:@"Hello %@!", nameString]; label.text = greeting; [greeting release]; } - (BOOL)textFieldShouldReturn:(UITextField *)theTextField { if(theTextField == textInput) { [textInput resignFirstResponder]; } return YES; } - (void)dealloc { [textInput release]; [label release]; [name release]; [super dealloc]; }
  • 11.
  • 12.
    @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Button sayButton = (Button) findViewById(R.id.say_button); sayButton.setOnClickListener(new OnClickListener(){ public void onClick(View v) { EditText nameBox = (EditText) findViewById(R.id.name); String name = nameBox.getText().toString(); TextView greeting = (TextView) findViewById(R.id.greeting); greeting.setText("Hello " + name); } }); }
  • 13.
  • 14.
    override def onCreate(savedInstanceState:Bundle){ import R.id._ super.onCreate(savedInstanceState) setContentView(R.layout.main) val sayButton = findViewById(say_button).asInstanceOf[Button] sayButton.setOnClickListener( () => { val user = findViewById(name).asInstanceOf[EditText].text findViewById(greeting).asInstanceOf[TextView].text = "Hello " + user }) }
  • 15.
  • 16.
    Closures • Strongly typed handlers • Forget one-method interfaces • Lexical scoping
  • 17.
    Closures • Strongly typed handlers • Forget one-method interfaces • Lexical scoping val computedValue = parseInt(findViewById(foo).asInstanceOf[EditText].text) this.post( () => { val url = new URL("http://foo.com?qs=" + computedValue) Source.fromStream(url.openStream).getLines.foreach(findViewById(results).text += _) }) mainMenu.onClick = (item:MenuItem) => { startActivity(item.activity) }
  • 18.
    Getters, Setters, Updates mapImage.alpha= if (topLevelGroup.searchButton.clickable) BLENDED else OPAQUE allTiles(selectedTileIndex) = throbberImageView
  • 19.
    Operators resultsLabelView += "Readmore..." keywordInput -= eventListener mainMenu ++ fileMenuItems
  • 20.
    Traits class SearchDetailsActivity extendsActivty with JsonParser, Trackable
  • 21.
    XML val results =for (suggested <- responseXml"Suggested"){ yield Term(sugested"Term" text, suggested"Priority" text) } nextNode match { case <code>{txt}</code> => this.status = parseInt(txt) case <state>{txt}</state> => this.state = txt case _ => }
  • 22.
  • 23.
    * Compiler Dex Compiler Compress Source Code Class files Dex File APK
  • 24.
    Implicits object Activity{ implicit def funcToClicker1(f:_root_.android.view.View => Unit):OnClickListener = new OnClickListener(){ def onClick(v:_root_.android.view.View)=f.apply(v)} implicit def funcToClicker0(f:() => Unit):OnClickListener = new OnClickListener() { def onClick(v:_root_.android.view.View)=f.apply} implicit def funcToLongClicker0(f:() => Boolean):OnLongClickListener = new OnLongClickListener() { def onLongClick(v:_root_.android.view.View) = f.apply} implicit def funcToLongClicker1(f:_root_.android.view.View => Boolean):OnLongClickListener = new OnLongClickListener() { def onLongClick(v:_root_.android.view.View) = f.apply(v)} implicit def viewToRichView(v:_root_.android.view.View):scala.android.view.View = new scala.android.view.View(v) implicit def richViewToView(view:scala.android.view.View):_root_.android.view.View = view.base }
  • 25.
    Bag o’ Tricks classViewGroup(base:_root_.android.view.ViewGroup) extends View(base){ object views{ def +=(v:_root_.android.view.View){ base.addView(v) } def apply(index:Int) = new { def update(v:_root_.android.view.View) = base.addView(index,v) } } ... }
  • 26.
    Bag o’ Tricks classViewGroup(base:_root_.android.view.ViewGroup) extends View(base){ object views{ def +=(v:_root_.android.view.View){ base.addView(v) } def apply(index:Int) = new { def update(v:_root_.android.view.View) = base.addView(index,v) } } ... } myGroup.views += someOtherView myGroup.views(3) = yetAnotherView
  • 27.
    Scala on Android •More Correct • More Concise • Simpler • Perfect Fit
  • 28.
  • 29.
  • 33.