使用 SwiftUI 和 GraphQL 的 Instagram 克隆
今天,我们将开始一系列博文,教你如何使用大量酷炫工具来创建自己的社交网络:一个类似 Instagram 的应用程序。
我们不会节省技术成本,我们将使用最新、最棒的工具:Parse、GraphQL、一些 NodeJS,尤其是(尚未发布的)Apple最新框架 SwiftUI。
这将需要几篇文章才能完全发挥作用,但当我们做到这一点时,你就会意识到在 Back4app,只需很少的努力就能让你的想法变得如此简单。
为了更好地学习,请下载带源代码的iOS Instagram 克隆项目。
看来是时候…
Contents
您想快速上手?
从我们的中枢克隆这个应用程序,并开始使用它,没有任何麻烦!
一些介绍
您可能已经知道 Parse 为您的开发过程带来的巨大好处,尤其是在 Back4app 托管的情况下,因为我们的工具集可以极大地提高工作效率。
我们最近关于GraphQL的文章向你展示了,如果使用GraphQL,随着时间的推移,维护 API 的开发会变得多么容易。如果您错过了这些文章,我强烈建议您花点时间阅读一下,因为它们将大大提高您的理解能力。
您可以阅读有关 GraphQL的更多信息,我们在Web API 一文中也有一篇关于如何使用GraphQL的完整文章。
您还可以在阅读GraphQL 和 NodeJS 的云代码函数中看到与NodeJS 的一些集成。
剩下的就是 SwiftUI 了。
根据 Apple 的说法,SwiftUI 是 “利用 Swift 的强大功能在所有 Apple 平台上构建用户界面的一种创新、异常简单的方法”。
我个人认为,SwiftUI 的功能远不止于此。
如果你像我一样开发软件有一段时间了,你可能会知道开发者社区一直在使用 Storyboards(Apple的拖放式用户界面构建界面)的便利性和程序化用户界面的强大功能之间徘徊。
SwiftUI 的出现带来了这两个世界的最佳选择:它既便于初学者学习,又保持了编码用户界面的可维护性。
将其与实时渲染相结合,开发人员就能轻松地看到他们正在编码的可视化输出,并使其具有双向性,这样,如果您更改代码,它就会反映到用户界面上,而从图形上更改用户界面,它也会反映到代码上。
那么,你需要什么呢?
在撰写这篇文章时,Apple仍在测试新的 macOS Catalina、XCode 11、iOS 13 和其他一些即将上架的产品。
虽然你可以在 macOS Mojave 中使用 XCode 11,但你需要使用 Catalina 才能实时呈现 SwiftUI 的预览效果。
没有 Catalina?就没有预览!
这是你必须做出的决定,因为我发现将 Bash 更改为 ZSH 作为 Catalina 中的 macOS 官方 shell 会破坏我在日常工作流程中使用的许多工具和脚本,因此我决定暂时使用 Mojave。你可以自行决定是否更新。
此外,您还需要一个已创建应用程序的 Back4app 账户。您可以在Back4app 文档中了解如何创建第一个应用程序。
最后,我们将使用Apollo Client将 GraphQL 集成到我们的项目中。如果您没有使用经验,我已经写过一篇非常详细的文章,介绍如何配置和使用Web Authentication。
因此,在我们开始之前,你需要
- 已安装 XCode 11(如果想看预览版,请安装 macOS Catalina)
- 在 Back4app 中创建新应用程序
- 安装并配置好 Apollo 客户端
我的应用程序将被命名为Back4Gram,因为它很像 Instagram。
我们的第一课
我经常跟你们讲 Parse 是如何创建用户类(User class)和角色类(Role class)的,前者可以注册新用户并对现有用户进行身份验证,后者可以对具有类似权限的用户进行分组。这很好,因为至少在身份验证时,我们不需要创建任何类。
用户类有三个自动创建的属性,其中两个是必须的,一个是可选的:
- 用户名(必填)
- 密码(必填)
- 电子邮件(可选)
这些就足够我们的应用程序注册和管理用户了。
你可能知道,Apple公司对存储用户电子邮件的政策非常严格,所以我建议不要询问或保留这些信息,否则可能会未获批准。
由于这只是一个教程,我将要求用户输入这些信息,让你看看它是如何工作的。
自动创建用户和角色类
如果这还不够有用,新创建的应用程序已经为我们自动创建了 GraphQL 查询和突变。这不仅适用于我们自动创建的类,也适用于您长期创建的任何其他类。我们还为您提供了相关文档!
我告诉过你我们还有自动完成功能吗?是的!
因此,我的第一个突变将如上所述,并使用我们特定的突变 signUp() 来创建一个新用户:
查询将取决于你使用的 Parse 版本:
Parse 3.7.2:
突变 signUpUser($username: 字符串!, $password: 字符串!, $email: 字符串) { 用户 { signUp( fields:{ username: $username, password: $password, email:$email } ){ 对象 ID } } }
Parse 3.8:
突变 signUpUser($username: 字符串!, $password: 字符串!, $email: 字符串) { signUp( fields:{ username: $username, password: $password, email:$email } ){ 对象 ID } }
Parse 3.9:
突变 signUpUser($username: 字符串!, $password: 字符串!, $email: 字符串) { signUp( fields:{ username: $username, password: $password, email:$email } ){ id } }
请注意,我在变量 username 和 password 的类型后面加上了”!”,使它们成为强制性变量,而 email 则是可选变量,在其类型后面没有加上”!”。
最后,我将返回新创建用户的 objectId。
既然到了这里,我们也来编写登录已存在用户的 GraphQL 变量代码。
在这种情况下,用户名和密码都是必须的,我将为用户获取 sessionToken:
Parse 3.7.2
突变 logInUser($username: String!, $password: String!){ 用户{ 登录(用户名:$username,密码:$password){ 会话令牌 } } }
Parse 3.8
突变 logInUser($username: String!, $password: String!){ logIn(username: $username, password: $password){ 会话令牌 } }
Parse 3.9
突变 logInUser($username: String!, $password: String!){ logIn(username: $username, password: $password){ 会话令牌 } }
最后但并非最不重要的一点是,用户注销的突变不需要任何参数,只返回一个布尔值:
Parse 3.7.2
变异 logOutUser{ 用户{ 注销 } }
Parse 3.8
变异 logOutUser{ logOut }
Parse 3.9
变异 logOutUser{ logOut }
将所有这些内容添加到一个名为UserMutations.graphql 的新文件中,同时保存你在上一篇文章中学过如何下载的schema.graphql文件。
请妥善保存这些文件,因为我们将把它们添加到我们的…
SwiftUI 项目
让我们创建 SwiftUI 项目。
启动 XCode 11,在 “欢迎使用 XCode “窗口中点击 “创建一个新的 XCode 项目”。
如果你没有看到该窗口,也可以进入 “文件”>”新建”>”项目”。
在顶部选择 “iOS 应用程序”,然后选择 “单视图应用程序”。给它起个好名字,记得选择 Swift 作为语言,SwiftUI 作为用户界面:
别忘了选择 SwiftUI 作为用户界面
现在,安装Apollo Client,就像在Web api 文章中学到的那样,然后将UserMutations.graphql和schema.graphql文件添加到项目中。
编译并将新生成的API.swift文件添加到项目中。
此时,我希望你具备以下条件
- 安装并运行 XCode 11
- Apollo客户端已安装并集成到 XCode 项目中
- 在 XCode 项目中添加 UserMutations.graphql 和 schema.graphql 文件
- 在 XCode 项目中添加 API.swift 生成文件
您的项目应与此相似:
所有内容均已集成至 XCode 11
现在我们可以开始使用 SwiftUI 了。
立即注册 Back4App, 开始创建您的 Instagram 克隆应用程序。
决定我们的控件
在登录或注销用户之前,我们必须先创建该用户。因此,我们首先要编写登录界面的代码。
通常,我们可以在该屏幕中设置 4 到 5 个控件:
- 某种类型的文本,告诉用户这是什么屏幕(文本)
- 用户名(文本输入控件)
- 密码(安全文本输入控件)
- 确认密码(可选,取决于平台,安全文本输入控件)
- 电子邮件(格式化文本输入控件)
- 注册按钮(按钮)
由于这将是一个移动应用程序,我决定不使用确认密码文本输入控件,因为大多数移动平台都允许用户查看最后输入的字符,在大多数情况下,这足以让用户确定密码是否输入正确。
这些控件将按照上面的顺序垂直排列,一个在另一个下面,每个控件都有自己的颜色。
实际构建用户界面
正如我前面提到的,我没有使用 macOS Catalina,因此不会启用预览功能,但我会按照我的代码编译并向你展示编译结果。
通过 “文件”>”新建”>”文件”,然后在 “用户界面 “部分选择 “SwiftUI 视图”,创建一个新的 SwiftUI 视图。
将该文件命名为 SignUpView.swift,然后它就会集成到你的项目中。
您会发现,新创建的视图中已经有一个文本控件,其中包含 “Hello World!”字符串。
Swift 的美妙之处在于,你可以创建多个简单的视图,其中只包含该视图所需的控件,然后将多个视图合并为一个更复杂的视图。这就是最佳的面向对象。
我们决定让控件垂直排列,SwiftUI 为此提供了一个名为 VStack(垂直堆叠)的特定控件:VStack 中的所有内容都将自动垂直堆叠,因此,为了进行测试,让我们将 VStack 控件添加到ContentView.swift中,并在其中添加两次 SignUpView:
在 VStack 中添加两次 SignUpView:它显示了两次垂直对齐的 SignUpView。
现在,这多酷啊!如果我们需要更多控件,可以继续添加!
让我们移除 VStack 和重复的控件,在该视图中只添加一个 SignUpView(),然后回到我们的 SignUpView 并开始更改它,因为我们已经看到了测试结果。现在,您的 ContentView 应该与下面相似:
struct ContentView:View { var body: some View { SignUpView() } }
我们已经知道我们需要在屏幕顶部使用带文本的 VStack,因此我们只需将 VStack 添加到 SignUpView 中,并将 “Hello World!” 字符串改为有用的字符串,如 “Sign Up”。我们的 SignUpView 应该是这样的:
struct SignUpView:View { var body: some View { VStack{ Text("Sign Up") } } }
由于我们已经知道必须在下面添加哪些控件,因此可以添加
- 一个用户名文本字段
- 一个密码安全字段
- 电子邮件文本字段
- 用于注册的按钮
TextFields 和 SecureField 都与旧的 UITextField 非常相似,但必须依赖于与状态的绑定,因此我们要将它们声明为
Control(“Placeholder”, text:stateValueToBindTo)
第一步是声明要绑定的状态:
@State var username: String = "" @State var password: String = "" @State var email:字符串 = ""
有了这些,我们就可以开始添加控件了:
Text("Sign Up") TextField("用户名", text: $username) SecureField("Password", text: $password) TextField("电子邮件(可选)", text: $email)
最后,我们可以添加结构如下的 Button:
Button(action:{
//触发时运行的代码
}){
//内容
}
正如你可能注意到的,它的工作原理与 UIButton 大同小异,但其结构要灵活得多,因为我们可以通过编程定义其内容,几乎可以在其中添加任何东西:文本、图片。你说什么都行。
旧的 target/action 结构已不复存在,取而代之的是一个新的 action:{} 结构,用于处理点击时的行为。
我们的按钮将包含一个文本,上面写着 “注册!”,让我们添加它:
Button(action:{ }){ Text("Sign Up!") }
最后的代码应该是这样的
struct SignUpView:View { @State var username: String = "" @State var password: String = "" @State var email:String = "" var body: some View { VStack{ 文本("注册") TextField("Username", text: $username) SecureField("Password", text: $password) TextField("电子邮件(可选)", text: $email) Button(action:{ }){ Text("Sign Up!") } } } }
预览效果如何?
看起来很不错,但通过设置一些可视化属性,我们可以改进很多。
如果说 SwiftUI 有什么超棒的地方,那就是你可以在代码中添加可视化属性,并实时查看变化!
让我们尝试一下!
首先,让我们声明要使用的颜色
let lightGreyColor = Color(red: 239.0/255.0, green: 243.0/255.0, blue: 244.0/255.0, opacity: 1.0) let lightBlueColor = Color(red: 36.0/255.0, green: 158.0/255.0, blue: 235.0/255.0, opacity: 1.0)
然后,让我们添加一些漂亮的功能:
一些填充,这样我们就可以在这些元素之间留出一些空间。
设置背景和前景色属性,这样我们的控件就能与我们的品牌保持一致。
设置角半径(cornerRadius),这样我们就能拥有圆角。
设置字体和 fontWeight,让效果更漂亮。
就是这样!
好看吗?
现在,你就可以跟踪我们的代码了:
struct SignUpView:View { @State var username: String = "" @State var password: String = "" @State var email:String = "" let lightGreyColor = Color(red: 239.0/255.0, green: 243.0/255.0, blue: 244.0/255.0, opacity: 1.0) let lightBlueColor = Color(红色:36.0/255.0,绿色:158.0/255.0,蓝色:235.0/255.0,不透明度:1.0) var body: some View { VStack{ 文本("注册) .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) TextField("电子邮件(可选)", text: $email) .padding() .background(lightGreyColor) .cornerRadius(5.0) .padding(.bottom, 20) Button(action:{ }){ Text("Sign Up!") .font(.headline) .foregroundColor(.white) .padding() .frame(width: 220, height: 60) .background(lightBlueColor) .cornerRadius(15.0) } .padding() } }
这样,我们漂亮闪亮的屏幕就差不多准备好了。
怎么样…
一些功能
现在我们的用户界面已经准备就绪,是时候在 GraphQL 方法中添加 Apollo 调用了。
由于我们将在几乎所有的视图中使用这些调用,所以我们应该在所有视图都能访问它的地方创建一个 Apollo 客户端。这个地方就是AppDelegate.swift。
在其中的
导入 UIKit
和代码块的开头
@UIApplicationMain 类 AppDelegate:UIResponder, UIApplicationDelegate { ...
正如我们在上一篇文章中所展示的,从现在起所有的 View 都可以执行 GraphQL 查询和突变。
首先,您必须导入 Apollo 框架,然后像这样实例化您的客户端:
导入 Apollo // Apollo 客户端初始化。 更多信息请点击:https://www.back4app.com/docs/ios/swift-graphql 让 apollo:ApolloClient = { let configuration = URLSessionConfiguration.default configuration.httpAdditionalHeaders = [ "X-Parse-Application-Id":"YourAppIdHere"、 "X-Parse-Client-Key":"YourClientKeyHere ] let url = URL(string: "https://parseapi.back4app.com/graphql")! return ApolloClient( networkTransport:HTTPNetworkTransport( url: url、 configuration: 配置 ) ) }()
只需将字符串YourAppIdHere和YourClientKeyHere替换为当前值,我们就可以继续了。
您可能已经猜到,我们必须在点击注册按钮时执行注册突变,因此,让我们在操作块中调用它:
// 执行 SignUpUser 突变,传递我们刚刚从 TextFields 中获得的参数 apollo.perform(mutation.SignUpUserMutation)SignUpUserMutation(username: self.username, password: self.password, email: self.email)){ result in // 让我们切换结果,以便将成功与错误区分开来 switch result { // 在成功的情况下 case .success(let graphQLResult): // 我们尝试Parse结果 if let objId = graphQLResult.data.users.signUp.objectId { print ("User created with ObjectId: " + objId) } // 但如果出现任何 GraphQL 错误,我们会显示该消息 else if let errors = graphQLResult.errors { // GraphQL 错误 print(errors) } // 如果失败,我们将显示该消息 case .failure(let error): // 网络或响应格式错误 print(error) } }
这样就可以了,但这些 print() 方法只能打印到控制台。我们需要向用户发出警报,因此我们将其改为警报,其结构如下:
Alert(title:Text(“Title”), message:Text(“Message”), dismissButton: .default(Text(“TextOfButton”))
由于我们需要在运行时更改标题和消息,我们必须设置一个 Struct 来处理这些值,因为自变量不能在运行时更改:
struct Message { var alertTitle:字符串 = "" var alertText:字符串 = "" } var myMessage = Message()
同时创建一个状态,以便 View 知道何时显示警报:
@State private var showingAlert = false
按钮操作的完整代码应如下所示:
Button(action:{ // 执行 SignUpUser 变量,传递我们刚刚从 TextFields 获取的参数 apollo.perform(mutation:SignUpUserMutation(username: self.username, password: self.password, email: self.email)){ result in // 让我们切换结果,以便将成功与错误区分开来 switch result { // 在成功的情况下 case .success(let graphQLResult): // 我们尝试Parse结果 if let objId = graphQLResult.data.users.signUp.objectId { myMessage.alertTitle = "耶!" myMessage.alertText = "用户已注册!" self.showingAlert = true print ("User created with ObjectId: " + objId) } // 但如果出现任何 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) } } }){ Text("Sign Up!") .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")) }
看起来很有希望,是吧?
是时候了
编译并运行。尝试注册一个新用户,你应该会得到…
看看用户是否已在 Parse 控制面板中创建!
结论
您已经创建了一个可以使用 Apollo 客户端执行 GraphQL mutations 的完整 SwiftUI 视图。这真是太棒了!
我们将继续开发 Instagram 克隆应用程序!下一步是登录和退出,文章已经出炉!
敬请期待!
参考资料
- 本系列的第 1 部分是Instagram 克隆。
- 第 2 部分是Instagram 登录。
- 第 3 部分是Instagram 个人档案。
- 第 4 部分是Instagram 主页视图。
- 在 iOSInstagram Clone Backend 查看应用程序后台。
- 下载带有源代码的iOS Instagram克隆项目并开始使用Back4App。
现在就注册Back4App, 开始创建您的Instagram克隆应用程序。