diff --git a/.github/actions/build/action.yml b/.github/actions/build/action.yml
new file mode 100644
index 000000000..0e3715428
--- /dev/null
+++ b/.github/actions/build/action.yml
@@ -0,0 +1,38 @@
+name: 'build'
+description: 'Compiles and stores artifacts for further use'
+inputs:
+ relay-endpoint:
+ description: 'The endpoint of the relay e.g. relay.walletconnect.com'
+ required: false
+ default: 'relay.walletconnect.com'
+ project-id:
+ description: 'WalletConnect project id'
+ required: true
+
+runs:
+ using: "composite"
+ steps:
+ - uses: actions/checkout@v3
+
+ - uses: actions/cache@v3
+ with:
+ path: |
+ **/SourcePackagesCache
+ DerivedDataCache
+ key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }}
+ restore-keys: |
+ ${{ runner.os }}-spm-
+
+ - name: Build for testing
+ shell: bash
+ run: make build_all RELAY_HOST=${{ inputs.relay-endpoint }} PROJECT_ID=${{ inputs.project-id }}
+
+ - name: Tar DerivedDataCache
+ shell: bash
+ run: test -d "DerivedDataCache" && tar cfPp products.tar --format posix DerivedDataCache/Build
+
+ - uses: actions/cache/save@v3
+ with:
+ path: |
+ products.tar
+ key: ${{ runner.os }}-deriveddata-${{ github.ref }}-${{ github.sha }}
\ No newline at end of file
diff --git a/.github/actions/ci/action.yml b/.github/actions/ci/action.yml
deleted file mode 100644
index cf03c7a7d..000000000
--- a/.github/actions/ci/action.yml
+++ /dev/null
@@ -1,69 +0,0 @@
-name: 'ci'
-description: 'Executes Swift specific CI steps'
-inputs:
- type:
- description: 'The type of CI step to run'
- required: true
- relay-endpoint:
- description: 'The endpoint of the relay e.g. relay.walletconnect.com'
- required: false
- default: 'relay.walletconnect.com'
- project-id:
- description: 'WalletConnect project id'
- required: true
-
-runs:
- using: "composite"
- steps:
- # Package builds
- - name: Run tests
- if: inputs.type == 'unit-tests'
- shell: bash
- run: make unit_tests
-
- # Integration tests
- - name: Run integration tests
- if: inputs.type == 'integration-tests'
- shell: bash
- env:
- RELAY_ENDPOINT: ${{ inputs.relay-endpoint }}
- PROJECT_ID: ${{ inputs.project-id }}
- run: make integration_tests RELAY_HOST=$RELAY_ENDPOINT PROJECT_ID=$PROJECT_ID
-
- # Relay Integration tests
- - name: Run integration tests
- if: inputs.type == 'relay-tests'
- shell: bash
- env:
- RELAY_ENDPOINT: ${{ inputs.relay-endpoint }}
- PROJECT_ID: ${{ inputs.project-id }}
- run: make relay_tests RELAY_HOST=$RELAY_ENDPOINT PROJECT_ID=$PROJECT_ID
-
- # Smoke tests
- - name: Run smoke tests
- if: inputs.type == 'smoke-tests'
- shell: bash
- env:
- RELAY_ENDPOINT: ${{ inputs.relay-endpoint }}
- PROJECT_ID: ${{ inputs.project-id }}
- run: make smoke_tests RELAY_HOST=$RELAY_ENDPOINT PROJECT_ID=$PROJECT_ID
-
-
- # Wallet build
- - name: Build Example Wallet
- if: inputs.type == 'build-example-wallet'
- shell: bash
- run: make build_wallet
-
- # DApp build
- - name: Build Example Dapp
- if: inputs.type == 'build-example-dapp'
- shell: bash
- run: make build_dapp
-
- # UI tests
- - name: UI Tests
- if: inputs.type == 'ui-tests'
- shell: bash
- run: make ui_tests
- continue-on-error: true
diff --git a/.github/actions/run_tests_without_building/action.yml b/.github/actions/run_tests_without_building/action.yml
new file mode 100644
index 000000000..d3abfb8cd
--- /dev/null
+++ b/.github/actions/run_tests_without_building/action.yml
@@ -0,0 +1,73 @@
+name: 'run_tests_without_building'
+description: 'Executes specific Swift tests using prebuilt artifacts from build_artifacts.yml workflow from main branch'
+inputs:
+ type:
+ description: 'The type of CI step to run'
+ required: true
+ relay-endpoint:
+ description: 'The endpoint of the relay e.g. relay.walletconnect.com'
+ required: false
+ default: 'relay.walletconnect.com'
+ project-id:
+ description: 'WalletConnect project id'
+ required: true
+
+runs:
+ using: "composite"
+ steps:
+ - name: Download artifact
+ id: download-artifact
+ uses: dawidd6/action-download-artifact@v2
+ with:
+ name: main-derivedData
+ workflow: build_artifacts.yml
+ repo: 'WalletConnect/WalletConnectSwiftV2'
+ if_no_artifact_found: warn
+
+ - name: Untar DerivedDataCache
+ shell: bash
+ run: test -f products.tar && tar xPpf products.tar || echo "No artifacts to untar"
+
+ # Package Unit tests
+ - name: Run tests
+ if: inputs.type == 'unit-tests'
+ shell: bash
+ run: make unit_tests
+
+ # Integration tests
+ - name: Run integration tests
+ if: inputs.type == 'integration-tests'
+ shell: bash
+ run: make integration_tests RELAY_HOST=${{ inputs.relay-endpoint }} PROJECT_ID=${{ inputs.project-id }}
+
+ # Relay Integration tests
+ - name: Run Relay integration tests
+ if: inputs.type == 'relay-tests'
+ shell: bash
+ run: make relay_tests RELAY_HOST=${{ inputs.relay-endpoint }} PROJECT_ID=${{ inputs.project-id }}
+
+ # Smoke tests
+ - name: Run smoke tests
+ if: inputs.type == 'smoke-tests'
+ shell: bash
+ run: make smoke_tests RELAY_HOST=${{ inputs.relay-endpoint }} PROJECT_ID=${{ inputs.project-id }}
+
+ - name: Publish Test Report
+ uses: mikepenz/action-junit-report@v3
+ if: success() || failure()
+ with:
+ check_name: ${{ inputs.type }} junit report
+ report_paths: 'test_results/report.junit'
+
+ - name: Zip test artifacts
+ if: always()
+ shell: bash
+ run: test -d "test_results" && zip artifacts.zip -r ./test_results || echo "Nothing to zip"
+
+ - name: Upload test artifacts
+ if: always()
+ uses: actions/upload-artifact@v3
+ with:
+ name: ${{ inputs.type }} test_results
+ path: ./artifacts.zip
+ if-no-files-found: warn
\ No newline at end of file
diff --git a/.github/workflows/build_artifacts.yml b/.github/workflows/build_artifacts.yml
new file mode 100644
index 000000000..b51a09e9f
--- /dev/null
+++ b/.github/workflows/build_artifacts.yml
@@ -0,0 +1,50 @@
+name: build_artifacts
+
+on:
+ workflow_dispatch:
+ inputs:
+ relay-endpoint:
+ description: 'The endpoint of the relay e.g. relay.walletconnect.com'
+ required: false
+ default: 'relay.walletconnect.com'
+ project-id:
+ description: 'WalletConnect project id'
+ required: true
+ push:
+ branches: [ main ]
+
+jobs:
+ build:
+ runs-on: macos-12
+ timeout-minutes: 15
+
+ steps:
+ - uses: actions/checkout@v3
+
+ - uses: actions/cache@v3
+ with:
+ path: |
+ **/SourcePackagesCache
+ DerivedDataCache
+ key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }}
+ restore-keys: |
+ ${{ runner.os }}-spm-
+
+ - name: Build for testing on workflow_dispatch
+ if: ${{ github.event_name == 'workflow_dispatch' }}
+ shell: bash
+ run: make build_all RELAY_HOST=${{ inputs.relay-endpoint }} PROJECT_ID=${{ inputs.project-id }}
+
+ - name: Build for testing on push
+ if: ${{ github.event_name == 'push' }}
+ shell: bash
+ run: make build_all RELAY_HOST=relay.walletconnect.com PROJECT_ID=${{ secrets.PROJECT_ID }}
+
+ - name: Tar DerivedDataCache
+ shell: bash
+ run: test -d "DerivedDataCache" && tar cfPp products.tar --format posix DerivedDataCache/Build
+
+ - uses: actions/upload-artifact@v3
+ with:
+ name: main-derivedData
+ path: products.tar
\ No newline at end of file
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 8726a1632..fd3aa5788 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -5,44 +5,84 @@ on:
branches: [ main, develop ]
concurrency:
- # Support push/pr as event types with different behaviors each:
- # 1. push: queue up builds by branch
- # 2. pr: only allow one run per PR
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.event.pull_request.number || github.ref_name }}
- # If there is already a workflow running for the same pull request, cancel it
- # For non-PR triggers queue up builds
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
- build:
+ prepare:
runs-on: macos-12
+ steps:
+ - uses: actions/checkout@v3
+
+ - uses: ./.github/actions/build
+ with:
+ project-id: ${{ secrets.PROJECT_ID }}
+
+ test:
+ needs: prepare
+ runs-on: macos-12
+ timeout-minutes: 15
strategy:
+ fail-fast: false
matrix:
- test-type: [unit-tests, integration-tests, build-example-wallet, build-example-dapp, relay-tests]
+ type: [integration-tests, relay-tests, unit-tests]
steps:
- - uses: actions/checkout@v2
-
- - name: Setup Xcode Version
- uses: maxim-lobanov/setup-xcode@v1
- with:
- xcode-version: 14.1
+ - uses: actions/checkout@v3
- - uses: actions/cache@v2
+ - uses: actions/cache/restore@v3
with:
path: |
- .build
- SourcePackagesCache
- DerivedDataCache
- key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }}
+ products.tar
+ key: ${{ runner.os }}-deriveddata-${{ github.ref }}-${{ github.sha }}
restore-keys: |
- ${{ runner.os }}-spm-
+ ${{ runner.os }}-deriveddata-${{ github.ref }}-
+
+ - name: Untar DerivedDataCache
+ shell: bash
+ run: test -f products.tar && tar xPpf products.tar || echo "No artifacts to untar"
+
+ # Package Unit tests
+ - name: Run tests
+ if: matrix.type == 'unit-tests'
+ shell: bash
+ run: make unit_tests
- - name: Resolve Dependencies
+ # Integration tests
+ - name: Run integration tests
+ if: matrix.type == 'integration-tests'
shell: bash
- run: make resolve_packages
+ run: make integration_tests RELAY_HOST=relay.walletconnect.com PROJECT_ID=${{ secrets.PROJECT_ID }}
- - uses: ./.github/actions/ci
+ # Relay Integration tests
+ - name: Run Relay integration tests
+ if: matrix.type == 'relay-tests'
+ shell: bash
+ run: make relay_tests RELAY_HOST=relay.walletconnect.com PROJECT_ID=${{ secrets.PROJECT_ID }}
+
+ # Smoke tests
+ - name: Run smoke tests
+ if: matrix.type == 'smoke-tests'
+ shell: bash
+ run: make smoke_tests RELAY_HOST=relay.walletconnect.com PROJECT_ID=${{ secrets.PROJECT_ID }}
+
+ - name: Publish Test Report
+ uses: mikepenz/action-junit-report@v3
+ if: success() || failure()
with:
- type: ${{ matrix.test-type }}
- project-id: ${{ secrets.PROJECT_ID }}
+ check_name: ${{ matrix.type }} junit report
+ report_paths: 'test_results/report.junit'
+
+ - name: Zip test artifacts
+ if: always()
+ shell: bash
+ run: test -d "test_results" && zip artifacts.zip -r ./test_results || echo "Nothing to zip"
+
+ - name: Upload test artifacts
+ if: always()
+ uses: actions/upload-artifact@v3
+ with:
+ name: ${{ matrix.type }} test_results
+ path: ./artifacts.zip
+ if-no-files-found: warn
+
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index c30113723..9a58e1a2f 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -12,14 +12,9 @@ jobs:
runs-on: macos-12
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- - name: Setup Xcode Version
- uses: maxim-lobanov/setup-xcode@v1
- with:
- xcode-version: 14.1
-
- - uses: actions/cache@v2
+ - uses: actions/cache@v3
with:
path: |
.build
diff --git a/.gitignore b/.gitignore
index cdf6e24d3..172334855 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,4 +30,5 @@ DerivedDataCache
# Artifacts
*.ipa
-*.zip
\ No newline at end of file
+*.zip
+test_results/
diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnect-Package.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnect-Package.xcscheme
new file mode 100644
index 000000000..bcf1ba2ab
--- /dev/null
+++ b/.swiftpm/xcode/xcshareddata/xcschemes/WalletConnect-Package.xcscheme
@@ -0,0 +1,640 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Example/DApp/Sign/Connect/ConnectView.swift b/Example/DApp/Sign/Connect/ConnectView.swift
index c1c075504..7004411a0 100644
--- a/Example/DApp/Sign/Connect/ConnectView.swift
+++ b/Example/DApp/Sign/Connect/ConnectView.swift
@@ -31,10 +31,18 @@ final class ConnectView: UIView {
return button
}()
+ let invisibleUriLabel: UILabel = {
+ let label = UILabel(frame: CGRect(origin: .zero, size: .init(width: 1, height: 1)))
+ label.numberOfLines = 0
+ label.textColor = .clear
+ return label
+ }()
+
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .systemBackground
addSubview(qrCodeView)
+ addSubview(invisibleUriLabel)
addSubview(copyButton)
addSubview(connectWalletButton)
addSubview(tableView)
diff --git a/Example/DApp/Sign/Connect/ConnectViewController.swift b/Example/DApp/Sign/Connect/ConnectViewController.swift
index 9784b062c..02c9020f7 100644
--- a/Example/DApp/Sign/Connect/ConnectViewController.swift
+++ b/Example/DApp/Sign/Connect/ConnectViewController.swift
@@ -34,6 +34,8 @@ class ConnectViewController: UIViewController, UITableViewDataSource, UITableVie
self.connectView.copyButton.isHidden = false
}
}
+
+ connectView.invisibleUriLabel.text = uri.absoluteString
connectView.copyButton.addTarget(self, action: #selector(copyURI), for: .touchUpInside)
connectView.connectWalletButton.addTarget(self, action: #selector(connectWithExampleWallet), for: .touchUpInside)
connectView.tableView.dataSource = self
diff --git a/Example/EchoUITests/Engine/App.swift b/Example/EchoUITests/Engine/App.swift
new file mode 100644
index 000000000..e273bbabc
--- /dev/null
+++ b/Example/EchoUITests/Engine/App.swift
@@ -0,0 +1,23 @@
+import Foundation
+import XCTest
+
+enum App {
+ case dapp
+ case wallet
+ case springboard
+
+ var instance: XCUIApplication {
+ return XCUIApplication(bundleIdentifier: bundleID)
+ }
+
+ private var bundleID: String {
+ switch self {
+ case .dapp:
+ return "com.walletconnect.dapp"
+ case .wallet:
+ return "com.walletconnect.walletapp"
+ case .springboard:
+ return "com.apple.springboard"
+ }
+ }
+}
diff --git a/Example/EchoUITests/Engine/DAppEngine.swift b/Example/EchoUITests/Engine/DAppEngine.swift
new file mode 100644
index 000000000..5711ccce8
--- /dev/null
+++ b/Example/EchoUITests/Engine/DAppEngine.swift
@@ -0,0 +1,35 @@
+import Foundation
+import XCTest
+
+struct DAppEngine {
+
+ var instance: XCUIApplication {
+ return App.dapp.instance
+ }
+
+ // Main screen
+
+ var connectButton: XCUIElement {
+ instance.buttons["Connect"]
+ }
+
+ // Accounts screen
+
+ var accountRow: XCUIElement {
+ instance.tables.cells.containing("0x").firstMatch
+ }
+
+ var methodRow: XCUIElement {
+ instance.tables.cells.firstMatch
+ }
+
+ // Pairing screen
+
+ var newPairingButton: XCUIElement {
+ instance.buttons["New Pairing"]
+ }
+
+ var copyURIButton: XCUIElement {
+ instance.buttons["Copy"]
+ }
+}
diff --git a/Example/EchoUITests/Engine/Engine.swift b/Example/EchoUITests/Engine/Engine.swift
new file mode 100644
index 000000000..c164f227a
--- /dev/null
+++ b/Example/EchoUITests/Engine/Engine.swift
@@ -0,0 +1,8 @@
+import Foundation
+import XCTest
+
+struct Engine {
+ let routing = RoutingEngine()
+ let dapp = DAppEngine()
+ let wallet = WalletEngine()
+}
diff --git a/Example/EchoUITests/Engine/RoutingEngine.swift b/Example/EchoUITests/Engine/RoutingEngine.swift
new file mode 100644
index 000000000..6de1874aa
--- /dev/null
+++ b/Example/EchoUITests/Engine/RoutingEngine.swift
@@ -0,0 +1,32 @@
+import Foundation
+import XCTest
+
+struct RoutingEngine {
+
+ var springboard: XCUIApplication {
+ return App.springboard.instance
+ }
+
+ func launch(app: App, clean: Bool) {
+ app.instance.terminate()
+
+ if clean {
+ let app = app.instance
+ app.launchArguments = ["-cleanInstall", "-disableAnimations"]
+ app.launch()
+ } else {
+ let app = app.instance
+ app.launch()
+ }
+ }
+
+ func activate(app: App) {
+ let app = app.instance
+ app.activate()
+ app.wait(until: \.exists)
+ }
+
+ func wait(for interval: TimeInterval) {
+ Thread.sleep(forTimeInterval: interval)
+ }
+}
diff --git a/Example/EchoUITests/Engine/WalletEngine.swift b/Example/EchoUITests/Engine/WalletEngine.swift
new file mode 100644
index 000000000..c45d327a8
--- /dev/null
+++ b/Example/EchoUITests/Engine/WalletEngine.swift
@@ -0,0 +1,39 @@
+import Foundation
+import XCTest
+
+struct WalletEngine {
+
+ var instance: XCUIApplication {
+ return App.wallet.instance
+ }
+
+ // Onboarding
+
+ var getStartedButton: XCUIElement {
+ instance.buttons["Get Started"]
+ }
+
+ // MainScreen
+
+ var pasteURIButton: XCUIElement {
+ instance.buttons["copy"]
+ }
+
+ var alertUriTextField: XCUIElement {
+ instance.textFields["wc://a13aef..."]
+ }
+
+ var alertConnectButton: XCUIElement {
+ instance.buttons["Connect"]
+ }
+
+ var sessionRow: XCUIElement {
+ instance.staticTexts["Swift Dapp"]
+ }
+
+ // Proposal
+
+ var allowButton: XCUIElement {
+ instance.buttons["Allow"]
+ }
+}
diff --git a/Example/EchoUITests/Extensions/XCTestCase.swift b/Example/EchoUITests/Extensions/XCTestCase.swift
new file mode 100644
index 000000000..f2698a34b
--- /dev/null
+++ b/Example/EchoUITests/Extensions/XCTestCase.swift
@@ -0,0 +1,18 @@
+import Foundation
+import XCTest
+
+extension XCTestCase {
+
+ func allowPushNotificationsIfNeeded(app: XCUIApplication) {
+ let pnPermission = addUIInterruptionMonitor(withDescription: "Push Notification Monitor") { alerts -> Bool in
+ if alerts.buttons["Allow"].exists {
+ alerts.buttons["Allow"].tap()
+ }
+
+ return true
+ }
+ app.swipeUp()
+
+ self.removeUIInterruptionMonitor(pnPermission)
+ }
+}
diff --git a/Example/EchoUITests/Extensions/XCUIElement.swift b/Example/EchoUITests/Extensions/XCUIElement.swift
new file mode 100644
index 000000000..514a23242
--- /dev/null
+++ b/Example/EchoUITests/Extensions/XCUIElement.swift
@@ -0,0 +1,74 @@
+import Foundation
+import XCTest
+
+extension XCUIElement {
+
+ static let waitTimeout: TimeInterval = 15
+
+ @discardableResult
+ func wait(
+ until expression: @escaping (XCUIElement) -> Bool,
+ timeout: TimeInterval = waitTimeout,
+ message: @autoclosure () -> String = "",
+ file: StaticString = #file,
+ line: UInt = #line
+ ) -> Self {
+ if expression(self) {
+ return self
+ }
+
+ let predicate = NSPredicate { _, _ in
+ expression(self)
+ }
+
+ let expectation = XCTNSPredicateExpectation(predicate: predicate, object: nil)
+
+ let result = XCTWaiter().wait(for: [expectation], timeout: timeout)
+
+ if result != .completed {
+ XCTFail(
+ message().isEmpty ? "expectation not matched after waiting" : message(),
+ file: file,
+ line: line
+ )
+ }
+
+ return self
+ }
+
+ @discardableResult
+ func wait(
+ until keyPath: KeyPath,
+ matches match: Value,
+ timeout: TimeInterval = waitTimeout,
+ message: @autoclosure () -> String = "",
+ file: StaticString = #file,
+ line: UInt = #line
+ ) -> Self {
+ wait(
+ until: { $0[keyPath: keyPath] == match },
+ timeout: timeout,
+ message: message(),
+ file: file,
+ line: line
+ )
+ }
+
+ @discardableResult
+ func wait(
+ until keyPath: KeyPath,
+ timeout: TimeInterval = waitTimeout,
+ message: @autoclosure () -> String = "",
+ file: StaticString = #file,
+ line: UInt = #line
+ ) -> Self {
+ wait(
+ until: keyPath,
+ matches: true,
+ timeout: timeout,
+ message: message(),
+ file: file,
+ line: line
+ )
+ }
+}
diff --git a/Example/EchoUITests/Extensions/XCUIElementQuery.swift b/Example/EchoUITests/Extensions/XCUIElementQuery.swift
new file mode 100644
index 000000000..a92ed3a08
--- /dev/null
+++ b/Example/EchoUITests/Extensions/XCUIElementQuery.swift
@@ -0,0 +1,11 @@
+import Foundation
+import XCTest
+
+extension XCUIElementQuery {
+
+ func containing(_ text: String) -> XCUIElementQuery {
+ let predicate = NSPredicate(format: "label CONTAINS[c] %@", text)
+ let elementQuery = self.containing(predicate)
+ return elementQuery
+ }
+}
diff --git a/Example/EchoUITests/Tests/PushNotificationTests.swift b/Example/EchoUITests/Tests/PushNotificationTests.swift
new file mode 100644
index 000000000..c9db0aeca
--- /dev/null
+++ b/Example/EchoUITests/Tests/PushNotificationTests.swift
@@ -0,0 +1,57 @@
+import XCTest
+
+class PushNotificationTests: XCTestCase {
+
+ private var engine: Engine!
+
+ override func setUp() {
+ super.setUp()
+ engine = Engine()
+ engine.routing.launch(app: .wallet, clean: true)
+ engine.routing.launch(app: .dapp, clean: true)
+ }
+
+ func testPushNotification() {
+
+ // Initiate connection & copy URI from dApp
+ engine.routing.activate(app: .dapp)
+ engine.dapp.connectButton.wait(until: \.exists).tap()
+ engine.dapp.newPairingButton.wait(until: \.exists).tap()
+
+ // Relies on existence of invisible label with uri in Dapp
+ let uri = engine.dapp.instance.staticTexts.containing("wc:").firstMatch.label
+
+ engine.dapp.copyURIButton.wait(until: \.exists).tap()
+
+ // Paste URI into Wallet & and allow connect
+ engine.routing.activate(app: .wallet)
+
+ allowPushNotificationsIfNeeded(app: engine.wallet.instance)
+
+ engine.wallet.getStartedButton.wait(until: \.exists).tap()
+ engine.wallet.pasteURIButton.wait(until: \.exists).tap()
+
+ engine.wallet.alertUriTextField.wait(until: \.exists).tap()
+ engine.wallet.alertUriTextField.typeText(uri)
+ engine.wallet.alertConnectButton.wait(until: \.exists).tap()
+
+ // Allow session
+ engine.wallet.allowButton.wait(until: \.exists, timeout: 15, message: "No session dialog appeared").tap()
+
+ // Trigger PN
+ engine.routing.activate(app: .dapp)
+ engine.dapp.accountRow.wait(until: \.exists, timeout: 15).tap()
+ engine.dapp.methodRow.wait(until: \.exists).tap()
+
+ // Launch springboard
+ engine.routing.activate(app: .springboard)
+
+ // Assert notification
+ let notification = engine.routing.springboard.otherElements.descendants(matching: .any)["NotificationShortLookView"]
+ notification
+ .wait(until: \.exists, timeout: 15)
+ .tap()
+
+ engine.wallet.instance.wait(until: \.exists)
+ }
+}
diff --git a/Example/ExampleApp.xcodeproj/project.pbxproj b/Example/ExampleApp.xcodeproj/project.pbxproj
index f16df11a9..cf8c21e00 100644
--- a/Example/ExampleApp.xcodeproj/project.pbxproj
+++ b/Example/ExampleApp.xcodeproj/project.pbxproj
@@ -18,6 +18,10 @@
84474A0129B9EB74005F520B /* Starscream in Frameworks */ = {isa = PBXBuildFile; productRef = 84474A0029B9EB74005F520B /* Starscream */; };
84474A0229B9ECA2005F520B /* DefaultSocketFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5629AEF2877F73000094373 /* DefaultSocketFactory.swift */; };
8448F1D427E4726F0000B866 /* WalletConnect in Frameworks */ = {isa = PBXBuildFile; productRef = 8448F1D327E4726F0000B866 /* WalletConnect */; };
+ 84536D6E29EEAE1F008EA8DB /* Web3InboxModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84536D6D29EEAE1F008EA8DB /* Web3InboxModule.swift */; };
+ 84536D7029EEAE28008EA8DB /* Web3InboxRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84536D6F29EEAE28008EA8DB /* Web3InboxRouter.swift */; };
+ 84536D7229EEAE32008EA8DB /* Web3InboxViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84536D7129EEAE32008EA8DB /* Web3InboxViewController.swift */; };
+ 84536D7429EEBCF0008EA8DB /* Web3Inbox in Frameworks */ = {isa = PBXBuildFile; productRef = 84536D7329EEBCF0008EA8DB /* Web3Inbox */; };
845B8D8C2934B36C0084A966 /* Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845B8D8B2934B36C0084A966 /* Account.swift */; };
847BD1D62989492500076C90 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 847BD1D12989492500076C90 /* MainViewController.swift */; };
847BD1D82989492500076C90 /* MainModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 847BD1D32989492500076C90 /* MainModule.swift */; };
@@ -31,7 +35,6 @@
847BD1E8298A806800076C90 /* NotificationsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 847BD1E3298A806800076C90 /* NotificationsView.swift */; };
847BD1EB298A87AB00076C90 /* SubscriptionsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 847BD1EA298A87AB00076C90 /* SubscriptionsViewModel.swift */; };
847CF3AF28E3141700F1D760 /* WalletConnectPush in Frameworks */ = {isa = PBXBuildFile; productRef = 847CF3AE28E3141700F1D760 /* WalletConnectPush */; settings = {ATTRIBUTES = (Required, ); }; };
- 8485617F295307C20064877B /* PushNotificationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8485617E295307C20064877B /* PushNotificationTests.swift */; };
849D7A93292E2169006A2BD4 /* PushTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849D7A92292E2169006A2BD4 /* PushTests.swift */; };
84AA01DB28CF0CD7005D48D8 /* XCTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84AA01DA28CF0CD7005D48D8 /* XCTest.swift */; };
84B8154E2991099000FAD54E /* BuildConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84B8154D2991099000FAD54E /* BuildConfiguration.swift */; };
@@ -264,6 +267,15 @@
C5F32A322954816C00A6476E /* ConnectionDetailsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5F32A312954816C00A6476E /* ConnectionDetailsPresenter.swift */; };
C5F32A342954817600A6476E /* ConnectionDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5F32A332954817600A6476E /* ConnectionDetailsView.swift */; };
C5F32A362954FE3C00A6476E /* Colors.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C5F32A352954FE3C00A6476E /* Colors.xcassets */; };
+ CF1A594529E5876600AAC16B /* XCUIElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF1A593A29E5876600AAC16B /* XCUIElement.swift */; };
+ CF1A594629E5876600AAC16B /* PushNotificationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF1A593C29E5876600AAC16B /* PushNotificationTests.swift */; };
+ CF1A594829E5876600AAC16B /* Engine.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF1A593F29E5876600AAC16B /* Engine.swift */; };
+ CF1A594929E5876600AAC16B /* WalletEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF1A594029E5876600AAC16B /* WalletEngine.swift */; };
+ CF1A594B29E5876600AAC16B /* DAppEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF1A594229E5876600AAC16B /* DAppEngine.swift */; };
+ CF1A594C29E5876600AAC16B /* RoutingEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF1A594329E5876600AAC16B /* RoutingEngine.swift */; };
+ CF1A594D29E5876600AAC16B /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF1A594429E5876600AAC16B /* App.swift */; };
+ CF6704DF29E59DDC003326A4 /* XCUIElementQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF6704DE29E59DDC003326A4 /* XCUIElementQuery.swift */; };
+ CF6704E129E5A014003326A4 /* XCTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF6704E029E5A014003326A4 /* XCTestCase.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -281,6 +293,27 @@
remoteGlobalIDString = 84CE641B27981DED00142511;
remoteInfo = DApp;
};
+ CF11913F29E5D86F000D4538 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 764E1D3426F8D3FC00A1FB15 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 84CE641B27981DED00142511;
+ remoteInfo = DApp;
+ };
+ CF11914129E5D873000D4538 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 764E1D3426F8D3FC00A1FB15 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = C56EE21A293F55ED004840D1;
+ remoteInfo = WalletApp;
+ };
+ CF12A92229E847D600B42F2A /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 764E1D3426F8D3FC00A1FB15 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 84E6B84629787A8000428BAF;
+ remoteInfo = PNDecryptionService;
+ };
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
@@ -309,6 +342,9 @@
8439CB88293F658E00F2F2E2 /* PushMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushMessage.swift; sourceTree = ""; };
844749F329B9E5B9005F520B /* RelayIntegrationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RelayIntegrationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
844749F529B9E5B9005F520B /* RelayClientEndToEndTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayClientEndToEndTests.swift; sourceTree = ""; };
+ 84536D6D29EEAE1F008EA8DB /* Web3InboxModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Web3InboxModule.swift; sourceTree = ""; };
+ 84536D6F29EEAE28008EA8DB /* Web3InboxRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Web3InboxRouter.swift; sourceTree = ""; };
+ 84536D7129EEAE32008EA8DB /* Web3InboxViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Web3InboxViewController.swift; sourceTree = ""; };
845AA7D929BA1EBA00F33739 /* IntegrationTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; name = IntegrationTests.xctestplan; path = ExampleApp.xcodeproj/IntegrationTests.xctestplan; sourceTree = ""; };
845AA7DC29BB424800F33739 /* SmokeTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = SmokeTests.xctestplan; sourceTree = ""; };
845B8D8B2934B36C0084A966 /* Account.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Account.swift; sourceTree = ""; };
@@ -323,7 +359,6 @@
847BD1E2298A806800076C90 /* NotificationsInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsInteractor.swift; sourceTree = ""; };
847BD1E3298A806800076C90 /* NotificationsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsView.swift; sourceTree = ""; };
847BD1EA298A87AB00076C90 /* SubscriptionsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionsViewModel.swift; sourceTree = ""; };
- 8485617E295307C20064877B /* PushNotificationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushNotificationTests.swift; sourceTree = ""; };
849A4F18298281E300E61ACE /* WalletAppRelease.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = WalletAppRelease.entitlements; sourceTree = ""; };
849A4F19298281F100E61ACE /* PNDecryptionServiceRelease.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = PNDecryptionServiceRelease.entitlements; sourceTree = ""; };
849D7A92292E2169006A2BD4 /* PushTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushTests.swift; sourceTree = ""; };
@@ -536,6 +571,17 @@
C5F32A312954816C00A6476E /* ConnectionDetailsPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionDetailsPresenter.swift; sourceTree = ""; };
C5F32A332954817600A6476E /* ConnectionDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionDetailsView.swift; sourceTree = ""; };
C5F32A352954FE3C00A6476E /* Colors.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Colors.xcassets; sourceTree = ""; };
+ CF1A593029E5873D00AAC16B /* EchoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = EchoUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ CF1A593A29E5876600AAC16B /* XCUIElement.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XCUIElement.swift; sourceTree = ""; };
+ CF1A593C29E5876600AAC16B /* PushNotificationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PushNotificationTests.swift; sourceTree = ""; };
+ CF1A593F29E5876600AAC16B /* Engine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Engine.swift; sourceTree = ""; };
+ CF1A594029E5876600AAC16B /* WalletEngine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletEngine.swift; sourceTree = ""; };
+ CF1A594229E5876600AAC16B /* DAppEngine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DAppEngine.swift; sourceTree = ""; };
+ CF1A594329E5876600AAC16B /* RoutingEngine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoutingEngine.swift; sourceTree = ""; };
+ CF1A594429E5876600AAC16B /* App.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = ""; };
+ CF6704DE29E59DDC003326A4 /* XCUIElementQuery.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCUIElementQuery.swift; sourceTree = ""; };
+ CF6704E029E5A014003326A4 /* XCTestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCTestCase.swift; sourceTree = ""; };
+ CF79389D29EDD9DC00441B4F /* RelayIntegrationTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = RelayIntegrationTests.xctestplan; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -608,6 +654,7 @@
files = (
C56EE27D293F56F8004840D1 /* WalletConnectChat in Frameworks */,
C5133A78294125CC00A8314C /* Web3 in Frameworks */,
+ 84536D7429EEBCF0008EA8DB /* Web3Inbox in Frameworks */,
C5B2F7052970573D000DBA0E /* SolanaSwift in Frameworks */,
C55D349929630D440004314A /* Web3Wallet in Frameworks */,
84E6B85429787AAE00428BAF /* WalletConnectPush in Frameworks */,
@@ -616,12 +663,20 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ CF1A592D29E5873D00AAC16B /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
764E1D3326F8D3FC00A1FB15 = {
isa = PBXGroup;
children = (
+ CF79389D29EDD9DC00441B4F /* RelayIntegrationTests.xctestplan */,
845AA7D929BA1EBA00F33739 /* IntegrationTests.xctestplan */,
845AA7DC29BB424800F33739 /* SmokeTests.xctestplan */,
A5A8E479293A1C4400FEB97D /* Shared */,
@@ -634,6 +689,7 @@
C56EE21C293F55ED004840D1 /* WalletApp */,
84E6B84829787A8000428BAF /* PNDecryptionService */,
844749F429B9E5B9005F520B /* RelayIntegrationTests */,
+ CF1A593129E5873D00AAC16B /* EchoUITests */,
764E1D3D26F8D3FC00A1FB15 /* Products */,
764E1D5326F8DAC800A1FB15 /* Frameworks */,
764E1D5626F8DB6000A1FB15 /* WalletConnectSwiftV2 */,
@@ -650,6 +706,7 @@
C56EE21B293F55ED004840D1 /* WalletApp.app */,
84E6B84729787A8000428BAF /* PNDecryptionService.appex */,
844749F329B9E5B9005F520B /* RelayIntegrationTests.xctest */,
+ CF1A593029E5873D00AAC16B /* EchoUITests.xctest */,
);
name = Products;
sourceTree = "";
@@ -681,6 +738,16 @@
path = RelayIntegrationTests;
sourceTree = "";
};
+ 84536D6C29EEAE0D008EA8DB /* Web3Inbox */ = {
+ isa = PBXGroup;
+ children = (
+ 84536D6D29EEAE1F008EA8DB /* Web3InboxModule.swift */,
+ 84536D6F29EEAE28008EA8DB /* Web3InboxRouter.swift */,
+ 84536D7129EEAE32008EA8DB /* Web3InboxViewController.swift */,
+ );
+ path = Web3Inbox;
+ sourceTree = "";
+ };
847BD1DB2989493F00076C90 /* Main */ = {
isa = PBXGroup;
children = (
@@ -1314,7 +1381,6 @@
isa = PBXGroup;
children = (
A5A4FC762840C12C00BBEC1E /* RegressionTests.swift */,
- 8485617E295307C20064877B /* PushNotificationTests.swift */,
);
path = Regression;
sourceTree = "";
@@ -1391,6 +1457,7 @@
C56EE229293F5668004840D1 /* Wallet */ = {
isa = PBXGroup;
children = (
+ 84536D6C29EEAE0D008EA8DB /* Web3Inbox */,
847BD1DB2989493F00076C90 /* Main */,
C55D3477295DD4AA0004314A /* Welcome */,
C55D3474295DCB850004314A /* AuthRequest */,
@@ -1575,6 +1642,46 @@
path = ConnectionDetails;
sourceTree = "";
};
+ CF1A593129E5873D00AAC16B /* EchoUITests */ = {
+ isa = PBXGroup;
+ children = (
+ CF1A593E29E5876600AAC16B /* Engine */,
+ CF1A593929E5876600AAC16B /* Extensions */,
+ CF1A593B29E5876600AAC16B /* Tests */,
+ );
+ path = EchoUITests;
+ sourceTree = "";
+ };
+ CF1A593929E5876600AAC16B /* Extensions */ = {
+ isa = PBXGroup;
+ children = (
+ CF1A593A29E5876600AAC16B /* XCUIElement.swift */,
+ CF6704DE29E59DDC003326A4 /* XCUIElementQuery.swift */,
+ CF6704E029E5A014003326A4 /* XCTestCase.swift */,
+ );
+ path = Extensions;
+ sourceTree = "";
+ };
+ CF1A593B29E5876600AAC16B /* Tests */ = {
+ isa = PBXGroup;
+ children = (
+ CF1A593C29E5876600AAC16B /* PushNotificationTests.swift */,
+ );
+ path = Tests;
+ sourceTree = "";
+ };
+ CF1A593E29E5876600AAC16B /* Engine */ = {
+ isa = PBXGroup;
+ children = (
+ CF1A593F29E5876600AAC16B /* Engine.swift */,
+ CF1A594029E5876600AAC16B /* WalletEngine.swift */,
+ CF1A594229E5876600AAC16B /* DAppEngine.swift */,
+ CF1A594329E5876600AAC16B /* RoutingEngine.swift */,
+ CF1A594429E5876600AAC16B /* App.swift */,
+ );
+ path = Engine;
+ sourceTree = "";
+ };
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -1735,18 +1842,39 @@
C55D349829630D440004314A /* Web3Wallet */,
C5B2F7042970573D000DBA0E /* SolanaSwift */,
84E6B85329787AAE00428BAF /* WalletConnectPush */,
+ 84536D7329EEBCF0008EA8DB /* Web3Inbox */,
);
productName = ChatWallet;
productReference = C56EE21B293F55ED004840D1 /* WalletApp.app */;
productType = "com.apple.product-type.application";
};
+ CF1A592F29E5873D00AAC16B /* EchoUITests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = CF1A593629E5873D00AAC16B /* Build configuration list for PBXNativeTarget "EchoUITests" */;
+ buildPhases = (
+ CF1A592C29E5873D00AAC16B /* Sources */,
+ CF1A592D29E5873D00AAC16B /* Frameworks */,
+ CF1A592E29E5873D00AAC16B /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ CF12A92329E847D600B42F2A /* PBXTargetDependency */,
+ CF11914229E5D873000D4538 /* PBXTargetDependency */,
+ CF11914029E5D86F000D4538 /* PBXTargetDependency */,
+ );
+ name = EchoUITests;
+ productName = EchoUITests;
+ productReference = CF1A593029E5873D00AAC16B /* EchoUITests.xctest */;
+ productType = "com.apple.product-type.bundle.ui-testing";
+ };
/* End PBXNativeTarget section */
/* Begin PBXProject section */
764E1D3426F8D3FC00A1FB15 /* Project object */ = {
isa = PBXProject;
attributes = {
- LastSwiftUpdateCheck = 1420;
+ LastSwiftUpdateCheck = 1430;
LastUpgradeCheck = 1250;
TargetAttributes = {
844749F229B9E5B9005F520B = {
@@ -1770,6 +1898,9 @@
C56EE21A293F55ED004840D1 = {
CreatedOnToolsVersion = 14.1;
};
+ CF1A592F29E5873D00AAC16B = {
+ CreatedOnToolsVersion = 14.3;
+ };
};
};
buildConfigurationList = 764E1D3726F8D3FC00A1FB15 /* Build configuration list for PBXProject "ExampleApp" */;
@@ -1798,6 +1929,7 @@
C56EE21A293F55ED004840D1 /* WalletApp */,
84E6B84629787A8000428BAF /* PNDecryptionService */,
844749F229B9E5B9005F520B /* RelayIntegrationTests */,
+ CF1A592F29E5873D00AAC16B /* EchoUITests */,
);
};
/* End PBXProject section */
@@ -1858,6 +1990,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ CF1A592E29E5873D00AAC16B /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@@ -1990,7 +2129,6 @@
buildActionMask = 2147483647;
files = (
A5E22D202840C8C700E36487 /* DAppEngine.swift in Sources */,
- 8485617F295307C20064877B /* PushNotificationTests.swift in Sources */,
A5E22D1E2840C8BF00E36487 /* RoutingEngine.swift in Sources */,
A5E22D222840C8D300E36487 /* WalletEngine.swift in Sources */,
A5E22D1C2840C85D00E36487 /* App.swift in Sources */,
@@ -2047,6 +2185,8 @@
C55D34B12965FB750004314A /* SessionProposalInteractor.swift in Sources */,
C56EE247293F566D004840D1 /* ScanModule.swift in Sources */,
C56EE28D293F5757004840D1 /* AppearanceConfigurator.swift in Sources */,
+ 84536D6E29EEAE1F008EA8DB /* Web3InboxModule.swift in Sources */,
+ 84536D7029EEAE28008EA8DB /* Web3InboxRouter.swift in Sources */,
847BD1D82989492500076C90 /* MainModule.swift in Sources */,
847BD1E7298A806800076C90 /* NotificationsInteractor.swift in Sources */,
C56EE241293F566D004840D1 /* WalletModule.swift in Sources */,
@@ -2074,6 +2214,7 @@
C55D347F295DD7140004314A /* AuthRequestModule.swift in Sources */,
C56EE242293F566D004840D1 /* ScanPresenter.swift in Sources */,
C56EE28B293F5757004840D1 /* SceneDelegate.swift in Sources */,
+ 84536D7229EEAE32008EA8DB /* Web3InboxViewController.swift in Sources */,
C56EE276293F56D7004840D1 /* UIViewController.swift in Sources */,
C56EE275293F56D7004840D1 /* InputConfig.swift in Sources */,
C55D3493295DFA750004314A /* WelcomeModule.swift in Sources */,
@@ -2132,6 +2273,22 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ CF1A592C29E5873D00AAC16B /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ CF1A594D29E5876600AAC16B /* App.swift in Sources */,
+ CF1A594B29E5876600AAC16B /* DAppEngine.swift in Sources */,
+ CF6704DF29E59DDC003326A4 /* XCUIElementQuery.swift in Sources */,
+ CF1A594629E5876600AAC16B /* PushNotificationTests.swift in Sources */,
+ CF1A594829E5876600AAC16B /* Engine.swift in Sources */,
+ CF1A594529E5876600AAC16B /* XCUIElement.swift in Sources */,
+ CF1A594C29E5876600AAC16B /* RoutingEngine.swift in Sources */,
+ CF6704E129E5A014003326A4 /* XCTestCase.swift in Sources */,
+ CF1A594929E5876600AAC16B /* WalletEngine.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
@@ -2145,6 +2302,21 @@
target = 84CE641B27981DED00142511 /* DApp */;
targetProxy = A5A4FC7D2840C5D400BBEC1E /* PBXContainerItemProxy */;
};
+ CF11914029E5D86F000D4538 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 84CE641B27981DED00142511 /* DApp */;
+ targetProxy = CF11913F29E5D86F000D4538 /* PBXContainerItemProxy */;
+ };
+ CF11914229E5D873000D4538 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = C56EE21A293F55ED004840D1 /* WalletApp */;
+ targetProxy = CF11914129E5D873000D4538 /* PBXContainerItemProxy */;
+ };
+ CF12A92329E847D600B42F2A /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 84E6B84629787A8000428BAF /* PNDecryptionService */;
+ targetProxy = CF12A92229E847D600B42F2A /* PBXContainerItemProxy */;
+ };
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
@@ -2580,6 +2752,7 @@
);
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+ OTHER_SWIFT_FLAGS = "";
PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.IntegrationTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
@@ -2613,11 +2786,10 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CODE_SIGN_ENTITLEMENTS = WalletApp/WalletApp.entitlements;
- "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
- CODE_SIGN_STYLE = Manual;
+ CODE_SIGN_IDENTITY = "Apple Development";
+ CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 7;
- DEVELOPMENT_TEAM = "";
- "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5R8AG9K22;
+ DEVELOPMENT_TEAM = W5R8AG9K22;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = WalletApp/Other/Info.plist;
@@ -2636,7 +2808,6 @@
PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.walletapp;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
- "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match Development com.walletconnect.walletapp";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@@ -2682,6 +2853,42 @@
};
name = Release;
};
+ CF1A593729E5873D00AAC16B /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = W5R8AG9K22;
+ GENERATE_INFOPLIST_FILE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 14.0;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.EchoUITests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_EMIT_LOC_STRINGS = NO;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ CF1A593829E5873D00AAC16B /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = W5R8AG9K22;
+ GENERATE_INFOPLIST_FILE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 14.0;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.walletconnect.EchoUITests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_EMIT_LOC_STRINGS = NO;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Release;
+ };
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
@@ -2757,6 +2964,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ CF1A593629E5873D00AAC16B /* Build configuration list for PBXNativeTarget "EchoUITests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ CF1A593729E5873D00AAC16B /* Debug */,
+ CF1A593829E5873D00AAC16B /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
/* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
@@ -2808,6 +3024,10 @@
isa = XCSwiftPackageProductDependency;
productName = WalletConnect;
};
+ 84536D7329EEBCF0008EA8DB /* Web3Inbox */ = {
+ isa = XCSwiftPackageProductDependency;
+ productName = Web3Inbox;
+ };
847CF3AE28E3141700F1D760 /* WalletConnectPush */ = {
isa = XCSwiftPackageProductDependency;
productName = WalletConnectPush;
diff --git a/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/BuildAll.xcscheme b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/BuildAll.xcscheme
new file mode 100644
index 000000000..4d2bf2723
--- /dev/null
+++ b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/BuildAll.xcscheme
@@ -0,0 +1,110 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/EchoUITests.xcscheme b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/EchoUITests.xcscheme
new file mode 100644
index 000000000..40fa61364
--- /dev/null
+++ b/Example/ExampleApp.xcodeproj/xcshareddata/xcschemes/EchoUITests.xcscheme
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Example/IntegrationTests/Push/PushTests.swift b/Example/IntegrationTests/Push/PushTests.swift
index dc83c0042..3be34d4f2 100644
--- a/Example/IntegrationTests/Push/PushTests.swift
+++ b/Example/IntegrationTests/Push/PushTests.swift
@@ -237,7 +237,50 @@ final class PushTests: XCTestCase {
wait(for: [expectation], timeout: InputConfig.defaultTimeout)
}
- private func sign(_ message: String) -> SigningResult {
+ // Push Subscribe
+ func testWalletCreatesSubscription() async {
+ let expectation = expectation(description: "expects to create push subscription")
+ let metadata = AppMetadata(name: "GM Dapp", description: "", url: "https://gm-dapp-xi.vercel.app/", icons: [])
+ try! await walletPushClient.subscribe(metadata: metadata, account: Account.stub(), onSign: sign)
+ walletPushClient.subscriptionsPublisher
+ .first()
+ .sink { [unowned self] subscriptions in
+ XCTAssertNotNil(subscriptions.first)
+ Task { try! await walletPushClient.deleteSubscription(topic: subscriptions.first!.topic) }
+ expectation.fulfill()
+ }.store(in: &publishers)
+ wait(for: [expectation], timeout: InputConfig.defaultTimeout)
+ }
+
+ func testWalletCreatesAndUpdatesSubscription() async {
+ let expectation = expectation(description: "expects to create and update push subscription")
+ let metadata = AppMetadata(name: "GM Dapp", description: "", url: "https://gm-dapp-xi.vercel.app/", icons: [])
+ let updateScope: Set = [NotificationScope.alerts]
+ try! await walletPushClient.subscribe(metadata: metadata, account: Account.stub(), onSign: sign)
+ walletPushClient.subscriptionsPublisher
+ .first()
+ .sink { [unowned self] subscriptions in
+ Task { try! await walletPushClient.update(topic: subscriptions.first!.topic, scope: updateScope) }
+ }
+ .store(in: &publishers)
+
+ walletPushClient.updateSubscriptionPublisher
+ .sink { [unowned self] result in
+ guard case .success(let subscription) = result else { XCTFail(); return }
+ let updatedScope = Set(subscription.scope.filter{ $0.value.enabled == true }.keys)
+ XCTAssertEqual(updatedScope, updateScope)
+ Task { try! await walletPushClient.deleteSubscription(topic: subscription.topic) }
+ expectation.fulfill()
+ }.store(in: &publishers)
+
+ wait(for: [expectation], timeout: InputConfig.defaultTimeout)
+ }
+
+}
+
+
+private extension PushTests {
+ func sign(_ message: String) -> SigningResult {
let privateKey = Data(hex: "305c6cde3846927892cd32762f6120539f3ec74c9e3a16b9b798b1e85351ae2a")
let signer = MessageSignerFactory(signerFactory: DefaultSignerFactory()).create(projectId: InputConfig.projectId)
return .signed(try! signer.sign(message: message, privateKey: privateKey, type: .eip191))
diff --git a/Example/IntegrationTests/Sign/SignClientTests.swift b/Example/IntegrationTests/Sign/SignClientTests.swift
index bc71628cf..583cd886a 100644
--- a/Example/IntegrationTests/Sign/SignClientTests.swift
+++ b/Example/IntegrationTests/Sign/SignClientTests.swift
@@ -164,7 +164,7 @@ final class SignClientTests: XCTestCase {
let uri = try await dapp.client.connect(requiredNamespaces: requiredNamespaces)!
try await wallet.client.pair(uri: uri)
- wait(for: [expectation], timeout: .infinity)
+ wait(for: [expectation], timeout: InputConfig.defaultTimeout)
}
func testSessionRequest() async throws {
diff --git a/Example/IntegrationTests/Stubs/PushMessage.swift b/Example/IntegrationTests/Stubs/PushMessage.swift
index db9f5d870..18bc68107 100644
--- a/Example/IntegrationTests/Stubs/PushMessage.swift
+++ b/Example/IntegrationTests/Stubs/PushMessage.swift
@@ -3,6 +3,6 @@ import WalletConnectPush
extension PushMessage {
static func stub() -> PushMessage {
- return PushMessage(title: "test_push_message", body: "", icon: "", url: "")
+ return PushMessage(title: "test_push_message", body: "", icon: "", url: "", type: "")
}
}
diff --git a/Example/RelayIntegrationTests.xctestplan b/Example/RelayIntegrationTests.xctestplan
new file mode 100644
index 000000000..b4026d55e
--- /dev/null
+++ b/Example/RelayIntegrationTests.xctestplan
@@ -0,0 +1,41 @@
+{
+ "configurations" : [
+ {
+ "id" : "3D7BF967-0C62-49DD-ABA1-BDDEE678ED85",
+ "name" : "Configuration 1",
+ "options" : {
+
+ }
+ }
+ ],
+ "defaultOptions" : {
+ "codeCoverage" : false,
+ "environmentVariableEntries" : [
+ {
+ "key" : "RELAY_HOST",
+ "value" : "$(RELAY_HOST)"
+ },
+ {
+ "key" : "PROJECT_ID",
+ "value" : "$(PROJECT_ID)"
+ }
+ ],
+ "mainThreadCheckerEnabled" : false,
+ "targetForVariableExpansion" : {
+ "containerPath" : "container:ExampleApp.xcodeproj",
+ "identifier" : "844749F229B9E5B9005F520B",
+ "name" : "RelayIntegrationTests"
+ },
+ "testTimeoutsEnabled" : true
+ },
+ "testTargets" : [
+ {
+ "target" : {
+ "containerPath" : "container:ExampleApp.xcodeproj",
+ "identifier" : "844749F229B9E5B9005F520B",
+ "name" : "RelayIntegrationTests"
+ }
+ }
+ ],
+ "version" : 1
+}
diff --git a/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxViewController.swift b/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxViewController.swift
index b31c33fb0..5e1111b53 100644
--- a/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxViewController.swift
+++ b/Example/Showcase/Classes/PresentationLayer/Web3Inbox/Web3InboxViewController.swift
@@ -18,7 +18,7 @@ final class Web3InboxViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
- Web3Inbox.configure(account: importAccount.account, onSign: onSing)
+ Web3Inbox.configure(account: importAccount.account, config: [.pushEnabled: false], onSign: onSing, environment: .sandbox)
edgesForExtendedLayout = []
navigationItem.title = "Web3Inbox SDK"
diff --git a/Example/SmokeTests.xctestplan b/Example/SmokeTests.xctestplan
index a5ce3e0df..34685f639 100644
--- a/Example/SmokeTests.xctestplan
+++ b/Example/SmokeTests.xctestplan
@@ -4,6 +4,7 @@
"id" : "EB662E8C-8DC0-4689-A2FF-B41B80A99F85",
"name" : "Configuration 1",
"options" : {
+ "mainThreadCheckerEnabled" : false,
"targetForVariableExpansion" : {
"containerPath" : "container:ExampleApp.xcodeproj",
"identifier" : "A5E03DEC286464DB00888481",
@@ -13,6 +14,7 @@
}
],
"defaultOptions" : {
+ "codeCoverage" : false,
"environmentVariableEntries" : [
{
"key" : "RELAY_HOST",
@@ -23,6 +25,7 @@
"value" : "$(PROJECT_ID)"
}
],
+ "mainThreadCheckerEnabled" : false,
"testTimeoutsEnabled" : true
},
"testTargets" : [
@@ -39,13 +42,16 @@
"ChatTests\/testInvite()",
"EIP1271VerifierTests",
"EIP191VerifierTests",
+ "EIP55Tests",
"ENSResolverTests",
"PairingTests",
"PushTests\/testDappDeletePushSubscription()",
"PushTests\/testRequestPush()",
"PushTests\/testWalletApprovesPushRequest()",
+ "PushTests\/testWalletCreatesSubscription()",
"PushTests\/testWalletDeletePushSubscription()",
"PushTests\/testWalletRejectsPushRequest()",
+ "PushTests\/testWalletUpdatesSubscription()",
"RegistryTests",
"RelayClientEndToEndTests",
"SignClientTests\/testCaip25SatisfyAllRequiredAllOptionalNamespacesSuccessful()",
diff --git a/Example/UITests/Regression/PushNotificationTests.swift b/Example/UITests/Regression/PushNotificationTests.swift
deleted file mode 100644
index c37df7a66..000000000
--- a/Example/UITests/Regression/PushNotificationTests.swift
+++ /dev/null
@@ -1,21 +0,0 @@
-
-import XCTest
-
-class PushNotificationTests: XCTestCase {
- let wallet = XCUIApplication(bundleIdentifier: "com.walletconnect.example")
- let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
-
- func testPushNotification() {
- wallet.launch()
-
- sleep(1)
-
- // Launch springboard
- springboard.activate()
- let text = "Is this working"
-
- let notification = springboard.otherElements["Notification"].descendants(matching: .any)["NotificationShortLookView"]
- XCTAssertTrue(notification.waitForExistence(timeout: 5))
- notification.tap()
- }
-}
diff --git a/Example/WalletApp/ApplicationLayer/AppDelegate.swift b/Example/WalletApp/ApplicationLayer/AppDelegate.swift
index 25fa75d10..d294b73f6 100644
--- a/Example/WalletApp/ApplicationLayer/AppDelegate.swift
+++ b/Example/WalletApp/ApplicationLayer/AppDelegate.swift
@@ -24,12 +24,8 @@ final class AppDelegate: UIResponder, UIApplicationDelegate {
_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
- Task(priority: .high) {
- // Use pasteboard for testing purposes
- let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) }
- let token = tokenParts.joined()
- let pasteboard = UIPasteboard.general
- pasteboard.string = token
+
+ Task(priority: .high) {
try await Push.wallet.register(deviceToken: deviceToken)
}
}
diff --git a/Example/WalletApp/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift b/Example/WalletApp/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift
index e06467da7..58064d376 100644
--- a/Example/WalletApp/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift
+++ b/Example/WalletApp/ApplicationLayer/Configurator/ThirdPartyConfigurator.swift
@@ -1,6 +1,6 @@
import WalletConnectNetworking
import Web3Wallet
-import WalletConnectPush
+import Web3Inbox
struct ThirdPartyConfigurator: Configurator {
@@ -16,9 +16,20 @@ struct ThirdPartyConfigurator: Configurator {
)
Web3Wallet.configure(metadata: metadata, crypto: DefaultCryptoProvider(), environment: BuildConfiguration.shared.apnsEnvironment)
- Push.configure(environment: BuildConfiguration.shared.apnsEnvironment)
+
+ let account = Account(blockchain: Blockchain("eip155:1")!, address: EthKeyStore.shared.address)!
+
+ Web3Inbox.configure(account: account, config: [.chatEnabled: false, .settingsEnabled: false], onSign: Web3InboxSigner.onSing, environment: BuildConfiguration.shared.apnsEnvironment)
}
}
+class Web3InboxSigner {
+ static func onSing(_ message: String) -> SigningResult {
+ let privateKey = EthKeyStore.shared.privateKeyRaw
+ let signer = MessageSignerFactory(signerFactory: DefaultSignerFactory()).create()
+ let signature = try! signer.sign(message: message, privateKey: privateKey, type: .eip191)
+ return .signed(signature)
+ }
+}
diff --git a/Example/WalletApp/ApplicationLayer/PushRegisterer.swift b/Example/WalletApp/ApplicationLayer/PushRegisterer.swift
index 1f5f19162..91a7d63d5 100644
--- a/Example/WalletApp/ApplicationLayer/PushRegisterer.swift
+++ b/Example/WalletApp/ApplicationLayer/PushRegisterer.swift
@@ -8,33 +8,25 @@ class PushRegisterer {
private var publishers = [AnyCancellable]()
func getNotificationSettings() {
- UNUserNotificationCenter.current().getNotificationSettings { settings in
- print("Notification settings: \(settings)")
- guard settings.authorizationStatus == .authorized else { return }
- DispatchQueue.main.async {
- UIApplication.shared.registerForRemoteNotifications()
- }
- }
+ UNUserNotificationCenter.current().getNotificationSettings { settings in
+ print("Notification settings: \(settings)")
+
+ guard settings.authorizationStatus == .authorized else { return }
+
+ DispatchQueue.main.async {
+ UIApplication.shared.registerForRemoteNotifications()
+ }
+ }
}
func registerForPushNotifications() {
UNUserNotificationCenter.current()
- .requestAuthorization(
- options: [.alert, .sound, .badge]) { [weak self] granted, _ in
- print("Permission granted: \(granted)")
+ .requestAuthorization(
+ options: [.alert, .sound, .badge]
+ ) { granted, error in
+ print("Permission granted: \(granted)")
guard granted else { return }
- self?.getNotificationSettings()
-#if targetEnvironment(simulator)
- Networking.interactor.socketConnectionStatusPublisher
- .first {$0 == .connected}
- .sink{ status in
- let deviceToken = InputConfig.simulatorIdentifier
- assert(deviceToken != "SIMULATOR_IDENTIFIER", "Please set your Simulator identifier")
- Task(priority: .high) {
- try await Push.wallet.register(deviceToken: deviceToken)
- }
- }.store(in: &self!.publishers)
-#endif
+ self.getNotificationSettings()
}
}
}
diff --git a/Example/WalletApp/Common/InputConfig.swift b/Example/WalletApp/Common/InputConfig.swift
index 4cdd5ee73..83b9fac4e 100644
--- a/Example/WalletApp/Common/InputConfig.swift
+++ b/Example/WalletApp/Common/InputConfig.swift
@@ -4,12 +4,6 @@ struct InputConfig {
static var projectId: String {
return config(for: "PROJECT_ID")!
}
-
-#if targetEnvironment(simulator)
- static var simulatorIdentifier: String {
- return config(for: "SIMULATOR_IDENTIFIER")!
- }
-#endif
private static func config(for key: String) -> String? {
return Bundle.main.object(forInfoDictionaryKey: key) as? String
diff --git a/Example/WalletApp/PresentationLayer/Wallet/Main/MainPresenter.swift b/Example/WalletApp/PresentationLayer/Wallet/Main/MainPresenter.swift
index 1ace0f96a..ee968adb0 100644
--- a/Example/WalletApp/PresentationLayer/Wallet/Main/MainPresenter.swift
+++ b/Example/WalletApp/PresentationLayer/Wallet/Main/MainPresenter.swift
@@ -14,7 +14,7 @@ final class MainPresenter {
var viewControllers: [UIViewController] {
return [
router.walletViewController(),
- router.notificationsViewController(),
+ router.web3InboxViewController()
]
}
@@ -34,7 +34,8 @@ extension MainPresenter {
interactor.pushRequestPublisher
.receive(on: DispatchQueue.main)
.sink { [weak self] request in
- self?.router.present(pushRequest: request)
+
+// self?.router.present(pushRequest: request)
}.store(in: &disposeBag)
interactor.sessionProposalPublisher
diff --git a/Example/WalletApp/PresentationLayer/Wallet/Main/MainRouter.swift b/Example/WalletApp/PresentationLayer/Wallet/Main/MainRouter.swift
index b8ea7765b..1bd145592 100644
--- a/Example/WalletApp/PresentationLayer/Wallet/Main/MainRouter.swift
+++ b/Example/WalletApp/PresentationLayer/Wallet/Main/MainRouter.swift
@@ -24,6 +24,11 @@ final class MainRouter {
.wrapToNavigationController()
}
+ func web3InboxViewController() -> UIViewController {
+ return Web3InboxModule.create(app: app)
+ .wrapToNavigationController()
+ }
+
func present(pushRequest: PushRequest) {
PushRequestModule.create(app: app, pushRequest: pushRequest)
.presentFullScreen(from: viewController, transparentBackground: true)
diff --git a/Example/WalletApp/PresentationLayer/Wallet/Main/Model/TabPage.swift b/Example/WalletApp/PresentationLayer/Wallet/Main/Model/TabPage.swift
index 0416f515c..d195c90ae 100644
--- a/Example/WalletApp/PresentationLayer/Wallet/Main/Model/TabPage.swift
+++ b/Example/WalletApp/PresentationLayer/Wallet/Main/Model/TabPage.swift
@@ -2,14 +2,17 @@ import UIKit
enum TabPage: CaseIterable {
case wallet
- case notifications
+// case notifications
+ case web3Inbox
var title: String {
switch self {
case .wallet:
return "Apps"
- case .notifications:
- return "Notifications"
+// case .notifications:
+// return "Notifications"
+ case .web3Inbox:
+ return "w3i"
}
}
@@ -17,7 +20,9 @@ enum TabPage: CaseIterable {
switch self {
case .wallet:
return UIImage(systemName: "house.fill")!
- case .notifications:
+// case .notifications:
+// return UIImage(systemName: "bell.fill")!
+ case .web3Inbox:
return UIImage(systemName: "bell.fill")!
}
}
@@ -27,6 +32,6 @@ enum TabPage: CaseIterable {
}
static var enabledTabs: [TabPage] {
- return [.wallet, .notifications]
+ return [.wallet, .web3Inbox]
}
}
diff --git a/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestInteractor.swift b/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestInteractor.swift
index 987c7920c..2afda2ff4 100644
--- a/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestInteractor.swift
+++ b/Example/WalletApp/PresentationLayer/Wallet/PushRequest/PushRequestInteractor.swift
@@ -3,17 +3,10 @@ import WalletConnectPush
final class PushRequestInteractor {
func approve(pushRequest: PushRequest) async throws {
- try await Push.wallet.approve(id: pushRequest.id, onSign: onSing(_:))
+ try await Push.wallet.approve(id: pushRequest.id, onSign: Web3InboxSigner.onSing)
}
func reject(pushRequest: PushRequest) async throws {
try await Push.wallet.reject(id: pushRequest.id)
}
-
- func onSing(_ message: String) async -> SigningResult {
- let privateKey = EthKeyStore.shared.privateKeyRaw
- let signer = MessageSignerFactory(signerFactory: DefaultSignerFactory()).create()
- let signature = try! signer.sign(message: message, privateKey: privateKey, type: .eip191)
- return .signed(signature)
- }
}
diff --git a/Example/WalletApp/PresentationLayer/Wallet/SessionRequest/SessionRequestPresenter.swift b/Example/WalletApp/PresentationLayer/Wallet/SessionRequest/SessionRequestPresenter.swift
index 968687208..910a5288e 100644
--- a/Example/WalletApp/PresentationLayer/Wallet/SessionRequest/SessionRequestPresenter.swift
+++ b/Example/WalletApp/PresentationLayer/Wallet/SessionRequest/SessionRequestPresenter.swift
@@ -7,9 +7,6 @@ final class SessionRequestPresenter: ObservableObject {
private let interactor: SessionRequestInteractor
private let router: SessionRequestRouter
- @Published var showError = false
- @Published var errorMessage = "Error"
-
let sessionRequest: Request
let verified: Bool?
@@ -17,6 +14,9 @@ final class SessionRequestPresenter: ObservableObject {
return String(describing: sessionRequest.params.value)
}
+ @Published var showError = false
+ @Published var errorMessage = "Error"
+
private var disposeBag = Set()
init(
diff --git a/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletPresenter.swift b/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletPresenter.swift
index b1c406c3f..930b3472e 100644
--- a/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletPresenter.swift
+++ b/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletPresenter.swift
@@ -4,12 +4,19 @@ import Combine
import Web3Wallet
final class WalletPresenter: ObservableObject {
+ enum Errors: Error {
+ case invalidUri(uri: String)
+ }
+
private let interactor: WalletInteractor
private let router: WalletRouter
+ private let uri: String?
+
@Published var sessions = [Session]()
- private let uri: String?
+ @Published var showError = false
+ @Published var errorMessage = "Error"
private var disposeBag = Set()
@@ -33,9 +40,11 @@ final class WalletPresenter: ObservableObject {
func onPasteUri() {
router.presentPaste { [weak self] uri in
guard let uri = WalletConnectURI(string: uri) else {
+ self?.errorMessage = Errors.invalidUri(uri: uri).localizedDescription
+ self?.showError.toggle()
return
}
- print(uri)
+ print("URI: \(uri)")
self?.pair(uri: uri)
} onError: { [weak self] error in
@@ -45,10 +54,15 @@ final class WalletPresenter: ObservableObject {
}
func onScanUri() {
- router.presentScan { [unowned self] value in
- guard let uri = WalletConnectURI(string: value) else { return }
- self.pair(uri: uri)
- self.router.dismiss()
+ router.presentScan { [weak self] uri in
+ guard let uri = WalletConnectURI(string: uri) else {
+ self?.errorMessage = Errors.invalidUri(uri: uri).localizedDescription
+ self?.showError.toggle()
+ return
+ }
+ print("URI: \(uri)")
+ self?.pair(uri: uri)
+ self?.router.dismiss()
} onError: { error in
print(error.localizedDescription)
self.router.dismiss()
@@ -84,9 +98,17 @@ extension WalletPresenter {
pairFromDapp()
}
+
private func pair(uri: WalletConnectURI) {
Task(priority: .high) { [unowned self] in
- try await self.interactor.pair(uri: uri)
+ do {
+ try await self.interactor.pair(uri: uri)
+ } catch {
+ Task.detached { @MainActor in
+ self.errorMessage = error.localizedDescription
+ self.showError.toggle()
+ }
+ }
}
}
@@ -116,3 +138,12 @@ extension WalletPresenter: SceneViewModel {
return .always
}
}
+
+// MARK: - LocalizedError
+extension WalletPresenter.Errors: LocalizedError {
+ var errorDescription: String? {
+ switch self {
+ case .invalidUri(let uri): return "URI invalid format\n\(uri)"
+ }
+ }
+}
diff --git a/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletView.swift b/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletView.swift
index 2c889076a..c4dddd391 100644
--- a/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletView.swift
+++ b/Example/WalletApp/PresentationLayer/Wallet/Wallet/WalletView.swift
@@ -70,6 +70,9 @@ struct WalletView: View {
}
.padding(.vertical, 20)
}
+ .alert(presenter.errorMessage, isPresented: $presenter.showError) {
+ Button("OK", role: .cancel) {}
+ }
}
private func connectionView(session: Session) -> some View {
diff --git a/Example/WalletApp/PresentationLayer/Wallet/Web3Inbox/Web3InboxModule.swift b/Example/WalletApp/PresentationLayer/Wallet/Web3Inbox/Web3InboxModule.swift
new file mode 100644
index 000000000..ad0633441
--- /dev/null
+++ b/Example/WalletApp/PresentationLayer/Wallet/Web3Inbox/Web3InboxModule.swift
@@ -0,0 +1,13 @@
+import SwiftUI
+
+final class Web3InboxModule {
+
+ @discardableResult
+ static func create(app: Application) -> UIViewController {
+ let router = Web3InboxRouter(app: app)
+ let viewController = Web3InboxViewController()
+ router.viewController = viewController
+ return viewController
+ }
+
+}
diff --git a/Example/WalletApp/PresentationLayer/Wallet/Web3Inbox/Web3InboxRouter.swift b/Example/WalletApp/PresentationLayer/Wallet/Web3Inbox/Web3InboxRouter.swift
new file mode 100644
index 000000000..3631c35be
--- /dev/null
+++ b/Example/WalletApp/PresentationLayer/Wallet/Web3Inbox/Web3InboxRouter.swift
@@ -0,0 +1,12 @@
+import UIKit
+
+final class Web3InboxRouter {
+
+ weak var viewController: UIViewController!
+
+ private let app: Application
+
+ init(app: Application) {
+ self.app = app
+ }
+}
diff --git a/Example/WalletApp/PresentationLayer/Wallet/Web3Inbox/Web3InboxViewController.swift b/Example/WalletApp/PresentationLayer/Wallet/Web3Inbox/Web3InboxViewController.swift
new file mode 100644
index 000000000..e4043cca5
--- /dev/null
+++ b/Example/WalletApp/PresentationLayer/Wallet/Web3Inbox/Web3InboxViewController.swift
@@ -0,0 +1,27 @@
+import UIKit
+import WebKit
+import Web3Inbox
+
+final class Web3InboxViewController: UIViewController {
+
+
+ init() {
+ super.init(nibName: nil, bundle: nil)
+ }
+
+ required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ edgesForExtendedLayout = []
+ navigationItem.title = "Web3Inbox SDK"
+ navigationItem.largeTitleDisplayMode = .never
+ view = Web3Inbox.instance.getWebView()
+ }
+}
+
+
+
diff --git a/Example/WalletApp/PresentationLayer/Wallet/Welcome/WelcomePresenter.swift b/Example/WalletApp/PresentationLayer/Wallet/Welcome/WelcomePresenter.swift
index 136dc41da..2b60f80ab 100644
--- a/Example/WalletApp/PresentationLayer/Wallet/Welcome/WelcomePresenter.swift
+++ b/Example/WalletApp/PresentationLayer/Wallet/Welcome/WelcomePresenter.swift
@@ -16,6 +16,7 @@ final class WelcomePresenter: ObservableObject {
}
func onGetStarted() {
+
let pasteboard = UIPasteboard.general
let clientId = try? Networking.interactor.getClientId()
pasteboard.string = clientId
diff --git a/Makefile b/Makefile
index 04ea64890..a816db3f0 100755
--- a/Makefile
+++ b/Makefile
@@ -16,38 +16,75 @@ ifeq "${EXISTS_FASTLANE}" ""
endif
@echo "All dependencies was installed"
+test_setup:
+ defaults write com.apple.dt.XCBuild IgnoreFileSystemDeviceInodeChanges -bool YES
+ rm -rf test_results
+ mkdir test_results
+
+build_all:
+ rm -rf test_results
+ set -o pipefail && xcodebuild -scheme "WalletConnect-Package" -destination "platform=iOS Simulator,name=iPhone 14" -derivedDataPath DerivedDataCache -clonedSourcePackagesDirPath ../SourcePackagesCache RELAY_HOST='$(RELAY_HOST)' PROJECT_ID='$(PROJECT_ID)' build-for-testing | xcpretty
+ set -o pipefail && xcodebuild -project "Example/ExampleApp.xcodeproj" -scheme "BuildAll" -destination "platform=iOS Simulator,name=iPhone 14" -derivedDataPath DerivedDataCache -clonedSourcePackagesDirPath ../SourcePackagesCache RELAY_HOST='$(RELAY_HOST)' PROJECT_ID='$(PROJECT_ID)' build-for-testing | xcpretty
+
build_dapp:
fastlane build scheme:DApp
build_wallet:
fastlane build scheme:WalletApp
+echo_ui_tests:
+ echo "EchoUITests disabled"
+
ui_tests:
echo "UI Tests disabled"
-unit_tests:
- fastlane tests scheme:WalletConnect
-
-integration_tests:
- fastlane tests scheme:IntegrationTests testplan:IntegrationTests relay_host:$(RELAY_HOST) project_id:$(PROJECT_ID)
-
-relay_tests:
- fastlane tests scheme:RelayIntegrationTests relay_host:$(RELAY_HOST) project_id:$(PROJECT_ID)
-
-smoke_tests:
- xcodebuild \
- -project Example/ExampleApp.xcodeproj \
- -scheme IntegrationTests \
- -testPlan SmokeTests \
- -clonedSourcePackagesDirPath SourcePackagesCache \
- -destination 'platform=iOS Simulator,name=iPhone 14' \
- -derivedDataPath DerivedDataCache \
- RELAY_HOST=$(RELAY_HOST) \
- PROJECT_ID=$(PROJECT_ID) \
- test
-
-resolve_packages:
- fastlane resolve scheme:WalletApp
+unitxctestrun = $(shell find . -name '*WalletConnect-Package*.xctestrun')
+
+unit_tests: test_setup
+ifneq ($(unitxctestrun),)
+ set -o pipefail && env NSUnbufferedIO=YES xcodebuild -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath DerivedDataCache -resultBundlePath 'test_results/UnitTests.xcresult' -xctestrun '$(unitxctestrun)' test-without-building | tee ./test_results/xcodebuild.log | xcpretty --report junit --output ./test_results/report.junit
+else
+ set -o pipefail && env NSUnbufferedIO=YES xcodebuild -scheme WalletConnect-Package -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath DerivedDataCache -resultBundlePath 'test_results/UnitTests.xcresult' test | tee ./test_results/xcodebuild.log | xcpretty --report junit --output ./test_results/report.junit
+endif
+
+integrationxctestrun = $(shell find . -name '*_IntegrationTests*.xctestrun')
+
+integration_tests: test_setup
+ifneq ($(integrationxctestrun),)
+# override ENV variables
+ plutil -replace TestConfigurations.0.TestTargets.0.EnvironmentVariables.RELAY_HOST -string $(RELAY_HOST) $(integrationxctestrun)
+ plutil -replace TestConfigurations.0.TestTargets.0.EnvironmentVariables.PROJECT_ID -string $(PROJECT_ID) $(integrationxctestrun)
+# test-without-building
+ set -o pipefail && env NSUnbufferedIO=YES xcodebuild -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath DerivedDataCache -resultBundlePath 'test_results/IntegrationTests.xcresult' -xctestrun '$(integrationxctestrun)' test-without-building | tee ./test_results/xcodebuild.log | xcpretty --report junit --output ./test_results/report.junit
+else
+ set -o pipefail && env NSUnbufferedIO=YES xcodebuild -project Example/ExampleApp.xcodeproj -scheme IntegrationTests -testPlan IntegrationTests -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath DerivedDataCache -resultBundlePath 'test_results/IntegrationTests.xcresult' RELAY_HOST='$(RELAY_HOST)' PROJECT_ID='$(PROJECT_ID)' test | tee ./test_results/xcodebuild.log | xcpretty --report junit --output ./test_results/report.junit
+endif
+
+relayxctestrun = $(shell find . -name '*_RelayIntegrationTests*.xctestrun')
+
+relay_tests: test_setup
+ifneq ($(relayxctestrun),)
+# override ENV variables
+ plutil -replace TestConfigurations.0.TestTargets.0.EnvironmentVariables.RELAY_HOST -string $(RELAY_HOST) $(relayxctestrun)
+ plutil -replace TestConfigurations.0.TestTargets.0.EnvironmentVariables.PROJECT_ID -string $(PROJECT_ID) $(relayxctestrun)
+# test-without-building
+ set -o pipefail && env NSUnbufferedIO=YES xcodebuild -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath DerivedDataCache -resultBundlePath 'test_results/RelayIntegrationTests.xcresult' -xctestrun '$(relayxctestrun)' test-without-building | tee ./test_results/xcodebuild.log | xcpretty --report junit --output ./test_results/report.junit
+else
+ set -o pipefail && env NSUnbufferedIO=YES xcodebuild -project Example/ExampleApp.xcodeproj -scheme RelayIntegrationTests -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath DerivedDataCache -resultBundlePath 'test_results/RelayIntegrationTests.xcresult' RELAY_HOST='$(RELAY_HOST)' PROJECT_ID='$(PROJECT_ID)' test | tee ./test_results/xcodebuild.log | xcpretty --report junit --output ./test_results/report.junit
+endif
+
+smokexctestrun = $(shell find . -name '*_SmokeTests*.xctestrun')
+
+smoke_tests: test_setup
+ifneq ($(smokexctestrun),)
+# override ENV variables
+ plutil -replace TestConfigurations.0.TestTargets.0.EnvironmentVariables.RELAY_HOST -string $(RELAY_HOST) $(smokexctestrun)
+ plutil -replace TestConfigurations.0.TestTargets.0.EnvironmentVariables.PROJECT_ID -string $(PROJECT_ID) $(smokexctestrun)
+# test-without-building
+ set -o pipefail && env NSUnbufferedIO=YES xcodebuild -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath DerivedDataCache -resultBundlePath 'test_results/SmokeTests.xcresult' -xctestrun '$(smokexctestrun)' test-without-building | tee ./test_results/xcodebuild.log | xcpretty --report junit --output ./test_results/report.junit
+else
+ set -o pipefail && env NSUnbufferedIO=YES xcodebuild -project Example/ExampleApp.xcodeproj -scheme IntegrationTests -testPlan SmokeTests -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath DerivedDataCache -resultBundlePath 'test_results/SmokeTests.xcresult' RELAY_HOST='$(RELAY_HOST)' PROJECT_ID='$(PROJECT_ID)' test | tee ./test_results/xcodebuild.log | xcpretty --report junit --output ./test_results/report.junit
+endif
release_wallet:
fastlane release_testflight username:$(APPLE_ID) token:$(TOKEN) relay_host:$(RELAY_HOST) project_id:$(PROJECT_ID) --env WalletApp
diff --git a/Package.swift b/Package.swift
index 1800221e1..f475f4684 100644
--- a/Package.swift
+++ b/Package.swift
@@ -64,7 +64,7 @@ let package = Package(
path: "Sources/Web3Wallet"),
.target(
name: "WalletConnectPush",
- dependencies: ["WalletConnectPairing", "WalletConnectEcho", "WalletConnectNetworking", "WalletConnectIdentity"],
+ dependencies: ["WalletConnectPairing", "WalletConnectEcho", "WalletConnectNetworking", "WalletConnectIdentity", "WalletConnectSigner"],
path: "Sources/WalletConnectPush"),
.target(
name: "WalletConnectEcho",
@@ -84,7 +84,7 @@ let package = Package(
dependencies: ["WalletConnectNetworking"]),
.target(
name: "Web3Inbox",
- dependencies: ["WalletConnectChat"]),
+ dependencies: ["WalletConnectChat", "WalletConnectPush"]),
.target(
name: "WalletConnectSigner",
dependencies: ["WalletConnectNetworking"]),
diff --git a/Sources/WalletConnectJWT/JWTEncoder.swift b/Sources/WalletConnectJWT/JWTEncoder.swift
index f9a2d5425..fd2ec386f 100644
--- a/Sources/WalletConnectJWT/JWTEncoder.swift
+++ b/Sources/WalletConnectJWT/JWTEncoder.swift
@@ -13,17 +13,8 @@ struct JWTEncoder {
}
public static func base64urlDecodedData(string: String) throws -> Data {
- var base64 = string
- .replacingOccurrences(of: "-", with: "+")
- .replacingOccurrences(of: "_", with: "/")
-
- if base64.count % 4 != 0 {
- base64.append(String(repeating: "=", count: 4 - base64.count % 4))
- }
-
- guard let result = Data(base64Encoded: base64)
+ guard let result = Data(base64url: string)
else { throw JWTError.notBase64String }
-
return result
}
}
diff --git a/Sources/WalletConnectKMS/Crypto/CryptoKitWrapper/AgreementCryptoKit.swift b/Sources/WalletConnectKMS/Crypto/CryptoKitWrapper/AgreementCryptoKit.swift
index 01f0ed2a2..f5e0f906e 100644
--- a/Sources/WalletConnectKMS/Crypto/CryptoKitWrapper/AgreementCryptoKit.swift
+++ b/Sources/WalletConnectKMS/Crypto/CryptoKitWrapper/AgreementCryptoKit.swift
@@ -18,6 +18,9 @@ extension Curve25519.KeyAgreement.PrivateKey: Equatable {
// MARK: - Public Key
public struct AgreementPublicKey: GenericPasswordConvertible, Equatable {
+ enum Errors: Error {
+ case invalidBase64urlString
+ }
fileprivate let key: Curve25519.KeyAgreement.PublicKey
@@ -34,6 +37,11 @@ public struct AgreementPublicKey: GenericPasswordConvertible, Equatable {
try self.init(rawRepresentation: data)
}
+ public init(base64url: String) throws {
+ guard let raw = Data(base64url: base64url) else { throw Errors.invalidBase64urlString }
+ try self.init(rawRepresentation: raw)
+ }
+
public var rawRepresentation: Data {
key.rawRepresentation
}
diff --git a/Sources/WalletConnectNetworking/NetworkingInteractor.swift b/Sources/WalletConnectNetworking/NetworkingInteractor.swift
index ede99be10..1f8a7cebf 100644
--- a/Sources/WalletConnectNetworking/NetworkingInteractor.swift
+++ b/Sources/WalletConnectNetworking/NetworkingInteractor.swift
@@ -134,7 +134,7 @@ public class NetworkingInteractor: NetworkInteracting {
if let (deserializedJsonRpcRequest, derivedTopic, decryptedPayload): (RPCRequest, String?, Data) = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) {
handleRequest(topic: topic, request: deserializedJsonRpcRequest, decryptedPayload: decryptedPayload, publishedAt: publishedAt, derivedTopic: derivedTopic)
} else if let (response, derivedTopic, _): (RPCResponse, String?, Data) = serializer.tryDeserialize(topic: topic, encodedEnvelope: encodedEnvelope) {
- handleResponse(response: response, publishedAt: publishedAt, derivedTopic: derivedTopic)
+ handleResponse(topic: topic, response: response, publishedAt: publishedAt, derivedTopic: derivedTopic)
} else {
logger.debug("Networking Interactor - Received unknown object type from networking relay")
}
@@ -149,11 +149,11 @@ public class NetworkingInteractor: NetworkInteracting {
}
}
- private func handleResponse(response: RPCResponse, publishedAt: Date, derivedTopic: String?) {
+ private func handleResponse(topic: String, response: RPCResponse, publishedAt: Date, derivedTopic: String?) {
do {
try rpcHistory.resolve(response)
let record = rpcHistory.get(recordId: response.id!)!
- responsePublisherSubject.send((record.topic, record.request, response, publishedAt, derivedTopic))
+ responsePublisherSubject.send((topic, record.request, response, publishedAt, derivedTopic))
} catch {
logger.debug("Handle json rpc response error: \(error)")
}
diff --git a/Sources/WalletConnectPairing/Services/Wallet/WalletPairService.swift b/Sources/WalletConnectPairing/Services/Wallet/WalletPairService.swift
index 0ec9e485b..cfbd6c930 100644
--- a/Sources/WalletConnectPairing/Services/Wallet/WalletPairService.swift
+++ b/Sources/WalletConnectPairing/Services/Wallet/WalletPairService.swift
@@ -2,7 +2,7 @@ import Foundation
actor WalletPairService {
enum Errors: Error {
- case pairingAlreadyExist
+ case pairingAlreadyExist(topic: String)
}
let networkingInteractor: NetworkInteracting
@@ -19,7 +19,7 @@ actor WalletPairService {
func pair(_ uri: WalletConnectURI) async throws {
guard !hasPairing(for: uri.topic) else {
- throw Errors.pairingAlreadyExist
+ throw Errors.pairingAlreadyExist(topic: uri.topic)
}
var pairing = WCPairing(uri: uri)
let symKey = try SymmetricKey(hex: uri.symKey)
@@ -33,3 +33,12 @@ actor WalletPairService {
return pairingStorage.hasPairing(forTopic: topic)
}
}
+
+// MARK: - LocalizedError
+extension WalletPairService.Errors: LocalizedError {
+ var errorDescription: String? {
+ switch self {
+ case .pairingAlreadyExist(let topic): return "Pairing with topic (\(topic)) already exist"
+ }
+ }
+}
diff --git a/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift
index 603b81bbb..e131f075b 100644
--- a/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift
+++ b/Sources/WalletConnectPush/Client/Dapp/ProposalResponseSubscriber.swift
@@ -35,7 +35,7 @@ class ProposalResponseSubscriber {
private func subscribeForProposalResponse() {
let protocolMethod = PushRequestProtocolMethod()
networkingInteractor.responseSubscription(on: protocolMethod)
- .sink { [unowned self] (payload: ResponseSubscriptionPayload) in
+ .sink { [unowned self] (payload: ResponseSubscriptionPayload) in
logger.debug("Received Push Proposal response")
Task(priority: .userInitiated) {
do {
@@ -49,15 +49,16 @@ class ProposalResponseSubscriber {
}.store(in: &publishers)
}
- private func handleResponse(payload: ResponseSubscriptionPayload) async throws -> (PushSubscription, String) {
+ private func handleResponse(payload: ResponseSubscriptionPayload) async throws -> (PushSubscription, String) {
let jwt = payload.response.jwtString
- _ = try AcceptSubscriptionJWTPayload.decodeAndVerify(from: payload.response)
+ let (_, claims) = try SubscriptionJWTPayload.decodeAndVerify(from: payload.response)
logger.debug("subscriptionAuth JWT validated")
guard let subscriptionTopic = payload.derivedTopic else { throw Errors.subscriptionTopicNotDerived }
+ let expiry = Date(timeIntervalSince1970: TimeInterval(claims.exp))
- let pushSubscription = PushSubscription(topic: subscriptionTopic, account: payload.request.account, relay: relay, metadata: metadata)
+ let pushSubscription = PushSubscription(topic: subscriptionTopic, account: payload.request.account, relay: relay, metadata: metadata, scope: [:], expiry: expiry)
logger.debug("Subscribing to Push Subscription topic: \(subscriptionTopic)")
subscriptionsStore.set(pushSubscription, forKey: subscriptionTopic)
try await networkingInteractor.subscribe(topic: subscriptionTopic)
diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateRequester.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateRequester.swift
new file mode 100644
index 000000000..402d8084c
--- /dev/null
+++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateRequester.swift
@@ -0,0 +1,51 @@
+
+import Foundation
+
+class NotifyUpdateRequester {
+ enum Errors: Error {
+ case noSubscriptionForGivenTopic
+ }
+
+ private let keyserverURL: URL
+ private let identityClient: IdentityClient
+ private let networkingInteractor: NetworkInteracting
+ private let logger: ConsoleLogging
+ private let subscriptionsStore: CodableStore
+
+ init(keyserverURL: URL,
+ identityClient: IdentityClient,
+ networkingInteractor: NetworkInteracting,
+ logger: ConsoleLogging,
+ subscriptionsStore: CodableStore
+ ) {
+ self.keyserverURL = keyserverURL
+ self.identityClient = identityClient
+ self.networkingInteractor = networkingInteractor
+ self.logger = logger
+ self.subscriptionsStore = subscriptionsStore
+ }
+
+ func update(topic: String, scope: Set) async throws {
+
+ logger.debug("NotifyUpdateRequester: updating subscription for topic: \(topic)")
+ guard let subscription = try subscriptionsStore.get(key: topic) else { throw Errors.noSubscriptionForGivenTopic }
+
+ let request = try createJWTRequest(subscriptionAccount: subscription.account, dappUrl: subscription.metadata.url, scope: scope)
+
+ let protocolMethod = NotifyUpdateProtocolMethod()
+
+ try await networkingInteractor.request(request, topic: topic, protocolMethod: protocolMethod)
+ }
+
+ private func createJWTRequest(subscriptionAccount: Account, dappUrl: String, scope: Set) throws -> RPCRequest {
+ let protocolMethod = NotifyUpdateProtocolMethod().method
+ let scopeClaim = scope.map {$0.rawValue}.joined(separator: " ")
+ let jwtPayload = SubscriptionJWTPayload(keyserver: keyserverURL, subscriptionAccount: subscriptionAccount, dappUrl: dappUrl, scope: scopeClaim)
+ let wrapper = try identityClient.signAndCreateWrapper(
+ payload: jwtPayload,
+ account: subscriptionAccount
+ )
+ print(wrapper.subscriptionAuth)
+ return RPCRequest(method: protocolMethod, params: wrapper)
+ }
+}
diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift
new file mode 100644
index 000000000..5847d1185
--- /dev/null
+++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_notifyUpdate/NotifyUpdateResponseSubscriber.swift
@@ -0,0 +1,72 @@
+
+import Foundation
+import Combine
+
+class NotifyUpdateResponseSubscriber {
+ enum Errors: Error {
+ case subscriptionDoesNotExist
+ }
+
+ private let networkingInteractor: NetworkInteracting
+ private var publishers = [AnyCancellable]()
+ private let logger: ConsoleLogging
+ private let subscriptionsStore: CodableStore
+ private let subscriptionScopeProvider: SubscriptionScopeProvider
+ private var subscriptionPublisherSubject = PassthroughSubject, Never>()
+ var updateSubscriptionPublisher: AnyPublisher, Never> {
+ return subscriptionPublisherSubject.eraseToAnyPublisher()
+ }
+
+ init(networkingInteractor: NetworkInteracting,
+ logger: ConsoleLogging,
+ subscriptionScopeProvider: SubscriptionScopeProvider,
+ subscriptionsStore: CodableStore
+ ) {
+ self.networkingInteractor = networkingInteractor
+ self.logger = logger
+ self.subscriptionsStore = subscriptionsStore
+ self.subscriptionScopeProvider = subscriptionScopeProvider
+ subscribeForUpdateResponse()
+ }
+
+ private func subscribeForUpdateResponse() {
+ let protocolMethod = NotifyUpdateProtocolMethod()
+ networkingInteractor.responseSubscription(on: protocolMethod)
+ .sink {[unowned self] (payload: ResponseSubscriptionPayload) in
+ Task(priority: .high) {
+ logger.debug("Received Push Update response")
+
+ let subscriptionTopic = payload.topic
+
+ let (_, claims) = try SubscriptionJWTPayload.decodeAndVerify(from: payload.request)
+ let scope = try await buildScope(selected: claims.scp, dappUrl: claims.aud)
+
+ guard let oldSubscription = try? subscriptionsStore.get(key: subscriptionTopic) else {
+ logger.debug("NotifyUpdateResponseSubscriber Subscription does not exist")
+ subscriptionPublisherSubject.send(.failure(Errors.subscriptionDoesNotExist))
+ return
+ }
+ let expiry = Date(timeIntervalSince1970: TimeInterval(claims.exp))
+
+ let updatedSubscription = PushSubscription(topic: subscriptionTopic, account: oldSubscription.account, relay: oldSubscription.relay, metadata: oldSubscription.metadata, scope: scope, expiry: expiry)
+
+ subscriptionsStore.set(updatedSubscription, forKey: subscriptionTopic)
+
+ subscriptionPublisherSubject.send(.success(updatedSubscription))
+
+ logger.debug("Updated Subscription")
+ }
+ }.store(in: &publishers)
+ }
+
+ private func buildScope(selected: String, dappUrl: String) async throws -> [NotificationScope: ScopeValue] {
+ let selectedScope = selected
+ .components(separatedBy: " ")
+ .compactMap { NotificationScope(rawValue: $0) }
+
+ let availableScope = try await subscriptionScopeProvider.getSubscriptionScope(dappUrl: dappUrl)
+ return availableScope.reduce(into: [:]) { $0[$1.name] = ScopeValue(description: $1.description, enabled: selectedScope.contains($1.name)) }
+ }
+
+ // TODO: handle error response
+}
diff --git a/Sources/WalletConnectPush/Client/Wallet/PushMessageSubscriber.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushMessage/PushMessageSubscriber.swift
similarity index 100%
rename from Sources/WalletConnectPush/Client/Wallet/PushMessageSubscriber.swift
rename to Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushMessage/PushMessageSubscriber.swift
diff --git a/Sources/WalletConnectPush/Client/Wallet/PushRequestResponder.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift
similarity index 87%
rename from Sources/WalletConnectPush/Client/Wallet/PushRequestResponder.swift
rename to Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift
index ae7b83357..cfebe88a3 100644
--- a/Sources/WalletConnectPush/Client/Wallet/PushRequestResponder.swift
+++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushRequest/PushRequestResponder.swift
@@ -1,5 +1,6 @@
import WalletConnectNetworking
import WalletConnectIdentity
+import Combine
import Foundation
class PushRequestResponder {
@@ -17,6 +18,10 @@ class PushRequestResponder {
// Keychain shared with UNNotificationServiceExtension in order to decrypt PNs
private let groupKeychainStorage: KeychainStorageProtocol
+ private var subscriptionPublisherSubject = PassthroughSubject, Never>()
+ var subscriptionPublisher: AnyPublisher, Never> {
+ return subscriptionPublisherSubject.eraseToAnyPublisher()
+ }
init(keyserverURL: URL,
networkingInteractor: NetworkInteracting,
@@ -65,13 +70,16 @@ class PushRequestResponder {
let response = try createJWTResponse(requestId: requestId, subscriptionAccount: requestParams.account, dappUrl: requestParams.metadata.url)
- let pushSubscription = PushSubscription(topic: pushTopic, account: requestParams.account, relay: RelayProtocolOptions(protocol: "irn", data: nil), metadata: requestParams.metadata)
+ // will be changed in stage 2 refactor, this method will depricate
+ let expiry = Date()
+ let pushSubscription = PushSubscription(topic: pushTopic, account: requestParams.account, relay: RelayProtocolOptions(protocol: "irn", data: nil), metadata: requestParams.metadata, scope: [:], expiry: expiry)
subscriptionsStore.set(pushSubscription, forKey: pushTopic)
try await networkingInteractor.respond(topic: responseTopic, response: response, protocolMethod: PushRequestProtocolMethod(), envelopeType: .type1(pubKey: keys.publicKey.rawRepresentation))
kms.deletePrivateKey(for: keys.publicKey.hexRepresentation)
+ subscriptionPublisherSubject.send(.success(pushSubscription))
}
func respondError(requestId: RPCID) async throws {
@@ -83,7 +91,7 @@ class PushRequestResponder {
}
private func createJWTResponse(requestId: RPCID, subscriptionAccount: Account, dappUrl: String) throws -> RPCResponse {
- let jwtPayload = AcceptSubscriptionJWTPayload(keyserver: keyserverURL, subscriptionAccount: subscriptionAccount, dappUrl: dappUrl)
+ let jwtPayload = SubscriptionJWTPayload(keyserver: keyserverURL, subscriptionAccount: subscriptionAccount, dappUrl: dappUrl, scope: "v1")
let wrapper = try identityClient.signAndCreateWrapper(
payload: jwtPayload,
account: subscriptionAccount
diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift
new file mode 100644
index 000000000..49217c554
--- /dev/null
+++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeRequester.swift
@@ -0,0 +1,98 @@
+
+import Foundation
+
+class PushSubscribeRequester {
+
+ enum Errors: Error {
+ case didDocDoesNotContainKeyAgreement
+ case noVerificationMethodForKey
+ case unsupportedCurve
+ }
+
+ private let keyserverURL: URL
+ private let identityClient: IdentityClient
+ private let networkingInteractor: NetworkInteracting
+ private let kms: KeyManagementService
+ private let logger: ConsoleLogging
+ private let webDidResolver: WebDidResolver
+ private let dappsMetadataStore: CodableStore
+
+ init(keyserverURL: URL,
+ networkingInteractor: NetworkInteracting,
+ identityClient: IdentityClient,
+ logger: ConsoleLogging,
+ kms: KeyManagementService,
+ webDidResolver: WebDidResolver = WebDidResolver(),
+ dappsMetadataStore: CodableStore
+ ) {
+ self.keyserverURL = keyserverURL
+ self.identityClient = identityClient
+ self.networkingInteractor = networkingInteractor
+ self.logger = logger
+ self.kms = kms
+ self.webDidResolver = webDidResolver
+ self.dappsMetadataStore = dappsMetadataStore
+ }
+
+ func subscribe(metadata: AppMetadata, account: Account, onSign: @escaping SigningCallback) async throws {
+
+ let dappUrl = metadata.url
+
+ logger.debug("Subscribing for Push")
+
+ let peerPublicKey = try await resolvePublicKey(dappUrl: metadata.url)
+ let subscribeTopic = peerPublicKey.rawRepresentation.sha256().toHexString()
+
+ let keysY = try generateAgreementKeys(peerPublicKey: peerPublicKey)
+
+ let responseTopic = keysY.derivedTopic()
+
+ dappsMetadataStore.set(metadata, forKey: responseTopic)
+
+ try kms.setSymmetricKey(keysY.sharedKey, for: subscribeTopic)
+
+ _ = try await identityClient.register(account: account, onSign: onSign)
+
+ try kms.setAgreementSecret(keysY, topic: responseTopic)
+
+ logger.debug("setting symm key for response topic \(responseTopic)")
+
+ let request = try createJWTRequest(subscriptionAccount: account, dappUrl: dappUrl)
+
+ let protocolMethod = PushSubscribeProtocolMethod()
+
+ logger.debug("PushSubscribeRequester: subscribing to response topic: \(responseTopic)")
+
+ try await networkingInteractor.subscribe(topic: responseTopic)
+
+ try await networkingInteractor.request(request, topic: subscribeTopic, protocolMethod: protocolMethod, envelopeType: .type1(pubKey: keysY.publicKey.rawRepresentation))
+ }
+
+ private func resolvePublicKey(dappUrl: String) async throws -> AgreementPublicKey {
+ logger.debug("PushSubscribeRequester: Resolving DIDDoc for: \(dappUrl)")
+ let didDoc = try await webDidResolver.resolveDidDoc(domainUrl: dappUrl)
+ guard let keyAgreement = didDoc.keyAgreement.first else { throw Errors.didDocDoesNotContainKeyAgreement }
+ guard let verificationMethod = didDoc.verificationMethod.first(where: { verificationMethod in verificationMethod.id == keyAgreement }) else { throw Errors.noVerificationMethodForKey }
+ guard verificationMethod.publicKeyJwk.crv == .X25519 else { throw Errors.unsupportedCurve}
+ let pubKeyBase64Url = verificationMethod.publicKeyJwk.x
+ return try AgreementPublicKey(base64url: pubKeyBase64Url)
+ }
+
+
+ private func generateAgreementKeys(peerPublicKey: AgreementPublicKey) throws -> AgreementKeys {
+ let selfPubKey = try kms.createX25519KeyPair()
+
+ let keys = try kms.performKeyAgreement(selfPublicKey: selfPubKey, peerPublicKey: peerPublicKey.hexRepresentation)
+ return keys
+ }
+
+ private func createJWTRequest(subscriptionAccount: Account, dappUrl: String) throws -> RPCRequest {
+ let protocolMethod = PushSubscribeProtocolMethod().method
+ let jwtPayload = SubscriptionJWTPayload(keyserver: keyserverURL, subscriptionAccount: subscriptionAccount, dappUrl: dappUrl, scope: "")
+ let wrapper = try identityClient.signAndCreateWrapper(
+ payload: jwtPayload,
+ account: subscriptionAccount
+ )
+ return RPCRequest(method: protocolMethod, params: wrapper)
+ }
+}
diff --git a/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift
new file mode 100644
index 000000000..63e8e0fa3
--- /dev/null
+++ b/Sources/WalletConnectPush/Client/Wallet/ProtocolEngine/wc_pushSubscribe/PushSubscribeResponseSubscriber.swift
@@ -0,0 +1,100 @@
+
+import Foundation
+import Combine
+
+class PushSubscribeResponseSubscriber {
+ enum Errors: Error {
+ case couldNotCreateSubscription
+ }
+
+ private let networkingInteractor: NetworkInteracting
+ private let kms: KeyManagementServiceProtocol
+ private var publishers = [AnyCancellable]()
+ private let logger: ConsoleLogging
+ private let subscriptionsStore: CodableStore
+ private let groupKeychainStorage: KeychainStorageProtocol
+ private let dappsMetadataStore: CodableStore
+ private let subscriptionScopeProvider: SubscriptionScopeProvider
+ private var subscriptionPublisherSubject = PassthroughSubject, Never>()
+ var subscriptionPublisher: AnyPublisher, Never> {
+ return subscriptionPublisherSubject.eraseToAnyPublisher()
+ }
+
+ init(networkingInteractor: NetworkInteracting,
+ kms: KeyManagementServiceProtocol,
+ logger: ConsoleLogging,
+ groupKeychainStorage: KeychainStorageProtocol,
+ subscriptionsStore: CodableStore,
+ dappsMetadataStore: CodableStore,
+ subscriptionScopeProvider: SubscriptionScopeProvider
+ ) {
+ self.networkingInteractor = networkingInteractor
+ self.kms = kms
+ self.logger = logger
+ self.groupKeychainStorage = groupKeychainStorage
+ self.subscriptionsStore = subscriptionsStore
+ self.dappsMetadataStore = dappsMetadataStore
+ self.subscriptionScopeProvider = subscriptionScopeProvider
+ subscribeForSubscriptionResponse()
+ }
+
+ private func subscribeForSubscriptionResponse() {
+ let protocolMethod = PushSubscribeProtocolMethod()
+ networkingInteractor.responseSubscription(on: protocolMethod)
+ .sink {[unowned self] (payload: ResponseSubscriptionPayload) in
+ Task(priority: .high) {
+ logger.debug("Received Push Subscribe response")
+
+ guard let responseKeys = kms.getAgreementSecret(for: payload.topic) else {
+ logger.debug("PushSubscribeResponseSubscriber: no symmetric key for topic \(payload.topic)")
+ subscriptionPublisherSubject.send(.failure(Errors.couldNotCreateSubscription))
+ return
+ }
+
+ // get keypair Y
+ let pubKeyY = responseKeys.publicKey
+ let peerPubKeyZ = payload.response.publicKey
+
+ var account: Account!
+ var metadata: AppMetadata!
+ var pushSubscriptionTopic: String!
+ var availableScope: Set!
+ let (_, claims) = try SubscriptionJWTPayload.decodeAndVerify(from: payload.request)
+ do {
+ // generate symm key P
+ let agreementKeysP = try kms.performKeyAgreement(selfPublicKey: pubKeyY, peerPublicKey: peerPubKeyZ)
+ pushSubscriptionTopic = agreementKeysP.derivedTopic()
+ try kms.setAgreementSecret(agreementKeysP, topic: pushSubscriptionTopic)
+ try groupKeychainStorage.add(agreementKeysP, forKey: pushSubscriptionTopic)
+ account = try Account(DIDPKHString: claims.sub)
+ metadata = try dappsMetadataStore.get(key: payload.topic)
+ availableScope = try await subscriptionScopeProvider.getSubscriptionScope(dappUrl: metadata!.url)
+ logger.debug("PushSubscribeResponseSubscriber: subscribing push subscription topic: \(pushSubscriptionTopic!)")
+ try await networkingInteractor.subscribe(topic: pushSubscriptionTopic)
+ } catch {
+ logger.debug("PushSubscribeResponseSubscriber: error: \(error)")
+ subscriptionPublisherSubject.send(.failure(Errors.couldNotCreateSubscription))
+ return
+ }
+
+ guard let metadata = metadata else {
+ logger.debug("PushSubscribeResponseSubscriber: no metadata for topic: \(pushSubscriptionTopic)")
+ return
+ }
+ dappsMetadataStore.delete(forKey: payload.topic)
+ let expiry = Date(timeIntervalSince1970: TimeInterval(claims.exp))
+ let scope: [NotificationScope: ScopeValue] = availableScope.reduce(into: [:]) { $0[$1.name] = ScopeValue(description: $1.description, enabled: true) }
+ let pushSubscription = PushSubscription(topic: pushSubscriptionTopic, account: account, relay: RelayProtocolOptions(protocol: "irn", data: nil), metadata: metadata, scope: scope, expiry: expiry)
+
+ subscriptionsStore.set(pushSubscription, forKey: pushSubscriptionTopic)
+
+ logger.debug("PushSubscribeResponseSubscriber: unsubscribing response topic: \(payload.topic)")
+ networkingInteractor.unsubscribe(topic: payload.topic)
+ subscriptionPublisherSubject.send(.success(pushSubscription))
+ }
+ }.store(in: &publishers)
+ }
+
+ // TODO: handle error response
+
+}
diff --git a/Sources/WalletConnectPush/Client/Wallet/SubscriptionScopeProvider.swift b/Sources/WalletConnectPush/Client/Wallet/SubscriptionScopeProvider.swift
new file mode 100644
index 000000000..a2140addd
--- /dev/null
+++ b/Sources/WalletConnectPush/Client/Wallet/SubscriptionScopeProvider.swift
@@ -0,0 +1,22 @@
+
+import Foundation
+
+class SubscriptionScopeProvider {
+ enum Errors: Error {
+ case invalidUrl
+ }
+
+ private var cache = [String: Set]()
+
+ func getSubscriptionScope(dappUrl: String) async throws -> Set {
+ if let availableScope = cache[dappUrl] {
+ return availableScope
+ }
+ guard let scopeUrl = URL(string: "\(dappUrl)/.well-known/wc-push-config.json") else { throw Errors.invalidUrl }
+ let (data, _) = try await URLSession.shared.data(from: scopeUrl)
+ let config = try JSONDecoder().decode(NotificationConfig.self, from: data)
+ let availableScope = Set(config.types)
+ cache[dappUrl] = availableScope
+ return availableScope
+ }
+}
diff --git a/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift b/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift
index c7848a79e..2740cb252 100644
--- a/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift
+++ b/Sources/WalletConnectPush/Client/Wallet/WalletPushClient.swift
@@ -9,6 +9,13 @@ public class WalletPushClient {
private var publishers = Set()
+ /// publishes new subscriptions
+ public var subscriptionPublisher: AnyPublisher, Never> {
+ return subscriptionPublisherSubject.eraseToAnyPublisher()
+ }
+
+ public var subscriptionPublisherSubject = PassthroughSubject, Never>()
+
public var subscriptionsPublisher: AnyPublisher<[PushSubscription], Never> {
return pushSubscriptionsObserver.subscriptionsPublisher
}
@@ -33,8 +40,13 @@ public class WalletPushClient {
deleteSubscriptionPublisherSubject.eraseToAnyPublisher()
}
+ public var updateSubscriptionPublisher: AnyPublisher, Never> {
+ return notifyUpdateResponseSubscriber.updateSubscriptionPublisher
+ }
+
private let deletePushSubscriptionService: DeletePushSubscriptionService
private let deletePushSubscriptionSubscriber: DeletePushSubscriptionSubscriber
+ private let pushSubscribeRequester: PushSubscribeRequester
public let logger: ConsoleLogging
@@ -45,6 +57,9 @@ public class WalletPushClient {
private let subscriptionsProvider: SubscriptionsProvider
private let pushMessagesDatabase: PushMessagesDatabase
private let resubscribeService: PushResubscribeService
+ private let pushSubscribeResponseSubscriber: PushSubscribeResponseSubscriber
+ private let notifyUpdateRequester: NotifyUpdateRequester
+ private let notifyUpdateResponseSubscriber: NotifyUpdateResponseSubscriber
init(logger: ConsoleLogging,
kms: KeyManagementServiceProtocol,
@@ -57,7 +72,12 @@ public class WalletPushClient {
deletePushSubscriptionService: DeletePushSubscriptionService,
deletePushSubscriptionSubscriber: DeletePushSubscriptionSubscriber,
resubscribeService: PushResubscribeService,
- pushSubscriptionsObserver: PushSubscriptionsObserver) {
+ pushSubscriptionsObserver: PushSubscriptionsObserver,
+ pushSubscribeRequester: PushSubscribeRequester,
+ pushSubscribeResponseSubscriber: PushSubscribeResponseSubscriber,
+ notifyUpdateRequester: NotifyUpdateRequester,
+ notifyUpdateResponseSubscriber: NotifyUpdateResponseSubscriber
+ ) {
self.logger = logger
self.pairingRegisterer = pairingRegisterer
self.proposeResponder = proposeResponder
@@ -69,9 +89,17 @@ public class WalletPushClient {
self.deletePushSubscriptionSubscriber = deletePushSubscriptionSubscriber
self.resubscribeService = resubscribeService
self.pushSubscriptionsObserver = pushSubscriptionsObserver
+ self.pushSubscribeRequester = pushSubscribeRequester
+ self.pushSubscribeResponseSubscriber = pushSubscribeResponseSubscriber
+ self.notifyUpdateRequester = notifyUpdateRequester
+ self.notifyUpdateResponseSubscriber = notifyUpdateResponseSubscriber
setupSubscriptions()
}
+ public func subscribe(metadata: AppMetadata, account: Account, onSign: @escaping SigningCallback) async throws {
+ try await pushSubscribeRequester.subscribe(metadata: metadata, account: account, onSign: onSign)
+ }
+
public func approve(id: RPCID, onSign: @escaping SigningCallback) async throws {
try await proposeResponder.respond(requestId: id, onSign: onSign)
}
@@ -80,6 +108,10 @@ public class WalletPushClient {
try await proposeResponder.respondError(requestId: id)
}
+ public func update(topic: String, scope: Set) async throws {
+ try await notifyUpdateRequester.update(topic: topic, scope: scope)
+ }
+
public func getActiveSubscriptions() -> [PushSubscription] {
subscriptionsProvider.getActiveSubscriptions()
}
@@ -114,9 +146,18 @@ private extension WalletPushClient {
pushMessageSubscriber.onPushMessage = { [unowned self] pushMessageRecord in
pushMessagePublisherSubject.send(pushMessageRecord)
}
+
deletePushSubscriptionSubscriber.onDelete = {[unowned self] topic in
deleteSubscriptionPublisherSubject.send(topic)
}
+
+ pushSubscribeResponseSubscriber.subscriptionPublisher.sink { [unowned self] result in
+ subscriptionPublisherSubject.send(result)
+ }.store(in: &publishers)
+
+ proposeResponder.subscriptionPublisher.sink { [unowned self] result in
+ subscriptionPublisherSubject.send(result)
+ }.store(in: &publishers)
}
}
diff --git a/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift b/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift
index ee88eb8de..adec22410 100644
--- a/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift
+++ b/Sources/WalletConnectPush/Client/Wallet/WalletPushClientFactory.swift
@@ -6,7 +6,7 @@ import WalletConnectIdentity
public struct WalletPushClientFactory {
public static func create(networkInteractor: NetworkInteracting, pairingRegisterer: PairingRegisterer, echoClient: EchoClient) -> WalletPushClient {
- let logger = ConsoleLogger(loggingLevel: .debug)
+ let logger = ConsoleLogger(suffix: "🔔",loggingLevel: .debug)
let keyValueStorage = UserDefaults.standard
let keyserverURL = URL(string: "https://keys.walletconnect.com")!
let keychainStorage = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk")
@@ -52,6 +52,19 @@ public struct WalletPushClientFactory {
let deletePushSubscriptionSubscriber = DeletePushSubscriptionSubscriber(networkingInteractor: networkInteractor, kms: kms, logger: logger, pushSubscriptionStore: subscriptionStore)
let resubscribeService = PushResubscribeService(networkInteractor: networkInteractor, subscriptionsStorage: subscriptionStore)
let pushSubscriptionsObserver = PushSubscriptionsObserver(store: subscriptionStore)
+
+
+ let dappsMetadataStore = CodableStore(defaults: keyValueStorage, identifier: PushStorageIdntifiers.dappsMetadataStore)
+
+ let pushSubscribeRequester = PushSubscribeRequester(keyserverURL: keyserverURL, networkingInteractor: networkInteractor, identityClient: identityClient, logger: logger, kms: kms, dappsMetadataStore: dappsMetadataStore)
+
+ let subscriptionScopeProvider = SubscriptionScopeProvider()
+ let pushSubscribeResponseSubscriber = PushSubscribeResponseSubscriber(networkingInteractor: networkInteractor, kms: kms, logger: logger, groupKeychainStorage: groupKeychainStorage, subscriptionsStore: subscriptionStore, dappsMetadataStore: dappsMetadataStore, subscriptionScopeProvider: subscriptionScopeProvider)
+
+ let notifyUpdateRequester = NotifyUpdateRequester(keyserverURL: keyserverURL, identityClient: identityClient, networkingInteractor: networkInteractor, logger: logger, subscriptionsStore: subscriptionStore)
+
+ let notifyUpdateResponseSubscriber = NotifyUpdateResponseSubscriber(networkingInteractor: networkInteractor, logger: logger, subscriptionScopeProvider: subscriptionScopeProvider, subscriptionsStore: subscriptionStore)
+
return WalletPushClient(
logger: logger,
kms: kms,
@@ -64,7 +77,11 @@ public struct WalletPushClientFactory {
deletePushSubscriptionService: deletePushSubscriptionService,
deletePushSubscriptionSubscriber: deletePushSubscriptionSubscriber,
resubscribeService: resubscribeService,
- pushSubscriptionsObserver: pushSubscriptionsObserver
+ pushSubscriptionsObserver: pushSubscriptionsObserver,
+ pushSubscribeRequester: pushSubscribeRequester,
+ pushSubscribeResponseSubscriber: pushSubscribeResponseSubscriber,
+ notifyUpdateRequester: notifyUpdateRequester,
+ notifyUpdateResponseSubscriber: notifyUpdateResponseSubscriber
)
}
}
diff --git a/Sources/WalletConnectPush/Client/Wallet/WebDidResolver.swift b/Sources/WalletConnectPush/Client/Wallet/WebDidResolver.swift
new file mode 100644
index 000000000..f7e6617ad
--- /dev/null
+++ b/Sources/WalletConnectPush/Client/Wallet/WebDidResolver.swift
@@ -0,0 +1,15 @@
+
+import Foundation
+
+class WebDidResolver {
+
+ enum Errors: Error {
+ case invalidUrl
+ }
+
+ func resolveDidDoc(domainUrl: String) async throws -> WebDidDoc {
+ guard let didDocUrl = URL(string: "\(domainUrl)/.well-known/did.json") else { throw Errors.invalidUrl }
+ let (data, _) = try await URLSession.shared.data(from: didDocUrl)
+ return try JSONDecoder().decode(WebDidDoc.self, from: data)
+ }
+}
diff --git a/Sources/WalletConnectPush/ProtocolMethods/NotifyUpdateProtocolMethod.swift b/Sources/WalletConnectPush/ProtocolMethods/NotifyUpdateProtocolMethod.swift
new file mode 100644
index 000000000..198ec859f
--- /dev/null
+++ b/Sources/WalletConnectPush/ProtocolMethods/NotifyUpdateProtocolMethod.swift
@@ -0,0 +1,11 @@
+
+import Foundation
+
+struct NotifyUpdateProtocolMethod: ProtocolMethod {
+ let method: String = "wc_pushUpdate"
+
+ let requestConfig: RelayConfig = RelayConfig(tag: 4008, prompt: true, ttl: 86400)
+
+ let responseConfig: RelayConfig = RelayConfig(tag: 4009, prompt: true, ttl: 86400)
+}
+
diff --git a/Sources/WalletConnectPush/ProtocolMethods/PushMessageProtocolMethod.swift b/Sources/WalletConnectPush/ProtocolMethods/PushMessageProtocolMethod.swift
index 287b2a3bb..808817290 100644
--- a/Sources/WalletConnectPush/ProtocolMethods/PushMessageProtocolMethod.swift
+++ b/Sources/WalletConnectPush/ProtocolMethods/PushMessageProtocolMethod.swift
@@ -4,7 +4,7 @@ import WalletConnectPairing
struct PushMessageProtocolMethod: ProtocolMethod {
let method: String = "wc_pushMessage"
- let requestConfig: RelayConfig = RelayConfig(tag: 4002, prompt: true, ttl: 86400)
+ let requestConfig: RelayConfig = RelayConfig(tag: 4002, prompt: true, ttl: 2592000)
- let responseConfig: RelayConfig = RelayConfig(tag: 4003, prompt: true, ttl: 86400)
+ let responseConfig: RelayConfig = RelayConfig(tag: 4003, prompt: true, ttl: 2592000)
}
diff --git a/Sources/WalletConnectPush/ProtocolMethods/PushSubscribeProtocolMethod.swift b/Sources/WalletConnectPush/ProtocolMethods/PushSubscribeProtocolMethod.swift
new file mode 100644
index 000000000..a101dc0e5
--- /dev/null
+++ b/Sources/WalletConnectPush/ProtocolMethods/PushSubscribeProtocolMethod.swift
@@ -0,0 +1,10 @@
+
+import Foundation
+
+struct PushSubscribeProtocolMethod: ProtocolMethod {
+ let method: String = "wc_pushSubscribe"
+
+ let requestConfig: RelayConfig = RelayConfig(tag: 4006, prompt: true, ttl: 86400)
+
+ let responseConfig: RelayConfig = RelayConfig(tag: 4007, prompt: true, ttl: 86400)
+}
diff --git a/Sources/WalletConnectPush/PushStorageIdntifiers.swift b/Sources/WalletConnectPush/PushStorageIdntifiers.swift
index 5dd0f0a53..57ac3611c 100644
--- a/Sources/WalletConnectPush/PushStorageIdntifiers.swift
+++ b/Sources/WalletConnectPush/PushStorageIdntifiers.swift
@@ -3,4 +3,5 @@ import Foundation
enum PushStorageIdntifiers {
static let pushSubscription = "com.walletconnect.sdk.pushSbscription"
static let pushMessagesRecords = "com.walletconnect.sdk.pushMessagesRecords"
+ static let dappsMetadataStore = "com.walletconnect.sdk.dappsMetadataStore"
}
diff --git a/Sources/WalletConnectPush/RPCRequests/SubscribeResponseParams.swift b/Sources/WalletConnectPush/RPCRequests/SubscribeResponseParams.swift
new file mode 100644
index 000000000..51e75148f
--- /dev/null
+++ b/Sources/WalletConnectPush/RPCRequests/SubscribeResponseParams.swift
@@ -0,0 +1,6 @@
+
+import Foundation
+
+struct SubscribeResponseParams: Codable {
+ let publicKey: String
+}
diff --git a/Sources/WalletConnectPush/RPCRequests/AcceptSubscriptionJWTPayload.swift b/Sources/WalletConnectPush/RPCRequests/SubscriptionJWTPayload.swift
similarity index 83%
rename from Sources/WalletConnectPush/RPCRequests/AcceptSubscriptionJWTPayload.swift
rename to Sources/WalletConnectPush/RPCRequests/SubscriptionJWTPayload.swift
index 5594aec83..d45ac558b 100644
--- a/Sources/WalletConnectPush/RPCRequests/AcceptSubscriptionJWTPayload.swift
+++ b/Sources/WalletConnectPush/RPCRequests/SubscriptionJWTPayload.swift
@@ -1,6 +1,6 @@
import Foundation
-struct AcceptSubscriptionJWTPayload: JWTClaimsCodable {
+struct SubscriptionJWTPayload: JWTClaimsCodable {
struct Claims: JWTClaims {
/// timestamp when jwt was issued
@@ -17,6 +17,8 @@ struct AcceptSubscriptionJWTPayload: JWTClaimsCodable {
let sub: String
/// description of action intent. Must be equal to "push_subscription"
let act: String
+
+ let scp: String
}
struct Wrapper: JWTWrapper {
@@ -34,28 +36,33 @@ struct AcceptSubscriptionJWTPayload: JWTClaimsCodable {
let keyserver: URL
let subscriptionAccount: Account
let dappUrl: String
+ let scope: String
- init(keyserver: URL, subscriptionAccount: Account, dappUrl: String) {
+ init(keyserver: URL, subscriptionAccount: Account, dappUrl: String, scope: String) {
self.keyserver = keyserver
self.subscriptionAccount = subscriptionAccount
self.dappUrl = dappUrl
+ self.scope = scope
}
init(claims: Claims) throws {
self.keyserver = try claims.ksu.asURL()
self.subscriptionAccount = try Account(DIDPKHString: claims.sub)
self.dappUrl = claims.aud
+ self.scope = claims.scp
}
func encode(iss: String) throws -> Claims {
return Claims(
- iat: expiry(days: 1),
- exp: defaultIatMilliseconds(),
+ iat: defaultIatMilliseconds(),
+ exp: expiry(days: 30),
iss: iss,
ksu: keyserver.absoluteString,
aud: dappUrl,
sub: subscriptionAccount.did,
- act: "push_subscription"
+ act: "push_subscription",
+ scp: scope
)
}
}
+
diff --git a/Sources/WalletConnectPush/Types/NotificationConfig.swift b/Sources/WalletConnectPush/Types/NotificationConfig.swift
new file mode 100644
index 000000000..c53e9b674
--- /dev/null
+++ b/Sources/WalletConnectPush/Types/NotificationConfig.swift
@@ -0,0 +1,9 @@
+
+import Foundation
+
+struct NotificationConfig: Codable {
+ let version: Int
+ let lastModified: TimeInterval
+ let types: [NotificationType]
+
+}
diff --git a/Sources/WalletConnectPush/Types/NotificationScope.swift b/Sources/WalletConnectPush/Types/NotificationScope.swift
new file mode 100644
index 000000000..b4c74e4be
--- /dev/null
+++ b/Sources/WalletConnectPush/Types/NotificationScope.swift
@@ -0,0 +1,9 @@
+
+import Foundation
+
+public enum NotificationScope: String, Hashable, Codable, CodingKeyRepresentable, CaseIterable {
+ case promotional
+ case transactional
+ case `private`
+ case alerts
+}
diff --git a/Sources/WalletConnectPush/Types/NotificationType.swift b/Sources/WalletConnectPush/Types/NotificationType.swift
new file mode 100644
index 000000000..0e6b2df35
--- /dev/null
+++ b/Sources/WalletConnectPush/Types/NotificationType.swift
@@ -0,0 +1,7 @@
+
+import Foundation
+
+public struct NotificationType: Codable, Hashable {
+ let name: NotificationScope
+ let description: String
+}
diff --git a/Sources/WalletConnectPush/Types/PushMessage.swift b/Sources/WalletConnectPush/Types/PushMessage.swift
index eb32df560..d48604937 100644
--- a/Sources/WalletConnectPush/Types/PushMessage.swift
+++ b/Sources/WalletConnectPush/Types/PushMessage.swift
@@ -5,11 +5,13 @@ public struct PushMessage: Codable, Equatable {
public let body: String
public let icon: String
public let url: String
+ public let type: String?
- public init(title: String, body: String, icon: String, url: String) {
+ public init(title: String, body: String, icon: String, url: String, type: String? = nil) {
self.title = title
self.body = body
self.icon = icon
self.url = url
+ self.type = type
}
}
diff --git a/Sources/WalletConnectPush/Types/PushSubscription.swift b/Sources/WalletConnectPush/Types/PushSubscription.swift
index 783ec3dd1..57522f2d8 100644
--- a/Sources/WalletConnectPush/Types/PushSubscription.swift
+++ b/Sources/WalletConnectPush/Types/PushSubscription.swift
@@ -7,4 +7,11 @@ public struct PushSubscription: Codable, Equatable {
public let account: Account
public let relay: RelayProtocolOptions
public let metadata: AppMetadata
+ public let scope: [NotificationScope: ScopeValue]
+ public let expiry: Date
+}
+
+public struct ScopeValue: Codable, Equatable {
+ let description: String
+ let enabled: Bool
}
diff --git a/Sources/WalletConnectPush/Types/WebDidDoc.swift b/Sources/WalletConnectPush/Types/WebDidDoc.swift
new file mode 100644
index 000000000..28f301773
--- /dev/null
+++ b/Sources/WalletConnectPush/Types/WebDidDoc.swift
@@ -0,0 +1,36 @@
+
+import Foundation
+
+// MARK: - WebDidDoc
+struct WebDidDoc: Codable {
+ let context: [String]
+ let id: String
+ let verificationMethod: [VerificationMethod]
+ let authentication: [String]?
+ let keyAgreement: [String]
+
+ enum CodingKeys: String, CodingKey {
+ case context = "@context"
+ case id, verificationMethod, authentication, keyAgreement
+ }
+}
+extension WebDidDoc {
+
+ struct VerificationMethod: Codable {
+ let id: String
+ let type: String
+ let controller: String
+ let publicKeyJwk: PublicKeyJwk
+ }
+
+ struct PublicKeyJwk: Codable {
+ enum Curve: String, Codable {
+ case X25519 = "X25519"
+ }
+ let kty: String
+
+ let crv: Curve
+ /// The x member contains the x coordinate for the elliptic curve point. It is represented as the base64url encoding of the coordinate's big endian representation.
+ let x: String
+ }
+}
diff --git a/Sources/WalletConnectRelay/Misc/NetworkError.swift b/Sources/WalletConnectRelay/Misc/NetworkError.swift
index 62a384ff0..bb96055a5 100644
--- a/Sources/WalletConnectRelay/Misc/NetworkError.swift
+++ b/Sources/WalletConnectRelay/Misc/NetworkError.swift
@@ -1,11 +1,16 @@
+import Foundation
+
enum NetworkError: Error {
case webSocketNotConnected
case sendMessageFailed(Error)
case receiveMessageFailure(Error)
}
-extension NetworkError {
-
+extension NetworkError: LocalizedError {
+ var errorDescription: String? {
+ return localizedDescription
+ }
+
var localizedDescription: String {
switch self {
case .webSocketNotConnected:
diff --git a/Sources/WalletConnectRelay/PackageConfig.json b/Sources/WalletConnectRelay/PackageConfig.json
index 7fce1d9ca..90a323c7b 100644
--- a/Sources/WalletConnectRelay/PackageConfig.json
+++ b/Sources/WalletConnectRelay/PackageConfig.json
@@ -1 +1 @@
-{"version": "1.5.14"}
+{"version": "1.6.0"}
diff --git a/Sources/WalletConnectRelay/RelayClient.swift b/Sources/WalletConnectRelay/RelayClient.swift
index 59846a852..6831041bb 100644
--- a/Sources/WalletConnectRelay/RelayClient.swift
+++ b/Sources/WalletConnectRelay/RelayClient.swift
@@ -77,7 +77,7 @@ public final class RelayClient {
keychainStorage: KeychainStorageProtocol = KeychainStorage(serviceIdentifier: "com.walletconnect.sdk"),
socketFactory: WebSocketFactory,
socketConnectionType: SocketConnectionType = .automatic,
- logger: ConsoleLogging = ConsoleLogger(loggingLevel: .off)
+ logger: ConsoleLogging = ConsoleLogger(loggingLevel: .debug)
) {
let clientIdStorage = ClientIdStorage(keychain: keychainStorage)
let socketAuthenticator = SocketAuthenticator(
diff --git a/Sources/WalletConnectSign/Namespace.swift b/Sources/WalletConnectSign/Namespace.swift
index 5e398757b..43ba9518a 100644
--- a/Sources/WalletConnectSign/Namespace.swift
+++ b/Sources/WalletConnectSign/Namespace.swift
@@ -1,4 +1,4 @@
-enum AutoNamespacesError: Error {
+public enum AutoNamespacesError: Error {
case requiredChainsNotSatisfied
case requiredAccountsNotSatisfied
case requiredMethodsNotSatisfied
diff --git a/Sources/WalletConnectUtils/Extensions/Data.swift b/Sources/WalletConnectUtils/Extensions/Data.swift
index 88624109f..cb2ffb6c8 100644
--- a/Sources/WalletConnectUtils/Extensions/Data.swift
+++ b/Sources/WalletConnectUtils/Extensions/Data.swift
@@ -53,4 +53,15 @@ extension Data {
public func toHexString() -> String {
return map({ String(format: "%02x", $0) }).joined()
}
+
+ public init?(base64url: String) {
+ var base64 = base64url
+ .replacingOccurrences(of: "-", with: "+")
+ .replacingOccurrences(of: "_", with: "/")
+
+ if base64.count % 4 != 0 {
+ base64.append(String(repeating: "=", count: 4 - base64.count % 4))
+ }
+ self.init(base64Encoded: base64)
+ }
}
diff --git a/Sources/WalletConnectVerify/AttestChallengeProvider.swift b/Sources/WalletConnectVerify/AttestChallengeProvider.swift
index 5d2891e67..ac9a0eaa3 100644
--- a/Sources/WalletConnectVerify/AttestChallengeProvider.swift
+++ b/Sources/WalletConnectVerify/AttestChallengeProvider.swift
@@ -6,7 +6,6 @@ protocol AttestChallengeProviding {
class AttestChallengeProvider: AttestChallengeProviding {
func getChallenge() async throws -> Data {
- return Data()
fatalError("not implemented")
}
}
diff --git a/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift b/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift
index 1363b0c6a..ef51bd3b4 100644
--- a/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift
+++ b/Sources/Web3Inbox/ChatClient/ChatClientProxy.swift
@@ -13,7 +13,7 @@ final class ChatClientProxy {
}
func request(_ request: RPCRequest) async throws {
- guard let event = WebViewEvent(rawValue: request.method)
+ guard let event = ChatWebViewEvent(rawValue: request.method)
else { throw Errors.unregisteredMethod }
switch event {
diff --git a/Sources/Web3Inbox/ConfigParam.swift b/Sources/Web3Inbox/ConfigParam.swift
new file mode 100644
index 000000000..429be268d
--- /dev/null
+++ b/Sources/Web3Inbox/ConfigParam.swift
@@ -0,0 +1,8 @@
+
+import Foundation
+
+public enum ConfigParam {
+ case chatEnabled
+ case pushEnabled
+ case settingsEnabled
+}
diff --git a/Sources/Web3Inbox/PushClientProxy/PushClientProxy.swift b/Sources/Web3Inbox/PushClientProxy/PushClientProxy.swift
new file mode 100644
index 000000000..c64b22f0d
--- /dev/null
+++ b/Sources/Web3Inbox/PushClientProxy/PushClientProxy.swift
@@ -0,0 +1,104 @@
+import Foundation
+
+final class PushClientProxy {
+
+ private let client: WalletPushClient
+
+ var onSign: SigningCallback
+ var onResponse: ((RPCResponse) async throws -> Void)?
+
+ init(client: WalletPushClient, onSign: @escaping SigningCallback) {
+ self.client = client
+ self.onSign = onSign
+ }
+
+ func request(_ request: RPCRequest) async throws {
+ guard let event = PushWebViewEvent(rawValue: request.method)
+ else { throw Errors.unregisteredMethod }
+
+ switch event {
+ case .approve:
+ let params = try parse(ApproveRequest.self, params: request.params)
+ try await client.approve(id: params.id, onSign: onSign)
+ try await respond(request: request)
+ case .update:
+ let params = try parse(UpdateRequest.self, params: request.params)
+ try await client.update(topic: params.topic, scope: params.scope)
+ try await respond(request: request)
+ case .reject:
+ let params = try parse(RejectRequest.self, params: request.params)
+ try await client.reject(id: params.id)
+ try await respond(request: request)
+ case .subscribe:
+ let params = try parse(SubscribeRequest.self, params: request.params)
+ try await client.subscribe(metadata: params.metadata, account: params.account, onSign: onSign)
+ try await respond(request: request)
+ case .getActiveSubscriptions:
+ let subscriptions = client.getActiveSubscriptions()
+ try await respond(with: subscriptions, request: request)
+ case .getMessageHistory:
+ let params = try parse(GetMessageHistoryRequest.self, params: request.params)
+ let messages = client.getMessageHistory(topic: params.topic)
+ try await respond(with: messages, request: request)
+ case .deleteSubscription:
+ let params = try parse(DeleteSubscriptionRequest.self, params: request.params)
+ try await client.deleteSubscription(topic: params.topic)
+ try await respond(request: request)
+ case .deletePushMessage:
+ let params = try parse(DeletePushMessageRequest.self, params: request.params)
+ client.deletePushMessage(id: params.id)
+ try await respond(request: request)
+ }
+ }
+}
+
+private extension PushClientProxy {
+
+ private typealias Blob = Dictionary
+
+ enum Errors: Error {
+ case unregisteredMethod
+ case unregisteredParams
+ }
+
+ struct ApproveRequest: Codable {
+ let id: RPCID
+ }
+
+ struct UpdateRequest: Codable {
+ let topic: String
+ let scope: Set
+ }
+
+ struct RejectRequest: Codable {
+ let id: RPCID
+ }
+
+ struct SubscribeRequest: Codable {
+ let metadata: AppMetadata
+ let account: Account
+ }
+
+ struct GetMessageHistoryRequest: Codable {
+ let topic: String
+ }
+
+ struct DeleteSubscriptionRequest: Codable {
+ let topic: String
+ }
+
+ struct DeletePushMessageRequest: Codable {
+ let id: String
+ }
+
+ func parse(_ type: Request.Type, params: AnyCodable?) throws -> Request {
+ guard let params = try params?.get(Request.self)
+ else { throw Errors.unregisteredParams }
+ return params
+ }
+
+ func respond(with object: Object = Blob(), request: RPCRequest) async throws {
+ let response = RPCResponse(matchingRequest: request, result: object)
+ try await onResponse?(response)
+ }
+}
diff --git a/Sources/Web3Inbox/PushClientProxy/PushClientRequest.swift b/Sources/Web3Inbox/PushClientProxy/PushClientRequest.swift
new file mode 100644
index 000000000..e03e3378c
--- /dev/null
+++ b/Sources/Web3Inbox/PushClientProxy/PushClientRequest.swift
@@ -0,0 +1,13 @@
+import Foundation
+
+enum PushClientRequest: String {
+ case pushRequest = "push_request"
+ case pushMessage = "push_message"
+ case pushUpdate = "push_update"
+ case pushDelete = "push_delete"
+ case pushSubscription = "push_subscription"
+
+ var method: String {
+ return rawValue
+ }
+}
diff --git a/Sources/Web3Inbox/PushClientProxy/PushClientRequestSubscriber.swift b/Sources/Web3Inbox/PushClientProxy/PushClientRequestSubscriber.swift
new file mode 100644
index 000000000..7cb07c45a
--- /dev/null
+++ b/Sources/Web3Inbox/PushClientProxy/PushClientRequestSubscriber.swift
@@ -0,0 +1,78 @@
+import Foundation
+import Combine
+
+final class PushClientRequestSubscriber {
+
+ private var publishers: Set = []
+
+ private let client: WalletPushClient
+ private let logger: ConsoleLogging
+
+ var onRequest: ((RPCRequest) async throws -> Void)?
+
+ init(client: WalletPushClient, logger: ConsoleLogging) {
+ self.client = client
+ self.logger = logger
+
+ setupSubscriptions()
+ }
+
+ func setupSubscriptions() {
+ client.requestPublisher.sink { [unowned self] id, account, metadata in
+ let params = RequestPayload(id: id, account: account, metadata: metadata)
+ handle(event: .pushRequest, params: params)
+ }.store(in: &publishers)
+
+ client.pushMessagePublisher.sink { [unowned self] record in
+ handle(event: .pushMessage, params: record)
+ }.store(in: &publishers)
+
+ client.deleteSubscriptionPublisher.sink { [unowned self] record in
+ handle(event: .pushDelete, params: record)
+ }.store(in: &publishers)
+
+ client.subscriptionPublisher.sink { [unowned self] record in
+ switch record {
+ case .success(let subscription):
+ handle(event: .pushSubscription, params: subscription)
+ case .failure:
+ //TODO - handle error
+ break
+
+ }
+ }.store(in: &publishers)
+
+ client.updateSubscriptionPublisher.sink { [unowned self] record in
+ switch record {
+ case .success(let subscription):
+ handle(event: .pushUpdate, params: subscription)
+ case .failure:
+ //TODO - handle error
+ break
+ }
+ }.store(in: &publishers)
+ }
+}
+
+private extension PushClientRequestSubscriber {
+
+ struct RequestPayload: Codable {
+ let id: RPCID
+ let account: Account
+ let metadata: AppMetadata
+ }
+
+ func handle(event: PushClientRequest, params: Codable) {
+ Task {
+ do {
+ let request = RPCRequest(
+ method: event.method,
+ params: params
+ )
+ try await onRequest?(request)
+ } catch {
+ logger.error("Client Request error: \(error.localizedDescription)")
+ }
+ }
+ }
+}
diff --git a/Sources/Web3Inbox/Web3Inbox.swift b/Sources/Web3Inbox/Web3Inbox.swift
index 1582c1ed1..32a1a87fb 100644
--- a/Sources/Web3Inbox/Web3Inbox.swift
+++ b/Sources/Web3Inbox/Web3Inbox.swift
@@ -4,13 +4,14 @@ public final class Web3Inbox {
/// Web3Inbox client instance
public static var instance: Web3InboxClient = {
- guard let account, let onSign else {
+ guard let account, let config = config, let onSign else {
fatalError("Error - you must call Web3Inbox.configure(_:) before accessing the shared instance.")
}
- return Web3InboxClientFactory.create(chatClient: Chat.instance, account: account, onSign: onSign)
+ return Web3InboxClientFactory.create(chatClient: Chat.instance, pushClient: Push.wallet, account: account, config: config, onSign: onSign)
}()
private static var account: Account?
+ private static var config: [ConfigParam: Bool]?
private static var onSign: SigningCallback?
private init() { }
@@ -18,9 +19,11 @@ public final class Web3Inbox {
/// Web3Inbox instance config method
/// - Parameters:
/// - account: Web3Inbox initial account
- static public func configure(account: Account, onSign: @escaping SigningCallback) {
+ static public func configure(account: Account, config: [ConfigParam: Bool] = [:], onSign: @escaping SigningCallback, environment: APNSEnvironment) {
Web3Inbox.account = account
+ Web3Inbox.config = config
Web3Inbox.onSign = onSign
Chat.configure()
+ Push.configure(environment: environment)
}
}
diff --git a/Sources/Web3Inbox/Web3InboxClient.swift b/Sources/Web3Inbox/Web3InboxClient.swift
index 1fbb15bd4..730180633 100644
--- a/Sources/Web3Inbox/Web3InboxClient.swift
+++ b/Sources/Web3Inbox/Web3InboxClient.swift
@@ -7,28 +7,42 @@ public final class Web3InboxClient {
private var account: Account
private let logger: ConsoleLogging
- private let clientProxy: ChatClientProxy
- private let clientSubscriber: ChatClientRequestSubscriber
+ private let chatClientProxy: ChatClientProxy
+ private let chatClientSubscriber: ChatClientRequestSubscriber
- private let webviewProxy: WebViewProxy
- private let webviewSubscriber: WebViewRequestSubscriber
+ private let pushClientProxy: PushClientProxy
+ private let pushClientSubscriber: PushClientRequestSubscriber
- init(
+ private let chatWebviewProxy: WebViewProxy
+ private let pushWebviewProxy: WebViewProxy
+
+ private let chatWebviewSubscriber: WebViewRequestSubscriber
+ private let pushWebviewSubscriber: WebViewRequestSubscriber
+
+init(
webView: WKWebView,
account: Account,
logger: ConsoleLogging,
- clientProxy: ChatClientProxy,
+ chatClientProxy: ChatClientProxy,
clientSubscriber: ChatClientRequestSubscriber,
- webviewProxy: WebViewProxy,
- webviewSubscriber: WebViewRequestSubscriber
+ chatWebviewProxy: WebViewProxy,
+ pushWebviewProxy: WebViewProxy,
+ chatWebviewSubscriber: WebViewRequestSubscriber,
+ pushWebviewSubscriber: WebViewRequestSubscriber,
+ pushClientProxy: PushClientProxy,
+ pushClientSubscriber: PushClientRequestSubscriber
) {
self.webView = webView
self.account = account
self.logger = logger
- self.clientProxy = clientProxy
- self.clientSubscriber = clientSubscriber
- self.webviewProxy = webviewProxy
- self.webviewSubscriber = webviewSubscriber
+ self.chatClientProxy = chatClientProxy
+ self.chatClientSubscriber = clientSubscriber
+ self.chatWebviewProxy = chatWebviewProxy
+ self.pushWebviewProxy = pushWebviewProxy
+ self.chatWebviewSubscriber = chatWebviewSubscriber
+ self.pushWebviewSubscriber = pushWebviewSubscriber
+ self.pushClientProxy = pushClientProxy
+ self.pushClientSubscriber = pushClientSubscriber
setupSubscriptions()
}
@@ -41,7 +55,7 @@ public final class Web3InboxClient {
_ account: Account,
onSign: @escaping SigningCallback
) async throws {
- clientProxy.onSign = onSign
+ chatClientProxy.onSign = onSign
try await authorize(account: account)
}
}
@@ -51,14 +65,35 @@ public final class Web3InboxClient {
private extension Web3InboxClient {
func setupSubscriptions() {
- webviewSubscriber.onRequest = { [unowned self] request in
- try await self.clientProxy.request(request)
+
+ // Chat
+
+ chatClientProxy.onResponse = { [unowned self] response in
+ try await self.chatWebviewProxy.respond(response)
+ }
+
+ chatClientSubscriber.onRequest = { [unowned self] request in
+ try await self.chatWebviewProxy.request(request)
+ }
+
+ chatWebviewSubscriber.onRequest = { [unowned self] request in
+ logger.debug("w3i: chat method \(request.method) requested")
+ try await self.chatClientProxy.request(request)
+ }
+
+ // Push
+
+ pushClientProxy.onResponse = { [unowned self] response in
+ try await self.pushWebviewProxy.respond(response)
}
- clientProxy.onResponse = { [unowned self] response in
- try await self.webviewProxy.respond(response)
+
+ pushClientSubscriber.onRequest = { [unowned self] request in
+ try await self.pushWebviewProxy.request(request)
}
- clientSubscriber.onRequest = { [unowned self] request in
- try await self.webviewProxy.request(request)
+
+ pushWebviewSubscriber.onRequest = { [unowned self] request in
+ logger.debug("w3i: push method \(request.method) requested")
+ try await self.pushClientProxy.request(request)
}
}
@@ -69,6 +104,6 @@ private extension Web3InboxClient {
method: ChatClientRequest.setAccount.method,
params: ["account": account.address]
)
- try await webviewProxy.request(request)
+ try await chatWebviewProxy.request(request)
}
}
diff --git a/Sources/Web3Inbox/Web3InboxClientFactory.swift b/Sources/Web3Inbox/Web3InboxClientFactory.swift
index 76c01863c..50d255e29 100644
--- a/Sources/Web3Inbox/Web3InboxClientFactory.swift
+++ b/Sources/Web3Inbox/Web3InboxClientFactory.swift
@@ -5,29 +5,48 @@ final class Web3InboxClientFactory {
static func create(
chatClient: ChatClient,
+ pushClient: WalletPushClient,
account: Account,
+ config: [ConfigParam: Bool],
onSign: @escaping SigningCallback
) -> Web3InboxClient {
- let host = hostUrlString(account: account)
- let logger = ConsoleLogger(suffix: "📬")
- let webviewSubscriber = WebViewRequestSubscriber(logger: logger)
- let webView = WebViewFactory(host: host, webviewSubscriber: webviewSubscriber).create()
- let webViewProxy = WebViewProxy(webView: webView)
+ let url = buildUrl(account: account, config: config)
+ let logger = ConsoleLogger(suffix: "📬", loggingLevel: .debug)
+ let chatWebviewSubscriber = WebViewRequestSubscriber(logger: logger)
+ let pushWebviewSubscriber = WebViewRequestSubscriber(logger: logger)
+ let webView = WebViewFactory(url: url, chatWebviewSubscriber: chatWebviewSubscriber, pushWebviewSubscriber: pushWebviewSubscriber).create()
+ let chatWebViewProxy = WebViewProxy(webView: webView, scriptFormatter: ChatWebViewScriptFormatter(), logger: logger)
+ let pushWebViewProxy = WebViewProxy(webView: webView, scriptFormatter: PushWebViewScriptFormatter(), logger: logger)
+
let clientProxy = ChatClientProxy(client: chatClient, onSign: onSign)
let clientSubscriber = ChatClientRequestSubscriber(chatClient: chatClient, logger: logger)
+ let pushClientProxy = PushClientProxy(client: pushClient, onSign: onSign)
+ let pushClientSubscriber = PushClientRequestSubscriber(client: pushClient, logger: logger)
+
return Web3InboxClient(
webView: webView,
account: account,
- logger: ConsoleLogger(),
- clientProxy: clientProxy,
+ logger: logger,
+ chatClientProxy: clientProxy,
clientSubscriber: clientSubscriber,
- webviewProxy: webViewProxy,
- webviewSubscriber: webviewSubscriber
+ chatWebviewProxy: chatWebViewProxy,
+ pushWebviewProxy: pushWebViewProxy,
+ chatWebviewSubscriber: chatWebviewSubscriber,
+ pushWebviewSubscriber: pushWebviewSubscriber,
+ pushClientProxy: pushClientProxy,
+ pushClientSubscriber: pushClientSubscriber
)
}
- private static func hostUrlString(account: Account) -> String {
- return "https://web3inbox-dev-hidden.vercel.app/?chatProvider=ios&account=\(account.address)"
+ private static func buildUrl(account: Account, config: [ConfigParam: Bool]) -> URL {
+ var urlComponents = URLComponents(string: "https://web3inbox-dev-hidden.vercel.app/")!
+ var queryItems = [URLQueryItem(name: "chatProvider", value: "ios"), URLQueryItem(name: "pushProvider", value: "ios"), URLQueryItem(name: "account", value: account.address)]
+
+ for param in config.filter({ $0.value == false}) {
+ queryItems.append(URLQueryItem(name: "\(param.key)", value: "false"))
+ }
+ urlComponents.queryItems = queryItems
+ return urlComponents.url!
}
}
diff --git a/Sources/Web3Inbox/Web3InboxImports.swift b/Sources/Web3Inbox/Web3InboxImports.swift
index 54b421e88..fd78f4977 100644
--- a/Sources/Web3Inbox/Web3InboxImports.swift
+++ b/Sources/Web3Inbox/Web3InboxImports.swift
@@ -1,3 +1,4 @@
#if !CocoaPods
@_exported import WalletConnectChat
+@_exported import WalletConnectPush
#endif
diff --git a/Sources/Web3Inbox/WebView/WebViewEvent.swift b/Sources/Web3Inbox/WebView/ChatWebViewEvent.swift
similarity index 86%
rename from Sources/Web3Inbox/WebView/WebViewEvent.swift
rename to Sources/Web3Inbox/WebView/ChatWebViewEvent.swift
index 3556bf3e8..eaf58ea47 100644
--- a/Sources/Web3Inbox/WebView/WebViewEvent.swift
+++ b/Sources/Web3Inbox/WebView/ChatWebViewEvent.swift
@@ -1,6 +1,6 @@
import Foundation
-enum WebViewEvent: String {
+enum ChatWebViewEvent: String {
case getReceivedInvites
case getSentInvites
case getThreads
diff --git a/Sources/Web3Inbox/WebView/PushWebViewEvent.swift b/Sources/Web3Inbox/WebView/PushWebViewEvent.swift
new file mode 100644
index 000000000..5f898fa98
--- /dev/null
+++ b/Sources/Web3Inbox/WebView/PushWebViewEvent.swift
@@ -0,0 +1,12 @@
+import Foundation
+
+enum PushWebViewEvent: String {
+ case approve
+ case update
+ case reject
+ case subscribe
+ case getActiveSubscriptions
+ case getMessageHistory
+ case deleteSubscription
+ case deletePushMessage
+}
diff --git a/Sources/Web3Inbox/WebView/WebViewFactory.swift b/Sources/Web3Inbox/WebView/WebViewFactory.swift
index bcddd0c9b..70a784c64 100644
--- a/Sources/Web3Inbox/WebView/WebViewFactory.swift
+++ b/Sources/Web3Inbox/WebView/WebViewFactory.swift
@@ -3,25 +3,36 @@ import WebKit
final class WebViewFactory {
- private let host: String
- private let webviewSubscriber: WebViewRequestSubscriber
+ private let url: URL
+ private let chatWebviewSubscriber: WebViewRequestSubscriber
+ private let pushWebviewSubscriber: WebViewRequestSubscriber
- init(host: String, webviewSubscriber: WebViewRequestSubscriber) {
- self.host = host
- self.webviewSubscriber = webviewSubscriber
+ init(
+ url: URL,
+ chatWebviewSubscriber: WebViewRequestSubscriber,
+ pushWebviewSubscriber: WebViewRequestSubscriber
+ ) {
+ self.url = url
+ self.chatWebviewSubscriber = chatWebviewSubscriber
+ self.pushWebviewSubscriber = pushWebviewSubscriber
}
func create() -> WKWebView {
let configuration = WKWebViewConfiguration()
configuration.allowsInlineMediaPlayback = true
configuration.userContentController.add(
- webviewSubscriber,
- name: WebViewRequestSubscriber.name
+ chatWebviewSubscriber,
+ name: WebViewRequestSubscriber.chat
+ )
+ configuration.userContentController.add(
+ pushWebviewSubscriber,
+ name: WebViewRequestSubscriber.push
)
let webview = WKWebView(frame: .zero, configuration: configuration)
- let request = URLRequest(url: URL(string: host)!)
+
+ let request = URLRequest(url: url)
webview.load(request)
- webview.uiDelegate = webviewSubscriber
+ webview.uiDelegate = chatWebviewSubscriber
return webview
}
}
diff --git a/Sources/Web3Inbox/WebView/WebViewProxy.swift b/Sources/Web3Inbox/WebView/WebViewProxy.swift
index 73130fede..5948b6660 100644
--- a/Sources/Web3Inbox/WebView/WebViewProxy.swift
+++ b/Sources/Web3Inbox/WebView/WebViewProxy.swift
@@ -4,29 +4,47 @@ import WebKit
actor WebViewProxy {
private let webView: WKWebView
+ private let scriptFormatter: WebViewScriptFormatter
+ private let logger: ConsoleLogging
- init(webView: WKWebView) {
+ init(webView: WKWebView,
+ scriptFormatter: WebViewScriptFormatter,
+ logger: ConsoleLogging) {
self.webView = webView
+ self.scriptFormatter = scriptFormatter
+ self.logger = logger
}
@MainActor
func respond(_ response: RPCResponse) async throws {
let body = try response.json()
- let script = await formatScript(body: body)
+ logger.debug("resonding to w3i with \(body)")
+ let script = scriptFormatter.formatScript(body: body)
webView.evaluateJavaScript(script, completionHandler: nil)
}
@MainActor
func request(_ request: RPCRequest) async throws {
let body = try request.json()
- let script = await formatScript(body: body)
+ logger.debug("requesting w3i with \(body)")
+ let script = scriptFormatter.formatScript(body: body)
webView.evaluateJavaScript(script, completionHandler: nil)
}
}
-private extension WebViewProxy {
+protocol WebViewScriptFormatter {
+ func formatScript(body: String) -> String
+}
+
+class ChatWebViewScriptFormatter: WebViewScriptFormatter {
+ func formatScript(body: String) -> String {
+ return "window.web3inbox.chat.postMessage(\(body))"
+ }
+}
+
+class PushWebViewScriptFormatter: WebViewScriptFormatter {
func formatScript(body: String) -> String {
- return "window.\(WebViewRequestSubscriber.name).chat.postMessage(\(body))"
+ return "window.web3inbox.push.postMessage(\(body))"
}
}
diff --git a/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift b/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift
index 9b43a57c7..7df7480c4 100644
--- a/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift
+++ b/Sources/Web3Inbox/WebView/WebViewRequestSubscriber.swift
@@ -3,7 +3,8 @@ import WebKit
final class WebViewRequestSubscriber: NSObject, WKScriptMessageHandler {
- static let name = "web3inboxChat"
+ static let chat = "web3inboxChat"
+ static let push = "web3inboxPush"
var onRequest: ((RPCRequest) async throws -> Void)?
@@ -17,13 +18,12 @@ final class WebViewRequestSubscriber: NSObject, WKScriptMessageHandler {
_ userContentController: WKUserContentController,
didReceive message: WKScriptMessage
) {
- guard message.name == WebViewRequestSubscriber.name else { return }
-
+ logger.debug("WebViewRequestSubscriber: received request from w3i")
guard
let body = message.body as? String, let data = body.data(using: .utf8),
let request = try? JSONDecoder().decode(RPCRequest.self, from: data)
else { return }
-
+ logger.debug("request method: \(request.method)")
Task {
do {
try await onRequest?(request)
diff --git a/fastlane/Fastfile b/fastlane/Fastfile
index 3c19c1be9..b7e71fc34 100644
--- a/fastlane/Fastfile
+++ b/fastlane/Fastfile
@@ -20,16 +20,35 @@ default_platform(:ios)
platform :ios do
lane :tests do |options|
+ xcargs = ""
+ xcargs << "RELAY_HOST='#{options[:relay_host]}' " if options[:relay_host]
+ xcargs << "PROJECT_ID='#{options[:project_id]}' " if options[:project_id]
+
+ xctestrun = ""
+ Dir.chdir("..") do
+ xctestrun = Dir.glob("**/*#{options[:testplan]}*.xctestrun").first
+
+ puts "Using following xctestrun file:"
+ puts xctestrun
+ end
+
run_tests(
project: 'Example/ExampleApp.xcodeproj',
scheme: options[:scheme],
- testplan: options[:testplan],
+ testplan: defined?(xctestrun) ? nil : options[:testplan],
cloned_source_packages_path: 'SourcePackagesCache',
- destination: 'platform=iOS Simulator,name=iPhone 13',
+ destination: 'platform=iOS Simulator,name=iPhone 14',
derived_data_path: 'DerivedDataCache',
skip_package_dependencies_resolution: true,
skip_build: true,
- xcargs: "RELAY_HOST='#{options[:relay_host]}' PROJECT_ID='#{options[:project_id]}'"
+ xcargs: xcargs,
+ xcodebuild_formatter: "xcbeautify --preserve-unbeautified",
+ output_directory: "test_results",
+ result_bundle: true,
+ buildlog_path: "test_results",
+ output_types: "junit",
+ xctestrun: xctestrun,
+ test_without_building: true,
)
end
@@ -37,7 +56,7 @@ platform :ios do
xcodebuild(
project: 'Example/ExampleApp.xcodeproj',
scheme: options[:scheme],
- destination: 'platform=iOS Simulator,name=iPhone 13',
+ destination: 'platform=iOS Simulator,name=iPhone 14',
xcargs: "-clonedSourcePackagesDirPath SourcePackagesCache -derivedDataPath DerivedDataCache"
)
end
@@ -67,7 +86,9 @@ platform :ios do
git_url: "https://github.com/WalletConnect/match-swift.git",
git_basic_authorization: options[:token],
api_key: api_key,
- include_all_certificates: true
+ include_all_certificates: true,
+ force_for_new_devices: true,
+ force_for_new_certificates: true
)
number = latest_testflight_build_number(
app_identifier: ENV["APP_IDENTIFIER"],