Nano Hash - криптовалюты, майнинг, программирование

Как опубликовать данные в firestore из богатого действия уведомления?

У меня есть приложение, которое реализует удаленные уведомления через API обмена сообщениями Firebase. В этом приложении я реализовал расширение службы уведомлений, которое, среди прочего, реализует 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 delivery_request ОШИБКА: write_all (21, 65 байт) не удалось

nssd_clientstub read_all(26) НЕФУНКЦИОНАЛЬНЫЙ

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

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

Обновить

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

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


Ответы:


1

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

Получается, что 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, иначе он вернется с сообщением об отсутствии подключения к Интернету.

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

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

12.06.2018

2

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

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


ЦЕЛЬ-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();
                    }
                }
            }
    
23.06.2018
Новые материалы

Кластеризация: более глубокий взгляд
Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

Как написать эффективное резюме
Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

Частный метод Python: улучшение инкапсуляции и безопасности
Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

Как я автоматизирую тестирование с помощью Jest
Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..