Ich versuche, einen einfachen Chat in IOS/Swift mit iCloudKit zu erstellen. Ich modelliere nach diesem Beispiel: Create an App like Twitter: Push Notifications with CloudKit, aber ich ändere es, um ein Chat anstelle von Süßigkeiten zu werden.Push-Benachrichtigung von CloudKit synchronisiert nicht richtig
Das Banner und das Logo des Codes funktionieren in gewissem Maße gut und das Übertragen von Daten auf CloudDashboard ist in Ordnung und schnell.
Die Synchronisation vom cloudKit zu den Geräten funktioniert jedoch meistens nicht. Manchmal sieht ein Gerät mehr als das andere, manchmal weniger, nur nicht zu zuverlässig. Ich verwende die DEVELOPMENT-Umgebung in CloudKit.
Was ist das Problem? Hier ist mein Code der implementierten Methoden in AppDelegate und der Viewcontroller:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
let notificationSettings = UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil)
UIApplication.sharedApplication().registerUserNotificationSettings(notificationSettings)
UIApplication.sharedApplication().registerForRemoteNotifications()
return true
}
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
let cloudKitNotification = CKNotification(fromRemoteNotificationDictionary: userInfo as! [String:NSObject])
if cloudKitNotification.notificationType == CKNotificationType.Query {
dispatch_async(dispatch_get_main_queue(), {() -> Void in
NSNotificationCenter.defaultCenter().postNotificationName("performReload", object: nil)
})
}
}
func resetBadge() {
let badgeReset = CKModifyBadgeOperation(badgeValue: 0)
badgeReset.modifyBadgeCompletionBlock = { (error) -> Void in
if error == nil {
UIApplication.sharedApplication().applicationIconBadgeNumber = 0
}
}
CKContainer.defaultContainer().addOperation(badgeReset)
}
func applicationWillResignActive(application: UIApplication) {
}
func applicationDidEnterBackground(application: UIApplication) {
resetBadge()
}
func applicationWillEnterForeground(application: UIApplication) {
dispatch_async(dispatch_get_main_queue(), {() -> Void in
NSNotificationCenter.defaultCenter().postNotificationName("performReload", object: nil)
})
}
func applicationDidBecomeActive(application: UIApplication) {
resetBadge()
}
und dies ist der Viewcontroller
import UIKit
import CloudKit
class ChatViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate {
@IBOutlet weak var dockViewHeightConstraint: NSLayoutConstraint!
@IBOutlet weak var messageTextField: UITextField!
@IBOutlet weak var sendButton: UIButton!
@IBOutlet weak var messageTableView: UITableView!
var chatMessagesArray = [CKRecord]()
var messagesArray: [String] = [String]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.messageTableView.delegate = self
self.messageTableView.dataSource = self
// set self as the delegate for the textfield
self.messageTextField.delegate = self
// add a tap gesture recognizer to the tableview
let tapGesture:UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(ChatViewController.tableViewTapped))
self.messageTableView.addGestureRecognizer(tapGesture)
setupCloudKitSubscription()
dispatch_async(dispatch_get_main_queue(), {() -> Void in
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ChatViewController.retrieveMessages), name: "performReload", object: nil)
})
// retrieve messages form iCloud
self.retrieveMessages()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
@IBAction func sendButtonTapped(sender: UIButton) {
// Call the end editing method for the text field
self.messageTextField.endEditing(true)
// Disable the send button and textfield
self.messageTextField.enabled = false
self.sendButton.enabled = false
// create a cloud object
//var newMessageObject
// set the text key to the text of the messageTextField
// save the object
if messageTextField.text != "" {
let newChat = CKRecord(recordType: "Chat")
newChat["content"] = messageTextField.text
newChat["user1"] = "john"
newChat["user2"] = "mark"
let publicData = CKContainer.defaultContainer().publicCloudDatabase
//TODO investigate if we want to do public or private
publicData.saveRecord(newChat, completionHandler: { (record:CKRecord?, error:NSError?) in
if error == nil {
dispatch_async(dispatch_get_main_queue(), {() -> Void in
print("chat saved")
self.retrieveMessages()
})
}
})
}
dispatch_async(dispatch_get_main_queue()) {
// Enable the send button and textfield
self.messageTextField.enabled = true
self.sendButton.enabled = true
self.messageTextField.text = ""
}
}
func retrieveMessages() {
print("inside retrieve messages")
// create a new cloud query
let publicData = CKContainer.defaultContainer().publicCloudDatabase
// TODO: we should use this
let predicate = NSPredicate(format: "user1 in %@ AND user2 in %@", ["john", "mark"], ["john", "mark"])
let query = CKQuery(recordType: "Chat", predicate: predicate)
//let query = CKQuery(recordType: "Chat", predicate: NSPredicate(format: "TRUEPREDICATE", argumentArray: nil))
query.sortDescriptors = [NSSortDescriptor(key:"creationDate", ascending: true)]
publicData.performQuery(query, inZoneWithID: nil) { (results: [CKRecord]?, error:NSError?) in
if let chats = results {
dispatch_async(dispatch_get_main_queue(), {() -> Void in
self.chatMessagesArray = chats
print("count is: \(self.chatMessagesArray.count)")
self.messageTableView.reloadData()
})
}
}
}
func tableViewTapped() {
// Force the textfied to end editing
self.messageTextField.endEditing(true)
}
// MARK: TextField Delegate Methods
func textFieldDidBeginEditing(textField: UITextField) {
// perform an animation to grow the dockview
self.view.layoutIfNeeded()
UIView.animateWithDuration(0.5, animations: {
self.dockViewHeightConstraint.constant = 350
self.view.layoutIfNeeded()
}, completion: nil)
}
func textFieldDidEndEditing(textField: UITextField) {
// perform an animation to grow the dockview
self.view.layoutIfNeeded()
UIView.animateWithDuration(0.5, animations: {
self.dockViewHeightConstraint.constant = 60
self.view.layoutIfNeeded()
}, completion: nil)
}
// MARK: TableView Delegate Methods
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Create a table cell
let cell = self.messageTableView.dequeueReusableCellWithIdentifier("MessageCell")! as UITableViewCell
// customize the cell
let chat = self.chatMessagesArray[indexPath.row]
if let chatContent = chat["content"] as? String {
let dateFormat = NSDateFormatter()
dateFormat.dateFormat = "MM/dd/yyyy"
let dateString = dateFormat.stringFromDate(chat.creationDate!)
cell.textLabel?.text = chatContent
//cell.detailTextLabel?.text = dateString
}
//cell.textLabel?.text = self.messagesArray[indexPath.row]
// return the cell
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//print(tableView.frame.size)
//print("count: \(self.chatMessagesArray.count)")
return self.chatMessagesArray.count
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
// MARK: Push Notifications
func setupCloudKitSubscription() {
let userDefaults = NSUserDefaults.standardUserDefaults()
print("the value of the bool is: ")
print(userDefaults.boolForKey("subscribed"))
print("print is above")
if userDefaults.boolForKey("subscribed") == false { // TODO: maybe here we do multiple types of subscriptions
let predicate = NSPredicate(format: "user1 in %@ AND user2 in %@", ["john", "mark"], ["john", "mark"])
//let predicate = NSPredicate(format: "TRUEPREDICATE", argumentArray: nil)
let subscription = CKSubscription(recordType: "Chat", predicate: predicate, options: CKSubscriptionOptions.FiresOnRecordCreation)
let notificationInfo = CKNotificationInfo()
notificationInfo.alertLocalizationKey = "New Chat"
notificationInfo.shouldBadge = true
subscription.notificationInfo = notificationInfo
let publicData = CKContainer.defaultContainer().publicCloudDatabase
publicData.saveSubscription(subscription) { (subscription: CKSubscription?, error: NSError?) in
if error != nil {
print(error?.localizedDescription)
} else {
userDefaults.setBool(true, forKey: "subscribed")
userDefaults.synchronize()
}
}
}
}
}