Learn Apple’s Swift Language: free online resources

During a meeting last week, I was discussing with a colleague about Swift courses, in his view it’s too early to attend a Swift course because nobody has mastered it completely yet. I think it’s better, in the beginning, to build your knowledge with online resources. But besides the iBook and the official documentation released by Apple*, what are some good resources to start learning Swift? 

I’ve done my homework and here is the list I’ve put together: 

An Absolute Beginner’s Guide to Swift by Treehouse  

Swift Cheat Sheet and Quick Reference by Ray Wenderlich 

Swift Cheat Sheet 

Swift Language on Youtube - the biggest set of Apple Swift programming language video tutorials and how-to guides.

We Swift - tutorials and guides about Swift

How to use Objective-C Classes in Swift 

Interesting Swift Features by Mike Ash 

Collections:

Sososwift - it’s a great collection of Swift materials which updates frequently.

LearnSwift.tips - A curated list of helpful resources to learn Swift. Tutorials, Code Samples, References and more

Ray Wenderlich’s tutorials - collection of Swift tutorials available on Ray Wenderlich’s site

Swift subreddit 

Swift Questions on Stackoverflow 

Swift repositories on Github 

Learn-by-doing resources or online courses:

Build Your First iOS Game - build a Tetris clone in Swift using SpriteKit

Swift Tutorial by Jameson Quave

Learn Apple’s New Programming Language Step By Step - An Udemy course

Fullstack.io made a swift implementation of Flappy Bird and they will soon release an online course (“Game programming on Swift”).

2048 - Swift implementation of popular 2048 game

* Apple’s resources:

iBook - freely available on iTunes

Official Documentation 

Swift Blog 

The social media hamster

(la muerte peluda is back)

Every hackathon we host at Mikamai usually means a lot of leftovers. From pizza crusts to burned LEDs, you can find a lot of useful and useless stuff even in the deepest and darkest corners of our huge open space.

And in one of these dark corners I found a scared little friend, which I suddenly decided to adopt.

image

We became BFFs, and we do everything together, coding, drinking, pranking the teammates and cycling, but he wanted to be useful to me, so I went into my cove and start thinking… what an ex cartoon eating plush hamster can do for me?

The answer was quite obvious! He can warn me whenever someone tweets me or cites me in a tweet! Genius!

So, here are the ingredients

  • Twitter streaming APIs
  • Electric IMP (I decided to use an Electric Imp just because I had a spare one, you can even use a spark core or an Arduino Yún)
  • A servo
  • Chopsticks
  • Some screws
  • Cable ties
  • Something heavy
  • Obviously, absulutely essential, a plush hamster
  • An USB power adapter or a USB battery pack if you want your project to be portable

image

I also used an LED for visual debugging, I tried to put a pair of LEDs instead of my hamster’s eyes, but it was too scary, and it remembered a lot my creepy doll, so I decided not to use them

Then I had to make the hamster move with the servo. It’s just a prototype, so I decided to keep it very very simple, and I secured the hamster to the servo with a chopstick, some copper wire and some screws to keep the hamster from doing a barrel roll. The surgery was not so traumatic, so my hamster is still happy. Then I fixed the servo to an heavy metal plate with a cable tie, just to provide a solid base.

I drilled some holes to the chopstick

image

Then put some screws in it and i secured the chopstick to the servo

image

And I used a bit of copper wire to secure the hamster to the screws

image

image

And a cable tie to fix the servo to the metal plate

image

Now everything’s in place!

image

The circuit is dead simple. Just connect the power pins of the servo to the GND-VIN pins of the Electric Imp, and the third pin to the Imp’s pin number 7

image

The Electric Imp is one of the easiest platforms to configure, thanks to the Blink app, so you can have it online in a couple of minutes. You can even use your phone as hotspot, it works perfectly and it makes your social hamster fully portable.

image

Now, the code. The Electric imp platform gives you all the instruments to interact with the whole world of APIs and webservices. Basically, the code is splitted in two: An agent, that runs on the Electric Imp Cloud servers, and a device, which is your electric Imp

[Copy & Paste from the official Electric Imp documentation]

The agent object represents the imp’s agent: the server-side Squirrel, running in the Electric Imp’s cloud servers, that deals with Internet requests and responses on behalf of the imp. The agent object is used to mediate communication between the imp and its agent.

The device object represents the server-based agent’s view of the imp and is used to mediate communication between the agent and the imp.

[End of copy&paste]

So here we go with our agent code. It’s based on the useful twitter library included in the Electric Imp webservices reference. I also added this piece of code to generate the tweet event manually

