Skip to content
Snippets Groups Projects
Commit e69aff29 authored by Dariusz Rybicki's avatar Dariusz Rybicki
Browse files

Merge branch 'development' into dev/update-bindings

# Conflicts:
#	Sources/XXClient/Functors/NewUdManagerFromBackup.swift
parents 5a1392bd 1f24e4dc
No related branches found
No related tags found
2 merge requests!102Release 1.0.0,!34Update Bindings
Showing
with 593 additions and 146 deletions
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1340"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "XXMessengerClient"
BuildableName = "XXMessengerClient"
BlueprintName = "XXMessengerClient"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "XXMessengerClientTests"
BuildableName = "XXMessengerClientTests"
BlueprintName = "XXMessengerClientTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "XXMessengerClient"
BuildableName = "XXMessengerClient"
BlueprintName = "XXMessengerClient"
ReferencedContainer = "container:">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
......@@ -20,6 +20,20 @@
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "XXMessengerClient"
BuildableName = "XXMessengerClient"
BlueprintName = "XXMessengerClient"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
......@@ -39,6 +53,16 @@
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "XXMessengerClientTests"
BuildableName = "XXMessengerClientTests"
BlueprintName = "XXMessengerClientTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
......
# XXClient Quick Start Guide
Add `XXClient` library as a dependency to your project using Swift Package Manager.
## ▶️ Instantiating cMix
You can use a convenient `CMixManager` wrapper to manage cMix stored on disk:
```swift
let cMixManager: CMixManager = .live(
passwordStorage: .init(
save: { password in
// securely save provided password
},
load: {
// load securely stored password
}
)
)
let cMix: CMix
if cMixManager.hasStorage() {
cMix = try cMixManager.load()
} else {
cMix = try cMixManager.create()
}
```
Check out included example iOS application for the `PasswordStorage` implementation that uses the iOS keychain.
## ▶️ Connecting to the network
Start network follower:
```swift
try cMix.startNetworkFollower(timeoutMS: 10_000)
```
Wait until connected:
```swift
let isNetworkHealthy = try cMix.waitForNetwork(timeoutMS: 30_000)
```
## ▶️ Making a new reception identity
Use the cMix to make a new reception identity:
```swift
let myIdentity = try cMix.makeReceptionIdentity()
```
## ▶️ Create new E2E
```swift
let login: Login = .live
let e2e = try login(
cMixId: cMix.getId(),
identity: myIdentity
)
```
## ▶️ Connecting to remote
Perform auth key negotiation with the given recipient to get the `Connection`:
```swift
let connection = try cMix.connect(
withAuthentication: false,
e2eId: e2e.getId(),
recipientContact: ...
)
```
Pass `true` for the `withAuthentication` parameter if you want to prove id ownership to remote as well.
## ▶️ Sending messages
Send a message to the connection's partner:
```swift
let sendReport = try connection.send(
messageType: 1,
payload: ...
)
```
Check if the round succeeded:
```swift
try cMix.waitForRoundResult(
roundList: try sendReport.encode(),
timeoutMS: 30_000,
callback: .init { result in
switch result {
case .delivered(let roundResults):
...
case .notDelivered(let timedOut):
...
}
}
)
```
## ▶️ Receiving messages
Use connection's message listener to receive messages from partner:
```swift
try connection.registerListener(
messageType: 1,
listener: .init { message in
...
}
)
```
\ No newline at end of file
# XXMessengerClient
`XXMessengerClient` is a client wrapper library for use in xx-messenger application.
## ▶️ Instantiate messenger
Example:
```swift
// setup environment:
var environment: MessengerEnvironment = .live()
// change cMix NDF environment if needed:
environment.ndfEnvironment = ...
// use alternative user-discovery if needed:
environment.udAddress = ...
environment.udCert = ...
environment.udContact = ...
// instantiate messenger:
let messenger: Messenger = .live(environment)
```
## 🚀 Start messenger
Example:
```
func start(messenger: Messenger) throws {
// check if messenger is loaded:
if messenger.isLoaded() == false {
// check if messenger is created and stored on disk:
if messenger.isCreated() == false {
// create new messenger and store it on disk:
try messenger.create()
}
// load messenger stored on disk:
try messenger.load()
}
// start messenger's network follower:
try messenger.start()
// check if messenger is connected:
if messenger.isConnected() == false {
// start end-to-end connection:
try messenger.connect()
}
// check if messenger is logged in with user-discovery:
if messenger.isLoggedIn() == false {
// check if messenger is registered with user-discovery:
if try messenger.isRegistered() == false {
// register new user with user-discovery:
try messenger.register(username: "new-username")
} else {
// login previously registered user with user-discovery:
try messenger.logIn()
}
}
}
```
## 🛠 Use client components directly
Example:
```swift
// get cMix:
let cMix = messenger.cMix()
// get E2E:
let e2e = messenger.e2e()
// get UserDicovery:
let ud = messenger.ud()
```
\ No newline at end of file
......@@ -26,8 +26,49 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AppFeatureTests"
BuildableName = "AppFeatureTests"
BlueprintName = "AppFeatureTests"
ReferencedContainer = "container:example-app">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "ErrorFeatureTests"
BuildableName = "ErrorFeatureTests"
BlueprintName = "ErrorFeatureTests"
ReferencedContainer = "container:example-app">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "LandingFeatureTests"
BuildableName = "LandingFeatureTests"
BlueprintName = "LandingFeatureTests"
ReferencedContainer = "container:example-app">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "SessionFeatureTests"
BuildableName = "SessionFeatureTests"
BlueprintName = "SessionFeatureTests"
ReferencedContainer = "container:example-app">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
......
......@@ -20,6 +20,7 @@ let package = Package(
],
products: [
.library(name: "XXClient", targets: ["XXClient"]),
.library(name: "XXMessengerClient", targets: ["XXMessengerClient"]),
],
dependencies: [
.package(
......@@ -30,6 +31,10 @@ let package = Package(
url: "https://github.com/pointfreeco/xctest-dynamic-overlay.git",
.upToNextMajor(from: "0.4.0")
),
.package(
url: "https://github.com/kishikawakatsumi/KeychainAccess.git",
.upToNextMajor(from: "4.2.2")
),
],
targets: [
.target(
......@@ -48,6 +53,23 @@ let package = Package(
],
swiftSettings: swiftSettings
),
.target(
name: "XXMessengerClient",
dependencies: [
.target(name: "XXClient"),
.product(name: "KeychainAccess", package: "KeychainAccess"),
.product(name: "XCTestDynamicOverlay", package: "xctest-dynamic-overlay"),
],
swiftSettings: swiftSettings
),
.testTarget(
name: "XXMessengerClientTests",
dependencies: [
.target(name: "XXMessengerClient"),
.product(name: "CustomDump", package: "swift-custom-dump"),
],
swiftSettings: swiftSettings
),
.binaryTarget(
name: "Bindings",
path: "Frameworks/Bindings.xcframework"
......
......@@ -3,132 +3,14 @@
![Swift 5.6](https://img.shields.io/badge/swift-5.6-orange.svg)
![platform iOS](https://img.shields.io/badge/platform-iOS-blue.svg)
## 📱 Demo
Refer to this [demo](https://git.xx.network/elixxir/shielded-help-demo/elixxir-dapp-demo) to see an example of how to build an app with the SDK to send `E2E` messages and send `RestLike` messsage.
Also you can checkout included example iOS application.
## 📖 Documentation
You can find full documentation with step by step guide [here](https://xxdk-dev.xx.network/mobile%20docs/ios-sdk)
## 🚀 Quick Start
Add `XXClient` library as a dependency to your project using Swift Package Manager.
### ▶️ Instantiating cMix
You can use a convenient `CMixManager` wrapper to manage cMix stored on disk:
```swift
let cMixManager: CMixManager = .live(
passwordStorage: .init(
save: { password in
// securely save provided password
},
load: {
// load securely stored password
}
)
)
let cMix: CMix
if cMixManager.hasStorage() {
cMix = try cMixManager.load()
} else {
cMix = try cMixManager.create()
}
```
Check out included example iOS application for the `PasswordStorage` implementation that uses the iOS keychain.
### ▶️ Connecting to the network
Start network follower:
```swift
try cMix.startNetworkFollower(timeoutMS: 10_000)
```
Wait until connected:
```swift
let isNetworkHealthy = try cMix.waitForNetwork(timeoutMS: 30_000)
```
### ▶️ Making a new reception identity
Use the cMix to make a new reception identity:
- [XXClient Quick Start Guide](Docs/XXClient-quick-start-guide.md)
- [XXMessengerClient](Docs/XXMessengerClient.md)
```swift
let myIdentity = try cMix.makeReceptionIdentity()
```
### ▶️ Create new E2E
```swift
let login: Login = .live
let e2e = try login(
cMixId: cMix.getId(),
identity: myIdentity
)
```
### ▶️ Connecting to remote
Perform auth key negotiation with the given recipient to get the `Connection`:
```swift
let connection = try cMix.connect(
withAuthentication: false,
e2eId: e2e.getId(),
recipientContact: ...
)
```
Pass `true` for the `withAuthentication` parameter if you want to prove id ownership to remote as well.
### ▶️ Sending messages
Send a message to the connection's partner:
```swift
let sendReport = try connection.send(
messageType: 1,
payload: ...
)
```
Check if the round succeeded:
```swift
try cMix.waitForRoundResult(
roundList: try sendReport.encode(),
timeoutMS: 30_000,
callback: .init { result in
switch result {
case .delivered(let roundResults):
...
case .notDelivered(let timedOut):
...
}
}
)
```
### ▶️ Receiving messages
Use connection's message listener to receive messages from partner:
## 📱 Demo
```swift
try connection.registerListener(
messageType: 1,
listener: .init { message in
...
}
)
```
Checkout included example iOS application.
## 🛠 Development
......@@ -139,7 +21,8 @@ Open `ElixxirDAppsSDK.xcworkspace` in Xcode (≥13.4).
```
ElixxirDAppsSDK [Xcode Workspace]
├─ elixxir-dapps-sdk-swift [Swift Package]
| └─ XXClient [Library]
| ├─ XXClient [Library]
| └─ XXMessengerClient [Library]
└─ Example [Xcode Project]
├─ ExampleApp (iOS) [iOS App Target]
├─ example-app [Swift Package]
......@@ -157,7 +40,7 @@ ElixxirDAppsSDK [Xcode Workspace]
- Use `example-app` scheme to build and test the example app package with all contained libraries.
- Use `ExampleAppIcon` scheme with macOS target to build and preview the example app icon.
- Use `example-app-icon-export` scheme with macOS target to build and update the example app icon.
- Use other schemes, like `AppFeature`, for building and testing individual libraries in isolation.
- Use other schemes, like `XXClient`, for building and testing individual libraries in isolation.
## 📄 License
......
......@@ -16,7 +16,7 @@ extension CMixManager {
.appendingPathComponent("xx.network.client")
.path,
fileManager: FileManager = .default,
environment: Environment = .mainnet,
ndfEnvironment: NDFEnvironment = .mainnet,
downloadNDF: DownloadAndVerifySignedNdf = .live,
generateSecret: GenerateSecret = .live,
passwordStorage: PasswordStorage,
......@@ -31,7 +31,7 @@ extension CMixManager {
fileManager: fileManager
),
create: .live(
environment: environment,
ndfEnvironment: ndfEnvironment,
downloadNDF: downloadNDF,
generateSecret: generateSecret,
passwordStorage: passwordStorage,
......@@ -42,7 +42,7 @@ extension CMixManager {
loadCMix: loadCMix
),
restore: .live(
environment: environment,
ndfEnvironment: ndfEnvironment,
downloadNDF: downloadNDF,
generateSecret: generateSecret,
passwordStorage: passwordStorage,
......
......@@ -11,7 +11,7 @@ public struct CMixManagerCreate {
extension CMixManagerCreate {
public static func live(
environment: Environment,
ndfEnvironment: NDFEnvironment,
downloadNDF: DownloadAndVerifySignedNdf,
generateSecret: GenerateSecret,
passwordStorage: PasswordStorage,
......@@ -22,7 +22,7 @@ extension CMixManagerCreate {
loadCMix: LoadCMix
) -> CMixManagerCreate {
CMixManagerCreate {
let ndfData = try downloadNDF(environment)
let ndfData = try downloadNDF(ndfEnvironment)
let password = generateSecret()
try passwordStorage.save(password)
try? fileManager.removeItem(atPath: directoryPath)
......
......@@ -14,7 +14,7 @@ public struct CMixManagerRestore {
extension CMixManagerRestore {
public static func live(
environment: Environment,
ndfEnvironment: NDFEnvironment,
downloadNDF: DownloadAndVerifySignedNdf,
generateSecret: GenerateSecret,
passwordStorage: PasswordStorage,
......@@ -23,7 +23,7 @@ extension CMixManagerRestore {
newCMixFromBackup: NewCMixFromBackup
) -> CMixManagerRestore {
CMixManagerRestore { backup, passphrase in
let ndfData = try downloadNDF(environment)
let ndfData = try downloadNDF(ndfEnvironment)
let password = generateSecret()
try passwordStorage.save(password)
try? fileManager.removeItem(atPath: directoryPath)
......
......@@ -2,9 +2,9 @@ import Bindings
import XCTestDynamicOverlay
public struct DownloadAndVerifySignedNdf {
public var run: (Environment) throws -> Data
public var run: (NDFEnvironment) throws -> Data
public func callAsFunction(_ env: Environment) throws -> Data {
public func callAsFunction(_ env: NDFEnvironment) throws -> Data {
try run(env)
}
}
......
......@@ -2,10 +2,9 @@ import Bindings
import XCTestDynamicOverlay
public struct NewOrLoadUd {
public struct Params {
public struct Params: Equatable {
public init(
e2eId: Int,
follower: UdNetworkStatus,
username: String?,
registrationValidationSignature: Data?,
cert: Data,
......@@ -13,7 +12,6 @@ public struct NewOrLoadUd {
address: String
) {
self.e2eId = e2eId
self.follower = follower
self.username = username
self.registrationValidationSignature = registrationValidationSignature
self.cert = cert
......@@ -22,7 +20,6 @@ public struct NewOrLoadUd {
}
public var e2eId: Int
public var follower: UdNetworkStatus
public var username: String?
public var registrationValidationSignature: Data?
public var cert: Data
......@@ -30,19 +27,22 @@ public struct NewOrLoadUd {
public var address: String
}
public var run: (Params) throws -> UserDiscovery
public var run: (Params, UdNetworkStatus) throws -> UserDiscovery
public func callAsFunction(_ params: Params) throws -> UserDiscovery {
try run(params)
public func callAsFunction(
params: Params,
follower: UdNetworkStatus
) throws -> UserDiscovery {
try run(params, follower)
}
}
extension NewOrLoadUd {
public static let live = NewOrLoadUd { params in
public static let live = NewOrLoadUd { params, follower in
var error: NSError?
let bindingsUD = BindingsNewOrLoadUd(
params.e2eId,
params.follower.makeBindingsUdNetworkStatus(),
follower.makeBindingsUdNetworkStatus(),
params.username,
params.registrationValidationSignature,
params.cert,
......
import Foundation
public struct Environment: Equatable {
public struct NDFEnvironment: Equatable {
public init(url: URL, cert: String) {
self.url = url
self.cert = cert
......@@ -10,8 +10,8 @@ public struct Environment: Equatable {
public var cert: String
}
extension Environment {
public static let mainnet = Environment(
extension NDFEnvironment {
public static let mainnet = NDFEnvironment(
url: URL(string: "https://elixxir-bins.s3.us-west-1.amazonaws.com/ndf/mainnet.json")!,
cert: """
-----BEGIN CERTIFICATE-----
......@@ -50,3 +50,10 @@ extension Environment {
"""
)
}
extension NDFEnvironment {
public static let unimplemented = NDFEnvironment(
url: URL(fileURLWithPath: "unimplemented"),
cert: "unimplemented"
)
}
import XXClient
import XCTestDynamicOverlay
public struct MessengerConnect {
public enum Error: Swift.Error, Equatable {
case notLoaded
}
public var run: () throws -> Void
public func callAsFunction() throws {
try run()
}
}
extension MessengerConnect {
public static func live(_ env: MessengerEnvironment) -> MessengerConnect {
MessengerConnect {
guard let cMix = env.cMix() else {
throw Error.notLoaded
}
env.e2e.set(try env.login(
cMixId: cMix.getId(),
identity: try cMix.makeLegacyReceptionIdentity(),
e2eParamsJSON: env.getE2EParams()
))
}
}
}
extension MessengerConnect {
public static let unimplemented = MessengerConnect(
run: XCTUnimplemented()
)
}
import XXClient
import XCTestDynamicOverlay
public struct MessengerCreate {
public var run: () throws -> Void
public func callAsFunction() throws {
try run()
}
}
extension MessengerCreate {
public static func live(_ env: MessengerEnvironment) -> MessengerCreate {
MessengerCreate {
let ndfData = try env.downloadNDF(env.ndfEnvironment)
let password = env.generateSecret()
try env.passwordStorage.save(password)
let storageDir = env.storageDir
try env.fileManager.removeDirectory(storageDir)
try env.fileManager.createDirectory(storageDir)
try env.newCMix(
ndfJSON: String(data: ndfData, encoding: .utf8)!,
storageDir: storageDir,
password: password,
registrationCode: nil
)
}
}
}
extension MessengerCreate {
public static let unimplemented = MessengerCreate(
run: XCTUnimplemented()
)
}
import XXClient
import XCTestDynamicOverlay
public struct MessengerIsConnected {
public var run: () -> Bool
public func callAsFunction() -> Bool {
run()
}
}
extension MessengerIsConnected {
public static func live(_ env: MessengerEnvironment) -> MessengerIsConnected {
MessengerIsConnected {
env.e2e() != nil
}
}
}
extension MessengerIsConnected {
public static let unimplemented = MessengerIsConnected(
run: XCTUnimplemented()
)
}
import XXClient
import XCTestDynamicOverlay
public struct MessengerIsCreated {
public var run: () -> Bool
public func callAsFunction() -> Bool {
run()
}
}
extension MessengerIsCreated {
public static func live(_ env: MessengerEnvironment) -> MessengerIsCreated {
MessengerIsCreated {
env.fileManager.isDirectoryEmpty(env.storageDir) == false
}
}
}
extension MessengerIsCreated {
public static let unimplemented = MessengerIsCreated(
run: XCTUnimplemented()
)
}
import XXClient
import XCTestDynamicOverlay
public struct MessengerIsLoaded {
public var run: () -> Bool
public func callAsFunction() -> Bool {
run()
}
}
extension MessengerIsLoaded {
public static func live(_ env: MessengerEnvironment) -> MessengerIsLoaded {
MessengerIsLoaded {
env.cMix() != nil
}
}
}
extension MessengerIsLoaded {
public static let unimplemented = MessengerIsLoaded(
run: XCTUnimplemented()
)
}
import XXClient
import XCTestDynamicOverlay
public struct MessengerIsLoggedIn {
public var run: () -> Bool
public func callAsFunction() -> Bool {
run()
}
}
extension MessengerIsLoggedIn {
public static func live(_ env: MessengerEnvironment) -> MessengerIsLoggedIn {
MessengerIsLoggedIn {
env.ud() != nil
}
}
}
extension MessengerIsLoggedIn {
public static let unimplemented = MessengerIsLoggedIn(
run: XCTUnimplemented()
)
}
import XXClient
import XCTestDynamicOverlay
public struct MessengerIsRegistered {
public enum Error: Swift.Error, Equatable {
case notConnected
}
public var run: () throws -> Bool
public func callAsFunction() throws -> Bool {
try run()
}
}
extension MessengerIsRegistered {
public static func live(_ env: MessengerEnvironment) -> MessengerIsRegistered {
MessengerIsRegistered {
guard let e2e = env.e2e() else {
throw Error.notConnected
}
return try env.isRegisteredWithUD(e2eId: e2e.getId())
}
}
}
extension MessengerIsRegistered {
public static let unimplemented = MessengerIsRegistered(
run: XCTUnimplemented()
)
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment