Skip to content

Commit 5178cfb

Browse files
author
YuYue
committed
Add sources
1 parent 6afe4a2 commit 5178cfb

File tree

10 files changed

+1238
-1
lines changed

10 files changed

+1238
-1
lines changed

.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// swift-tools-version: 5.5
2+
// The swift-tools-version declares the minimum version of Swift required to build this package.
3+
4+
import PackageDescription
5+
6+
let package = Package(
7+
name: "ReerRouter",
8+
platforms: [
9+
.iOS(.v10)
10+
],
11+
products: [
12+
.library(
13+
name: "ReerRouter",
14+
targets: ["ReerRouter"]),
15+
],
16+
targets: [
17+
.target(
18+
name: "ReerRouter",
19+
path: "Sources")
20+
]
21+
)
22+

README.md

Lines changed: 219 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,220 @@
11
# ReerRouter
2-
App URL router for iOS (Swift only).
2+
App URL router for iOS (Swift only). Inspired by [URLNavigator](https://github.com/devxoul/URLNavigator).
3+
4+
5+
## Example App
6+
To run the example project, clone the repo, and run `pod install` from the Example directory first.
7+
8+
## Requirements
9+
At least iOS 10.0
10+
Xcode 13.2
11+
12+
## Installation
13+
14+
### CocoaPods
15+
ReerRouter is available through [CocoaPods](https://cocoapods.org). To install
16+
it, simply add the following line to your Podfile:
17+
18+
```ruby
19+
pod 'ReerRouter'
20+
```
21+
### Swift Package Manager
22+
```
23+
import PackageDescription
24+
25+
let package = Package(
26+
name: "YOUR_PROJECT_NAME",
27+
targets: [],
28+
dependencies: [
29+
.package(url: "https://github.com/reers/ReerRouter.git", from: "0.1.0")
30+
]
31+
)
32+
```
33+
```
34+
.target(
35+
name: "YOUR_TARGET_NAME",
36+
dependencies: ["ReerRouter",]
37+
),
38+
```
39+
40+
## Getting Started
41+
### 1. Understanding `Route.Key`
42+
`Route.Key` means URL `host` + `path`
43+
```
44+
/// myapp://example.com/over/there?name=phoenix#nose
45+
/// \______/\_________/\_________/ \__________/ \__/
46+
/// | | | | |
47+
/// scheme host path queries fragment
48+
/// \_________/
49+
/// |
50+
/// route key
51+
```
52+
### 2. Register Route
53+
54+
* Register an action.
55+
```
56+
Router.shared.registerAction(with: "abc_action") { _ in
57+
print("action executed.")
58+
}
59+
```
60+
61+
* Register a view controller by its type and a route key.
62+
```
63+
extension Route.Key {
64+
static let userPage: Self = "user"
65+
}
66+
Router.shared.register(UserViewController.self, forKey: .userPage)
67+
Router.shared.register(UserViewController.self, forKey: "user")
68+
```
69+
70+
* Register view controllers by their types and route keys.
71+
```
72+
Router.shared.registerPageClasses(with: ["preference": PreferenceViewController.self])
73+
```
74+
75+
* Register view controllers by their type names and route keys.
76+
```
77+
Router.shared.registerPageClasses(with: ["preference": "ReerRouter_Example.PreferenceViewController"])
78+
```
79+
80+
* Implement `Routable` for view controller.
81+
```
82+
class UserViewController: UIViewController, Routable {
83+
var params: [String: Any]
84+
85+
required init?(param: Route.Param) {
86+
self.params = param.allParams
87+
super.init(nibName: nil, bundle: nil)
88+
}
89+
}
90+
```
91+
92+
### 3. Execute an route action.
93+
```
94+
Router.shared.executeAction(byKey: "abc_action")
95+
Router.shared.open("myapp://abc_action")
96+
```
97+
98+
### 4. Open a view controller.
99+
```
100+
Router.shared.open("myapp://user?name=phoenix")
101+
Router.shared.push("myapp://user?name=phoenix")
102+
Router.shared.present("myapp://user?name=phoenix")
103+
```
104+
105+
### 5. Delegate for for the app about the route.
106+
```
107+
extension RouteManager: RouterDelegate {
108+
func router(_ router: Router, willOpenURL url: URL, userInfo: [String : Any]) -> URL? {
109+
print("will open \(url)")
110+
if let _ = url.absoluteString.range(of: "google") {
111+
return URL(string: url.absoluteString + "&extra1=234244&extra2=afsfafasd")
112+
} else if let _ = url.absoluteString.range(of: "bytedance"), !isUserLoggedIn() {
113+
print("intercepted by delegate")
114+
return nil
115+
}
116+
return url
117+
}
118+
119+
func router(_ router: Router, didOpenURL url: URL, userInfo: [String : Any]) {
120+
print("did open \(url) success")
121+
}
122+
123+
func router(_ router: Router, didFailToOpenURL url: URL, userInfo: [String : Any]) {
124+
print("did fail to open \(url)")
125+
}
126+
127+
func router(_ router: Router, didFallbackToURL url: URL, userInfo: [String: Any]) {
128+
print("did fallback to \(url)")
129+
}
130+
}
131+
```
132+
133+
### 6. Fallback
134+
* Use `route_fallback_url` key as a fallback url when some thing went wrong.
135+
```
136+
Router.shared.open("myapp://unregisteredKey?route_fallback_url=myapp%3A%2F%2Fuser%3Fname%3Di_am_fallback")
137+
```
138+
139+
### 7. Redirect
140+
* Implement `redirectURLWithRouteParam(_:)` method to redirect to a new url for the view controller.
141+
```
142+
class PreferenceViewController: UIViewController, Routable {
143+
144+
required init?(param: Route.Param) {
145+
super.init(nibName: nil, bundle: nil)
146+
}
147+
148+
class func redirectURLWithRouteParam(_ param: Route.Param) -> URL? {
149+
if let value = param.allParams["some_key"] as? String, value == "redirect" {
150+
return URL(string: "myapp://new_preference")
151+
}
152+
return nil
153+
}
154+
}
155+
```
156+
157+
### 8. Global instance for the router singleton.
158+
```
159+
public let AppRouter = Router.shared
160+
AppRouter.open("myapp://user")
161+
```
162+
163+
### 9. Notifications when will open and did open.
164+
```
165+
NotificationCenter.default.addObserver(
166+
forName: Notification.Name.routeWillOpenURL,
167+
object: nil,
168+
queue: .main
169+
) { notification in
170+
if let param = notification.userInfo?[Route.notificationUserInfoKey] as? Route.Param {
171+
print("notification: route will open \(param.sourceURL)")
172+
}
173+
}
174+
175+
NotificationCenter.default.addObserver(
176+
forName: Notification.Name.routeDidOpenURL,
177+
object: nil,
178+
queue: .main
179+
) { notification in
180+
if let param = notification.userInfo?[Route.notificationUserInfoKey] as? Route.Param {
181+
print("notification: route did open \(param.sourceURL)")
182+
}
183+
}
184+
```
185+
186+
### 10. Custom controlling for transition.
187+
```
188+
public typealias UserTransition = (
189+
_ fromNavigationController: UINavigationController?,
190+
_ fromViewController: UIViewController?,
191+
_ toViewController: UIViewController
192+
) -> Bool
193+
194+
public enum TransitionExecutor {
195+
/// Transition will be handled by router automatically.
196+
case router
197+
/// Transition will be handled by user who invoke the router `push` or `present` method.
198+
case user(UserTransition)
199+
/// Transition will be handled by user who invoke the router `push` or `present` method.
200+
case delegate
201+
}
202+
203+
let transition: Route.UserTransition = { fromNavigationController, fromViewController, toViewController in
204+
toViewController.transitioningDelegate = self.animator
205+
toViewController.modalPresentationStyle = .currentContext
206+
// Use the router found view controller directly, or just handle transition by yourself.
207+
// fromViewController?.present(toViewController, animated: true)
208+
self.present(toViewController, animated: true)
209+
return true
210+
}
211+
AppRouter.present(user.urlString, transitionExecutor: .user(transition))
212+
```
213+
214+
## Author
215+
216+
phoenix, x.rhythm@qq.com
217+
218+
## License
219+
220+
ReerRouter is available under the MIT license. See the LICENSE file for more info.

ReerRouter.podspec

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#
2+
# Be sure to run `pod lib lint ReerRouter.podspec' to ensure this is a
3+
# valid spec before submitting.
4+
#
5+
# Any lines starting with a # are optional, but their use is encouraged
6+
# To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html
7+
#
8+
9+
Pod::Spec.new do |s|
10+
s.name = 'ReerRouter'
11+
s.version = '0.1.0'
12+
s.summary = 'A router for iOS app.'
13+
14+
s.description = <<-DESC
15+
A router for iOS app.
16+
DESC
17+
18+
s.homepage = 'https://github.com/reers/ReerRouter'
19+
s.license = { :type => 'MIT', :file => 'LICENSE' }
20+
s.author = { 'phoenix' => 'x.rhythm@qq.com' }
21+
s.source = { :git => 'https://github.com/reers/ReerRouter.git', :tag => s.version.to_s }
22+
23+
s.ios.deployment_target = '10.0'
24+
25+
s.source_files = 'Sources/**/*'
26+
end

Sources/Define.swift

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
//
2+
// RouteDefine.swift
3+
//
4+
//
5+
// Created by YuYue on 2022/7/24.
6+
//
7+
8+
import UIKit
9+
10+
// MARK: - Definitions
11+
12+
/// Namespcace
13+
public enum Route {}
14+
15+
extension Route {
16+
17+
public enum OpenStyle {
18+
case push
19+
case present(UIModalPresentationStyle)
20+
}
21+
22+
public typealias UserTransition = (
23+
_ fromNavigationController: UINavigationController?,
24+
_ fromViewController: UIViewController?,
25+
_ toViewController: UIViewController
26+
) -> Bool
27+
28+
public enum TransitionExecutor {
29+
/// Transition will be handled by router automatically.
30+
case router
31+
/// Transition will be handled by user who invoke the router `push` or `present` method.
32+
case user(UserTransition)
33+
/// Transition will be handled by delgate.
34+
case delegate
35+
}
36+
37+
/// Internal default scheme.
38+
public static let defaultScheme = "reer"
39+
40+
public static let fallbackURLKey = "route_fallback_url"
41+
42+
public typealias Action = (_ params: Route.Param) -> Void
43+
}
44+
45+
public typealias UIViewControllerClassName = String
46+
47+
/// Global instance of the Router.
48+
public let AppRouter = Router.shared
49+
50+
51+
// MARK: - Routable
52+
53+
/// Reresent a UIViewController that can be routed by ReerRouter.
54+
public protocol Routable: UIViewController {
55+
56+
/// Initializer for the view controller, param will take the source url, query params and user info params.
57+
init?(param: Route.Param)
58+
59+
/// Implement this method if the view controller needs to redirect to another view controller under some conditions.
60+
static func redirectURLWithRouteParam(_ param: Route.Param) -> URL?
61+
}
62+
63+
/// Make the protocol optional.
64+
public extension Routable {
65+
static func redirectURLWithRouteParam(_ param: Route.Param) -> URL? { return nil }
66+
}
67+
68+
69+
// MARK: - Router Delegate
70+
71+
public protocol RouterDelegate {
72+
/// Callback when url will be opened.
73+
func router(_ router: Router, willOpenURL url: URL, userInfo: [String: Any]) -> URL?
74+
/// Callback when url has been opened.
75+
func router(_ router: Router, didOpenURL url: URL, userInfo: [String: Any])
76+
/// Callback when failed to open url.
77+
func router(_ router: Router, didFailToOpenURL url: URL, userInfo: [String: Any])
78+
/// Callback when fallback url has been opened.
79+
func router(_ router: Router, didFallbackToURL url: URL, userInfo: [String: Any])
80+
/// Callback when router ask the delegate to handle transition.
81+
func routeTransition(
82+
with router: Router,
83+
param: Route.Param,
84+
fromNavigationController: UINavigationController?,
85+
fromViewController: UIViewController?,
86+
toViewController: UIViewController
87+
) -> Bool
88+
}
89+
90+
/// Make the protocol optional.
91+
public extension RouterDelegate {
92+
func router(_ router: Router, willOpenURL url: URL, userInfo: [String: Any]) -> URL? { return url }
93+
func router(_ router: Router, didOpenURL url: URL, userInfo: [String: Any]) {}
94+
func router(_ router: Router, didFailToOpenURL url: URL, userInfo: [String: Any]) {}
95+
func router(_ router: Router, didFallbackToURL url: URL, userInfo: [String: Any]) {}
96+
func routeTransition(
97+
with router: Router,
98+
param: Route.Param,
99+
fromNavigationController: UINavigationController?,
100+
fromViewController: UIViewController?,
101+
toViewController: UIViewController
102+
) -> Bool { return false }
103+
}
104+
105+
106+
// MARK: - Router Notification
107+
108+
public extension Notification.Name {
109+
static let routeWillOpenURL = Notification.Name("ReerRouterWillOpenURLNotification")
110+
static let routeDidOpenURL = Notification.Name("ReerRouterDidOpenURLNotification")
111+
}
112+
113+
extension Route {
114+
/// Get user info from notification instance.
115+
///
116+
/// if let param = notification.userInfo?[Route.notificationUserInfoKey] as? Route.Param {}
117+
public static let notificationUserInfoKey = "ReerRouterNotificationUserInfoKey"
118+
}

0 commit comments

Comments
 (0)