// test function for manual hamster shaking
function requestHandler(request, response) {
  try {
    // check if the user sent led as a query parameter
    if ("tweet" in request.query) {
        device.send("tweet", null); 
    }
    // send a response back saying everything was OK.
    response.send(200, "tweet test ok");
  } catch (ex) {
    response.send(500, "Internal Server Error: " + ex);
  }
}

// register the HTTP handler
http.onrequest(requestHandler);

To enable the twitter stream real time parsing, I just needed to configure these twitter constants

// Twitter Keys
const API_KEY = "";
const API_SECRET = "";
const AUTH_TOKEN = "";
const TOKEN_SECRET = "";

and this line

twitter.stream("@jeko", onTweet);

which basically tells to the stream object to catch all the tweets containing the string “@jeko”

So, here’s the complete agent code

// Copyright (c) 2013 Electric Imp
// This file is licensed under the MIT License
// http://opensource.org/licenses/MIT

// Twitter Keys
const API_KEY = "";
const API_SECRET = "";
const AUTH_TOKEN = "";
const TOKEN_SECRET = "";

class Twitter {
    // OAuth
    _consumerKey = null;
    _consumerSecret = null;
    _accessToken = null;
    _accessSecret = null;

    // URLs
    streamUrl = "https://stream.twitter.com/1.1/";
    tweetUrl = "https://api.twitter.com/1.1/statuses/update.json";

    // Streaming
    streamingRequest = null;
    _reconnectTimeout = null;
    _buffer = null;


    constructor (consumerKey, consumerSecret, accessToken, accessSecret) {
        this._consumerKey = consumerKey;
        this._consumerSecret = consumerSecret;
        this._accessToken = accessToken;
        this._accessSecret = accessSecret;

        this._reconnectTimeout = 60;
        this._buffer = "";
    }

    /***************************************************************************
     * function: Tweet
     *   Posts a tweet to the user's timeline
     * 
     * Params:
     *   status - the tweet
     *   cb - an optional callback
     * 
     * Return:
     *   bool indicating whether the tweet was successful(if no cb was supplied)
     *   nothing(if a callback was supplied)
     **************************************************************************/
    function tweet(status, cb = null) {
        local headers = { };

        local request = _oAuth1Request(tweetUrl, headers, { "status": status} );
        if (cb == null) {
            local response = request.sendsync();
            if (response && response.statuscode != 200) {
                server.log(format("Error updating_status tweet. HTTP Status Code %i:\r\n%s", response.statuscode, response.body));
                return false;
            } else {
                return true;
            }
        } else {
            request.sendasync(cb);
        }
    }

    /***************************************************************************
     * function: Stream
     *   Opens a connection to twitter's streaming API
     * 
     * Params:
     *   searchTerms - what we're searching for
     *   onTweet - callback function that executes whenever there is data
     *   onError - callback function that executes whenever there is an error
     **************************************************************************/
    function stream(searchTerms, onTweet, onError = null) {
        server.log("Opening stream for: " + searchTerms);
        // Set default error handler
        if (onError == null) onError = _defaultErrorHandler.bindenv(this);

        local method = "statuses/filter.json"
        local headers = { };
        local post = { track = searchTerms };
        local request = _oAuth1Request(streamUrl + method, headers, post);


        this.streamingRequest = request.sendasync(

            function(resp) {
                // connection timeout
                server.log("Stream Closed (" + resp.statuscode + ": " + resp.body +")");
                // if we have autoreconnect set
                if (resp.statuscode == 28) {
                    stream(searchTerms, onTweet, onError);
                } else if (resp.statuscode == 420) {
                    imp.wakeup(_reconnectTimeout, function() { stream(searchTerms, onTweet, onError); }.bindenv(this));
                    _reconnectTimeout *= 2;
                }
            }.bindenv(this),

            function(body) {
                 try {
                    if (body.len() == 2) {
                        _reconnectTimeout = 60;
                        _buffer = "";
                        return;
                    }

                    local data = null;
                    try {
                        data = http.jsondecode(body);
                    } catch(ex) {
                        _buffer += body;
                        try {
                            data = http.jsondecode(_buffer);
                        } catch (ex) {
                            return;
                        }
                    }
                    if (data == null) return;

                    // if it's an error
                    if ("errors" in data) {
                        server.log("Got an error");
                        onError(data.errors);
                        return;
                    } 
                    else {
                        if (_looksLikeATweet(data)) {
                            onTweet(data);
                            return;
                        }
                    }
                } catch(ex) {
                    // if an error occured, invoke error handler
                    onError([{ message = "Squirrel Error - " + ex, code = -1 }]);
                }
            }.bindenv(this)

        );
    }

