📱📲 Navigate between view controllers with ease. 💫
Clone or download
Latest commit 848ea41 May 5, 2018

README.md

CoreNavigation 📱📲

Navigate between view controllers with ease. 💫

Platform Build Status Documentation codecov CocoaPods Compatible Carthage compatible

Getting Started

These instructions will help you integrate CoreNavigation into your project.

Prerequisities

  • Xcode 9 or higher
  • iOS 8 or higher
  • Cocoapods

Installation

CocoaPods

CocoaPods is a dependency manager for Cocoa projects. You can install it with the following command:

$ gem install cocoapods

CocoaPods 1.1+ is required to build CoreNavigation 1.0+.

To integrate CoreNavigation into your Xcode project using CocoaPods, specify it in your Podfile:

target '<Your Target Name>' do
    use_frameworks!
    
    pod 'CoreNavigation', '1.0.0-beta-4'
end

Then, run the following command:

$ pod install

Carthage

Carthage is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.

You can install Carthage with Homebrew using the following command:

$ brew update
$ brew install carthage

To integrate CoreNavigation into your Xcode project using Carthage, specify it in your Cartfile:

github "aronbalog/CoreNavigation" == "1.0.0-beta-4"

API Reference

API reference

Example Use

Defining view controller:

class PersonProfileViewController: UIViewController, DataReceivable {

    // DataReceivable associatedtype
    typealias DataType = Person

    func didReceiveData(_ data: Person) {
        // configure UI with data
    }
}

Presenting view controller:

Code Example

Navigate.present { $0
    .to(PersonProfileViewController())
    .withData(person)
}

Pushing view controller:

Code Example

Navigate.push { $0
    .to(PersonProfileViewController())
    .withData(person)
}

Routing & deep linking:

Why use the Destination instead navigating directly to view controller?

Read about it on Medium:

Defining Destination

Code Example

struct PersonProfile: Destination, Routable {

    // Destination associatedtype
    typealias ViewControllerType = PersonProfileViewController

    // Routable patterns
    static var patterns: [String] = [
        "https://myapp.com/person/:personId(.*)",
        "https://myapp.com/user/:personId(.*)"
    ]
    
    let personId: String
    
    init(_ personId: String) {
        self.personId = personId
    }
    
    var parameters: [String : Any]? {
        return [
            "personId": personId
        ]
    }

    static func resolve(context: Context<PersonProfile>) {
        guard let personId = context.parameters?["personId"] as? String else {
            // cancel navigation with some error
            context.cancel(error: NavigationError.Destination.notFound)
            return
        }
        
        // fetch person
        fetchPerson(id: personId, completion: { (person: Person) in
            // continue to navigation
            context.complete(data: person)
        }, failure: { (error: Error) in
            // cancel navigation with some error
            context.cancel(error: error)
        })
    }
}

Registering Routable types

In order to use Matchable types (String, URL, etc.) to navigate, every Destination type must be registered. Think about it as internal DNS.

PersonProfile.register()
Additional syntax
Navigate.router.register(routableType: PersonProfile.self)

Destination type can be routable without conforming to Routable protocol. Use this if you intend to create some kind of destination manifest and/or if route patterns are fetched from an external source:

Navigate.router.register(destinationType: PersonProfile.self, patterns: [
    "https://myapp.com/person/:personId(.*)",
    "https://myapp.com/user/:personId(.*)"
])
Additional syntax
PersonProfile.self <- [
    "https://myapp.com/person/:personId(.*)",
    "https://myapp.com/user/:personId(.*)"
]

Settings.self <- [
    "https://myapp.com/settings"
]

Navigating using Destination

// present
Navigate.present { $0
    .to(PersonProfile("sherlock_holmes"))
    ...
}

// or push
Navigate.push { $0
    .to(PersonProfile("sherlock_holmes"))
    ...
}
Additional syntax
// present
PersonProfile("sherlock_holmes").present { $0
    ...
}

// or push
PersonProfile("sherlock_holmes").push { $0
    ...
}
Additional syntax
// present
PersonProfile("sherlock_holmes").present()

// or push
PersonProfile("sherlock_holmes").push()

Navigating using route

Code Example

// present
Navigate.present { $0
    .to("https://myapp.com/person/sherlock_holmes")
    ...
}

// or push
Navigate.push { $0
    .to("https://myapp.com/person/sherlock_holmes")
    ...
}
Additional syntax
// present
"https://myapp.com/person/sherlock_holmes".present { $0
    ...
}

// or push
"https://myapp.com/person/sherlock_holmes".push { $0
    ...
}
Additional syntax
// present
"https://myapp.com/person/sherlock_holmes".present()

// or push
"https://myapp.com/person/sherlock_holmes".push()

Getting view controller asynchronously using Destination

PersonProfile("sherlock_holmes").viewController { (viewController) in
    // vc is `PersonProfileViewController`
}

Getting view controller asynchronously using route

"https://myapp.com/person/sherlock_holmes".viewController { (viewController) in
    ...
}

Getting view controller synchronously using Destination

Code Example

do {
    let viewController = try PersonProfile("sherlock_holmes").viewController()
} catch let error {
    // handle error
}

Getting view controller synchronously using route

do {
    let viewController = try "https://myapp.com/person/sherlock_holmes".viewController()
} catch let error {
    // handle error
}

Note:

If you implement custom destination resolving, it must happen on the main thread; otherwise, an error is thrown.


Matchable protocol

URL types can also be used to navigate or resolve view controller. Actually, any type conforming Matchable protocol can be used.

Conforming to matchable:
struct Person {
    let id: String
    ...
}

extension Person: Matchable {
    var uri: String {
        return "https://myapp.com/person/" + id
    }
}
Example usage:
let person: Person = Person(id: "sherlock_holmes", ...)

// getting view controller
let personProfileViewController = try! person.viewController

// or navigating
person.present()
person.push()

// or more configurable syntax
Navigate.present { $0
    .to(person)
    ...
}

Configuration

Example Apps

Running the Tests

Available in CoreNavigationTests target.

Versioning

Current release:

  • 1.0.0-beta-4

Authors

Contributing

Please read Contributing for details on code of conduct, and the process for submitting pull requests.

License

This project is licensed under the MIT License - see the LICENSE file for details.