微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

如何解决如何使用在 swiftui 中存储为 UserDefaults 的环境变量创建 http 连接?

我是 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)
    }

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

    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,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")
            }
        }    
}

但我收到一个错误提示我需要在 deinit 中执行 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 我需要利用他设置的用户认设置并进行数据库连接之类的事情。这样我就可以从不同的视图进行各种休息查询。什么是最好的做法?

解决方法

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

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()
                    }
            }
        }
}

注意:无法测试,因此可能需要一些错字修正或调整

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。