diff --git a/.github/package.xcworkspace/contents.xcworkspacedata b/.github/package.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..0fd0bc3
--- /dev/null
+++ b/.github/package.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/.github/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/.github/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/.github/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/.github/package.xcworkspace/xcshareddata/swiftpm/Package.resolved b/.github/package.xcworkspace/xcshareddata/swiftpm/Package.resolved
new file mode 100644
index 0000000..12fe5cc
--- /dev/null
+++ b/.github/package.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -0,0 +1,32 @@
+{
+ "pins" : [
+ {
+ "identity" : "swift-interception",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/capturecontext/swift-interception.git",
+ "state" : {
+ "revision" : "8e6231d629a3cb04b929c5a3ba2ea2797151d0b6",
+ "version" : "0.3.0"
+ }
+ },
+ {
+ "identity" : "swift-macro-toolkit",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/stackotter/swift-macro-toolkit.git",
+ "state" : {
+ "revision" : "106daeb38eb3f52b1540aed981fc63fa22274576",
+ "version" : "0.3.1"
+ }
+ },
+ {
+ "identity" : "swift-syntax",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-syntax.git",
+ "state" : {
+ "revision" : "74203046135342e4a4a627476dd6caf8b28fe11b",
+ "version" : "509.0.0"
+ }
+ }
+ ],
+ "version" : 2
+}
diff --git a/.github/package.xcworkspace/xcshareddata/xcschemes/CombineInterception.xcscheme b/.github/package.xcworkspace/xcshareddata/xcschemes/CombineInterception.xcscheme
new file mode 100644
index 0000000..ae1057d
--- /dev/null
+++ b/.github/package.xcworkspace/xcshareddata/xcschemes/CombineInterception.xcscheme
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.github/package.xcworkspace/xcshareddata/xcschemes/CombineInterceptionMacros.xcscheme b/.github/package.xcworkspace/xcshareddata/xcschemes/CombineInterceptionMacros.xcscheme
new file mode 100644
index 0000000..0f7d1d8
--- /dev/null
+++ b/.github/package.xcworkspace/xcshareddata/xcschemes/CombineInterceptionMacros.xcscheme
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.github/package.xcworkspace/xcshareddata/xcschemes/CombineInterceptionMacrosTests.xcscheme b/.github/package.xcworkspace/xcshareddata/xcschemes/CombineInterceptionMacrosTests.xcscheme
new file mode 100644
index 0000000..b7c57a7
--- /dev/null
+++ b/.github/package.xcworkspace/xcshareddata/xcschemes/CombineInterceptionMacrosTests.xcscheme
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.github/package.xcworkspace/xcshareddata/xcschemes/CombineInterceptionTests.xcscheme b/.github/package.xcworkspace/xcshareddata/xcschemes/CombineInterceptionTests.xcscheme
new file mode 100644
index 0000000..32425e1
--- /dev/null
+++ b/.github/package.xcworkspace/xcshareddata/xcschemes/CombineInterceptionTests.xcscheme
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.github/package.xcworkspace/xcshareddata/xcschemes/combine-interception-Package.xcscheme b/.github/package.xcworkspace/xcshareddata/xcschemes/combine-interception-Package.xcscheme
new file mode 100644
index 0000000..710fed6
--- /dev/null
+++ b/.github/package.xcworkspace/xcshareddata/xcschemes/combine-interception-Package.xcscheme
@@ -0,0 +1,130 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.github/package.xcworkspace/xcshareddata/xcschemes/combine-interception.xcscheme b/.github/package.xcworkspace/xcshareddata/xcschemes/combine-interception.xcscheme
new file mode 100644
index 0000000..40777d2
--- /dev/null
+++ b/.github/package.xcworkspace/xcshareddata/xcschemes/combine-interception.xcscheme
@@ -0,0 +1,116 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..2cc0f43
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,34 @@
+name: CI
+
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+ branches:
+ - '*'
+
+concurrency:
+ group: ci-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ library-swift-latest:
+ name: Library
+ if: |
+ !contains(github.event.head_commit.message, '[ci skip]') &&
+ !contains(github.event.head_commit.message, '[ci skip test]') &&
+ !contains(github.event.head_commit.message, '[ci skip library-swift-latest]')
+ runs-on: macos-13
+ timeout-minutes: 30
+ strategy:
+ matrix:
+ config:
+ - debug
+ - release
+ steps:
+ - uses: actions/checkout@v4
+ - name: Select Xcode 15.2
+ run: sudo xcode-select -s /Applications/Xcode_15.2.app
+ - name: Run test
+ run: make test
diff --git a/.gitignore b/.gitignore
index 59e2947..0023a53 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,8 +1,8 @@
.DS_Store
/.build
/Packages
-/*.xcodeproj
xcuserdata/
DerivedData/
+.swiftpm/configuration/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
-Package.resolved
+.netrc
diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/CombineInterception.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/CombineInterception.xcscheme
new file mode 100644
index 0000000..ae1057d
--- /dev/null
+++ b/.swiftpm/xcode/xcshareddata/xcschemes/CombineInterception.xcscheme
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/CombineInterceptionMacros.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/CombineInterceptionMacros.xcscheme
new file mode 100644
index 0000000..0f7d1d8
--- /dev/null
+++ b/.swiftpm/xcode/xcshareddata/xcschemes/CombineInterceptionMacros.xcscheme
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/CombineInterceptionMacrosTests.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/CombineInterceptionMacrosTests.xcscheme
new file mode 100644
index 0000000..b7c57a7
--- /dev/null
+++ b/.swiftpm/xcode/xcshareddata/xcschemes/CombineInterceptionMacrosTests.xcscheme
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/CombineInterceptionTests.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/CombineInterceptionTests.xcscheme
new file mode 100644
index 0000000..32425e1
--- /dev/null
+++ b/.swiftpm/xcode/xcshareddata/xcschemes/CombineInterceptionTests.xcscheme
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/combine-interception-Package.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/combine-interception-Package.xcscheme
new file mode 100644
index 0000000..710fed6
--- /dev/null
+++ b/.swiftpm/xcode/xcshareddata/xcschemes/combine-interception-Package.xcscheme
@@ -0,0 +1,130 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/combine-interception.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/combine-interception.xcscheme
new file mode 100644
index 0000000..40777d2
--- /dev/null
+++ b/.swiftpm/xcode/xcshareddata/xcschemes/combine-interception.xcscheme
@@ -0,0 +1,116 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Makefile b/Makefile
index 65574b9..e05199d 100644
--- a/Makefile
+++ b/Makefile
@@ -1,3 +1,59 @@
-format:
- swift format --in-place --recursive \
- ./Package.swift ./Sources/CombineExtensions
\ No newline at end of file
+CONFIG = debug
+PLATFORM_IOS = iOS Simulator,id=$(call udid_for,iOS 17.2,iPhone \d\+ Pro [^M])
+PLATFORM_MACOS = macOS
+PLATFORM_MAC_CATALYST = macOS,variant=Mac Catalyst
+PLATFORM_TVOS = tvOS Simulator,id=$(call udid_for,tvOS 17,TV)
+PLATFORM_WATCHOS = watchOS Simulator,id=$(call udid_for,watchOS 10,Watch)
+
+default: test-all
+
+test-all:
+ $(MAKE) test
+ $(MAKE) test-docs
+
+test:
+ $(MAKE) CONFIG=debug test-library
+ $(MAKE) CONFIG=debug test-library-macros
+
+test-library:
+ for platform in "$(PLATFORM_IOS)" "$(PLATFORM_MACOS)" "$(PLATFORM_MAC_CATALYST)" "$(PLATFORM_TVOS)" "$(PLATFORM_WATCHOS)"; do \
+ echo "\nTesting library on $$platform\n" && \
+ (xcodebuild test \
+ -skipMacroValidation \
+ -configuration $(CONFIG) \
+ -workspace .github/package.xcworkspace \
+ -scheme CombineInterceptionTests \
+ -destination platform="$$platform" | xcpretty && exit 0 \
+ ) \
+ || exit 1; \
+ done;
+
+test-library-macros:
+ for platform in "$(PLATFORM_IOS)" "$(PLATFORM_MACOS)" "$(PLATFORM_MAC_CATALYST)" "$(PLATFORM_TVOS)" "$(PLATFORM_WATCHOS)"; do \
+ echo "\nTesting library-macros on $$platform\n" && \
+ (xcodebuild test \
+ -skipMacroValidation \
+ -configuration $(CONFIG) \
+ -workspace .github/package.xcworkspace \
+ -scheme CombineInterceptionMacrosTests \
+ -destination platform="$$platform" | xcpretty && exit 0 \
+ ) \
+ || exit 1; \
+ done;
+
+DOC_WARNINGS = $(shell xcodebuild clean docbuild \
+ -scheme Interception \
+ -destination platform="$(PLATFORM_IOS)" \
+ -quiet \
+ 2>&1 \
+ | grep "couldn't be resolved to known documentation" \
+ | sed 's|$(PWD)|.|g' \
+ | tr '\n' '\1')
+test-docs:
+ @test "$(DOC_WARNINGS)" = "" \
+ || (echo "xcodebuild docbuild failed:\n\n$(DOC_WARNINGS)" | tr '\1' '\n' \
+ && exit 1)
+
+define udid_for
+$(shell xcrun simctl list devices available '$(1)' | grep '$(2)' | sort -r | head -1 | awk -F '[()]' '{ print $$(NF-3) }')
+endef
diff --git a/Package.resolved b/Package.resolved
new file mode 100644
index 0000000..12fe5cc
--- /dev/null
+++ b/Package.resolved
@@ -0,0 +1,32 @@
+{
+ "pins" : [
+ {
+ "identity" : "swift-interception",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/capturecontext/swift-interception.git",
+ "state" : {
+ "revision" : "8e6231d629a3cb04b929c5a3ba2ea2797151d0b6",
+ "version" : "0.3.0"
+ }
+ },
+ {
+ "identity" : "swift-macro-toolkit",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/stackotter/swift-macro-toolkit.git",
+ "state" : {
+ "revision" : "106daeb38eb3f52b1540aed981fc63fa22274576",
+ "version" : "0.3.1"
+ }
+ },
+ {
+ "identity" : "swift-syntax",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-syntax.git",
+ "state" : {
+ "revision" : "74203046135342e4a4a627476dd6caf8b28fe11b",
+ "version" : "509.0.0"
+ }
+ }
+ ],
+ "version" : 2
+}
diff --git a/Package.swift b/Package.swift
index 9a4eef5..5d90cb4 100644
--- a/Package.swift
+++ b/Package.swift
@@ -17,12 +17,17 @@ let package = Package(
name: "CombineInterception",
type: .static,
targets: ["CombineInterception"]
- )
+ ),
+ .library(
+ name: "CombineInterceptionMacros",
+ type: .static,
+ targets: ["CombineInterceptionMacros"]
+ ),
],
dependencies: [
.package(
url: "https://github.com/capturecontext/swift-interception.git",
- .upToNextMinor(from: "0.2.0")
+ .upToNextMinor(from: "0.3.0")
)
],
targets: [
@@ -35,11 +40,26 @@ let package = Package(
)
]
),
+ .target(
+ name: "CombineInterceptionMacros",
+ dependencies: [
+ .product(
+ name: "_InterceptionMacros",
+ package: "swift-interception"
+ )
+ ]
+ ),
.testTarget(
name: "CombineInterceptionTests",
dependencies: [
.target(name: "CombineInterception"),
]
),
+ .testTarget(
+ name: "CombineInterceptionMacrosTests",
+ dependencies: [
+ .target(name: "CombineInterceptionMacros"),
+ ]
+ ),
]
)
diff --git a/README.md b/README.md
index 2790828..ff2d0f7 100644
--- a/README.md
+++ b/README.md
@@ -4,8 +4,6 @@
Combine API for interception of objc selectors in Swift.
-Macros: [`combine-interception-macros`](https://github.com/capturecontext/combine-interception-macros)
-
## Usage
### Basic
@@ -15,7 +13,7 @@ Observe selectors on NSObject instances
```swift
navigationController.intercept(_makeMethodSelector(
selector: UINavigationController.popViewController,
- signature: UINavigationController.popViewController
+ signature: navigationController.popViewController
))
.sink { result in
print(result.args) // `animated` flag
@@ -23,6 +21,19 @@ navigationController.intercept(_makeMethodSelector(
}
```
+You can also simplify creating method selector with `CombineInterceptionMacros` if you are open for macros
+
+```swift
+navigationController.intercept(
+ #methodSelector(UINavigationController.popViewController)
+).sink { result in
+ print(result.args) // `animated` flag
+ print(result.output) // popped `UIViewController?`
+}
+```
+
+> Macros require `swift-syntax` compilation, so it will affect cold compilation time
+
### Library
If you use it to create a library it may be a good idea to export this one implicitly
@@ -32,6 +43,13 @@ If you use it to create a library it may be a good idea to export this one impli
@_exported import CombineInterception
```
+It's a good idea to add a separate macros target to your library as well
+
+```swift
+// Exports.swift
+@_exported import CombineInterceptionMacros
+```
+
## Installation
### Basic
@@ -49,7 +67,7 @@ If you use SwiftPM for your project, you can add CombineInterception to your pac
```swift
.package(
url: "https://github.com/capturecontext/combine-interception.git",
- .upToNextMinor(from: "0.2.0")
+ .upToNextMinor(from: "0.3.0")
)
```
@@ -62,6 +80,15 @@ Do not forget about target dependencies:
)
```
+```swift
+.product(
+ name: "CombineInterceptionMacros",
+ package: "combine-interception"
+)
+```
+
+
+
## License
This library is released under the MIT license. See [LICENCE](LICENCE) for details.
diff --git a/Sources/CombineInterceptionMacros/Exports.swift b/Sources/CombineInterceptionMacros/Exports.swift
new file mode 100644
index 0000000..6a9be07
--- /dev/null
+++ b/Sources/CombineInterceptionMacros/Exports.swift
@@ -0,0 +1,2 @@
+@_exported import _InterceptionMacros
+@_exported import CombineInterception
diff --git a/Tests/CombineInterceptionMacrosTests/InterceptionMacrosTests.swift b/Tests/CombineInterceptionMacrosTests/InterceptionMacrosTests.swift
new file mode 100644
index 0000000..beac43f
--- /dev/null
+++ b/Tests/CombineInterceptionMacrosTests/InterceptionMacrosTests.swift
@@ -0,0 +1,173 @@
+import XCTest
+@testable import CombineInterceptionMacros
+
+final class InterceptionMacrosTests: XCTestCase {
+ func testNoInputWithOutput() {
+ let object = Object()
+ var _args: [Void] = []
+ var _outputs: [Int] = []
+ var _expectedOutputs: [Int] = []
+ var _count = 0
+
+ let cancellable = object.intercept(#methodSelector(Object.zero)).sink { result in
+ _args.append(result.args)
+ _outputs.append(result.output)
+ _count += 1
+ }
+
+ let runs = 3
+ for _ in 0.. Int { 0 }
+
+ @objc dynamic
+ func discard(_ value: Int) {}
+
+ @discardableResult
+ @objc dynamic
+ func booleanForInteger(_ value: Int) -> Bool {
+ value != 0
+ }
+
+ @objc dynamic
+ func booleanAndForTwoIntegers(_ first: Int, _ second: Int) -> Bool {
+ booleanForInteger(first) && booleanForInteger(second)
+ }
+
+ @objc dynamic
+ func toString(_ first: Int, _ second: Bool) -> String {
+ String(describing: (first, second))
+ }
+}
+
+fileprivate struct Pair: Equatable {
+ var left: Left
+ var right: Right
+
+ init(_ pair: (Left, Right)) {
+ self.left = pair.0
+ self.right = pair.1
+ }
+}