使用Firebase的iOS应用开发面向初学者
使用Pring开发iOS应用
本文是针对尚未在Firebase上进行开发的开发人员的。 我将尽可能详细地解释这项工作,以便Android开发人员和Web开发人员可以立即开始。
https://github.com/1amageek/Pring
开发环境
- macOS High Sierra
- Xcode 9.4
- 斯威夫特4
- iOS 11
创建一个Xcode项目并准备Firebase
让我们创建一个新的Xcode项目
首先,启动Xcode。 让我们做一个新项目。
在这里,选择Master-Detail App。
请在产品名称中输入项目的名称。 我命名为Firebase Sample。
这是非常重要的,因为捆绑包标识符是从组织标识符生成的。 如果您有域,请使用它。
请关闭一次Xcode项目。
使用Cocoapods安装Firebase SDK
请使用Terminal移至项目目录。
在项目目录中执行以下命令。
荚初始化
如果生成Podfile
,则表示成功。
如果遇到错误,请安装Cocoapods。
https://cocoapods.org/
编辑您的Podfile
平台:ios,“ 11.0”
目标“ Firebase示例”
use_frameworks!
吊舱“ Pring”
结束
保存您的Podfile并执行以下命令
吊舱安装
如果显示如下,则表示成功。
更多文件将添加到项目目录。
创建一个名为Firebase Sample.xcworkspace的文件。 打开这个
请确认Pods已添加到项目中,如下所示。
Firebase SDK安装到此结束。
准备Firebase
我安装了Firebase SDK,但这并不意味着我可以使用Firebase。 访问Firebase控制台并配置数据库。
https://console.firebase.google.com/?pli=1
让我们转到Firebase并添加一个新项目。
从这里开始,已经提出了很棒的文章,所以请在这里参考。
迅捷で始めるFirebase入门
完成这项工作后,将像这样添加GoogleService-Info.plist 。
让我们开始开发
在使用Xcode创建的项目的初始设置中,使用Main.storyboard启动ViewController,但这一次,使用AppDelegate启动ViewController的应用程序。
首先,删除项目的“部署信息”的主界面 。
接下来,如下编辑AppDelegate
导入UIKit
导入Firebase
@UIApplicationMain
AppDelegate类:UIResponder,UIApplicationDelegate,UISplitViewControllerDelegate {
var window:UIWindow?
func application(_ application:UIApplication,didFinishLaunchingWithOptions launchOptions:[UIApplicationLaunchOptionsKey:Any]?)->布尔{
FirebaseApp.configure()
让故事板:UIStoryboard = UIStoryboard(名称:“ Main”,包:nil)
让splitViewController:UISplitViewController = storyboard.instantiateInitialViewController()为! UISplitViewController
让navigationController = splitViewController.viewControllers [splitViewController.viewControllers.count-1]为! UINavigationController
navigationController.topViewController!.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem
splitViewController.delegate =自我
self.window = UIWindow(框架:UIScreen.main.bounds)
self.window?.rootViewController = splitViewController
self.window?.makeKeyAndVisible()
返回真
}
// MARK:-拆分视图
func splitViewController(_ splitViewController:UISplitViewController,collapseSecondary secondaryViewController:UIViewController,移至primaryViewController:UIViewController)-> Bool {
守卫让secondaryAsNavController = secondaryViewController为? UINavigationController else {返回false}
警卫让topAsDetailController = secondaryAsNavController.topViewController为? DetailViewController else {返回false}
如果topAsDetailController.detailItem == nil {
//返回true表示我们已经采取了任何措施来处理崩溃; 辅助控制器将被丢弃。
返回真
}
返回假
}
}
让我们在这里构建一次。
如果显示纯白色MasterViewController,将成功。
注意
您这次没有从Xcode启动Main.storyboard的原因是在Firebase初始化时。
在
application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool
写入FirebaseApp.configure()
application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool
由于Main.storyboard在此之前已加载,因此Firebase不能与MainViewController一起使用。
保存到Firebase
首先,让我们编写一个保存数据的模型。
接下来,将一个新的Swift文件添加到项目中。
这次我们创建一个名为Item的模型。
像这样编辑项目
Pring是一个可以将Cloud Firestore文档作为对象处理的库,您可以按以下方式编写Item。
进口品
@objcMembers
类项目:对象{
动态var名称:字符串?
}
让我们将Item保存到Firebase。
如下编辑MasterViewController
func insertNewObject(_ sender: Any)
。
@objc
func insertNewObject(_ sender:Any){
让项目:项目= Item()
item.name =“巨大”
item.save()
}
用Xcode构建它。
让我们点击右上角的“ +”按钮。
如果您访问Firebase控制台并按如下所示出现在CloudFirestore中,则表示成功。
如果不是,请检查安全规则。 现在,我们正在应用一个非常宽松的安全规则来执行示例代码,但是请注意,因为它不能作为产品使用。
连接MasterView Controller和Firebase
物品可以保存在Firebase中。 接下来,让我们在MasterViewController的TableView中显示项目。
首先,将Pring导入MasterViewController 。
导入UIKit
进口品
接下来,我们将准备数据源
var dataSource:DataSource ?
请在viewDidLoad
添加以下viewDidLoad
。 在这里,我不会对其进行深入的解释,但这将是相关的。
覆盖func viewDidLoad(){
super.viewDidLoad()
//加载视图后进行其他任何设置,通常是从笔尖进行。
navigationItem.leftBarButtonItem = editButtonItem
让addButton = UIBarButtonItem(barButtonSystemItem:.add,target:self,action:#selector(insertNewObject(_ :)))
navigationItem.rightBarButtonItem = addButton
如果让split = splitViewController {
让控制器= split.viewControllers
detailViewController =(controllers [controllers.count-1] as!UINavigationController).topViewController as? DetailViewController
}
// 数据源
self.dataSource = Item.query.dataSource()
.on({[弱自我](快照,更改)在
守护让tableView:UITableView = self?.tableView else {return}
切换更改{
大小写.initial:
tableView.reloadData()
case .update(允许删除,让插入,让修改):
tableView.beginUpdates()
tableView.insertRows(at:inserts.map {IndexPath(row:$ 0,section:0)},其中:.automatic)
tableView.deleteRows(at:deletes.map {IndexPath(row:$ 0,section:0)},其中:.automatic)
tableView.reloadRows(at:changes.map {IndexPath(row:$ 0,section:0)},其中:.automatic)
tableView.endUpdates()
大小写.error(让错误):
打印(错误)
}
})。听()
}
通过重写TableView的委托,完成工作,如下所示。
覆盖func tableView(_ tableView:UITableView,numberOfRowsInSection部分:Int)-> Int {
返回self.dataSource?.count? 0
}
覆盖func tableView(_ tableView:UITableView,cellForRowAt indexPath:IndexPath)-> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier:“ Cell”,for:indexPath)
让项目:项目= self.dataSource![indexPath.row]
cell.textLabel!.text = item.name
返回单元
}
覆盖func tableView(_ tableView:UITableView,提交editingStyle:UITableViewCellEditingStyle,forRowAt indexPath:IndexPath){
如果editingStyle == .delete {
self.dataSource!.removeDocument(at:indexPath.row)
}否则,如果editingStyle == .insert {
//创建适当类的新实例,将其插入数组,然后在表视图中添加新行。
}
}
加载MasterView Controller后,它如下所示。
导入UIKit
进口品
类MasterViewController:UITableViewController {
var detailViewController:DetailViewController? =无
var dataSource:DataSource ?
覆盖func viewDidLoad(){
super.viewDidLoad()
//加载视图后进行其他任何设置,通常是从笔尖进行。
navigationItem.leftBarButtonItem = editButtonItem
让addButton = UIBarButtonItem(barButtonSystemItem:.add,target:self,action:#selector(insertNewObject(_ :)))
navigationItem.rightBarButtonItem = addButton
如果让split = splitViewController {
让控制器= split.viewControllers
detailViewController =(controllers [controllers.count-1] as!UINavigationController).topViewController as? DetailViewController
}
self.dataSource = Item.query.dataSource()
.on({[弱自我](快照,更改)在
守护让tableView:UITableView = self?.tableView else {return}
切换更改{
大小写.initial:
tableView.reloadData()
case .update(允许删除,让插入,让修改):
tableView.beginUpdates()
tableView.insertRows(at:inserts.map {IndexPath(row:$ 0,section:0)},其中:.automatic)
tableView.deleteRows(at:deletes.map {IndexPath(row:$ 0,section:0)},其中:.automatic)
tableView.reloadRows(at:changes.map {IndexPath(row:$ 0,section:0)},其中:.automatic)
tableView.endUpdates()
大小写.error(让错误):
打印(错误)
}
})。听()
}
覆盖func viewWillAppear(_动画:布尔){
clearsSelectionOnViewWillAppear = splitViewController!.isCollapsed
super.viewWillAppear(动画)
}
覆盖func didReceiveMemoryWarning(){
super.didReceiveMemoryWarning()
//处理所有可以重新创建的资源。
}
@objc
func insertNewObject(_ sender:Any){
让项目:项目= Item()
item.name =“巨大”
item.save()
}
//标记:-Segues
//这是一条注释
//覆盖func prepare(用于segue:UIStoryboardSegue,发件人:任意?){
//如果segue.identifier ==“ showDetail” {
//如果让indexPath = tableView.indexPathForSelectedRow {
//让object = objects [indexPath.row]为! NSDate
//让controller =(segue.destination as!UINavigationController).topViewController as! DetailViewController
// controller.detailItem = object
// controller.navigationItem.leftBarButtonItem = splitViewController?.displayModeButtonItem
// controller.navigationItem.leftItemsSupplementBackButton = true
//}
//}
//}
// MARK:-表格视图
覆盖func numberOfSections(在tableView中:UITableView)-> Int {
返回1
}
覆盖func tableView(_ tableView:UITableView,numberOfRowsInSection部分:Int)-> Int {
返回self.dataSource?.count? 0
}
覆盖func tableView(_ tableView:UITableView,cellForRowAt indexPath:IndexPath)-> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier:“ Cell”,for:indexPath)
让项目:项目= self.dataSource![indexPath.row]
cell.textLabel!.text = item.name
返回单元
}
覆盖func tableView(_ tableView:UITableView,canEditRowAt indexPath:IndexPath)->布尔{
//如果您不希望指定的项目可编辑,则返回false。
返回真
}
覆盖func tableView(_ tableView:UITableView,提交editingStyle:UITableViewCellEditingStyle,forRowAt indexPath:IndexPath){
如果editingStyle == .delete {
self.dataSource!.removeDocument(at:indexPath.row)
}否则,如果editingStyle == .insert {
//创建适当类的新实例,将其插入数组,然后在表视图中添加新行。
}
}
}
霍格出来如下? 这是成功的。
让我们体验Firebase的实时性
好吧,让我们体验一下Firebase的实时感觉。
让我们点击右上角的“ +”按钮。 箱子增加了,对吗?
让我们体验Firebase的脱机性能
接下来,让我们关闭Mac WiFi并创建一个离线环境。 我们还点击右上角的“ +”按钮。
如果您可以确认模拟器的“ Hoge”增加了,请检查Firebase控制台并确保数据还没有增加。
让我们也删除数据。
您可以通过按左上方的“编辑”按钮删除数据。 删除后,确认数据从控制台中消失。
更新数据
为了更新保存的数据,我们首先修改DetailViewController 。 首先将detailItem更改为Item 。
var detailItem:项目? {
didSet {
//更新视图。
configureView()
}
}
func configureView(){
//更新明细项目的用户界面。
如果让item = detailItem {
如果让label = detailDescriptionLabel {
label.text = item.name
}
}
}
接下来,我们还修改MasterViewController 。 更改注释掉了以下内容。
覆盖func prepare(用于segue:UIStoryboardSegue,发件人:任意?){
如果segue.identifier ==“ showDetail” {
如果让indexPath = tableView.indexPathForSelectedRow {
let对象:Item = self.dataSource![indexPath.row]
让controller =(segue.destination as!UINavigationController).topViewController as! DetailViewController
controller.detailItem =对象
controller.navigationItem.leftBarButtonItem = splitViewController?.displayModeButtonItem
controller.navigationItem.leftItemsSupplementBackButton = true
}
}
}
跑。 如果如下显示hoge,则表示成功。
接下来,修改Storyboard并添加TextField和Button 。 请将DetailViewController与IBOutlet和IBAction连接。
最终,DetailViewController如下所示:
导入UIKit
进口品
类DetailViewController:UIViewController {
@IBOutlet弱var detailDescriptionLabel:UILabel!
@IBOutlet弱var textField:UITextField!
@IBAction func updateAction(_ sender:Any){
self.detailItem?.name = self.textField.text
self.detailItem?.update()
}
func configureView(){
//更新明细项目的用户界面。
如果让item = detailItem {
如果让label = detailDescriptionLabel {
label.text = item.name
}
}
}
覆盖func viewDidLoad(){
super.viewDidLoad()
configureView()
}
覆盖func didReceiveMemoryWarning(){
super.didReceiveMemoryWarning()
//处理所有可以重新创建的资源。
}
var disposer:Disposer ?
var detailItem:项目? {
didSet {
//更新视图。
configureView()
self.disposer = detailItem?.listen {[弱自我](项目,错误)在
如果让错误:错误=错误{
打印(错误)
返回
}
自我?.configureView()
}
}
}
deinit {
self.disposer?.dispose()
}
}
如果显示在TextLabel中输入的文本,则将成功。
请注意听这里。
可以按以下方式实时监视对象。
self.disposer = detailItem?.listen {[弱自我](项目,错误)在
如果让错误:错误=错误{
打印(错误)
返回
}
自我?.configureView()
}
上传图片
为了创建服务,您需要上传媒体。 我将解释如何在Pring中上传图片。
请如下修改项目。 Pring可以使用文件类型上传图像。 通过将File排列为数组可以上传多张图像。
进口品
@objcMembers
类项目:对象{
动态var名称:字符串?
动态var图片:文件?
动态var图片:[文件] = []
}
接下来,请进一步修改情节提要。
将ImageView添加到DetailViewController并与IBOutlet连接
由于我们将使用UIImagePickerController选择图像,因此请如下修改DetailViewController。
点击带有viewDidLoad的ImageView,以便可以调用UIImagePicker。
覆盖func viewDidLoad(){
super.viewDidLoad()
configureView()
让tapGestureRecognizer:UITapGestureRecognizer = UITapGestureRecognizer(目标:自我,行动:#selector(didTapImageView))
self.imageView.addGestureRecognizer(tapGestureRecognizer)
self.imageView.isUserInteractionEnabled = true
}
@objc func didTapImageView(){
让imagePickerController:UIImagePickerController = UIImagePickerController()
imagePickerController.sourceType = .photoLibrary
imagePickerController.delegate =自我
self.present(imagePickerController,动画:true,完成:无)
}
接下来,上传所选图像。 要上传图像,只需将文件设置为item并执行update即可 。
扩展DetailViewController:UINavigationControllerDelegate,UIImagePickerControllerDelegate {
func imagePickerController(__ picker:UIImagePickerController,didFinishPickingMediaWithInfo信息:[String:任何]){
守护让图像:UIImage = info [UIImagePickerControllerOriginalImage]如? UIImage else {return}
让数据:数据= UIImageJPEGRepresentation(image,0.5)!
self.detailItem?.image = File(数据:数据)
self.uploadTasks = self.detailItem?.update()
picker.dismiss(动画:true,完成:无)
}
}
可以使用uploadTasks中包含的StorageUploadTask获得上传过程中的进度。
var uploadTasks:[String:StorageUploadTask]? {
didSet {
让uploadTask:StorageUploadTask = uploadTasks![uploadTasks!.keys.first!]!
uploadTask.observe(.progress){(快照)在
打印(快照。进度)
}
}
}
接下来,我们还对configureView进行了一些修改。
var donwloadTask:StorageDownloadTask?
func configureView(){
//更新明细项目的用户界面。
如果让item = detailItem {
如果让label = detailDescriptionLabel {
label.text = item.name
}
donwloadTask = item.image?.getData(完成:{[弱自我](数据,错误)在
如果让错误=错误{
打印(错误)
返回
}
self?.imageView.image = UIImage(数据:数据!)
})
}
}
最终, DetailViewController如下所示:
导入UIKit
导入Firebase
进口品
类DetailViewController:UIViewController {
@IBOutlet弱var detailDescriptionLabel:UILabel!
@IBOutlet弱var imageView:UIImageView!
@IBOutlet弱var textField:UITextField!
@IBAction func updateAction(_ sender:Any){
self.detailItem?.name = self.textField.text
self.detailItem?.update()
}
var uploadTasks:[String:StorageUploadTask]? {
didSet {
让uploadTask:StorageUploadTask = uploadTasks![uploadTasks!.keys.first!]!
uploadTask.observe(.progress){(快照)在
打印(快照。进度)
}
}
}
var donwloadTask:StorageDownloadTask?
func configureView(){
//更新明细项目的用户界面。
如果让item = detailItem {
如果让label = detailDescriptionLabel {
label.text = item.name
}
donwloadTask = item.image?.getData(完成:{[弱自我](数据,错误)在
如果让错误=错误{
打印(错误)
返回
}
self?.imageView.image = UIImage(数据:数据!)
})
}
}
覆盖func viewDidLoad(){
super.viewDidLoad()
configureView()
让tapGestureRecognizer:UITapGestureRecognizer = UITapGestureRecognizer(目标:自我,行动:#selector(didTapImageView))
self.imageView.addGestureRecognizer(tapGestureRecognizer)
self.imageView.isUserInteractionEnabled = true
}
@objc func didTapImageView(){
让imagePickerController:UIImagePickerController = UIImagePickerController()
imagePickerController.sourceType = .photoLibrary
imagePickerController.delegate =自我
self.present(imagePickerController,动画:true,完成:无)
}
覆盖func didReceiveMemoryWarning(){
super.didReceiveMemoryWarning()
//处理所有可以重新创建的资源。
}
var disposer:Disposer ?
var detailItem:项目? {
didSet {
//更新视图。
configureView()
self.disposer = detailItem?.listen {[弱自我](项目,错误)在
如果让错误:错误=错误{
打印(错误)
返回
}
自我?.configureView()
}
}
}
deinit {
self.disposer?.dispose()
}
}
扩展DetailViewController:UINavigationControllerDelegate,UIImagePickerControllerDelegate {
func imagePickerController(__ picker:UIImagePickerController,didFinishPickingMediaWithInfo信息:[String:任何]){
守护让图像:UIImage = info [UIImagePickerControllerOriginalImage]如? UIImage else {return}
让数据:数据= UIImageJPEGRepresentation(image,0.5)!
self.detailItem?.image = File(数据:数据)
self.uploadTasks = self.detailItem?.update()
picker.dismiss(动画:true,完成:无)
}
}
跑。
如果点击ImageView并选择一个图像,则应在几秒钟内上传该图像。 并且,如果图像显示在ImageView中,则表示成功。
另外,让我们看一下打印时的进度显示。
这样, Pring提供了功能强大的功能,可帮助开发App开发所需的功能。
https://github.com/1amageek/Pring