Skip to content
/ Elf Public

a lightweight, pure-swift router for url handling

License

Notifications You must be signed in to change notification settings

limboy/Elf

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Elf

Swift Cocoapods

Elf is a lightweight, pure-swift router for url handling. Make incoming url find the right handler. that's all Elf do.

Elf Workflow

So what is a handler?

protocol Handler: class {
    func convert(params:Dictionary<String, String>, queryParams: Dictionary<String, String>)
    func handle()
}

if Elf find the right handler, it will first call convert method with params and queryParams. Within this method, these params can be converted to internal properties.

handle() will be called next. you can do all stuff here. like build a View Controller, push it or present it, or just make a switch on based on incoming params, or popup an alert window, etc.

Getting Started

First Let's create some handlers.

class ProfileHandler:Handler {
	var profileID: String?

	func convert(params: Dictionary<String, String>, queryParams: Dictionary<String, String>) {
		profileID = queryParams["id"]
	}
	
	func handle() {
		if let id = profileID {
			let vc = ProfileViewController()
			vc.profileID = id
			navigationController.push(vc)
		} else {
			// present error page
		}
	}
}

class UserHandler:Handler {
	var username: String?

	func convert(params: Dictionary<String, String>, queryParams: Dictionary<String, String>) {
		username = params["username"]
	}
	
	func handle() {
		if let id = username {
			let vc = UserFollowingViewController()
			navigationController.push(vc)
		}
	}
}

Then connect these handlers with appropriate url patterns.

let routeTable:[String:Handler] = [
	"app://profile": ProfileHandler(),
	"app://user/{username}/following": UserHandler(),
]

Elf.instance.registerRoutingTable(routeTable, notFoundHandler: {url in print("\(url) not found")})

Now let's handle urls!

// for test case
Elf.instance.handleURL(url: "app://profile?id=1024")
Elf.instance.handleURL(url: "app://user/limboy/following")

// usually it should be put in `AppDelegate's` method
func application(_ application: UIApplication,
                   open url: URL,
                   sourceApplication: String?,
                   annotation: Any) -> Bool {

	// ...
	Elf.instance.handleURL(url: url.absoluteString)
  }

if Elf found url's pattern in registered table, target handler will be triggered, else notFoundHandler will be called, in there a custom ViewController can be presented.

Installation

Podfile

pod 'Elf'

Scenarios

Push a viewcontroller

for example, tap an item in ShopItemListViewController, a DetailViewController should be pushed. it can be done like this:

let handler = DetailHandler()
handler.id = 1024
handler.handle()

DetailHandler is used as an entry/wrapper for DetailViewController, no matter it's came from url or called internally. both handled in a universal way.

So you don't have to open target View Controller, trying to find out what's needed to init. It's also flexible for DetailViewController if some properties should be changed as long as DetailHandler is not affected.

Pinterest Style

When a Pinterest's waterfall list item is tapped, its model will be passed to detail page, so it won't be blank at first. but when opened by url, no model will be passed in. These two scenarios are all handled by DetailHandler.

class DetailHandler:Handler {
	var itemModel: ItemModel?
	var itemID: String?

	func convert(params: Dictionary<String, String>, queryParams: Dictionary<String, String>) {
		itemID = params["id"]
	}
	
	func handle() {
		// opened internal
		if let model = itemModel {
			let vc = DetailViewController()
			vc.model = itemModel
			navigationController.push(vc)
			return
		}

		// opened by url
		if let id = itemID {
			let vc = DetailViewController()
			vc.id = itemID
			navigationController.push(vc)
		}
	}
}

When the basic model is filled or user has made some change, previous View Controller should know, and update accordingly. just add a property like onModelUpdate(), when model is updated just call this function (if it not nil).

Router Design

URL patterns are stored in a radix like tree, it will first split incoming url into sections, then match them one by one through the tree.

Tips

  • to keep things simple, url pattern only match to String. app://profile/{id} will get params["id"] as String in convert method.
  • a navigator can be injected into Handler's child protocol, to make it easier to navigate.
  • SomeHandler can be used to act as a config handler, when opened by url like ?debugcode=xxx, debug mode is on.

Contact

Follow and contact me on Twitter. If you find an issue, just open a ticket. Pull requests are warmly welcome as well.

License

Elf is released under the MIT license. See LICENSE for details.