    /***** Private Function - Do Not Call *****/
    function _encode(str) {
        return http.urlencode({ s = str }).slice(2);
    }

    function _oAuth1Request(postUrl, headers, data) {
        local time = time();
        local nonce = time;

        local parm_string = http.urlencode({ oauth_consumer_key = _consumerKey });
        parm_string += "&" + http.urlencode({ oauth_nonce = nonce });
        parm_string += "&" + http.urlencode({ oauth_signature_method = "HMAC-SHA1" });
        parm_string += "&" + http.urlencode({ oauth_timestamp = time });
        parm_string += "&" + http.urlencode({ oauth_token = _accessToken });
        parm_string += "&" + http.urlencode({ oauth_version = "1.0" });
        parm_string += "&" + http.urlencode(data);

        local signature_string = "POST&" + _encode(postUrl) + "&" + _encode(parm_string);

        local key = format("%s&%s", _encode(_consumerSecret), _encode(_accessSecret));
        local sha1 = _encode(http.base64encode(http.hash.hmacsha1(signature_string, key)));

        local auth_header = "oauth_consumer_key=\""+_consumerKey+"\", ";
        auth_header += "oauth_nonce=\""+nonce+"\", ";
        auth_header += "oauth_signature=\""+sha1+"\", ";
        auth_header += "oauth_signature_method=\""+"HMAC-SHA1"+"\", ";
        auth_header += "oauth_timestamp=\""+time+"\", ";
        auth_header += "oauth_token=\""+_accessToken+"\", ";
        auth_header += "oauth_version=\"1.0\"";

        local headers = { 
            "Authorization": "OAuth " + auth_header
        };

        local url = postUrl + "?" + http.urlencode(data);
        local request = http.post(url, headers, "");
        return request;
    }

    function _looksLikeATweet(data) {
        return (
            "created_at" in data &&
            "id" in data &&
            "text" in data &&
            "user" in data
        );
    }

    function _defaultErrorHandler(errors) {
        foreach(error in errors) {
            server.log("ERROR " + error.code + ": " + error.message);
        }
    }

}

twitter <- Twitter(API_KEY, API_SECRET, AUTH_TOKEN, TOKEN_SECRET);

function onTweet(tweetData) {
    // log the tweet, and who tweeted it (there is a LOT more info in tweetData)
    server.log(format("%s - %s", tweetData.text, tweetData.user.screen_name));
    device.send("tweet", null); 
}


// test function for manual hamster shaking
function requestHandler(request, response) {
  try {
    // check if the user sent led as a query parameter
    if ("tweet" in request.query) {
        device.send("tweet", null); 
    }
    // send a response back saying everything was OK.
    response.send(200, "tweet test ok");
  } catch (ex) {
    response.send(500, "Internal Server Error: " + ex);
  }
}

twitter.stream("yoursearchstring", onTweet);

// register the HTTP handler
http.onrequest(requestHandler);

Then, here’s the device code, based on the electric Imp PWM Servo example

// These values may be different for your servo
const SERVO_MIN = 0.03;
const SERVO_MAX = 0.1;

// create global variable for servo and configure
servo <- hardware.pin7;
servo.configure(PWM_OUT, 0.02, SERVO_MIN);

// assign pin9 to a global variable
led <- hardware.pin9;
// configure LED pin for DIGITAL_OUTPUT
led.configure(DIGITAL_OUT);

// global variable to track current state of LED pin
state <- 0;
// set LED pin to initial value (0 = off, 1 = on)
led.write(state);



// expects a value between 0.0 and 1.0
function SetServo(value) {
  local scaledValue = value * (SERVO_MAX-SERVO_MIN) + SERVO_MIN;
  servo.write(scaledValue);
}

// expects a value between -80.0 and 80.0
function SetServoDegrees(value) {
  local scaledValue = (value + 81) / 161.0 * (SERVO_MAX-SERVO_MIN) + SERVO_MIN;
  servo.write(scaledValue);
}

// current position (we'll flip between 0 and 1)
position <- 0;

function HamsterDance() {
  SetServoDegrees(-10);
  imp.sleep(0.5);
  SetServoDegrees(0);
  imp.sleep(0.2);
  SetServoDegrees(-15);
  imp.sleep(0.2);
  SetServoDegrees(5);  
  imp.sleep(0.2);
  SetServoDegrees(-20);
  imp.sleep(0.2);
  SetServoDegrees(0);
  imp.sleep(0.2);
  SetServoDegrees(-78);
} 


