Search code examples
iosrubymotionrubymotion-promotion

Using asynchronously obtained data in Motion-Kit layouts


I'm reverse geocoding a pair of coordinates to find the user's city, but am having a hard time gettingthe city into a Motion-Kit layout. What's the best way to get the city into the layout? I also will be adding other information from an API to the layout, so that will probably run into the same problem.

There is a basic MK layout like this:

class HomeLayout < MK::Layout

  def initialize(data)
    @city = data[:city]
    super
  end

  def layout
    add UILabel, :location
  end

  def location_style
    text @city
    color :white.uicolor
    font UIFont.fontWithName("Avenir", size: 22)
    size_to_fit
    center ['50%', 80]
  end

end

I get @city from this method in HomeScreen:

def city
  loc = CLLocation.alloc.initWithLatitude App::Persistence['latitude'], longitude: App::Persistence['longitude']
  geo = CLGeocoder.new
  geo.reverseGeocodeLocation loc, completionHandler: lambda { |result, x|
    return result[0].locality
  }
  # This currently returns a CLGeocoder object, but I want it to return the city as a String.
end

I get App::Persistence['latitude'] from in on_activate in AppDelegate, like so:

def on_activate
  BW::Location.get_once do |result|
    if result.is_a?(CLLocation)
      App::Persistence['latitude'] = result.coordinate.latitude
      App::Persistence['longitude'] = result.coordinate.longitude
      open HomeScreen.new
    else
      LocationError.handle(result[:error])
    end
  end
end

Any help would be appreciated. Thanks ahead of time.


Solution

  • I'd have to see how you are instantiating the layout, but even without that I have a guess: you should consider supporting a fetching location message that is dismissed when the data is available.

    The workflow would look something like this:

    • create your layout without providing the location data. It will start in a "waiting for location" state
    • fetch the city location, much like you are now
    • provide the location to the layout, and it can animate the view changes

    class HomeLayout < MK::Layout def location(value) # update view locations. # you might also provide an `animate: true/false` argument, # so that you can update the UI w/out animation if the # location is available at startup end end

    Once this is done you can do a second pass: providing the city data at startup. If the data is available, you should be able to pass it in to your initializer like you are above, and the layout should bypass the "loading" state.

    The reason I recommend this approach is because it makes the controller more "idempotent". Whether it is provided the location data at startup or not, it can handle both cases.

    Also, you'll be able to open HomeScreen.new before waiting for the get_once block to finish.