Adds a viewmodifier for SwiftUI for routing
This commit is contained in:
parent
4471d4d946
commit
eeee6fb04d
|
@ -4,6 +4,9 @@ import PackageDescription
|
|||
|
||||
let package = Package(
|
||||
name: "RouteKit",
|
||||
platforms: [
|
||||
.iOS(.v14),
|
||||
],
|
||||
products: [
|
||||
.library(
|
||||
name: "RouteKit",
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
29984C392763E400006C359A /* ViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29984C382763E400006C359A /* ViewModifier.swift */; };
|
||||
29984C3A2763E400006C359A /* ViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29984C382763E400006C359A /* ViewModifier.swift */; };
|
||||
430EC1EA20598F7600F84EA2 /* RouteKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 430EC1E120598F7600F84EA2 /* RouteKit.framework */; };
|
||||
430EC1F820598FDB00F84EA2 /* AnyRoute.swift in Sources */ = {isa = PBXBuildFile; fileRef = 435264D82048705600F23D74 /* AnyRoute.swift */; };
|
||||
430EC1F920598FDB00F84EA2 /* Navigator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 435BFF8320583A9000346484 /* Navigator.swift */; };
|
||||
|
@ -57,6 +59,7 @@
|
|||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
29984C382763E400006C359A /* ViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModifier.swift; sourceTree = "<group>"; };
|
||||
430EC1DA20598E2C00F84EA2 /* Package.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = "<group>"; };
|
||||
430EC1E120598F7600F84EA2 /* RouteKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RouteKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
430EC1E920598F7600F84EA2 /* RouteKit-iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "RouteKit-iOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
|
@ -147,6 +150,7 @@
|
|||
436CD12B2050589800BC228D /* PayloadDecoder.swift */,
|
||||
435264D92048705600F23D74 /* Route.swift */,
|
||||
435BFF7D2058332700346484 /* Router.swift */,
|
||||
29984C382763E400006C359A /* ViewModifier.swift */,
|
||||
436CD126204F02F400BC228D /* Extensions */,
|
||||
);
|
||||
path = RouteKit;
|
||||
|
@ -398,7 +402,7 @@
|
|||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi";
|
||||
shellScript = "if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n";
|
||||
};
|
||||
439B5875231280D500BA9D21 /* SwiftFormat */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
|
@ -444,6 +448,7 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
430EC1FA20598FDB00F84EA2 /* Pattern.swift in Sources */,
|
||||
29984C392763E400006C359A /* ViewModifier.swift in Sources */,
|
||||
430EC1FF20598FDB00F84EA2 /* URL+Extension.swift in Sources */,
|
||||
430EC1F820598FDB00F84EA2 /* AnyRoute.swift in Sources */,
|
||||
430EC1F920598FDB00F84EA2 /* Navigator.swift in Sources */,
|
||||
|
@ -472,6 +477,7 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
430EC2232059932F00F84EA2 /* Pattern.swift in Sources */,
|
||||
29984C3A2763E400006C359A /* ViewModifier.swift in Sources */,
|
||||
430EC2282059933200F84EA2 /* URL+Extension.swift in Sources */,
|
||||
430EC2212059932F00F84EA2 /* AnyRoute.swift in Sources */,
|
||||
430EC2222059932F00F84EA2 /* Navigator.swift in Sources */,
|
||||
|
@ -522,7 +528,7 @@
|
|||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
INFOPLIST_FILE = "${SRCROOT}/Sources/${PROJECT}/Info.plist";
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = woxtu.RouteKit;
|
||||
PRODUCT_NAME = "${PROJECT}";
|
||||
|
@ -543,7 +549,7 @@
|
|||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
INFOPLIST_FILE = "${SRCROOT}/Sources/${PROJECT}/Info.plist";
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = woxtu.RouteKit;
|
||||
PRODUCT_NAME = "${PROJECT}";
|
||||
|
@ -712,6 +718,7 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
|
@ -767,6 +774,7 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
|
|
|
@ -27,6 +27,15 @@
|
|||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "430EC1E020598F7600F84EA2"
|
||||
BuildableName = "RouteKit.framework"
|
||||
BlueprintName = "RouteKit-iOS"
|
||||
ReferencedContainer = "container:RouteKit.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
|
@ -39,17 +48,6 @@
|
|||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "430EC1E020598F7600F84EA2"
|
||||
BuildableName = "RouteKit.framework"
|
||||
BlueprintName = "RouteKit-iOS"
|
||||
ReferencedContainer = "container:RouteKit.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
|
@ -70,8 +68,6 @@
|
|||
ReferencedContainer = "container:RouteKit.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
|
|
|
@ -6,46 +6,46 @@
|
|||
// Copyright (c) 2018 woxtu. All rights reserved.
|
||||
//
|
||||
|
||||
#if os(iOS) || os(tvOS)
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
open class Navigator {
|
||||
private static var router = Router<UIViewController>()
|
||||
open class Navigator {
|
||||
var router = Router<UIViewController>()
|
||||
|
||||
private static var rootViewController: UIViewController? {
|
||||
return UIApplication.shared.keyWindow?.rootViewController
|
||||
}
|
||||
let rootViewController: UIViewController
|
||||
|
||||
public static func append<R>(route: R) where R: Route, R.Response == UIViewController {
|
||||
router.append(route: route)
|
||||
}
|
||||
public init(rootViewController: UIViewController) {
|
||||
self.rootViewController = rootViewController
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public static func push(url: URL, animated: Bool) -> UIViewController? {
|
||||
if
|
||||
let viewController = self.router.push(url: url),
|
||||
let navigationController = self.rootViewController as? UINavigationController {
|
||||
navigationController.pushViewController(viewController, animated: animated)
|
||||
return viewController
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public func append<R>(route: R) where R: Route, R.Response == UIViewController {
|
||||
router.append(route: route)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public static func present(url: URL, animated: Bool, transform: ((UIViewController) -> UIViewController) = { $0 }) -> UIViewController? {
|
||||
return present(url: url, animated: animated, transform: transform, completion: nil)
|
||||
}
|
||||
@discardableResult
|
||||
public func push(url: URL, animated: Bool) -> UIViewController? {
|
||||
if
|
||||
let viewController = self.router.push(url: url),
|
||||
let navigationController = self.rootViewController as? UINavigationController {
|
||||
navigationController.pushViewController(viewController, animated: animated)
|
||||
return viewController
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public static func present(url: URL, animated: Bool, transform: ((UIViewController) -> UIViewController) = { $0 }, completion: (() -> Void)? = nil) -> UIViewController? {
|
||||
if let viewController = self.router.push(url: url) {
|
||||
rootViewController?.present(transform(viewController), animated: animated, completion: completion)
|
||||
return viewController
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@discardableResult
|
||||
public func present(url: URL, animated: Bool, transform: ((UIViewController) -> UIViewController) = { $0 }) -> UIViewController? {
|
||||
return present(url: url, animated: animated, transform: transform, completion: nil)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public func present(url: URL, animated: Bool, transform: ((UIViewController) -> UIViewController) = { $0 }, completion: (() -> Void)? = nil) -> UIViewController? {
|
||||
if let viewController = self.router.push(url: url) {
|
||||
rootViewController.present(transform(viewController), animated: animated, completion: completion)
|
||||
return viewController
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
//
|
||||
// ViewModifier.swift
|
||||
// RouteKit
|
||||
//
|
||||
// Created by Larry Tran on 12/10/21.
|
||||
// Copyright © 2021 woxtu. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
struct RouterViewModifier: ViewModifier {
|
||||
|
||||
@Environment(\.navigator) var navigator
|
||||
|
||||
let url: URL
|
||||
let presentation: Presentation
|
||||
let animated: Bool
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.onTapGesture {
|
||||
switch presentation {
|
||||
case .present:
|
||||
navigator.present(url: url, animated: animated)
|
||||
case .push:
|
||||
navigator.push(url: url, animated: animated)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
func routeOnTap(url: URL, presentation: Presentation, animated: Bool) -> some View {
|
||||
self.modifier(RouterViewModifier(url: url, presentation: presentation, animated: animated))
|
||||
}
|
||||
}
|
||||
|
||||
enum Presentation {
|
||||
case present
|
||||
case push
|
||||
}
|
||||
|
||||
private struct NavigatorKey: EnvironmentKey {
|
||||
static let defaultValue = Navigator()
|
||||
}
|
||||
|
||||
extension EnvironmentValues {
|
||||
var navigator: Navigator {
|
||||
get { self[NavigatorKey.self] }
|
||||
set { self[NavigatorKey.self] = newValue }
|
||||
}
|
||||
}
|
|
@ -12,14 +12,16 @@
|
|||
import XCTest
|
||||
|
||||
class NavigatorTests: XCTestCase {
|
||||
let navigtor = Navigator()
|
||||
|
||||
func test() {
|
||||
let transform = { (vc: UIViewController) -> UIViewController in vc }
|
||||
let completion = { () -> Void in }
|
||||
|
||||
Navigator.present(url: URL(string: "/")!, animated: true)
|
||||
Navigator.present(url: URL(string: "/")!, animated: true, transform: transform)
|
||||
Navigator.present(url: URL(string: "/")!, animated: true, completion: completion)
|
||||
Navigator.present(url: URL(string: "/")!, animated: true, transform: transform, completion: completion)
|
||||
navigtor.present(url: URL(string: "/")!, animated: true)
|
||||
navigtor.present(url: URL(string: "/")!, animated: true, transform: transform)
|
||||
navigtor.present(url: URL(string: "/")!, animated: true, completion: completion)
|
||||
navigtor.present(url: URL(string: "/")!, animated: true, transform: transform, completion: completion)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue