如何从 NotificationServiceExtension 将远程通知数据保存到核心数据(数据库)中

Posted

技术标签:

【中文标题】如何从 NotificationServiceExtension 将远程通知数据保存到核心数据(数据库)中【英文标题】:How to save remote notification data into Core Data(Database) from NotificationServiceExtension 【发布时间】:2018-01-03 10:15:42 【问题描述】:

我已经在我的目标应用程序中实现了 NotificationServiceExtension,它运行良好。

创建的 AppgroupID

 lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator? = 

     var coordinator: NSPersistentStoreCoordinator? = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
     let options = [
         NSMigratePersistentStoresAutomaticallyOption: true,
         NSInferMappingModelAutomaticallyOption: true
         ]

     let oldStoreUrl = self.applicationDocumentsDirectory.appendingPathComponent("Model.sqlite")
     let directory: NSURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: AppGroupID)! as NSURL
     let newStoreUrl = directory.appendingPathComponent("Model.sqlite")!

     var targetUrl : URL? = nil
     var needMigrate = false
     var needDeleteOld = false


     if FileManager.default.fileExists(atPath: oldStoreUrl.path)
        needMigrate = true
        targetUrl = oldStoreUrl
     
     if FileManager.default.fileExists(atPath: newStoreUrl.path)
        needMigrate = false
        targetUrl = newStoreUrl

        if FileManager.default.fileExists(atPath: oldStoreUrl.path)
            needDeleteOld = true
        
     
     if targetUrl == nil 
        targetUrl = newStoreUrl
     

     if needMigrate 
        do 
            try coordinator?.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: targetUrl!, options: options) 
            if let store = coordinator?.persistentStore(for: targetUrl!) 
                do 
                    try coordinator?.migratePersistentStore(store, to: newStoreUrl, options: options, withType: NSSQLiteStoreType)

                 catch let error 
                    print("migrate failed with error : \(error)")
                
            
         catch let error 
            //CrashlyticsHelper.reportCrash(err: error as NSError, strMethodName: "migrateStore")
            print(error)
        
    

    if needDeleteOld 
        self.deleteDocumentAtUrl(url: oldStoreUrl)
        self.deleteDocumentAtUrl(url: self.applicationDocumentsDirectory.appendingPathComponent("Model.sqlite-shm"))
        self.deleteDocumentAtUrl(url: self.applicationDocumentsDirectory.appendingPathComponent("Model.sqlite-wal"))
    

    do 
        try coordinator!.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: targetUrl, options: options)
     catch var error as NSError 
        coordinator = nil
        NSLog("Unresolved error \(error), \(error.userInfo)")
        abort()
     catch 
        fatalError()
    
    return coordinator

() 

  **EDIT:**

lazy var applicationDocumentsDirectory: URL = 

let urls = Foundation.FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
return urls[urls.count-1]()

override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) 

 self.contentHandler = contentHandler
 bestAttemptContent = (request.content.mutableCopy() as? 
 UNMutableNotificationContent)

if let bestAttemptContent = bestAttemptContent 
    // Modify the notification content here...
    print("Mutable Data %@",bestAttemptContent)
    bestAttemptContent.title = "\(bestAttemptContent.title)"
    //bestAttemptContent.title = "Notification"

     let apnsDic = bestAttemptContent.userInfo["aps"] as! NSDictionary
        // NotificationManager.sharedInstance.handlePushNotification(byUserTapped: true, content: apnsDic)
        contentHandler(bestAttemptContent)

 

  // Storing APNS dictionary into database from extension

  func handlePushNotification(byUserTapped : Bool,content : NSDictionary) 

 let moduelDataDic = content.mutableCopy() as! NSMutableDictionary

let entity2 =  NSEntityDescription.entity(forEntityName: "TextMessage",
                                          in: CoreDataStorage.sharedInstance.mainQueueCtxt!)

