如何通过丰富的通知操作将数据发布到Firestore?

时间:2018-06-11 11:55:12

标签: ios swift firebase notifications firebase-cloud-messaging

我有一个通过firebase messaging api实现远程通知的应用程序。在这个应用程序中,我实现了一个通知服务扩展,其中包括实现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 deliver_request错误:write_all(21,65字节)失败

     

nssd_clientstub read_all(26)DEFUNCT

运行post方法时,不会出现firebase的错误。

这是我能想到的初步信息。如果需要,我可以提供更多代码或信息。

更新

我发现如果我在锁定屏幕上按下按钮,但是当iPhone处于解锁状态时,没有任何反应 - 只要我向上滑动,应用程序显示,请求就会发送。这确实是一个背景问题。

亲切的问候克里斯

2 个答案:

答案 0 :(得分:1)

所以我终于找到了解决方案/解决方法。

事实证明,firebase不会在后台发布,而是在本地存储数据,直到应用程序进入前台,然后才会发布。

解决方案是添加另一个监听HTTP请求的firebase函数。在这个函数中,我添加了一个方法,用来自动作的数据发布到数据库。

然后,我将Alamofire定期但修改后的http发布请求从动作转到网址,如下所示:

      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否则,它会回来说与互联网无关。

这使该功能可以对来自锁定屏幕的通知进行评论。

希望这些信息有助于其他人寻找相同的信息。

答案 1 :(得分:1)

对于使用标准NSURLSession来完成firestore HTTP REST请求(而不是像@ChrisEenberg的excellent answer above中那样的Alamofire)的任何人

[注意::此答案的先前版本使用了后台任务,这些任务不是必需的。此修改使用普通的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();
                    }
                }
            }