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

🔄 synced file(s) with circlefin/w3s-ios-sample-app-wallets-internal #12

Merged
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
1 change: 1 addition & 0 deletions Sample App/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

source 'https://cdn.cocoapods.org/'
source 'https://github.com/circlefin/w3s-ios-sdk.git'
platform :ios, '13.0'

target 'w3s-ios-sample-app-wallets' do
# Comment the next line if you don't want to use dynamic frameworks
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
B3BEDEDF2A5064E800861533 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3BEDEDD2A5064E800861533 /* ContentView.swift */; };
B3BEDEE62A50651700861533 /* UserDefault.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3BEDEE32A50651700861533 /* UserDefault.swift */; };
B3BEDEE82A50651700861533 /* Toast.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3BEDEE52A50651700861533 /* Toast.swift */; };
BAE182562AF36AD3004D6494 /* ChallengeResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAE182552AF36AD3004D6494 /* ChallengeResultView.swift */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
Expand All @@ -31,6 +32,7 @@
B3BEDEDD2A5064E800861533 /* ContentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
B3BEDEE32A50651700861533 /* UserDefault.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserDefault.swift; sourceTree = "<group>"; };
B3BEDEE52A50651700861533 /* Toast.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Toast.swift; sourceTree = "<group>"; };
BAE182552AF36AD3004D6494 /* ChallengeResultView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChallengeResultView.swift; sourceTree = "<group>"; };
ED19DFB9C55BB7A8A6609DD9 /* Pods-w3s-ios-sample-app-wallets.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-w3s-ios-sample-app-wallets.release.xcconfig"; path = "Target Support Files/Pods-w3s-ios-sample-app-wallets/Pods-w3s-ios-sample-app-wallets.release.xcconfig"; sourceTree = "<group>"; };
F095FAC15E5990E5728EEDA8 /* Pods_w3s_ios_sample_app_wallets.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_w3s_ios_sample_app_wallets.framework; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
Expand Down Expand Up @@ -88,6 +90,7 @@
B3CD172C2A5DA09D000664EA /* Resources */,
B3BEDECE2A50648300861533 /* w3s_ios_sample_app_walletsApp.swift */,
B3BEDEDD2A5064E800861533 /* ContentView.swift */,
BAE182552AF36AD3004D6494 /* ChallengeResultView.swift */,
B3BEDEDC2A5064E800861533 /* WalletSdkAdapter.swift */,
B3BEDEE22A50651700861533 /* Helpers */,
B3BEDED42A50648400861533 /* Preview Content */,
Expand Down Expand Up @@ -243,6 +246,7 @@
B3BEDECF2A50648300861533 /* w3s_ios_sample_app_walletsApp.swift in Sources */,
B3BEDEDE2A5064E800861533 /* WalletSdkAdapter.swift in Sources */,
B3BEDEDF2A5064E800861533 /* ContentView.swift in Sources */,
BAE182562AF36AD3004D6494 /* ChallengeResultView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -376,6 +380,7 @@
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = "w3s-ios-sample";
INFOPLIST_KEY_NSFaceIDUsageDescription = "Enable Biometrics PIN";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
Expand Down Expand Up @@ -408,6 +413,7 @@
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = "w3s-ios-sample";
INFOPLIST_KEY_NSFaceIDUsageDescription = "Enable Biometrics PIN";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
Expand Down
111 changes: 111 additions & 0 deletions Sample App/w3s-ios-sample-app-wallets/ChallengeResultView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
//
// ChallengeResultView.swift
// w3s-ios-sample-app-wallets
//
// Created by CIRCLE on 2023/11/1.
//

import SwiftUI
import CircleProgrammableWalletSDK

struct ChallengeResultView: View {
var executeResult: CircleProgrammableWalletSDK.ExecuteCompletionStruct

@Binding var path: NavigationPath

@State var challengeId: String = ""

@State var showToast = false
@State var toastMessage: String?
@State var toastConfig: Toast.Config = .init()

var body: some View {
VStack {
List {
/// Section I
if let challengeId = executeResult.challenges.first {
sectionInputField("Challenge ID", text: challengeId)
}

/// Section II
switch executeResult.result {
case .success(let result):
let challeangeType = result.resultType.rawValue
sectionInputField("Challeange Type", text: challeangeType)

let challengeStatus = result.status.rawValue
sectionInputField("Challenge Status", text: challengeStatus)

/// Support from version 1.0.11 (622)
if let signature = result.data?.signature {
sectionInputField("Signature", text: signature)
}

case .failure(let error):
let errorCode = error.errorCode
sectionInputField("Error Code", text: String(describing: errorCode))

let errorDisplayString = error.displayString
sectionInputField("Error Message", text: errorDisplayString)
}

/// Section III
if let executeWarning = executeResult.onWarning {
let warningType = executeWarning.warningType
sectionInputField("Warning Type", text: String(describing: warningType))

let warningString = executeWarning.warningString
sectionInputField("Warning Message", text: warningString)
}
}
.listStyle(InsetGroupedListStyle())
versionText
}
.navigationBarBackButtonHidden()
.toolbar {
ToolbarItem(placement: ToolbarItemPlacement.navigationBarLeading) {
Button {
path.removeLast()
} label: {
Image(uiImage: UIImage(named: "ic_navi_back")!)
}
}
}
.toast(message: toastMessage ?? "",
isShowing: $showToast,
config: toastConfig)
}

var versionText: some View {
Text("CircleProgrammableWalletSDK - \(WalletSdk.shared.sdkVersion() ?? "")").font(.footnote)
}

func sectionInputField(_ title: String, text: String) -> Section<Text, some View, EmptyView> {
Section {
Button(action: {
UIPasteboard.general.string = text
showToast(message: "Copied: \(text)")
}) {
Text(text)
.foregroundColor(.black)
.frame(maxWidth: .infinity, alignment: .leading)
.multilineTextAlignment(.leading)
}
.buttonStyle(.bordered)
.buttonBorderShape(.roundedRectangle)
} header: {
Text(title + " :")
}
}
}

extension ChallengeResultView {

func showToast(message: String) {
toastMessage = message
showToast = true

toastConfig = Toast.Config()
}

}
139 changes: 123 additions & 16 deletions Sample App/w3s-ios-sample-app-wallets/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,26 +25,35 @@ struct ContentView: View {
@State var userToken = ""
@State var encryptionKey = ""
@State var challengeId = ""
@State var enableBiometrics = false

@State var showToast = false
@State var toastMessage: String?
@State var toastConfig: Toast.Config = .init()

@State var path = NavigationPath()

var body: some View {
VStack {
List {
titleText
sectionEndPoint
sectionInputField("App ID", binding: $appId)
sectionInputField("User Token", binding: $userToken)
sectionInputField("Encryption Key", binding: $encryptionKey)
sectionInputField("Challenge ID", binding: $challengeId)
sectionExecuteButton

Spacer()
// TestButtons
NavigationStack(path: $path) {
VStack {
List {
titleText
sectionEndPoint
sectionInputField("App ID", binding: $appId)
sectionInputField("User Token", binding: $userToken)
sectionInputField("Encryption Key", binding: $encryptionKey)
sectionInputField("Challenge ID", binding: $challengeId)
sectionToggle("Biometrics", binding: $enableBiometrics)
sectionButtons

// TestButtons
}
.listStyle(InsetGroupedListStyle())
versionText
}
.navigationDestination(for: CircleProgrammableWalletSDK.ExecuteCompletionStruct.self) { executeResult in
ChallengeResultView(executeResult: executeResult, path: $path)
}
versionText
}
.scrollContentBackground(.hidden)
.onAppear {
Expand All @@ -55,9 +64,16 @@ struct ContentView: View {
}
}
.onChange(of: appId) { newValue in
self.adapter.updateEndPoint(endPoint, appId: newValue)
if let errStr = self.adapter.updateEndPoint(endPoint, appId: newValue, biometrics: enableBiometrics) {
showToast(.failure, message: "Error: " + errStr)
}
self.adapter.storedAppId = newValue
}
.onChange(of: enableBiometrics) { newValue in
if let errStr = self.adapter.updateEndPoint(endPoint, appId: appId, biometrics: newValue) {
showToast(.failure, message: "Error: " + errStr)
}
}
.toast(message: toastMessage ?? "",
isShowing: $showToast,
config: toastConfig)
Expand Down Expand Up @@ -88,7 +104,26 @@ struct ContentView: View {
}
}

var sectionExecuteButton: some View {
func sectionToggle(_ title: String, binding: Binding<Bool>) -> some View {
Section {
Toggle(isOn: binding) {
HStack {
Text(title)
Image(systemName: "faceid")
}
}
}
}

var sectionButtons: some View {
Section {
executeButton
setBiometricsButton
Spacer()
}
}

var executeButton: some View {
Button {
guard !userToken.isEmpty else { showToast(.general, message: "User Token is Empty"); return }
guard !encryptionKey.isEmpty else { showToast(.general, message: "Encryption Key is Empty"); return }
Expand All @@ -102,6 +137,28 @@ struct ContentView: View {
.buttonStyle(.borderedProminent)
.listRowSeparator(.hidden)
}

var setBiometricsButton: some View {
Button {
biometricsPIN(userToken: userToken, encryptionKey: encryptionKey)
} label: {
Text("Set Biometrics")
.frame(maxWidth: .infinity)
}
.buttonStyle(.borderedProminent)
.listRowSeparator(.hidden)
.padding([.top], 8)
}
}

extension CircleProgrammableWalletSDK.ExecuteCompletionStruct: Hashable {
public func hash(into hasher: inout Hasher) {
hasher.combine(challenges)
}

public static func == (lhs: CircleProgrammableWalletSDK.ExecuteCompletionStruct, rhs: CircleProgrammableWalletSDK.ExecuteCompletionStruct) -> Bool {
return lhs.challenges.first == rhs.challenges.first
}
}

extension ContentView {
Expand All @@ -127,10 +184,48 @@ extension ContentView {
}

func executeChallenge(userToken: String, encryptionKey: String, challengeId: String) {
var showChallengeResult = true

WalletSdk.shared.execute(userToken: userToken,
encryptionKey: encryptionKey,
challengeIds: [challengeId]) { response in
switch response.result {
case .success(let result):
let challengeStatus = result.status.rawValue
let challeangeType = result.resultType.rawValue
let warningType = response.onWarning?.warningType
let warningString = warningType != nil ?
" (\(warningType!))" : ""
showToast(.success, message: "\(challeangeType) - \(challengeStatus)\(warningString)")

response.onErrorController?.dismiss(animated: true)

case .failure(let error):
showToast(.failure, message: "Error: " + error.displayString)
errorHandler(apiError: error, onErrorController: response.onErrorController)

if error.errorCode == .userCanceled {
showChallengeResult = false
}
}

if let onWarning = response.onWarning {
print(onWarning)
}

if showChallengeResult {
path.append(response)
}
}
}

func biometricsPIN(userToken: String, encryptionKey: String) {
guard !userToken.isEmpty else { showToast(.general, message: "User Token is Empty"); return }
guard !encryptionKey.isEmpty else { showToast(.general, message: "Encryption Key is Empty"); return }

WalletSdk.shared.setBiometricsPin(userToken: userToken, encryptionKey: encryptionKey) {
response in
switch response.result {
case .success(let result):
let challengeStatus = result.status.rawValue
let challeangeType = result.resultType.rawValue
Expand All @@ -145,7 +240,16 @@ extension ContentView {

func errorHandler(apiError: ApiError, onErrorController: UINavigationController?) {
switch apiError.errorCode {
case .userHasSetPin:
case .userHasSetPin,
.biometricsSettingNotEnabled,
.deviceNotSupportBiometrics,
.biometricsKeyPermanentlyInvalidated,
.biometricsUserSkip,
.biometricsUserDisableForPin,
.biometricsUserLockout,
.biometricsUserLockoutPermanent,
.biometricsUserNotAllowPermission,
.biometricsInternalError:
onErrorController?.dismiss(animated: true)
default:
break
Expand All @@ -158,6 +262,9 @@ extension ContentView {
Button("Change PIN", action: changePIN)
Button("Restore PIN", action: restorePIN)
Button("Enter PIN", action: enterPIN)
Button("Set Biometrics PIN") {
biometricsPIN(userToken: userToken, encryptionKey: encryptionKey)
}

} header: {
Text("UI Customization Entry")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "eye_closed.pdf",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.
Loading