Пользовательский класс обслуживания данных Firebase: Swift 3

#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