Singleton not accessible in Sirikit IntentHandler - swift

I have a project that I want to add sirikit to. I added the intent and wanted to store values in my datastorage which is realm, when I tried to access the function that is used to create this task , I get an eeror. this is my code below
extension IntentHandler : INCreateTaskListIntentHandling {
public func handle(intent: INCreateTaskListIntent,
completion: #escaping (INCreateTaskListIntentResponse) -> Swift.Void) {
guard let title = intent.title else {
completion(INCreateTaskListIntentResponse(code: .failure, userActivity: nil))
return
}
CategoryFunctions.instance.createList(name: title.spokenPhrase,.....)
var tasks: [INTask] = []
if let taskTitles = intent.taskTitles {
let taskTitlesStrings = taskTitles.map {
taskTitle -> String in
return taskTitle.spokenPhrase
}
tasks = createTasks(fromTitles: taskTitlesStrings)
CategoryFunctions.instance.add(tasks: taskTitlesStrings, toList: title.spokenPhrase)
}
let response = INCreateTaskListIntentResponse(code: .success, userActivity: nil)
response.createdTaskList = INTaskList(title: title,
tasks: tasks,
groupName: nil,
createdDateComponents: nil,
modifiedDateComponents: nil,
identifier: nil)
completion(response)
}
}
this singlton instantiation works well in my app but I do not know why I get an error saying Use of unresolved identifier 'CategoryFunctions'
my CategoryFunctions singleton
class CategoryFunctions {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
var database:Realm!
static let instance = CategoryFunctions()
.....
...

Select your file in xcode, on the right, choose the File Inspector, then under Target Membership, pick your Intent.

Related

UICloudSharingController error: recordChangeTag specified, but record not found

I'm using NSPersistentCloudKitContainer for storing data and UICloudSharingController for sharing.
Here's how UICloudSharingController is created (that's pretty basic stuff):
func share(_ record: Record, avatar: UIImage) {
let controller = shareControllerForRecord(record, avatar: avatar)
controller.delegate = self
controller.popoverPresentationController?.sourceView = self.view
controller.popoverPresentationController?.permittedArrowDirections = []
present(controller, animated: true)
}
func shareControllerForRecord(_ record: Record, avatar: UIImage) -> UICloudSharingController {
if let share = share(for: record) {
let controller = UICloudSharingController(share: share, container: self.cloudKitContainer)
return controller
} else {
let controller = UICloudSharingController { [weak self] (controller, completion: #escaping (CKShare?, CKContainer?, Error?) -> Void) in
guard let self = self else { return }
self.persistentContainer.share([record], to: nil) { objectIDs, share, container, error in
if let share = share {
record.managedObjectContext?.performAndWait {
share[CKShare.SystemFieldKey.title] = record.title
share[CKShare.SystemFieldKey.thumbnailImageData] = avatar.pngData()
}
}
completion(share, container, error)
}
}
return controller
}
}
Once UICloudSharingController is created the user do basic stuff, and in most cases sharing works fine. But some users (I can see it in analytics) get this error:
Error Domain=_UIShareErrorDomain Code=0 "(null)" UserInfo={NSUnderlyingError=0x28399e340 {Error Domain=CKErrorDomain Code=2 "CKInternalErrorDomain: 1011" UserInfo={ContainerID=iCloud.net.bundleidentifier, NSDebugDescription=CKInternalErrorDomain: 1011, CKPartialErrors={ "<CKRecordID: 0x2837f2280; recordName=cloudkit.zoneshare, zoneID=com.apple.coredata.cloudkit.share.9A2156CD-2E69-4987-966E-AC5A3F593737:defaultOwner>" = "<CKError 0x28399d890: "Unknown Item" (11/2003); server message = "recordChangeTag specified, but record not found"; op = CE76405043B1BE49; uuid = C567647F-331B-4002-9893-2791F38131B3; container ID = "iCloud.net.bundleidentifier">"; }, RequestUUID=C567647F-331B-4002-9893-2791F38131B3, NSLocalizedDescription=Failed to modify some records, CKErrorDescription=Failed to modify some records, NSUnderlyingError=0x28399e430 {Error Domain=CKInternalErrorDomain Code=1011 "Failed to modify some records"
For me it seems like remote database doesn't have the Record uploaded yet. But it's strange, because these are not newly created records. Besides, for majority of users sharing works.
Can anyone advise?

CKShare - Failed to modify some records error - CloudKit

I'm trying to share a record with other users in CloudKit but I keep getting an error. When I tap one of the items/records on the table I'm presented with the UICloudSharingController and I can see the iMessage app icon, but when I tap on it I get an error and the UICloudSharingController disappears, the funny thing is that even after the error I can still continue using the app.
Here is what I have.
Code
var items = [CKRecord]()
var itemName: String?
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let item = items[indexPath.row]
let share = CKShare(rootRecord: item)
if let itemName = item.object(forKey: "name") as? String {
self.itemName = item.object(forKey: "name") as? String
share[CKShareTitleKey] = "Sharing \(itemName)" as CKRecordValue?
} else {
share[CKShareTitleKey] = "" as CKRecordValue?
self.itemName = "item"
}
share[CKShareTypeKey] = "bundle.Identifier.Here" as CKRecordValue
prepareToShare(share: share, record: item)
}
private func prepareToShare(share: CKShare, record: CKRecord){
let sharingViewController = UICloudSharingController(preparationHandler: {(UICloudSharingController, handler: #escaping (CKShare?, CKContainer?, Error?) -> Void) in
let modRecordsList = CKModifyRecordsOperation(recordsToSave: [record, share], recordIDsToDelete: nil)
modRecordsList.modifyRecordsCompletionBlock = {
(record, recordID, error) in
handler(share, CKContainer.default(), error)
}
CKContainer.default().privateCloudDatabase.add(modRecordsList)
})
sharingViewController.delegate = self
sharingViewController.availablePermissions = [.allowPrivate]
self.navigationController?.present(sharingViewController, animated:true, completion:nil)
}
// Delegate Methods:
func cloudSharingControllerDidSaveShare(_ csc: UICloudSharingController) {
print("saved successfully")
}
func cloudSharingController(_ csc: UICloudSharingController, failedToSaveShareWithError error: Error) {
print("failed to save: \(error.localizedDescription)")// the error is generated in this method
}
func itemThumbnailData(for csc: UICloudSharingController) -> Data? {
return nil //You can set a hero image in your share sheet. Nil uses the default.
}
func itemTitle(for csc: UICloudSharingController) -> String? {
return self.itemName
}
ERROR
Failed to modify some records
Here is what I see...
Any idea what could be wrong?
EDIT:
By the way, the error is generated in the cloudSharingController failedToSaveShareWithError method.
Looks like you're trying to share in the default zone which isn't allowed. From the docs here
Sharing is only supported in zones with the
CKRecordZoneCapabilitySharing capability. The default zone does not
support sharing.
So you should set up a custom zone in your private database, and save your share and records there.
Possibly it is from the way you're trying to instantiate the UICloudSharingController? I cribbed my directly from the docs and it works:
let cloudSharingController = UICloudSharingController { [weak self] (controller, completion: #escaping (CKShare?, CKContainer?, Error?) -> Void) in
guard let `self` = self else {
return
}
self.share(rootRecord: rootRecord, completion: completion)
}
If that's not the problem it's something with either one or both of the records themselves. If you upload the record without trying to share it, does it work?
EDIT TO ADD:
What is the CKShareTypeKey? I don't use that in my app. Also I set my system fields differently:
share?[CKShare.SystemFieldKey.title] = "Something"
Try to add this to your info.plist
<key>CKSharingSupported</key>
<true/>

NSTouchBar integration not calling

I am integrating TouchBar support to my App. I used the how to from Rey Wenderlich and implemented everything as follows:
If self.touchBarArraygot filled the makeTouchBar() Method returns the NSTouchBar object. If I print out some tests the identifiers object is filled and works.
What not work is that the makeItemForIdentifier method not get triggered. So the items do not get created and the TouchBar is still empty.
Strange behavior: If I add print(touchBar) and a Breakpoint before returning the NSTouchBar object it works and the TouchBar get presented as it should (also the makeItemForIdentifier function gets triggered). Even if it disappears after some seconds... also strange.
#available(OSX 10.12.2, *)
extension ViewController: NSTouchBarDelegate {
override func makeTouchBar() -> NSTouchBar? {
if(self.touchBarArray.count != 0) {
let touchBar = NSTouchBar()
touchBar.delegate = self
touchBar.customizationIdentifier = NSTouchBarCustomizationIdentifier("com.TaskControl.ViewController.WorkspaceBar")
var identifiers: [NSTouchBarItemIdentifier] = []
for (workspaceId, _) in self.touchBarArray {
identifiers.append(NSTouchBarItemIdentifier("com.TaskControl.ViewController.WorkspaceBar.\(workspaceId)"))
}
touchBar.defaultItemIdentifiers = identifiers
touchBar.customizationAllowedItemIdentifiers = identifiers
return touchBar
}
return nil
}
func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItemIdentifier) -> NSTouchBarItem? {
if(self.touchBarArray.count != 0) {
for (workspaceId, data) in self.touchBarArray {
if(identifier == NSTouchBarItemIdentifier("com.TaskControl.ViewController.WorkspaceBar.\(workspaceId)")) {
let saveItem = NSCustomTouchBarItem(identifier: identifier)
let button = NSButton(title: data["name"] as! String, target: self, action: #selector(self.touchBarPressed))
button.bezelColor = NSColor(red:0.35, green:0.61, blue:0.35, alpha:1.00)
saveItem.view = button
return saveItem
}
}
}
return nil
}
}
self.view.window?.makeFirstResponder(self) in viewDidLoad() did solve the problem.

How to update data on cloudKit without creating a new record?

Here is what I am trying to do. I have a simple journaling app with two views: a tableView that lists the titles of the entries and a viewController that has a text field for a title, and a textView for the text body (and a save button to save to cloudKit). On the viewController, I hit save and the record is saved to cloudKit and also added to the tableView successfully. This is all good.
I want to be able to edit/update the journal entry. But when I go back into the journal entry, change it in any way, then hit save again, the app returns to the tableView controller with an updated entry, but cloudKit creates a NEW entry separate from the one I wanted to edit. Then when I reload the app, my fetchRecords function fetches any extra records cloudKit has created.
Question: How do I edit/update an existing journal entry without creating a new entry in cloudKit?
Let me know if you need something else to further clarify my question.
Thanks!
Here are my cloudKit functions:
import Foundation
import CloudKit
class CloudKitManager {
let privateDB = CKContainer.default().publicCloudDatabase //Since this is a journaling app, we'll make it private.
func fetchRecordsWith(type: String, completion: #escaping ((_ records: [CKRecord]?, _ error: Error?) -> Void)) {
let predicate = NSPredicate(value: true) // Like saying I want everything returned to me with the recordType: type. This isn't a good idea if you have a massive app like instagram because you don't want all posts ever made to be loaded, just some from that day and from your friends or something.
let query = CKQuery(recordType: type, predicate: predicate)
privateDB.perform(query, inZoneWith: nil, completionHandler: completion) //Allows us to handle the completion in the EntryController to maintain proper MVC.
}
func save(records: [CKRecord], perRecordCompletion: ((_ record: CKRecord?, _ error: Error?) -> Void)?, completion: ((_ records: [CKRecord]?, _ error: Error?) -> Void)?) {
modify(records: records, perRecordCompletion: perRecordCompletion, completion: completion )
}
func modify(records: [CKRecord], perRecordCompletion: ((_ record: CKRecord?, _ error: Error?) -> Void)?, completion: ((_ records: [CKRecord]?, _ error: Error?) -> Void)?) {
let operation = CKModifyRecordsOperation(recordsToSave: records, recordIDsToDelete: nil)
operation.savePolicy = .ifServerRecordUnchanged //This is what updates certain changes within a record.
operation.queuePriority = .high
operation.qualityOfService = .userInteractive
operation.perRecordCompletionBlock = perRecordCompletion
operation.modifyRecordsCompletionBlock = { (records, _, error) in
completion?(records, error)
}
privateDB.add(operation) //This is what actually saves your data to the database on cloudkit. When there is an operation, you need to add it.
}
}
This is my model controller where my cloudKit functions are being used:
import Foundation
import CloudKit
let entriesWereSetNotification = Notification.Name("entriesWereSet")
class EntryController {
private static let EntriesKey = "entries"
static let shared = EntryController()
let cloudKitManager = CloudKitManager()
init() {
loadFromPersistentStorage()
}
func addEntryWith(title: String, text: String) {
let entry = Entry(title: title, text: text)
entries.append(entry)
saveToPersistentStorage()
}
func remove(entry: Entry) {
if let entryIndex = entries.index(of: entry) {
entries.remove(at: entryIndex)
}
saveToPersistentStorage()
}
func update(entry: Entry, with title: String, text: String) {
entry.title = title
entry.text = text
saveToPersistentStorage()
}
// MARK: Private
private func loadFromPersistentStorage() {
cloudKitManager.fetchRecordsWith(type: Entry.TypeKey) { (records, error) in
if let error = error {
print(error.localizedDescription)
}
guard let records = records else { return } //Make sure there are records.
let entries = records.flatMap({Entry(cloudKitRecord: $0)})
self.entries = entries //This is connected to the private(set) property "entries"
}
}
private func saveToPersistentStorage() {
let entryRecords = self.entries.map({$0.cloudKitRecord})
cloudKitManager.save(records: entryRecords, perRecordCompletion: nil) { (records, error) in
if error != nil {
print(error?.localizedDescription as Any)
return
} else {
print("Successfully saved records to cloudKit")
}
}
}
// MARK: Properties
private(set) var entries = [Entry]() {
didSet {
DispatchQueue.main.async {
NotificationCenter.default.post(name: entriesWereSetNotification, object: nil)
}
}
}
}
Here's a couple threads that might be helpful.
If you were caching the data locally you would use the encodesystemfields method to create a new CKRecord that will update an existing one on the server.
How (and when) do I use iCloud's encodeSystemFields method on CKRecord?
It doesn't appear you are caching locally. I don't have experience doing it without using encodesystemfields, but it looks like you have to pull the record down and save it back in the completion handler of the convenience method:
Trying to modify ckrecord in swift

Alamofire background working on simulator but not on device

As the title mentions I've set up a backgroundURL with Alamofire. It works like a charm in simulator but on my device doesn't. I'm sure I'm missing something here since I'm not that experienced with URL.
Here's the code I have so far:
class NetworkManager {
static let shared = NetworkManager()
private lazy var backgroundManager: Alamofire.SessionManager = {
let bundleIdentifier = MyStruct.identifier
return Alamofire.SessionManager(configuration: URLSessionConfiguration.background(withIdentifier: bundleIdentifier))
}()
var backgroundCompletionHandler: (() -> Void)? {
get{
return backgroundManager.backgroundCompletionHandler
}
set{
backgroundManager.backgroundCompletionHandler = newValue
}
}
}
func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: #escaping () -> Void) {
NetworkManager.shared.backgroundCompletionHandler = completionHandler
}
In my ViewController:
func populateArrays(){
Alamofire.request("http://www.aps.anl.gov/Accelerator_Systems_Division/Accelerator_Operations_Physics/sddsStatus/mainStatus.sdds.gz").responseData { response in
switch response.result{
case .success:
print("Validation Successful")
case .failure(let error):
print(error.localizedDescription)
}
if let data = response.result.value{
Solved it. For anyone else that has this problem you need to add the following code to your appDelegate.
func applicationDidEnterBackground(_ application: UIApplication) {
var bgTask = 0
var app = UIApplication.shared
bgTask = app.beginBackgroundTask(expirationHandler: {() -> Void in
app.endBackgroundTask(bgTask)
})
It seems to me that you are not using the background manager you've created. Instead of
Alamofire.request("http://www.aps.anl.gov...")
which calls the default (not background) session manager, you should use:
backgroundManager.request("http://www.aps.anl.gov...")
Which Jon Shier mentioned in the comments by the way.