function ShakeRattleAndRoll(ledState){
    server.log("Let's shake the hamster!");  
    // turn on the led for visual debugging
    led.write(1);
    // 
    HamsterDance();
    imp.sleep(2);
    // turn off the led
    led.write(0);

} 

// initialize the servo to the start position
SetServoDegrees(-78);

//shake the hamster when got a tweet message from the Agent
agent.on("tweet", ShakeRattleAndRoll);

And… that’s it! Just connect the Electric Imp to a power source, configure the wifi via the Electric Imp mobile App, and start tweeting! Your hamster will be soo happy to warn you whenever a tweet is coming!!

Ciao and happy hacking!

Stefano

Advanced filters in ActiveAdmin

Use jQuery plugin Chosen for select boxes filters

The Chosen plugin aims to improve select boxes by adding search-based filtering. When a user clicks into a Chosen element, their cursor is placed in a search field. Users can select their choice just the same as a standard select element.

Integrate Chosen in active admin is pretty simple:

  • Add gem 'chosen-rails' in your gemfile
  • Configure Chosen adding some options (in app/assets/javascripts/active_admin/chosen.js.coffee):
    $ ->
        $('.chosen-filter').chosen
            allow_single_deselect: true
            width: '100%'
  • Include Chosen javascript assets in app/assets/javascripts/active_admin.js adding //= require chosen-jquery
  • Include Chosen stylesheet in app/assets/stylesheets/active_admin.css.scss adding @import "chosen";

Now you need to add the Chosen HTML class, in this case .chosen-filter, in formtastic select input. Create a file config/initializers/active_admin/chosen_filter.rb and add:

module ActiveAdmin
  module Inputs
    class Formtastic::Inputs::SelectInput
      include FilterBase

      def extra_input_html_options
        {
          class: 'chosen-filter'
        }
      end
    end
  end
end

Chosen with multiple select

Another option offered by Chosen is multiple select. User-selected options are displayed as boxes at the top of the element and are always visible. They can be removed with a single click or using backspace. Chosen has in-line search that allows you to navigate through associations so easily.

Create config/initializers/active_admin/multiple_chosen_filter.rb and add:

module ActiveAdmin
  module Inputs
    class FilterMultipleSelectInInput < Formtastic::Inputs::SelectInput
      include FilterBase

      def input_name
        "#{super}_in"
      end

      def extra_input_html_options
        {
          class: 'chosen-filter',
          multiple: true
        }
      end
    end
  end
end

Active Admin will pick the most relevant filter based on the attribute type. You can force the type by passing the :as option. In this case you need to use multiple_select_in to apply FilterMultipleSelectInInput class above.

Here’s an example of filter definition:

filter :country_id, label: 'Country', as: :multiple_select_in,  collection: -> { Country.all }

Overriding input_name method we configure ransack predicate. In this case we search all selected elements. Predicates are used within Ransack search queries to determine what information to match. Ransack predicates can be found here.

Dead simple view layer with Opal and jQuery

Dead simple view layer with Opal and jQuery

Lately I noticed that I started to port from project to project a simple class that implements a basic view layer with Opal and jQuery.

In this post we will rebuild it from scratch, you can consider it as an introduction for both Opal and Ruby, and maybe also a bit of OOP

The View class

If you still generate your HTML on the server (making happy search engines) and progressively add functionality via CSS and JavaScript then this class is probably a good fit.

Let’s start by defining our API. We want an object that:

  • takes charge of a piece of HTML
  • can setup listeners and handlers for events on that HTML node
  • can be easily composed with other objects
  • exposes behavior, hiding implementation

Step 1: An object, representing a piece of HTML

Say we have this HTML, representing a search bar:

<section class="search">
  <form>
    <input type="search" placeholder="Type here"></input>
    <input type="submit" value="Search">
  </form>
</section>

This is how we want to instantiate our view:

Document.ready? do
  element = Element.find('section.search')
  search_bar = SearchBar.new(element)
  search_bar.setup
end

The SearchBar class can look like this:

class SearchBar
  def initialize(element)
    @element = element
  end

  attr_reader :element

  def setup
    # do your thing here…
  end
end

Step 2: Adding behavior

Now that we have a place let’s add some behavior. For example we want to clear the field if the ESC key is pressed and to block the submission if the field is empty. We need to concentrate on the #setup method.

feature 1: “clear the field on ESC”

class SearchBar
  # …

  ESC_KEY = 27

  def setup
    element.on :keypress do |event|
      clear if event.key_code == ESC_KEY
    end
  end


  private

  def clear
    input.value = ''
  end

  def input
    @input ||= element.find('input')
  end
end

