Adds a viewmodifier for SwiftUI for routing

This commit is contained in:
Larry 2021-12-10 11:56:57 -08:00
parent 4471d4d946
commit eeee6fb04d
6 changed files with 119 additions and 57 deletions

View File

@ -4,6 +4,9 @@ import PackageDescription
let package = Package(
name: "RouteKit",
platforms: [
.iOS(.v14),
],
products: [
.library(
name: "RouteKit",

View File

@ -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";

View File

@ -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"

View File

@ -6,23 +6,24 @@
// Copyright (c) 2018 woxtu. All rights reserved.
//
#if os(iOS) || os(tvOS)
import Foundation
import UIKit
open class Navigator {
private static var router = Router<UIViewController>()
var router = Router<UIViewController>()
private static var rootViewController: UIViewController? {
return UIApplication.shared.keyWindow?.rootViewController
let rootViewController: UIViewController
public init(rootViewController: UIViewController) {
self.rootViewController = rootViewController
}
public static func append<R>(route: R) where R: Route, R.Response == UIViewController {
public func append<R>(route: R) where R: Route, R.Response == UIViewController {
router.append(route: route)
}
@discardableResult
public static func push(url: URL, animated: Bool) -> UIViewController? {
public func push(url: URL, animated: Bool) -> UIViewController? {
if
let viewController = self.router.push(url: url),
let navigationController = self.rootViewController as? UINavigationController {
@ -34,18 +35,17 @@
}
@discardableResult
public static func present(url: URL, animated: Bool, transform: ((UIViewController) -> UIViewController) = { $0 }) -> UIViewController? {
public func present(url: URL, animated: Bool, transform: ((UIViewController) -> UIViewController) = { $0 }) -> UIViewController? {
return present(url: url, animated: animated, transform: transform, completion: nil)
}
@discardableResult
public static func present(url: URL, animated: Bool, transform: ((UIViewController) -> UIViewController) = { $0 }, completion: (() -> Void)? = nil) -> UIViewController? {
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)
rootViewController.present(transform(viewController), animated: animated, completion: completion)
return viewController
} else {
return nil
}
}
}
#endif

View File

@ -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 }
}
}

View File

@ -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