#ios #swift #firebase #firebase-realtime-database #firebase-authentication
#iOS #swift #firebase #firebase-база данных в реальном времени #firebase-аутентификация
Вопрос:
Я ищу простой способ извлечения (а иногда и сохранения) данных из Firebase в Swift. Меня раздражает, что все мои вызовы базы данных записываются в середине кода контроллера представления. Итак, я ищу какой-то пользовательский класс обслуживания данных. Я нашел этот учебник, который близок к тому, что я хочу: http://www.mobilecyberpunks.com/?p=82 .
Они обещали часть II, но я не могу найти эту вторую часть, поэтому, я думаю, это никогда не было сделано. Во второй части они пообещали рассказать о получении и сохранении данных с помощью этой пользовательской службы данных (что для меня является наиболее важной частью всего этого).
Я думаю о классе API (как в руководстве), и когда я извлекаю данные, и они завершают извлечение из firebase, я сохраняю их в наборе данных в этом классе api. Затем я отправлю уведомление в Центр уведомлений. Но я не уверен, является ли это лучшей практикой или хорошим способом сделать это.
Есть ли у кого-нибудь идея, как это сделать (закончить этот урок, который я нашел, или другим способом)?
Заранее спасибо!
Комментарии:
1. Действительно, интересный вопрос, с нетерпением жду ответов. Интересным последующим вопросом будет стратегия достижения независимости от серверной службы, чтобы вы могли использовать свой пользовательский серверный API в своих приложениях, и его можно было бы настраивать для использования Firebase, Parse, Backendless или любой другой службы.
2. Кажется, вы правильно поняли, что именно вы ищете? Как взаимодействовать между классом firebase с вашими ViewControllers?
3. @Dravidian да, это правильно. Я ищу способ связи между классом Firebase с моими ViewControllers, но избавляюсь от всех обратных вызовов методов Firebase. Объединение этих методов в отдельный класс. этот класс содержит, например, метод getUsers() -> [User] или просто getUsers (хотя предпочтительнее первый).
4. @xpereta Я разработал удобную структуру для этого (первая часть вопроса: пользовательский класс службы данных). Код см. В Принятом ответе, который я опубликовал. Для вашего последующего вопроса: очень интересно, но на данный момент было бы бесполезно настраивать это для Parse, потому что Parse завершает работу в январе 2017 года. Я не пробовал другие сервисы, потому что в Firebase есть все, что мне нужно до сих пор. Но если это наткнется на мою работу, и я разработаю такую структуру, я буду держать вас (и всех остальных) в курсе на этой странице!
Ответ №1:
Создание пользовательского класса для обмена данными, как правило, является хорошей идеей, если вам нужны расширенные функции и вы совершаете многочисленные вызовы на свой сервер.
Два предпочтительных метода для этого:-
-
Protocol-Delegate
Метод -
_completionBlocks:
Приведенный ниже ответ содержит оба.
Пользовательский класс
import Foundation
import Firebase
@objc protocol FIRShowAlertDelegate {
func showFIRAlert(_ message : String)
@objc optional func activityIndic()
}
class FIRController :{
var delegate : FIRShowAlertDelegate!
func loginUser(_ emailAddress : String!, password : String , completionBlock : @escaping ((currentUserID : String!) -> Void)){
FIRAuth.auth()?.signIn(withEmail: emailAddress, password: password,
completion: {(user,err) in
if err != nil{
self.delegate.showFIRAlert("Error logging you in,(err?.localizedDescription)")
}else{
completionBlock(user!.uid)
}
})
}
func retrieveUserData(_ currentId : String!, completionBlock : @escaping ((_ userName : String?) -> Void)){
FIRDatabase.database().reference().child("Users").child(currentId).observeSingleEvent(of: .value, with: {(userSnap) in
if userSnap.exists(){
if let userDict = userSnap.value! as? [String:AnyObject]{
completionBlock(userDict["username"] as! String
}
}else{
completionBlock(nil, nil)
print("No such user exists: (currentId)")
}
})
}
}
Ваш ViewController
class AnyViewController : UIViewController, FIRShowAlertDelegate{
let firebaseControllerHandle : FIRController = FIRController()
override func viewDidLoad() {
super.viewDidLoad()
firebaseControllerHandle.delegate = self
firebaseControllerHandle.loginUser("abc@xyz.com", password: "123454321", completionBlock: { (userID) in
print("user : (userID), logged in")
})
}
func showFIRAlert(_ message : String){
let alertController : UIAlertController = UIAlertController(title: "MyApp", message: message, preferredStyle: .alert)
let okAction : UIAlertAction = UIAlertAction(title: "Ok", style: .default) { (alert) in
print("User pressed ok function")
}
alertController.addAction(okAction)
alertController.popoverPresentationController?.sourceView = view
alertController.popoverPresentationController?.sourceRect = view.frame
self.present(alertController, animated: true, completion: nil)
}
func activityIndic() {
// Use for showing the activity indicator while the data is being retrieved
}
}
Комментарии:
1. Спасибо! Это именно то, что я хотел! Я использовал блоки завершения, поэтому у меня все еще есть блоки завершения в моем ViewController, но намного меньше кода. Весь код в исходном обратном вызове в ViewController переместился в
2. Извините, слишком рано нажал enter. Таким образом, весь код в исходном обратном вызове переместился в мой пользовательский класс FirebaseAPI. Спасибо!
Ответ №2:
Я начал использовать это решение и немного его отшлифовал, и я пришел к довольно удобному решению.
Я создал пользовательский класс с именем FirebaseAPI. Это одноэлементный класс. Этот класс содержит все методы Firebase (аутентификация, база данных, хранилище, …).
Пример:
FirebaseAPI.swift
import FirebaseAuth
import FirebaseDatabase
class FirebaseAPI {
static let shared = FirebaseAPI()
private init() {}
//Authentication
func logInUser(onCompletion: @escaping (String?) -> Void {
FIRAuth.auth().signInAnonymously(completion: {(user, error) in
if error == nil {
onCompletion(user!.uid)
} else {
onCompletion(nil)
}
})
}
//Database
func getObjects(parameter: ParamaterClass, onCompletion: @escaping ([ObjectClass]) -> Void) {
Constants.Firebase.References.Object?.observe(.value, with: { snapshot in
var objects = [ObjectClass]()
if snapshot.exists() {
for child in snapshot.children.allObjects {
let object = Object(snapshot: child as! FIRDataSnapshot)
objects.append(object)
}
}
onCompletion(objects)
})
}
}
Константы.swift
import FirebaseDatabase
struct Constants {
struct Firebase {
static var CurrentUser: FIRDatabaseReference?
static var Objects: FIRDatabaseReference?
}
}
AppDelegate.swift
import UIKit
import Firebase
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
FIRApp.configure()
FirebaseAPI.shared.logInUser(onCompletion { uid in
if uid != nil {
Constants.Firebase.References.CurrentUser = FIRDatabase.database().reference().child("users").child(uid!)
Constants.Firebase.References.CurrentUser.keepSynced(true)
Constants.Firebase.References.Objects = FIRDatabase.database().reference().child("objects")
Constants.Firebase.Reference.Objects?.keepSynced(true)
}
})
}
return true
}
Я могу привести вам пример вызова методов в FirebaseAPI в ViewController, но пример такого метода приведен в коде AppDelegate.swift здесь (FirebaseAPI.shared .Метод LoginUser).
До сих пор использовал эту структуру в 3 разных проектах, и она работает плавно!
Комментарии:
1. Хотя с вашим кодом все в порядке, я помню, как мой технический директор (босс), который имеет большой опыт в разработке приложений для iOS (как игровых, так и корпоративных приложений), сказал мне, что я не должен раздувать свой класс AppDelegate. В любом случае, вы используете наблюдение, а не одиночное наблюдение Firebase, и, похоже, вы не удаляете наблюдателей. Я использую пользовательские классы / API-сервис для своих приложений, подключенных к Firebase, но только для SingleObserveEvent. Я публикую вопрос об этом, возможно, вы захотите ответить или заинтересовать.
2. Эй, есть какие-нибудь объяснения, почему вы пишете это в одноэлементном шаблоне?
3. Можете ли вы поделиться тем, как вы используете его в VC, если есть различия с AppDelegate (например, возможно, вы не хотите иметь ВСЕ объекты для VC? @Charlotte1993