diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..95c4320 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.DS_Store +/.build +/Packages +/*.xcodeproj +xcuserdata/ diff --git a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..5d2311d --- /dev/null +++ b/Package.swift @@ -0,0 +1,29 @@ +// swift-tools-version:5.3 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "Swappable", + platforms: [.iOS(.v13)], + products: [ + // Products define the executables and libraries a package produces, and make them visible to other packages. + .library( + name: "Swappable", + targets: ["Swappable"]), + ], + dependencies: [ + // Dependencies declare other packages that this package depends on. + // .package(url: /* package url */, from: "1.0.0"), + ], + targets: [ + // Targets are the basic building blocks of a package. A target can define a module or a test suite. + // Targets can depend on other targets in this package, and on products in packages this package depends on. + .target( + name: "Swappable", + dependencies: []), + .testTarget( + name: "SwappableTests", + dependencies: ["Swappable"]), + ] +) diff --git a/README.md b/README.md new file mode 100644 index 0000000..3fbf8de --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# swappable + +A description of this package. diff --git a/Sources/Swappable/Swappable.swift b/Sources/Swappable/Swappable.swift new file mode 100644 index 0000000..77a820d --- /dev/null +++ b/Sources/Swappable/Swappable.swift @@ -0,0 +1,75 @@ +import UIKit +import SwiftUI + +extension EnvironmentObject { + var hasValue: Bool { + Mirror(reflecting: self).children.contains { ($0.value is ObjectType) } + } +} + +class ViewSwap: ObservableObject where S: View, S: Swappable { + + init(with customView: @escaping (S.InputType) -> V, insteadOf _: S.Type) + where V: View { + self.view = { AnyView(customView($0)) } + } + + let view: (S.InputType) -> AnyView +} + +struct SwappedIfNeeded: View where S: View, S: Swappable { + + @EnvironmentObject var swap: ViewSwap + + let input: S.InputType + let content: S.DefaultBody + + init(input: S.InputType, content: S.DefaultBody) { + self.input = input + self.content = content + } + + var body: some View { + Group { + if _swap.hasValue { + swap.view(input) + } else { + content + } + } + } +} + + +public protocol Swappable { + + associatedtype InputType + var input: InputType { get } + init(input: InputType) + + associatedtype DefaultBody : View + @ViewBuilder var defaultBody: DefaultBody { get } +} + +public extension View { + + func swapView(_ initV : @escaping (S.InputType) -> V, insteadOf typeS: S.Type) -> some View + where S: View, S: Swappable, + V: View { + environmentObject(ViewSwap(with: initV, insteadOf: typeS)) + } + + @available(*, unavailable, message: "Init parameters must match!") + func swapView(_ initV : (P) -> V, insteadOf typeS: S.Type) -> some View + where S: View, S: Swappable, + V: View { + self + } +} + +extension Swappable where Self: View { + + var body: some View { + SwappedIfNeeded(input: input, content: defaultBody) + } +} diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift new file mode 100644 index 0000000..ca59101 --- /dev/null +++ b/Tests/LinuxMain.swift @@ -0,0 +1,7 @@ +import XCTest + +import swappableTests + +var tests = [XCTestCaseEntry]() +tests += swappableTests.allTests() +XCTMain(tests) diff --git a/Tests/SwappableTests/SwappableTests.swift b/Tests/SwappableTests/SwappableTests.swift new file mode 100644 index 0000000..653788b --- /dev/null +++ b/Tests/SwappableTests/SwappableTests.swift @@ -0,0 +1,7 @@ +import XCTest +@testable import Swappable + +final class SwappableTests: XCTestCase { + + static var allTests: [(String, ()->())] = [] +} diff --git a/Tests/SwappableTests/XCTestManifests.swift b/Tests/SwappableTests/XCTestManifests.swift new file mode 100644 index 0000000..4c19cd2 --- /dev/null +++ b/Tests/SwappableTests/XCTestManifests.swift @@ -0,0 +1,9 @@ +import XCTest + +#if !canImport(ObjectiveC) +public func allTests() -> [XCTestCaseEntry] { + return [ + testCase(swappableTests.allTests), + ] +} +#endif