使用 SwiftUI 和 GraphQL 克隆 Instagram – 登录

在上一篇关于如何创建Instagram 克隆应用程序的文章中,你了解了如何在 XCode 11 中配置 SwiftUI 启动和运行的一切,并使用 GraphQL 创建了一个完全可用的 Sing Up 视图。

今天,我们将学习如何创建登录视图并让用户注销。

我们需要使用上一篇文章中的项目,所以如果你没有关注那篇文章,我强烈建议你关注一下。

系好安全带,我们出发吧!

为了更好地学习,请下载带源代码的iOS Instagram 克隆项目

你想快速入门吗?

从我们的中枢克隆这个应用程序,然后开始使用它,不会有任何麻烦!

创建登录视图

我们的登录视图与注册视图非常相似,甚至更简单。

在登录或登录用户突变中,我们只需要两个参数:用户名和密码:

查询和突变将取决于你选择的 Parse 版本:

Parse 3.7.2:

突变 logInUser($username: String!, $password: String!){
  用户{
    登录(用户名:$username,密码:$password){
      会话令牌
    }
  }
}

Parse 3.8:

突变 logInUser($username: String!, $password: String!){
    登录(用户名:$username,密码:$password){
      会话令牌
    }
}

Parse 3.9:

突变 logInUser($username: String!, $password: String!){
    登录(用户名:$username,密码:$password){
      会话令牌
    }
}

所以我们只需要向用户询问这些信息。

首先,让我们添加一个新的 SwiftUI 视图,访问文件 > 新建 > 文件,然后选择我们的 SwiftUI 视图

screen-shot-2019-08-26-at-11-08-58

将此视图命名为LogInView.swift,并在项目中打开它:

screen-shot-2019-08-28-at-10-54-07

正如您已经学习过的那样,用我们需要的控件创建 VStack:

  • 用户名文本字段
  • 密码安全字段
  • 用于执行操作的按钮

为了保持设计的一致性,我将我们将要使用的颜色移到了 AppDelegate.swift 中,因此我还必须在这里导入 SwiftUI:

导入 SwiftUI

let lightGreyColor = Color(红色:239.0/255.0,绿色:243.0/255.0,蓝色:244.0/255.0,不透明度:1.0)
let lightBlueColor = Color(red: 36.0/255.0, green: 158.0/255.0, blue: 235.0/255.0, opacity: 1.0)

记住要删除SignUpView.swiftLogInView.swift 中的颜色线。

此外,为了保持控件的一致性,我复制并粘贴了 SignUp 视图中的内容,删除了电子邮件 TextField,并更改了 TextFields 以反映新的功能。我的代码最终是这样的

struct LogInView:View {
    @State var username: String = ""
    @State var password: String = ""
    
    @State private var showingAlert = false

