我有一个通过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处于解锁状态时,没有任何反应 - 只要我向上滑动,应用程序显示,请求就会发送。这确实是一个背景问题。
亲切的问候克里斯
答案 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上传任务,并且按预期在应用程序后台或关闭时似乎可以正常工作。]
在didReceiveNotificationResponse
内部:
准备请求
// 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);
撰写请求
// 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];
开始上传任务
// 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();
}
}
}