我如何创建一个http连接使用环境变量存储为UserDefaults在swiftui?



我是swift/swiftui编程新手

我有两个用户通过TextFields输入的环境变量。基于这两个文本字段,我必须初始化我的客户端。我使用UserDefaults而不是EnvironmentObject,因为这两个字段可以被视为用户设置。

UserPreferences类

class UserPreferences: ObservableObject {
@Published var accessKey: String {
didSet {
UserDefaults.standard.set(accessKey, forKey: "accessKey")
}
}

@Published var secretKey: String {
didSet {
UserDefaults.standard.set(secretKey, forKey: "secretKey")
}
}

@Published var region: String {
didSet {
UserDefaults.standard.set(region, forKey: "region")
}
}

var regions = ["us-east-1", "us-east-2"]

init() {
self.accessKey = UserDefaults.standard.object(forKey: "accessKey") as? String ?? ""
self.secretKey = UserDefaults.standard.object(forKey: "secretKey") as? String ?? ""
self.region = UserDefaults.standard.object(forKey: "region") as? String ?? "us-east-1"
}

}

我需要根据用户的accessKey和secretKey创建一个客户端。我试着把这个客户端作为用户首选项类中的附加变量。

var client: AWSClient {
return AWSClient(
credentialProvider: .static(accessKeyId: accessKey, secretAccessKey: secretKey),
httpClientProvider: .createNew)
}

但这并不是一个真正的用户设置。最重要的是,我还得到了需要在初始化之前关闭AWSClient的错误。我能够使它与硬编码的accessKeysecretKey一起工作,并在类的初始化中关闭客户端,如下所示。

deinit {
do {
try client.syncShutdown()
} catch {
print("client shutdown error deinit")
}
}

我尝试的另一种方法是在UserPreferences对象中不包含客户端,而是在视图本身中创建一个新的客户端变量。但在一个视图中,我有一个辅助函数,我不能做一个优雅的关闭客户端作为函数和结构体没有deinit在他们。

但我觉得必须有一个更好的方式来初始化这个客户端,并在我的任何视图中使用它。

谢谢你的帮助。

编辑:

这是我的偏好视图。

struct PreferencesView: View {

@ObservedObject var userPreferences = UserPreferences()

var body: some View{
VStack{

Text("Preferences")
.font(.title)

HStack{
Text("Access Key:")
SecureField("Access Key", text: $userPreferences.accessKey)
}
HStack{
Text("Access Secret:")
SecureField("Access Secret", text: $userPreferences.secretKey)
}
HStack {
Picker(selection: $userPreferences.region, label: Text("Region:")) {
ForEach(userPreferences.regions, id: .self) { region in Text(region)
}
}
Spacer()
Button{checkAWSClient(accessKey: userPreferences.accessKey, secretKey: userPreferences.secretKey)} label: {
Text("Check AWS Credentials")
}
Button{print("done button clicked")} label:{
Text("Done")
}
}
Spacer()
}.frame(maxWidth: .infinity, maxHeight: .infinity)
.padding()
}
}

它有两个文本字段,用于两个用户首选项字符串,我将它们存储为用户默认值。还有一个按钮,当点击时,将使用用户首选项检查与一些外部服务的连接。

函数CheckAWSClient如下

func checkAWSClient(accessKey: String, secretKey: String){
print("Checking aws client")
let client = AWSClient(credentialProvider: .static(accessKeyId: accessKey, secretAccessKey: secretKey), httpClientProvider: .createNew)
print(client)
let s3 = S3(client: client, region: .useast1)
s3.listBuckets()
.whenComplete {response in
switch response {
case .failure(let error):
print(error)
print("Failure s3")

case .success(let output):
print(output)
print("Success s3")

}
}

let ec2 = EC2(client: client, region: .useast1)
let describeInstancesRequest = EC2.DescribeInstancesRequest(dryRun: false)
ec2.describeInstances(describeInstancesRequest)
.whenComplete {response in
switch response {
case .failure(let error):
print(error)
print("Failure EC2")
case .success(let output):
print(output)
print("Success EC2")
}
}    
}

但我得到一个错误说我需要做client.shutdown()在初始化。但这意味着什么呢?我在一些视图的辅助函数中初始化客户端。

这里是错误

Assertion failed: AWSClient not shut down before the deinit. Please call client.syncShutdown() when no longer needed.: file SotoCore/AWSClient.swift, line 95
2021-02-23 03:41:20.839170+0530 EC2 Menu Bar[3047:23906299] Assertion failed: AWSClient not shut down before the deinit. Please call client.syncShutdown() when no longer needed.: file SotoCore/AWSClient.swift, line 95

tl;dr我需要使用他设置的用户默认设置,并创建一个数据库连接之类的东西。这样我就可以从不同的视图进行各种rest查询。这样做的好处是什么?

假设所有这些调用都是异步的,我认为函数应该如下所示。主要思想是在所有活动结束之前捕获客户端(因为它是在堆栈上创建的,所以没有所有者,并且一旦不再引用它就会被销毁),并仅在失败或最后关闭它。

func checkAWSClient(accessKey: String, secretKey: String){
print("Checking aws client")
let client = AWSClient(credentialProvider: .static(accessKeyId: accessKey, secretAccessKey: secretKey), httpClientProvider: .createNew)
print(client)

let shutdown = {[client] in     // << capture client
do {
try client.syncShutdown()
} catch {
print("client shutdown error deinit")
}
}

let s3 = S3(client: client, region: .useast1)
s3.listBuckets()
.whenComplete {response in
switch response {
case .failure(let error):
print(error)
print("Failure s3")

// failure: client is not needed anymore - shutdown
shutdown()

case .success(let output):
print(output)
print("Success s3")

// continue with EC2 only on list success
let ec2 = EC2(client: client, region: .useast1)
let describeInstancesRequest = EC2.DescribeInstancesRequest(dryRun: false)
ec2.describeInstances(describeInstancesRequest)
.whenComplete {response in
switch response {
case .failure(let error):
print(error)
print("Failure EC2")
case .success(let output):
print(output)
print("Success EC2")
}

//completed: client is not needed anymore - shutdown
shutdown()
}
}
}
}

注意:不能测试,所以可能需要一些打字修复或调优

最新更新