    var body: some View {
       VStack{
           Text("Log In")
               .font(.largeTitle)
               .foregroundColor(lightBlueColor)
               .fontWeight(.semibold)
               .padding(.bottom, 20)
           TextField("Username", text: $username)
               .padding()
               .background(lightGreyColor)
               .cornerRadius(5.0)
               .padding(.bottom, 20)
           SecureField("Password", text: $password)
               .padding()
               .background(lightGreyColor)
               .cornerRadius(5.0)
               .padding(.bottom, 20)
           Button(action:{
            
           }){
               Text("Log In!")
                .font(.headline)
                .foregroundColor(.white)
                .padding()
                .frame(width: 220, height: 60)
                .background(lightBlueColor)
                .cornerRadius(15.0)
           }
       .padding()
    }
}

这很简单。看看效果如何?
更改 ContentView.swift 以显示该视图:

struct ContentView : View {
    var body: some View {
        登录视图
    }
}

screen-shot-2019-08-28-at-11-16-13

看起来很整洁!

让我们在顶部添加徽标,使其更加整洁!
我们的徽标由一张图片组成,我们将把这张图片整合到我们的项目中。我使用了这张图片。

将图片拖放到项目中的 Assets.xcassets 文件夹中:

screen-shot-2019-08-28-at-13-25-54

看起来应该是这样的:

screen-shot-2019-08-28-at-13-27-30

从现在起,我们可以在代码中引用它,并将其命名为:logo-social。

现在就注册 Back4App开始创建您的 Instagram 克隆应用程序。

我们的徽标

我们的徽标将由该图片组成,但简单地将图片放在那里会显得很业余。我们要让它熠熠生辉:圆形,大小固定,在边框上有一些描边,当然还有阴影,因为……阴影。

代码如下

图像("Logo-social")
    .resizable()
    .aspectRatio(contentMode: .fit)
    .frame(width: 150, height: 150)
    .clipShape(Circle())
    .overlay(Circle().stroke(Color.blue, lineWidth: 2))
    .shadow(radius: 5)
    .padding(.bottom, 75)

然后将其放在 VStack 的顶部:

var body: some View {
       VStack{
           图片("logo-social")
                .resizable()
                .aspectRatio(contentMode: .fit)
                .frame(width: 150, height: 150)
                .clipShape(Circle())
                .overlay(Circle().stroke(Color.blue, lineWidth: 2))
                .shadow(radius: 5)
                .padding(.bottom, 75)
           文本("登录)
               .font(.largeTitle)
               .foregroundColor(lightBlueColor)
               .fontWeight(.semibold)
               .padding(.bottom, 20)
...

screen-shot-2019-08-28-at-13-33-41

现在,是不是很漂亮?

会话令牌

我们的登录过程将返回一个会话令牌字符串。我们必须保证会话令牌的安全,因为它将在我们的操作过程中使用:当会话令牌有效时,我们就可以访问我们的应用程序。当它被删除或失效时,我们的调用将被拒绝。

由于这与安全直接相关,我们需要安全地存储它。这样做的正确位置是 iOS 的钥匙串。

钥匙串的一个缺点是使用起来既麻烦又无聊,因此我决定使用这个封装器来管理它。它让生活变得简单多了,而且由于我们已经在使用Cocoapods,所以它是完全合理的。

让我们编辑Podfile并将其添加到 pod 中:

pod "SwiftKeychainWrapper

然后使用以下命令更新 Pod

pod install

更新 Pod,最后重新打开xcworkspace项目。

现在,我们可以使用钥匙串引擎…

存储会话令牌

根据我们新 pod 的文档,将值保存到钥匙串的方法是

KeychainWrapper.standard.set("SomeValue", forKey: "SomeKey")

但我们也要为它添加一些逻辑。

首先,让我们导入包装器:

导入 SwiftKeychainWrapper

首先,我们必须调用我们的logInUser变量,当该变量响应时,如果有会话令牌,我们就存储它。如果没有,我们必须发出警报通知用户。

现在,如果你还记得一篇文章,我们已经编写了一个警报,包括其结构。让我们从SingUpView.swift中移除下面的代码,并将其传递给AppDelegate.swift文件,以便所有视图都能访问它,从而重复使用该代码:

struct Message {
    var alertTitle:字符串 = ""
    var alertText:字符串 = ""
}

var myMessage = Message()

现在,回到我们的逻辑,我们要做的第一件事就是确定用户是否填写了用户名和密码文本框。如果没有,则登录过程中没有任何信息,我们必须通知用户。

我们的登录按钮操作代码应检查这一点。让我们添加这段代码,检查与这些文本框链接的状态变量的字符串大小:

           Button(action:{
            // 检查是否输入了密码
            if (self.password.count == 0 || self.username.count == 0){
                
                // 如果没有,我们必须显示警报
                myMessage.alertText = "您必须提供用户名和密码"。
                myMessage.alertTitle = "哎呀......"
                self.showingAlert = true
            } else {
                // 如果是,我们可以继续
                
            }
           }){
               Text("Log In!")
                .font(.headline)
                .foregroundColor(.white)
                .padding()
                .frame(width: 220, height: 60)
                .background(lightBlueColor)
                .cornerRadius(15.0)
           }
           .alert(isPresented: $showingAlert) {
               警报(title:Text(myMessage.alertTitle), message:Text(myMessage.alertText), dismissButton: .default(Text("OK"))
           }

并进行测试…

screen-shot-2019-09-02-at-10-22-15

很好

现在,我们必须调用 GraphQL 突变,以检索会话令牌,如果有的话,将其存储到钥匙串中。
我们已经学习了如何调用突变,所以现在就开始调用,但这次如果我们获得了 sessionToken,就会将其存储起来:

// 执行 LogInUser 突变,传递我们刚刚从 TextFields 获取的参数
apollo.perform(mutation:LogInUserMutation(username: self.username, password: self.password)){ result in
    // 让我们切换结果,以便将成功与错误区分开来
    switch result {
        // 在成功的情况下
        case .success(let graphQLResult):
            // 我们尝试Parse结果
            if let sessionToken = graphQLResult.data.users.logIn.sessionToken { 我的消息。
                myMessage.alertTitle = "耶!"
                myMessage.alertText = "用户已登录!"
                
                self.showingAlert = true

                print ("User sessionToken " + sessionToken)
                

                // 将会话令牌写入我们的钥匙串
                让 _:Bool = KeychainWrapper.standard.set(sessionToken, forKey: "Back4Gram.sessionToken")
            }
            // 但如果出现任何 GraphQL 错误,我们会显示该消息
            else if let errors = graphQLResult.errors {
                // GraphQL 错误

                myMessage.alertTitle = "哎呀!"
                myMessage.alertText = "我们有一个 GraphQL 错误:" + errors.description
                self.showingAlert = true

                print(errors)
            }
        // 如果出现故障,我们将显示该信息
        case .failure(let error):
            // 网络或响应格式错误
            myMessage.alertTitle = "哎呀!"
            myMessage.alertText = "我们出错了:" + error.localizedDescription
            self.showingAlert = true
            
            print(error)
    }
}

我们来测试一下!

screen-shot-2019-09-02-at-13-11-02

真不错但我们怎么知道它真的成功了呢?
我们可以进入应用程序的 Parse 控制面板,检查是否写入了新的会话对象:

screen-shot-2019-09-02-at-13-11-34

就像开了挂一样!

既然来了…

添加一个注销按钮如何?只是为了测试,让我们知道一切都很顺利:

Button(action:{
    // 检查是否存在会话令牌存储空间Stringhould only log out if logged in.
    if (KeychainWrapper.standard.string(forKey: "Back4Gram.sessionToken") != nil) {
        print("Found sessionToken! We can logout.")
        
        // 执行注销用户突变
        apollo.perform(mutation.LogOutUserMutation)LogOutUserMutation()){ result in
            // 让我们切换结果,以便将成功与错误区分开来
            switch result {
                // 在成功的情况下
                case .success(let graphQLResult):
                    // 我们尝试Parse结果
                    if let result = graphQLResult.data.users.logOut {
                        if (result) {
                            myMessage.alertTitle = "耶!"
                            myMessage.alertText = "用户已注销!"
                            
                            self.showingAlert = true
                            
                            // 清除存储的会话令牌
                            让 _:Bool = KeychainWrapper.standard.set("", forKey: "Back4Gram.sessionToken")
                        } else {
                            myMessage.alertTitle = "哎呀!"
                            myMessage.alertText = "用户注销操作返回错误"。
                            self.showingAlert = true
                        }
                    }
                    // 但如果出现任何 GraphQL 错误,我们会显示该消息
                    else if let errors = graphQLResult.errors {
                        // GraphQL 错误

                        myMessage.alertTitle = "哎呀!"
                        myMessage.alertText = "我们有一个 GraphQL 错误:" + errors.description
                        self.showingAlert = true

                        print(errors)
                    }
                // 如果出现故障,我们将显示该信息
                case .failure(let error):
                    // 网络或响应格式错误
                    myMessage.alertTitle = "哎呀!"
                    myMessage.alertText = "我们出错了:" + error.localizedDescription
                    self.showingAlert = true
                    
                    print(error)
            }
        }
    } else {
        // 网络或响应格式错误
        myMessage.alertTitle = "哎呀!"
        myMessage.alertText = "用户似乎未登录。"
        self.showingAlert = true
    }
}){
    文本("注销登录")
        .font(.headline)
        .foregroundColor(.white)
        .padding()
        .frame(width: 220, height: 60)
        .background(lightBlueColor)
        .cornerRadius(15.0)
}
.alert(isPresented: $showingAlert) {
    警报(title:Text(myMessage.alertTitle), message:Text(myMessage.alertText), dismissButton: .default(Text("OK"))
}

再次测试一下!

screen-shot-2019-09-02-at-13-51-50

很好!

我们稍后将把注销按钮移到其他地方,但现在,它可以显示我们的流程正在运行。

但是,我们注销后,我们的会话对象怎么样了?

screen-shot-2019-09-02-at-14-04-31

如预期一样,自动消失了!

结论

恭喜你!你现在已经实现了登录和注销功能!不仅如此,你还学会了如何调用不同的突变、验证结果并将值存储到钥匙串中!真是太棒了!

在下一章中,我们将开始使用多视图,并开始构建主视图!

敬请期待!

参考资料

现在就注册Back4App开始创建你的Instagram克隆应用程序。

什么是 SwiftUI?

SwiftUI 是一种为 Apple 平台上的应用程序创建用户界面的新方法。它允许开发者使用 Swift 代码来确定 UI。

什么是 sessionToken?

我们正在开发的登录流程将返回一个 sessionToken 字符串。它需要得到保护。如果我们能够保持 sessionToken 有效,我们将拥有对应用程序的访问权限;否则,我们将失去对应用程序的访问权限。这关系到安全性。

什么是钥匙串?

我们知道 sessionToken 与应用的安全性息息相关。因此,它需要存储在一个安全的地方。这个安全的地方叫做钥匙串。使用起来可能有点棘手,也可能感觉很无聊。


Leave a reply

Your email address will not be published.