Class: Routable::Router

Inherits:
Object
  • Object
show all
Defined in:
lib/routable/router.rb

Constant Summary collapse

TRANSITION_STYLES =
{
  :cover => UIModalTransitionStyleCoverVertical,
  :flip => UIModalTransitionStyleFlipHorizontal,
  :dissolve => UIModalTransitionStyleCrossDissolve,
  :curl => UIModalTransitionStylePartialCurl
}
PRESENTATION_STYLES =
{
  :full_screen => UIModalPresentationFullScreen,
  :page_sheet => UIModalPresentationPageSheet,
  :form_sheet => UIModalPresentationFormSheet,
  :current => UIModalPresentationCurrentContext
}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

The root UINavigationController we use to push/pop view controllers



26
27
28
# File 'lib/routable/router.rb', line 26

def navigation_controller
  @navigation_controller
end

Class Method Details

.routerObject



20
21
22
# File 'lib/routable/router.rb', line 20

def router
  @router ||= Router.new
end

Instance Method Details

#controller_for_url(url) ⇒ Object

Returns a UIViewController for the given url EX router.controller_for_url(“users/3”)

> #<UsersController @id=‘3’>



216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
# File 'lib/routable/router.rb', line 216

def controller_for_url(url)
  return shared_vc_cache[url] if shared_vc_cache[url]

  open_options = options_for_url(url)
  open_params = open_options[:open_params]
  open_klass = open_options[:klass]
  controller = open_klass.alloc
  if controller.respond_to? :initWithParams
    controller = controller.initWithParams(open_params)
  else
    controller = controller.init
  end

  if open_options[:shared]
    shared_vc_cache[url] = controller
    # when controller.viewDidUnload called, remove from cache.
    controller.class.class_eval do
      define_method(:new_dealloc) do
        shared_vc_cache.delete url
      end
    end
    controller.instance_eval do
      def viewDidUnload
        new_dealloc
        super
      end
    end
  end

  transition = open_options[:transition]
  if transition
    controller.modalTransitionStyle = TRANSITION_STYLES[transition]
  end

  presentation = open_options[:presentation]
  if presentation
    controller.modalPresentationStyle = PRESENTATION_STYLES[presentation] 
  end

  controller
end

#map(url, klass = nil, options = {}, &callback) ⇒ Object

Map a URL to a UIViewController EX router.map “/users/:id”, UsersController PARAMS

url => the URL to map
klass (optional) => the UIViewController class to open
options (optional) => hash of options
&callback => the block to be run when opening the URL

OPTIONS

:modal => true/false
  - We present the VC modally (router is not shared between the new nav VC)
:shared => true/false
  - If URL is called again, we pop to that VC if it's in memory.
:resets => true/false
  - Resets the navigation stack with the target view controller
:transition => [:cover, :flip, :dissolve, :curl]
  - A symbol to represented transition style used. Mapped to UIModalTransitionStyle.
:presentation => [:full_screen, :page_sheet, :form_sheet, :current]
  - A symbol to represented presentation style used. Mapped to UIModalPresentationStyle.


56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/routable/router.rb', line 56

def map(url, klass = nil, options = {}, &callback)
  format = url

  if callback
    self.routes[format] = options.merge!(callback: callback)
  end

  if options[:transition] && !TRANSITION_STYLES.keys.include?(options[:transition])
    raise ArgumentError, ":transition must be one of #{TRANSITION_STYLES.keys}"
  end

  if options[:presentation] && !PRESENTATION_STYLES.keys.include?(options[:presentation])
    raise ArgumentError, ":presentation must be one of #{PRESENTATION_STYLES.keys}"
  end

  self.routes[format] = options.merge!(klass: klass)
end

#open(url, animated = true, &block) ⇒ Object

Push the UIViewController for the given url EX router.open(“users/3”)

> router.navigation_controller pushes a UsersController



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/routable/router.rb', line 86

