diff --git a/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj b/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj index a0d4ea61d..979e61bad 100644 --- a/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj +++ b/ios/Capacitor/Capacitor.xcodeproj/project.pbxproj @@ -80,6 +80,8 @@ 62FABD1A25AE5C01007B3814 /* Array+Capacitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62FABD1925AE5C01007B3814 /* Array+Capacitor.swift */; }; 62FABD2325AE60BA007B3814 /* BridgedTypesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 62FABD2225AE60BA007B3814 /* BridgedTypesTests.m */; }; 62FABD2B25AE6182007B3814 /* BridgedTypesHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62FABD2A25AE6182007B3814 /* BridgedTypesHelper.swift */; }; + A71289E627F380A500DADDF3 /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = A71289E527F380A500DADDF3 /* Router.swift */; }; + A71289EB27F380FD00DADDF3 /* RouterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A71289EA27F380FD00DADDF3 /* RouterTests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -217,6 +219,8 @@ 62FABD1925AE5C01007B3814 /* Array+Capacitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Capacitor.swift"; sourceTree = ""; }; 62FABD2225AE60BA007B3814 /* BridgedTypesTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BridgedTypesTests.m; sourceTree = ""; }; 62FABD2A25AE6182007B3814 /* BridgedTypesHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BridgedTypesHelper.swift; sourceTree = ""; }; + A71289E527F380A500DADDF3 /* Router.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Router.swift; sourceTree = ""; }; + A71289EA27F380FD00DADDF3 /* RouterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RouterTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -292,6 +296,7 @@ 621ECCCD254204C400D3D615 /* CapacitorTests-Bridging-Header.h */, 62E79C712638B23300414164 /* JSExportTests.swift */, 62959BBD2526510200A3D7F1 /* Info.plist */, + A71289EA27F380FD00DADDF3 /* RouterTests.swift */, ); path = CapacitorTests; sourceTree = ""; @@ -349,6 +354,7 @@ 373A69C0255C9360000A6F44 /* NotificationHandlerProtocol.swift */, 373A69F1255C95D0000A6F44 /* NotificationRouter.swift */, 62E79C562638AF7500414164 /* assets */, + A71289E527F380A500DADDF3 /* Router.swift */, ); path = Capacitor; sourceTree = ""; @@ -580,6 +586,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + A71289E627F380A500DADDF3 /* Router.swift in Sources */, 62959B362524DA7800A3D7F1 /* CAPBridgeViewController.swift in Sources */, 621ECCB72542045900D3D615 /* CAPBridgedJSTypes.m in Sources */, 62959B402524DA7800A3D7F1 /* TmpViewController.swift in Sources */, @@ -634,6 +641,7 @@ 62A91C3425535F5700861508 /* ConfigurationTests.swift in Sources */, 62FABD2325AE60BA007B3814 /* BridgedTypesTests.m in Sources */, 621ECCC3254204B700D3D615 /* BridgedTypesTests.swift in Sources */, + A71289EB27F380FD00DADDF3 /* RouterTests.swift in Sources */, 6263686025F6EC0100576C1C /* PluginCallAccessorTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/ios/Capacitor/Capacitor/CAPBridgeViewController.swift b/ios/Capacitor/Capacitor/CAPBridgeViewController.swift index 12c3abaaa..865909b52 100644 --- a/ios/Capacitor/Capacitor/CAPBridgeViewController.swift +++ b/ios/Capacitor/Capacitor/CAPBridgeViewController.swift @@ -42,7 +42,7 @@ import Cordova setScreenOrientationDefaults() // get the web view - let assetHandler = WebViewAssetHandler() + let assetHandler = WebViewAssetHandler(router: router()) assetHandler.setAssetPath(configuration.appLocation.path) let delegationHandler = WebViewDelegationHandler() prepareWebView(with: configuration, assetHandler: assetHandler, delegationHandler: delegationHandler) @@ -89,7 +89,11 @@ import Cordova } return descriptor } - + + open func router() -> Router { + return _Router() + } + /** The WKWebViewConfiguration to use for the webview. diff --git a/ios/Capacitor/Capacitor/Router.swift b/ios/Capacitor/Capacitor/Router.swift new file mode 100644 index 000000000..6ac417d31 --- /dev/null +++ b/ios/Capacitor/Capacitor/Router.swift @@ -0,0 +1,27 @@ +// +// Router.swift +// Capacitor +// +// Created by Steven Sherry on 3/29/22. +// Copyright © 2022 Drifty Co. All rights reserved. +// + +import Foundation + +public protocol Router { + func route(for path: String) -> String +} + +// swiftlint:disable:next type_name +internal struct _Router: Router { + func route(for path: String) -> String { + let pathUrl = URL(string: path) + + // if the pathUrl is null, then it is an invalid url (meaning it is empty or just plain invalid) then we want to route to /index.html + if pathUrl?.pathExtension.isEmpty ?? true { + return "/index.html" + } + + return path + } +} diff --git a/ios/Capacitor/Capacitor/WebViewAssetHandler.swift b/ios/Capacitor/Capacitor/WebViewAssetHandler.swift index 9dbff7a0c..da63326d3 100644 --- a/ios/Capacitor/Capacitor/WebViewAssetHandler.swift +++ b/ios/Capacitor/Capacitor/WebViewAssetHandler.swift @@ -3,8 +3,13 @@ import MobileCoreServices @objc(CAPWebViewAssetHandler) internal class WebViewAssetHandler: NSObject, WKURLSchemeHandler { - + private let router: Router private var basePath: String = "" + + init(router: Router) { + self.router = router + super.init() + } func setAssetPath(_ assetPath: String) { self.basePath = assetPath @@ -14,14 +19,14 @@ internal class WebViewAssetHandler: NSObject, WKURLSchemeHandler { var startPath = self.basePath let url = urlSchemeTask.request.url! let stringToLoad = url.path - + + let resolvedRoute = router.route(for: stringToLoad) if stringToLoad.starts(with: CapacitorBridge.fileStartIdentifier) { startPath = stringToLoad.replacingOccurrences(of: CapacitorBridge.fileStartIdentifier, with: "") - } else if stringToLoad.isEmpty || url.pathExtension.isEmpty { - startPath.append("/index.html") } else { - startPath.append(stringToLoad) + startPath.append(resolvedRoute) } + let localUrl = URL.init(string: url.absoluteString)! let fileUrl = URL.init(fileURLWithPath: startPath) diff --git a/ios/Capacitor/CapacitorTests/CapacitorTests.swift b/ios/Capacitor/CapacitorTests/CapacitorTests.swift index 00131f439..beb4b3445 100644 --- a/ios/Capacitor/CapacitorTests/CapacitorTests.swift +++ b/ios/Capacitor/CapacitorTests/CapacitorTests.swift @@ -23,7 +23,7 @@ class CapacitorTests: XCTestCase { super.setUp() // Put setup code here. This method is called before the invocation of each test method in the class. let descriptor = InstanceDescriptor.init() - bridge = MockBridge(with: InstanceConfiguration(with: descriptor, isDebug: true), delegate: MockBridgeViewController(), cordovaConfiguration: descriptor.cordovaConfiguration, assetHandler: MockAssetHandler(), delegationHandler: MockDelegationHandler()) + bridge = MockBridge(with: InstanceConfiguration(with: descriptor, isDebug: true), delegate: MockBridgeViewController(), cordovaConfiguration: descriptor.cordovaConfiguration, assetHandler: MockAssetHandler(router: _Router()), delegationHandler: MockDelegationHandler()) } override func tearDown() { diff --git a/ios/Capacitor/CapacitorTests/RouterTests.swift b/ios/Capacitor/CapacitorTests/RouterTests.swift new file mode 100644 index 000000000..f328c9c7c --- /dev/null +++ b/ios/Capacitor/CapacitorTests/RouterTests.swift @@ -0,0 +1,30 @@ +// +// RouterTests.swift +// CapacitorTests +// +// Created by Steven Sherry on 3/29/22. +// Copyright © 2022 Drifty Co. All rights reserved. +// + +import XCTest +@testable import Capacitor + +class RouterTests: XCTestCase { + let router = _Router() + + func testRouterReturnsIndexWhenProvidedInvalidPath() { + XCTAssertEqual(router.route(for: "/skull.💀"), "/index.html") + } + + func testRouterReturnsIndexWhenProvidedEmptyPath() { + XCTAssertEqual(router.route(for: ""), "/index.html") + } + + func testRouterReturnsIndexWhenProviedPathWithoutExtension() { + XCTAssertEqual(router.route(for: "/a/valid/path/no/ext"), "/index.html") + } + + func testRouterReturnsPathWhenProvidedValidPath() { + XCTAssertEqual(router.route(for: "/a/valid/path.ext"), "/a/valid/path.ext") + } +}