Как публиковать данные в FireStore с помощью активных уведомлений? - PullRequest
0 голосов
/ 11 июня 2018

У меня есть приложение, которое реализует удаленные уведомления через API Firebase Messaging.В этом приложении я реализовал расширение службы уведомлений, которое, среди прочего, реализует действия UNNotificationActions.

В одном из этих действий я реализовал поле ввода, где вы можете написать что-то, что затем должно быть отправлено в firestore.

Я пытался реализовать это, но безуспешно.Поэтому мой вопрос: как мне написать в firestore из полнофункционального уведомления, работающего в фоновом режиме - возможно ли это вообще?

Моя реализация выглядит следующим образом:

    let likeAction = UNNotificationAction(identifier: "likeAction", title: "Like", options: [])
    let commentAction = UNTextInputNotificationAction(identifier: "commentAction", title: "Comment", options: [UNNotificationActionOptions.authenticationRequired], textInputButtonTitle: "Send", textInputPlaceholder: "Type your message")

    let category = UNNotificationCategory(identifier: "posts", actions: [likeAction, commentAction], intentIdentifiers: [], options: [])
    UNUserNotificationCenter.current().setNotificationCategories([category])

Затем в AppDelegate яреализовать функцию, которая запускается всякий раз, когда это срабатывает, следующим образом:

 func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
    switch response.actionIdentifier {
    case "commentAction":
      guard let data = response.notification.request.content.userInfo["data"] as? [String: Any] else { return }

      guard
        let channelName = data["channelName"],
        let postId = data["postId"]
        else { return }

      if let message = response as? UNTextInputNotificationResponse {
        let documentPath = "\(channelName)/\(postId))"

        let post = Post()
        post.documentPath = documentPath
        post.addComment(text: message.userText, postDocumentPath: documentPath)
      }

Я отладил код, и метод post.addComment() действительно запускается, и каждое поле имеет значение.Когда я проверяю базу данных, в нее ничего не вставляется.Консоль распечатывает это, но я не знаю, связано ли это с проблемой, я не смог найти в Интернете ничего о следующих строках:

dnssd_clientstub upload_request ОШИБКА: write_all (21,65 байт) не удалось

nssd_clientstub read_all (26) DEFUNCT

При запуске метода post не возникает ошибка из firebase.

Это была исходная информация Iмог придумать.Я могу предоставить больше кода или информации, если это необходимо.

Обновление

Я обнаружил, нажимаю ли я кнопку, когда на экране блокировки, но с iPhone вразблокированное состояние, ничего не происходит - как только я проведу вверх, и приложение покажет, запрос отправляется.Это действительно похоже на фоновую проблему.

С уважением, Крис

Ответы [ 2 ]

0 голосов
/ 23 июня 2018

Для тех, кто использует стандартную сессию NSURLS для выполнения HTTP-запроса REST пожарного хранилища (а не Alamofire, как в превосходном ответе @ ChrisEenberg выше )

[ NB: В предыдущей версии этого ответа использовались фоновые задачи, в которых нет необходимости.Это редактирование использует обычную задачу загрузки NSURLSessionDataTask и, кажется, работает, когда приложение находится в фоновом режиме или закрыто, как и ожидалось.]


OBJECTIVE-C

Внутри didReceiveNotificationResponse:

  1. Подготовить запрос

        // Grab authenticated firestore user
            FIRUser *db_user = [FIRAuth auth].currentUser;
    
        // Set up the response
            NSDictionary *reqFields;
    
        // Fields for firestore object (REST API)
            NSDictionary *my_uid_val = [[NSDictionary alloc] initWithObjectsAndKeys: (db_user.uid ?: [NSNull null]), (db_user.uid ? @"stringValue" : @"nullValue"), nil];
            NSDictionary *action_val = [[NSDictionary alloc] initWithObjectsAndKeys: response.actionIdentifier, (response.actionIdentifier ? @"stringValue" : @"nullValue"), nil];
    
        // Create object
            reqFields = @{
                @"my_uid": my_uid_val,
                @"action": action_val
            };
    
        // Place fields into expected reqBody format (i.e. under 'fields' property)
            NSDictionary *reqBody = [[NSDictionary alloc] initWithObjectsAndKeys:
                reqFields, @"fields", 
            nil];
    
        // Confirm...?
            NSLog(@"%@", reqBody);
    
  2. Составить запрос

        // Grab current user's token (for authenticated firestore REST API call)
            [db_user getIDTokenWithCompletion:^(NSString * _Nullable token, NSError * _Nullable error) {
                if (!error && token) {
                    NSLog(@"Successfully obtained getIDTokenWithCompletion: %@", token);
    
                // Compose stringified response
                // + (per https://stackoverflow.com/a/44923210/1183749 )
                    NSError *error;
                    NSData *postData = [NSJSONSerialization dataWithJSONObject:reqBody options:kNilOptions error:&error];
                    if(!postData){
                        NSLog(@"Error creating JSON: %@", [error localizedDescription]);
                    }else{
                        NSLog(@"Successfully created JSON.");
                    }
    
                // Create the request
                    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
                    [request setURL:[NSURL URLWithString:[NSString stringWithFormat:@"https://firestore.googleapis.com/v1beta1/projects/%@/databases/%@/documents/%@", @"project_id", @"(default)", @"collection_id"]]];
    
                    [request setHTTPMethod:@"POST"];
                    [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
                    [request setValue:[NSString stringWithFormat:@"Bearer %@", token] forHTTPHeaderField:@"Authorization"]; // 'token' is returned in [[FIRAuth auth].currentUser getIDTokenWithCompletion]. (All of this code resides inside the getIDTokenWithCompletion block so we can pass it along with the request and let firebase security rules take care of authenticating the request.)
    
                    [request setHTTPBody:postData];
    
                // Set up the session configuration
                    NSURLSessionConfiguration *sessionConfig;
                    sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
    
                // Set up the session
                    NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:nil];
    
  3. Запустить загрузку

                // Start the upload task
                    NSURLSessionDataTask *uploadTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *uploadTaskResp, NSError *error) {
    
                        NSLog(@"dataTask Request reply: %@", uploadTaskResp);
                        if(error){
                            NSLog(@"dataTask Request error: %@", error);
                        }
    
                    // Call completion handler
                        completionHandler();
                    }
                }
            }
    
0 голосов
/ 12 июня 2018

Итак, я наконец-то нашел решение / обходной путь.

Оказывается, что firebase не будет публиковать в фоновом режиме, вместо этого он хранит данные локально, пока приложение не выйдет на передний план, а затем опубликует его.

Решением было добавить еще одну функцию firebase, которая прослушивает HTTP-запросы.В этой функции я добавил метод публикации в базу данных с данными из действия.

Затем я выполняю обычный, но модифицированный HTTP-запрос публикации с Alamofire от действия к URL-адресу, как это:

      let headers: HTTPHeaders = [
        "Authorization": "Bearer \(token)",
        "Content-Type": "application/json"
      ]
      let configuration = URLSessionConfiguration.default
      configuration.timeoutIntervalForRequest = 15.0
      configuration.timeoutIntervalForResource = 15.0
      configuration.waitsForConnectivity = true
      self.alamofireManager = Alamofire.SessionManager(configuration: configuration)

      guard let manager = self.alamofireManager else {
        return
      }

      manager.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: headers).responseString(completionHandler: { (response) in
        let _ = manager
        print(response)
        closure(true)
      })

Важной частью этого, для меня, было установить configuration.waitsForConnectivity = true в противном случае, он вернется, говоря, что нет подключения к Интернету.

Это позволяет функции комментировать уведомление отэкран блокировки.

Надеюсь, эта информация поможет другим, ищущим то же самое.

...