As you may have noted we’re memoizing #input and we’re searching the element inside our current HTML subtree. The latter is quite important, especially if you’re coming from jQuery and used to $-search everything every time. Sticking to this convention will avoid down the road those nasty bugs caused by selector ambiguities.

feature 2: “prevent submit if the field’s empty”

class SearchBar
  # …

  def setup
    element.on :keypress do |event|
      clear if event.key_code == ESC_KEY
    end

    form.on :submit do |event|
      event.prevent_default if input.value.empty?
    end
  end


  private

  def form
    @form ||= element.find('form')
  end
end

YAY! Sounds like we’re almost done!

Step 3: Extracting the View class

Now seems a good time to extract our view layer, thus leaving the SearchBar class with just the business logic:

class View
  def initialize(element)
    @element = element
  end

  attr_reader :element

  def setup
    # noop, implementation in subclasses
  end
end


class SearchBar < View
  def setup
    # …
  end

  private

  # …
end

Step 4: Topping with some “Usability”

Now that the View class has come to life we can add some sugar to make out lives easier.

Default Selector

We already know that the class will always stick to some specific HTML and its selector, it’s a good thing then to have some sensible defaults while still allowing to customize. We’ll define a default selector at the class definition, this way:

class SearchBar < View
  self.selector = 'section.search'
end

Document.ready? { SearchBar.new.setup }

The implementation looks like this:

class View
  class << self
    attr_accessor :selector
  end

  def initialize(options)
    parent   = options[:parent] || Element
    @element = options[:element] || parent.find(self.class.selector)
  end
end

ActiveRecord style creation

I’d also like to get rid of that Document.ready? that pops up every time. As always let’s define the API first:

class SearchBar < View
  self.selector = 'section.search'
end

SearchBar.create

And then the implementation

class View
  # …

  def self.create(*args)
    Document.ready? { create!(*args) }
    nil
  end

  def self.create!(*args)
    instance = new(*args)
    if instance.exist?
      instances << instance
      instance.setup
    end
    instance
  end

  def exist?
    element.any?
  end

  def self.instances
    @instances ||= []
  end
end

While there we also added a check on element existence and a list of active instances so that we can play nice with JS garbage collection and instantiate the class even if the HTML it needs is missing (e.g. we’re on a different page).

Conclusion

Hope you enjoyed and maybe learned a couple of things about Opal and opal-jquery, below are some links for you:

The full implementation is available in this gist: https://gist.github.com/elia/50a723e6133a645b4858

Opal’s website: http://opalrb.org
Opal API documentation: http://opalrb.org/docs/api
Opal-jQuery API documentation: http://opal.github.io/opal-jquery/doc/master/api
jQuery documentation: http://jqapi.com (unofficial but handy)

Screensavers, black screens and power management

"Oh nice, for today I’m done! Now lets watch a movie before going to sleep!"

This could be considered a justified desire right? After a day of work why not enjoy a movie?

Unfortunately, if you are running a Linux distro you may encouter some problems depending on the Desktop Environment (DE) and the power management system you are relying on.

Those problems can all be summarized in more or less random screensavers activations and/or consequent screens blanking.

image

image

Some DE comes with proper shortcuts to disable the system that handle the functionality under discussion.

However, it may happens that the functionality gets handled not entirely by a single application (i.e. system) but by more than one (e.g. screensaver, DE locker application, and eventual power management system)

In this case you may not find a quick solution that works right out of the box but nothing stops you from implementing your personal custom solution!

This is exactly what I did and here’s what I end up for my system (i.e. XFCE 4.10 with LightLocker and the default xfce4-power-manager).

This isn’t really nothing special, just a switch in a few lines of bash! ;)

 
#!/bin/sh 

enabled_flag='DPMS is Enabled' 

if [ "`xset -q | grep "${enabled_flag}"`" ]; then 
  notify-send "Screensaver disabled" 
  xset s off -dpms 
else 
  notify-send "Screensaver enabled" 
  xset s on +dpms 
fi 
    

As you can see I just check the state of DPMS by grepping the result of querying the xset utility and then adjust the settings (still through xset) accordingly.

The notify-send "..." is only used to get some visual feedback.

Once saved and made it executable you can simply bind the tiny script to a custom shortcut. Depending on the DE you are using there may be some differences in this last step but it shouldn’t be particular difficult to get it done.

For a more comprehensive explanation of what is going on under the hood take a look at the great Arch doc linked above (https://wiki.archlinux.org/index.php/Display_Power_Management_Signaling).

Now you only need to fire up your favorite player and invoke the over mentioned script through the chosen shortcut to watch what you want without being bothered!

Enjoy it! :)