def open(url, animated = true, &block)
  controller_options = options_for_url(url)

  if controller_options[:callback]
    params = controller_options[:open_params]
    callback = controller_options[:callback]
    case callback.arity
    when 0
      callback.call
    when 1
      callback.call(params)
    end
    return
  end

  controller = controller_for_url(url)

  # Allow configuration of the view controller after
  # initialization but before navigating to the view controller
  if block_given?
    block.call(controller)
  end

  if self.navigation_controller.modalViewController
    dismiss_animated = animated

    # Can't dismiss and present two controllers animated at the same time,
    # so we just dismiss the current one without an animation
    if controller_options[:modal] && dismiss_animated
      dismiss_animated = false
    end

    self.navigation_controller.dismissModalViewControllerAnimated(dismiss_animated)
  end

  if controller_options[:resets]
    navigation_controller.setViewControllers([controller], animated: animated)
  elsif controller_options[:modal]
    if controller.class == UINavigationController
      self.navigation_controller.presentModalViewController(controller, animated: animated)
    else
      tempNavigationController = UINavigationController.alloc.init
      tempNavigationController.pushViewController(controller, animated: false)
      tempNavigationController.modalTransitionStyle = controller.modalTransitionStyle
      tempNavigationController.modalPresentationStyle = controller.modalPresentationStyle
      self.navigation_controller.presentModalViewController(tempNavigationController, animated: animated)
    end
  else
    if self.navigation_controller.viewControllers.member? controller
      self.navigation_controller.popToViewController(controller, animated:animated)
    else
      self.navigation_controller.pushViewController(controller, animated:animated)
    end
  end
end

#open_external(url) ⇒ Object

Open the given URL via the iOS protocol EX router.open_external(“google.com”)

> Opens Google in Safari.app



78
79
80
# File 'lib/routable/router.rb', line 78

def open_external(url)
  UIApplication.sharedApplication.openURL(NSURL.URLWithString(url))
end

#options_for_url(url) ⇒ Object



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/routable/router.rb', line 153

def options_for_url(url)
  # map of url => options

  @url_options_cache ||= {}
  if @url_options_cache[url]
    return @url_options_cache[url]
  end

  parts = url.split("/")

  open_options = nil
  open_params = {}

  self.routes.each { |format, options|

    # If the # of path components isn't the same, then
    # it for sure isn't a match.
    format_parts = format.split("/")
    if format_parts.count != parts.count
      next
    end

    matched = true
    format_params = {}
    # go through each of the path compoenents and
    # check if they match up (symbols aside)
    format_parts.each_with_index {|format_part, index|
      check_part = parts[index]

      # if we're looking at a symbol (ie :user_id),
      # then note it and move on.
      if format_part[0] == ":"
        format_params[format_part[1..-1].to_sym] = check_part
        next
      end

      # if we're looking at normal strings,
      # check equality.
      if format_part != check_part
        matched = false
        break
      end
    }

    if !matched
      next
    end

    open_options = options
    open_params = format_params
  }

  if open_options == nil
    raise "No route found for url #{url}"
  end

  @url_options_cache[url] = open_options.merge(open_params: open_params)
end

#pop(animated = true) ⇒ Object

Pop the top level UIViewController EX router.pop



145
146
147
148
149
150
151
# File 'lib/routable/router.rb', line 145

def pop(animated = true)
  if self.navigation_controller.modalViewController
    self.navigation_controller.dismissModalViewControllerAnimated(animated)
  else
    self.navigation_controller.popViewControllerAnimated(animated)
  end
end

#routesObject

Hash of URL => UIViewController classes EX {“users/:id” => UsersController,

"users/:id/posts" => PostsController,
"users/:user_id/posts/:id" => PostController }


33
34
35
# File 'lib/routable/router.rb', line 33

def routes
  @routes ||= {}
end

#shared_vc_cacheObject



258
259
260
# File 'lib/routable/router.rb', line 258

def shared_vc_cache
  @shared_vc_cache ||= {}
end