Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update readme, update Concurrency #217

Merged
merged 3 commits into from
May 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 1 addition & 63 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,75 +5,13 @@ Swift implementation of WalletConnect v.2 protocol for native iOS applications.
- XCode 13
- Swift 5

## Documentation
## Documentation & Usage
- In order to build API documentation in XCode go to Product -> Build Documentation
- [Getting started with wallet integration](https://docs.walletconnect.com/2.0/quick-start/wallets/swift)
- [Beginner guide to WalletConnect v2.0 for iOS Developers](https://medium.com/walletconnect/beginner-guide-to-walletconnect-v2-0-for-swift-developers-4534b0975218)
- [Protocol Documentation](https://docs.walletconnect.com/2.0/protocol/client-communication)
- [Glossary](https://docs.walletconnect.com/2.0/protocol/glossary)

## Usage
### Responder
Responder client is usually a wallet.
##### Instantiate a Client
You usually want to have a single instance of a client in you app.
```Swift
let metadata = AppMetadata(name: String?,
description: String?,
url: String?,
icons: [String]?)
let client = AuthClient(metadata: AppMetadata,
projectId: String,
relayHost: String)
```

After instantiation of a client set its delegate.
#### Pair Clients
Pair client with a uri generated by the `proposer` client.
```Swift
let uri = "wc:..."
try! client.pair(uri: uri)
```
#### Approve Session
Sessions are always proposed by the `Proposer` client so `Responder` client needs either reject or approve a session proposal.
```Swift
class ClientDelegate: AuthClientDelegate {
...
func didReceive(sessionProposal: Session.Proposal) {
client.approve(proposal: proposal, accounts: [String])
}
...
```
or
```Swift
func didReceive(sessionProposal: Session.Proposal) {
client.reject(proposal: proposal, reason: Reason)
}
```
NOTE: addresses provided in `accounts` array should follow [CAPI10](https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-10.md) semantics.
#### Handle Delegate methods
```Swift
func didReceive(sessionProposal: Session.Proposal) {
// handle session proposal
}
func didReceive(sessionRequest: Request) {
// handle session request
}
```
#### JSON-RPC Payloads
#### Receive
You can parse JSON-RPC Requests received from "Requester" in `didReceive(sessionRequest: Request)` delegate function.

Request parameters can be type casted based on request method as below:
```Swift
let params = try! sessionRequest.request.params.get([EthSendTransaction].self)
```
##### Respond

```Swift
let jsonrpcResponse = JSONRPCResponse<AnyCodable>(id: request.id, result: AnyCodable(responseParams))
client.respond(topic: sessionRequest.topic, response: .response(jsonrpcResponse))
```

## Installation
### Swift Package Manager
Expand Down
8 changes: 4 additions & 4 deletions Sources/WalletConnectAuth/AuthClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -176,18 +176,18 @@ public final class AuthClient {
/// - Parameters:
/// - topic: Topic of the session that is intended to be updated.
/// - methods: Sets of methods that will replace existing ones.
public func update(topic: String, namespaces: [String: SessionNamespace]) throws {
try controllerSessionStateMachine.update(topic: topic, namespaces: namespaces)
public func update(topic: String, namespaces: [String: SessionNamespace]) async throws {
try await controllerSessionStateMachine.update(topic: topic, namespaces: namespaces)
}

/// For controller to update expiry of a session
/// - Parameters:
/// - topic: Topic of the Session, it can be a pairing or a session topic.
/// - ttl: Time in seconds that a target session is expected to be extended for. Must be greater than current time to expire and than 7 days
public func extend(topic: String) throws {
public func extend(topic: String) async throws {
let ttl: Int64 = Session.defaultTimeToLive
if sessionEngine.hasSession(for: topic) {
try controllerSessionStateMachine.extend(topic: topic, by: ttl)
try await controllerSessionStateMachine.extend(topic: topic, by: ttl)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,23 +28,23 @@ final class ControllerSessionStateMachine: SessionStateMachineValidating {
}

// TODO: Change to new namespace spec
func update(topic: String, namespaces: [String: SessionNamespace]) throws {
func update(topic: String, namespaces: [String: SessionNamespace]) async throws {
var session = try getSession(for: topic)
try validateControlledAcknowledged(session)
try Validator.validate(namespaces)
logger.debug("Controller will update methods")
session.updateNamespaces(namespaces)
sessionStore.setSession(session)
networkingInteractor.request(.wcSessionUpdate(SessionType.UpdateParams(namespaces: namespaces)), onTopic: topic)
try await networkingInteractor.request(.wcSessionUpdate(SessionType.UpdateParams(namespaces: namespaces)), onTopic: topic)
}

func extend(topic: String, by ttl: Int64) throws {
func extend(topic: String, by ttl: Int64) async throws {
var session = try getSession(for: topic)
try validateControlledAcknowledged(session)
try session.updateExpiry(by: ttl)
let newExpiry = Int64(session.expiryDate.timeIntervalSince1970 )
sessionStore.setSession(session)
networkingInteractor.request(.wcSessionExtend(SessionType.UpdateExpiryParams(expiry: newExpiry)), onTopic: topic)
try await networkingInteractor.request(.wcSessionExtend(SessionType.UpdateExpiryParams(expiry: newExpiry)), onTopic: topic)
}

// MARK: - Handle Response
Expand Down
13 changes: 13 additions & 0 deletions Tests/TestingUtils/XCTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,17 @@ extension XCTest {
errorHandler(error)
}
}

public func XCTAssertNoThrowAsync<T: Sendable>(
_ expression: @autoclosure () async throws -> T,
_ message: @autoclosure () -> String = "",
file: StaticString = #filePath,
line: UInt = #line
) async {
do {
_ = try await expression()
} catch {
XCTFail(message(), file: file, line: line)
}
}
}
32 changes: 16 additions & 16 deletions Tests/WalletConnectTests/ControllerSessionStateMachineTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,16 @@ class ControllerSessionStateMachineTests: XCTestCase {
// XCTAssertEqual(namespacesToUpdate, updatedSession?.namespaces)
// }

func testUpdateNamespacesErrorSessionNotFound() {
XCTAssertThrowsError(try sut.update(topic: "", namespaces: SessionNamespace.stubDictionary())) { error in
func testUpdateNamespacesErrorSessionNotFound() async {
await XCTAssertThrowsErrorAsync( try await sut.update(topic: "", namespaces: SessionNamespace.stubDictionary())) { error in
XCTAssertTrue(error.isNoSessionMatchingTopicError)
}
}

func testUpdateNamespacesErrorSessionNotAcknowledged() {
func testUpdateNamespacesErrorSessionNotAcknowledged() async {
let session = WCSession.stub(acknowledged: false)
storageMock.setSession(session)
XCTAssertThrowsError(try sut.update(topic: session.topic, namespaces: SessionNamespace.stubDictionary())) { error in
await XCTAssertThrowsErrorAsync( try await sut.update(topic: session.topic, namespaces: SessionNamespace.stubDictionary())) { error in
XCTAssertTrue(error.isSessionNotAcknowledgedError)
}
}
Expand All @@ -60,55 +60,55 @@ class ControllerSessionStateMachineTests: XCTestCase {
// }
// }

func testUpdateNamespacesErrorCalledByNonController() {
func testUpdateNamespacesErrorCalledByNonController() async {
let session = WCSession.stub(isSelfController: false)
storageMock.setSession(session)
XCTAssertThrowsError(try sut.update(topic: session.topic, namespaces: SessionNamespace.stubDictionary())) { error in
await XCTAssertThrowsErrorAsync( try await sut.update(topic: session.topic, namespaces: SessionNamespace.stubDictionary())) { error in
XCTAssertTrue(error.isUnauthorizedNonControllerCallError)
}
}

// MARK: - Update Expiry

func testUpdateExpirySuccess() {
func testUpdateExpirySuccess() async {
let tomorrow = TimeTraveler.dateByAdding(days: 1)
let session = WCSession.stub(isSelfController: true, expiryDate: tomorrow)
storageMock.setSession(session)
let twoDays = 2*Time.day
XCTAssertNoThrow(try sut.extend(topic: session.topic, by: Int64(twoDays)))
await XCTAssertNoThrowAsync(try await sut.extend(topic: session.topic, by: Int64(twoDays)))
let extendedSession = storageMock.getAcknowledgedSessions().first{$0.topic == session.topic}!
XCTAssertEqual(extendedSession.expiryDate.timeIntervalSinceReferenceDate, TimeTraveler.dateByAdding(days: 2).timeIntervalSinceReferenceDate, accuracy: 1)
}

func testUpdateExpirySessionNotSettled() {
func testUpdateExpirySessionNotSettled() async {
let tomorrow = TimeTraveler.dateByAdding(days: 1)
let session = WCSession.stub(isSelfController: false, expiryDate: tomorrow, acknowledged: false)
storageMock.setSession(session)
let twoDays = 2*Time.day
XCTAssertThrowsError(try sut.extend(topic: session.topic, by: Int64(twoDays)))
await XCTAssertThrowsErrorAsync(try await sut.extend(topic: session.topic, by: Int64(twoDays)))
}

func testUpdateExpiryOnNonControllerClient() {
func testUpdateExpiryOnNonControllerClient() async {
let tomorrow = TimeTraveler.dateByAdding(days: 1)
let session = WCSession.stub(isSelfController: false, expiryDate: tomorrow)
storageMock.setSession(session)
let twoDays = 2*Time.day
XCTAssertThrowsError(try sut.extend(topic: session.topic, by: Int64(twoDays)))
await XCTAssertThrowsErrorAsync( try await sut.extend(topic: session.topic, by: Int64(twoDays)))
}

func testUpdateExpiryTtlTooHigh() {
func testUpdateExpiryTtlTooHigh() async {
let tomorrow = TimeTraveler.dateByAdding(days: 1)
let session = WCSession.stub(isSelfController: true, expiryDate: tomorrow)
storageMock.setSession(session)
let tenDays = 10*Time.day
XCTAssertThrowsError(try sut.extend(topic: session.topic, by: Int64(tenDays)))
await XCTAssertThrowsErrorAsync( try await sut.extend(topic: session.topic, by: Int64(tenDays)))
}

func testUpdateExpiryTtlTooLow() {
func testUpdateExpiryTtlTooLow() async {
let dayAfterTommorow = TimeTraveler.dateByAdding(days: 2)
let session = WCSession.stub(isSelfController: true, expiryDate: dayAfterTommorow)
storageMock.setSession(session)
let oneDay = Int64(1*Time.day)
XCTAssertThrowsError(try sut.extend(topic: session.topic, by: oneDay))
await XCTAssertThrowsErrorAsync( try await sut.extend(topic: session.topic, by: oneDay))
}
}
3 changes: 1 addition & 2 deletions Tests/WalletConnectTests/PairEngineTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,8 @@ final class PairEngineTests: XCTestCase {
func testPairMultipleTimesOnSameURIThrows() async {
let uri = WalletConnectURI.stub()
for i in 1...10 {
usleep(100)
if i == 1 {
XCTAssertNoThrow(Task{try await engine.pair(uri)})
await XCTAssertNoThrowAsync(try await engine.pair(uri))
} else {
await XCTAssertThrowsErrorAsync(try await engine.pair(uri))
}
Expand Down