diff --git a/Example/example-app/Package.swift b/Example/example-app/Package.swift
index 0c0ef22230153b65f531bbdb29f044158041a35f..72fd8b73e72ed93e7d71bcba0f0e74d6040f933a 100644
--- a/Example/example-app/Package.swift
+++ b/Example/example-app/Package.swift
@@ -144,6 +144,10 @@ let package = Package(
           name: "ComposableArchitecture",
           package: "swift-composable-architecture"
         ),
+        .product(
+          name: "ElixxirDAppsSDK",
+          package: "elixxir-dapps-sdk-swift"
+        ),
       ],
       swiftSettings: swiftSettings
     ),
diff --git a/Example/example-app/Sources/AppFeature/App.swift b/Example/example-app/Sources/AppFeature/App.swift
index 16d9b87a7ff78c79866c82132d612bf5d68427d3..37007c70eb7168a0f217baf3e7cc0f51f2522ea3 100644
--- a/Example/example-app/Sources/AppFeature/App.swift
+++ b/Example/example-app/Sources/AppFeature/App.swift
@@ -23,6 +23,7 @@ struct App: SwiftUI.App {
 extension AppEnvironment {
   static func live() -> AppEnvironment {
     let clientSubject = CurrentValueSubject<Client?, Never>(nil)
+    let identitySubject = CurrentValueSubject<Identity?, Never>(nil)
     let mainScheduler = DispatchQueue.main.eraseToAnyScheduler()
     let bgScheduler = DispatchQueue(
       label: "xx.network.dApps.ExampleApp.bg",
@@ -48,7 +49,12 @@ extension AppEnvironment {
         mainScheduler: mainScheduler,
         makeId: UUID.init,
         error: ErrorEnvironment(),
-        myIdentity: MyIdentityEnvironment()
+        myIdentity: MyIdentityEnvironment(
+          getClient: { clientSubject.value },
+          observeIdentity: { identitySubject.eraseToAnyPublisher() },
+          bgScheduler: bgScheduler,
+          mainScheduler: mainScheduler
+        )
       )
     )
   }
diff --git a/Example/example-app/Sources/MyIdentityFeature/MyIdentityFeature.swift b/Example/example-app/Sources/MyIdentityFeature/MyIdentityFeature.swift
index fb4c7200466290ea5f950eaaa65605f1948042ca..4fe79ffdddde683f4898e05793fd3892964574a6 100644
--- a/Example/example-app/Sources/MyIdentityFeature/MyIdentityFeature.swift
+++ b/Example/example-app/Sources/MyIdentityFeature/MyIdentityFeature.swift
@@ -1,4 +1,6 @@
+import Combine
 import ComposableArchitecture
+import ElixxirDAppsSDK
 
 public struct MyIdentityState: Equatable {
   public init(
@@ -8,18 +10,67 @@ public struct MyIdentityState: Equatable {
   }
 
   public var id: UUID
+  public var identity: Identity?
 }
 
-public enum MyIdentityAction: Equatable {}
+public enum MyIdentityAction: Equatable {
+  case viewDidLoad
+  case observeMyIdentity
+  case didUpdateMyIdentity(Identity?)
+}
 
 public struct MyIdentityEnvironment {
-  public init() {}
+  public init(
+    getClient: @escaping () -> Client?,
+    observeIdentity: @escaping () -> AnyPublisher<Identity?, Never>,
+    bgScheduler: AnySchedulerOf<DispatchQueue>,
+    mainScheduler: AnySchedulerOf<DispatchQueue>
+  ) {
+    self.getClient = getClient
+    self.observeIdentity = observeIdentity
+    self.bgScheduler = bgScheduler
+    self.mainScheduler = mainScheduler
+  }
+
+  public var getClient: () -> Client?
+  public var observeIdentity: () -> AnyPublisher<Identity?, Never>
+  public var bgScheduler: AnySchedulerOf<DispatchQueue>
+  public var mainScheduler: AnySchedulerOf<DispatchQueue>
 }
 
-public let myIdentityReducer = Reducer<MyIdentityState, MyIdentityAction, MyIdentityEnvironment>.empty
+public let myIdentityReducer = Reducer<MyIdentityState, MyIdentityAction, MyIdentityEnvironment>
+{ state, action, env in
+  switch action {
+  case .viewDidLoad:
+    return .merge([
+      .init(value: .observeMyIdentity),
+    ])
+
+  case .observeMyIdentity:
+    struct EffectId: Hashable {
+      let id: UUID
+    }
+    return env.observeIdentity()
+      .removeDuplicates()
+      .map(MyIdentityAction.didUpdateMyIdentity)
+      .subscribe(on: env.bgScheduler)
+      .receive(on: env.mainScheduler)
+      .eraseToEffect()
+      .cancellable(id: EffectId(id: state.id), cancelInFlight: true)
+
+  case .didUpdateMyIdentity(let identity):
+    state.identity = identity
+    return .none
+  }
+}
 
 #if DEBUG
 extension MyIdentityEnvironment {
-  public static let failing = MyIdentityEnvironment()
+  public static let failing = MyIdentityEnvironment(
+    getClient: { fatalError() },
+    observeIdentity: { fatalError() },
+    bgScheduler: .failing,
+    mainScheduler: .failing
+  )
 }
 #endif
diff --git a/Example/example-app/Sources/MyIdentityFeature/MyIdentityView.swift b/Example/example-app/Sources/MyIdentityFeature/MyIdentityView.swift
index c50941b6a987a909aa2440541c69f6ad133ce82a..62cea2bbfa8095805777cdb74b91e8e460637575 100644
--- a/Example/example-app/Sources/MyIdentityFeature/MyIdentityView.swift
+++ b/Example/example-app/Sources/MyIdentityFeature/MyIdentityView.swift
@@ -15,6 +15,9 @@ public struct MyIdentityView: View {
   public var body: some View {
     WithViewStore(store.scope(state: ViewState.init)) { viewStore in
       Text("MyIdentityView")
+        .task {
+          viewStore.send(.viewDidLoad)
+        }
     }
   }
 }
diff --git a/Example/example-app/Tests/MyIdentityFeatureTests/MyIdentityFeatureTests.swift b/Example/example-app/Tests/MyIdentityFeatureTests/MyIdentityFeatureTests.swift
index f4a6a410a779bdc85e3c8742289d87012399b353..d3fcdc08fcfc6637c74824f2f44223894e9fcc3d 100644
--- a/Example/example-app/Tests/MyIdentityFeatureTests/MyIdentityFeatureTests.swift
+++ b/Example/example-app/Tests/MyIdentityFeatureTests/MyIdentityFeatureTests.swift
@@ -1,9 +1,57 @@
+import Combine
 import ComposableArchitecture
+import ElixxirDAppsSDK
 import XCTest
 @testable import MyIdentityFeature
 
 final class MyIdentityFeatureTests: XCTestCase {
-  func testExample() {
-    XCTAssert(true)
+  func testViewDidLoad() {
+    let myIdentitySubject = PassthroughSubject<Identity?, Never>()
+    let bgScheduler = DispatchQueue.test
+    let mainScheduler = DispatchQueue.test
+
+    var env = MyIdentityEnvironment.failing
+    env.observeIdentity = { myIdentitySubject.eraseToAnyPublisher() }
+    env.bgScheduler = bgScheduler.eraseToAnyScheduler()
+    env.mainScheduler = mainScheduler.eraseToAnyScheduler()
+
+    let store = TestStore(
+      initialState: MyIdentityState(id: UUID()),
+      reducer: myIdentityReducer,
+      environment: env
+    )
+
+    store.send(.viewDidLoad)
+    store.receive(.observeMyIdentity)
+
+    bgScheduler.advance()
+    let identity = Identity.stub()
+    myIdentitySubject.send(identity)
+    mainScheduler.advance()
+
+    store.receive(.didUpdateMyIdentity(identity)) {
+      $0.identity = identity
+    }
+
+    myIdentitySubject.send(nil)
+    mainScheduler.advance()
+
+    store.receive(.didUpdateMyIdentity(nil)) {
+      $0.identity = nil
+    }
+
+    myIdentitySubject.send(completion: .finished)
+    mainScheduler.advance()
+  }
+}
+
+private extension Identity {
+  static func stub() -> Identity {
+    Identity(
+      id: "\(Int.random(in: 100...999))".data(using: .utf8)!,
+      rsaPrivatePem: "\(Int.random(in: 100...999))".data(using: .utf8)!,
+      salt: "\(Int.random(in: 100...999))".data(using: .utf8)!,
+      dhKeyPrivate: "\(Int.random(in: 100...999))".data(using: .utf8)!
+    )
   }
 }