let textMsg = NSManagedObject(entity: entity2!,
                              insertInto: CoreDataStorage.sharedInstance.mainQueueCtxt) as? TextMessage

let trimmedMessage = (moduelDataDic.object(forKey: "msgbody")! as AnyObject).trimmingCharacters(in: .whitespacesAndNewlines)

textMsg?.message = trimmedMessage
msgInfo?.toText = textMsg
rosterInfo.time = composeMsgDate as Date
rosterInfo.addToToMessageInfo(NSSet(object: msgInfo!))

DispatchQueue.main.async 

    let context = CoreDataStorage.sharedInstance.mainQueueCtxt
    if let moc = context 
        if moc.hasChanges 
            do 
                try moc.save()
             catch 
                let nserror = error as NSError
                print("Could not save In NotficationManager \(nserror), \(nserror.userInfo)")
            
        
    

 

在应用程序后台模式下,我正在获取存储在上述 NotificationServiceExtension 方法中的数据,结果我可以从 manageObject 表 (messageInfo) 中获取数据,但没有任何数据进入关系“let setArray = rosterInfo?.toMessageInfo”

let resultPredicate = NSPredicate(format: "messageID == %@", 
moduelDataDic.object(forKey: "message_id")! as! CVarArg)

let messageInfoAry = 
self.fetchMessagesFromCoreDataWithPredicate(resultPredicate: 
resultPredicate, sortDescriptorKey: "time", isAscending: true, 
fetchLimit: false, managedContext: self.getManageObjectContext())
if messageInfoAry.count > 0 

let rosterInfo = (messageInfoAry[0] as! MessageInfo).toRoster
let msgInfo = messageInfoAry[0] as! MessageInfo

// Here i don't see msgInfo in this relationship but after relaunching my application i can able to see that same object

let setArray = rosterInfo?.toMessageInfo 

任何人的帮助?

【问题讨论】:

究竟发生了什么“我可以看到数据只有奇怪的行为”? @shallowThought :在我的情况下,我可以在应用程序进入终止模式时保存数据,但是在技术上首先处于后台模式时,我的扩展委托调用了我将远程通知数据存储到核心数据的位置,之后我的应用程序在这里调用的远程通知代表我正在尝试获取我从扩展中存储但看不到的相同数据。重新启动应用程序后,我可以看到相同的数据。 我没看懂,抱歉。 您发布了您的 persistentStoreCoordinator 的代码。请您也发布通知保存的代码吗? 尝试检查模拟器中的核心数据模型位置(您可以查看您对所有内容都具有权限的模拟器)。我认为 Notification 扩展程序的应用程序目录与实际应用程序不同,因为它是作为所有扩展程序的新单独捆绑包。 【参考方案1】:

尝试将以下代码添加到您的 AppDelegate 类并进行测试。

    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) 

    completionHandler(.newData)
    performSelector(inBackground: #selector(saveNewNotificationInBackground(userInfo:)), with: userInfo)


@objc func saveNewNotificationInBackground(userInfo: [AnyHashable: Any]) -> Void 
    //save notification using core data

【讨论】:

Jayachandra A:当我的应用程序进入后台模式时,反向关系不起作用。 rosterInfo.addToToMessageInfo(NSSet(object: msgInfo!)) 在这里我以一对多的关系形式存储数据!

以上是关于如何从 NotificationServiceExtension 将远程通知数据保存到核心数据(数据库)中的主要内容,如果未能解决你的问题,请参考以下文章

如何将数据从回收器适配器发送到片段 |如何从 recyclerview 适配器调用片段函数

如何从 Firebase 获取所有设备令牌?

如何直接从类调用从接口继承的方法?

如何从服务器获取和设置 android 中的 API(从服务器获取 int 值)?如何绑定和实现这个

如何从Mac从android studio中的fabric注销? [复制]

如何从设备中获取 PDF 文件以便能够从我的应用程序中上传?