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

Implement contact verification

parent 7735cfe7
No related branches found
No related tags found
2 merge requests!102Release 1.0.0,!81Messenger example - contact authorization improvements
......@@ -231,6 +231,7 @@ let package = Package(
dependencies: [
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
.product(name: "XXClient", package: "elixxir-dapps-sdk-swift"),
.product(name: "XXMessengerClient", package: "elixxir-dapps-sdk-swift"),
]
),
.testTarget(
......
......@@ -44,7 +44,11 @@ extension AppEnvironment {
)
},
verifyContact: {
VerifyContactEnvironment()
VerifyContactEnvironment(
messenger: messenger,
mainQueue: mainQueue,
bgQueue: bgQueue
)
}
)
......
......@@ -149,7 +149,7 @@ public let contactReducer = Reducer<ContactState, ContactAction, ContactEnvironm
case .verifyContactTapped:
if let marshaled = state.dbContact?.marshaled {
state.verifyContact = VerifyContactState(
xxContact: .live(marshaled)
contact: .live(marshaled)
)
}
return .none
......
import ComposableArchitecture
import Foundation
import XCTestDynamicOverlay
import XXClient
import XXMessengerClient
public struct VerifyContactState: Equatable {
public enum Result: Equatable {
case success(Bool)
case failure(String)
}
public init(
xxContact: XXClient.Contact
contact: Contact,
isVerifying: Bool = false,
result: Result? = nil
) {
self.xxContact = xxContact
self.contact = contact
self.isVerifying = isVerifying
self.result = result
}
public var xxContact: XXClient.Contact
public var contact: Contact
public var isVerifying: Bool
public var result: Result?
}
public enum VerifyContactAction: Equatable {
case start
case verifyTapped
case didVerify(VerifyContactState.Result)
}
public struct VerifyContactEnvironment {
public init() {}
public init(
messenger: Messenger,
mainQueue: AnySchedulerOf<DispatchQueue>,
bgQueue: AnySchedulerOf<DispatchQueue>
) {
self.messenger = messenger
self.mainQueue = mainQueue
self.bgQueue = bgQueue
}
public var messenger: Messenger
public var mainQueue: AnySchedulerOf<DispatchQueue>
public var bgQueue: AnySchedulerOf<DispatchQueue>
}
#if DEBUG
extension VerifyContactEnvironment {
public static let unimplemented = VerifyContactEnvironment()
public static let unimplemented = VerifyContactEnvironment(
messenger: .unimplemented,
mainQueue: .unimplemented,
bgQueue: .unimplemented
)
}
#endif
public let verifyContactReducer = Reducer<VerifyContactState, VerifyContactAction, VerifyContactEnvironment>
{ state, action, env in
switch action {
case .start:
case .verifyTapped:
state.isVerifying = true
state.result = nil
return Effect.result { [state] in
do {
let result = try env.messenger.verifyContact(state.contact)
return .success(.didVerify(.success(result)))
} catch {
return .success(.didVerify(.failure(error.localizedDescription)))
}
}
.subscribe(on: env.bgQueue)
.receive(on: env.mainQueue)
.eraseToEffect()
case .didVerify(let result):
state.isVerifying = false
state.result = result
return .none
}
}
......@@ -9,16 +9,78 @@ public struct VerifyContactView: View {
let store: Store<VerifyContactState, VerifyContactAction>
struct ViewState: Equatable {
init(state: VerifyContactState) {}
var username: String?
var email: String?
var phone: String?
var isVerifying: Bool
var result: VerifyContactState.Result?
init(state: VerifyContactState) {
username = try? state.contact.getFact(.username)?.value
email = try? state.contact.getFact(.email)?.value
phone = try? state.contact.getFact(.phone)?.value
isVerifying = state.isVerifying
result = state.result
}
}
public var body: some View {
WithViewStore(store, observe: ViewState.init) { viewStore in
Form {
Section {
Label(viewStore.username ?? "", systemImage: "person")
Label(viewStore.email ?? "", systemImage: "envelope")
Label(viewStore.phone ?? "", systemImage: "phone")
} header: {
Text("Facts")
}
Section {
Button {
viewStore.send(.verifyTapped)
} label: {
HStack {
Text("Verify")
Spacer()
if viewStore.isVerifying {
ProgressView()
} else {
Image(systemName: "play")
}
}
}
.disabled(viewStore.isVerifying)
}
if let result = viewStore.result {
Section {
HStack {
switch result {
case .success(true):
Text("Contact verified")
Spacer()
Image(systemName: "person.fill.checkmark")
case .success(false):
Text("Contact not verified")
Spacer()
Image(systemName: "person.fill.xmark")
case .failure(_):
Text("Verification failed")
Spacer()
Image(systemName: "xmark")
}
}
if case .failure(let failure) = result {
Text(failure)
}
} header: {
Text("Result")
}
}
}
.navigationTitle("Verify Contact")
.task { viewStore.send(.start) }
}
}
}
......@@ -28,7 +90,7 @@ public struct VerifyContactView_Previews: PreviewProvider {
public static var previews: some View {
VerifyContactView(store: Store(
initialState: VerifyContactState(
xxContact: .unimplemented("contact-data".data(using: .utf8)!)
contact: .unimplemented("contact-data".data(using: .utf8)!)
),
reducer: .empty,
environment: ()
......
......@@ -181,7 +181,7 @@ final class ContactFeatureTests: XCTestCase {
store.send(.verifyContactTapped) {
$0.verifyContact = VerifyContactState(
xxContact: .live(contactData)
contact: .unimplemented(contactData)
)
}
}
......@@ -191,7 +191,7 @@ final class ContactFeatureTests: XCTestCase {
initialState: ContactState(
id: "contact-id".data(using: .utf8)!,
verifyContact: VerifyContactState(
xxContact: .unimplemented("contact-data".data(using: .utf8)!)
contact: .unimplemented("contact-data".data(using: .utf8)!)
)
),
reducer: contactReducer,
......
import ComposableArchitecture
import XCTest
import XXClient
@testable import VerifyContactFeature
final class VerifyContactFeatureTests: XCTestCase {
func testStart() {
func testVerify() {
let store = TestStore(
initialState: VerifyContactState(
xxContact: .unimplemented("contact-data".data(using: .utf8)!)
contact: .unimplemented("contact-data".data(using: .utf8)!)
),
reducer: verifyContactReducer,
environment: .unimplemented
)
store.send(.start)
var didVerifyContact: [Contact] = []
store.environment.mainQueue = .immediate
store.environment.bgQueue = .immediate
store.environment.messenger.verifyContact.run = { contact in
didVerifyContact.append(contact)
return true
}
store.send(.verifyTapped) {
$0.isVerifying = true
$0.result = nil
}
store.receive(.didVerify(.success(true))) {
$0.isVerifying = false
$0.result = .success(true)
}
}
func testVerifyNotVerified() {
let store = TestStore(
initialState: VerifyContactState(
contact: .unimplemented("contact-data".data(using: .utf8)!)
),
reducer: verifyContactReducer,
environment: .unimplemented
)
store.environment.mainQueue = .immediate
store.environment.bgQueue = .immediate
store.environment.messenger.verifyContact.run = { _ in false }
store.send(.verifyTapped) {
$0.isVerifying = true
$0.result = nil
}
store.receive(.didVerify(.success(false))) {
$0.isVerifying = false
$0.result = .success(false)
}
}
func testVerifyFailure() {
let store = TestStore(
initialState: VerifyContactState(
contact: .unimplemented("contact-data".data(using: .utf8)!)
),
reducer: verifyContactReducer,
environment: .unimplemented
)
struct Failure: Error {}
let error = Failure()
store.environment.mainQueue = .immediate
store.environment.bgQueue = .immediate
store.environment.messenger.verifyContact.run = { _ in throw error }
store.send(.verifyTapped) {
$0.isVerifying = true
$0.result = nil
}
store.receive(.didVerify(.failure(error.localizedDescription))) {
$0.isVerifying = false
$0.result = .failure(error.localizedDescription)
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment