I have an iOS and iPadOS app that use iCloud to sync. They are in the App Store and the database was pushed to production. They work as advertised. I wrote also a macOS app that uses Cloudkit, the container it set as NSPersistentCloudKitContainer and the merge policies are set ok. I also checked that the profile for the app and certificates are good to go, as well as the permissions of the app.
Apple signed the app, but, it doesn't sync with iCloud and I can't figure out why. I checked iCloud on my Mac and the app is there, set to be syncing. I have already read a few articles and have tried all the possible solutions, including signing out and back in, rebuilding the app, etc.
One thing, thought, when I delete an item, the app takes sometime to react to it, as if it's trying to connect. I tried with TCPDump, and other things to check for connection issues, but I can't find that app trying to connect.
I took a few screenshots of the different configs, etc so you can see as well, but has anyone run into this issue? Do you know a possible solution?
Thank you.
Screenshots:
Code in persistance.swift:
import CoreData
struct PersistenceController {
static let shared = PersistenceController()
static var preview: PersistenceController = {
let result = PersistenceController(inMemory: true)
let viewContext = result.container.viewContext
for _ in 0..<10 {
let newBolt = Jot(context: viewContext)
newBolt.id = UUID()
newBolt.text = "random"
}
do {
try viewContext.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
return result
}()
let container: NSPersistentCloudKitContainer
init(inMemory: Bool = false) {
container = NSPersistentCloudKitContainer(name: "Simple_Bolt")
let description = container.persistentStoreDescriptions.first
description?.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
description?.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
if inMemory {
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
}
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
container.viewContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy
container.viewContext.automaticallyMergesChangesFromParent = true
}
}
Post
Replies
Boosts
Views
Activity
I have an application for macOS that uses core data to save data. I switched to CloudKit so I can sync that data with its iOS counterpart. This is on Xcode 13.4. Even since I did that I get the error:
When I try to submit it for a distribution with "Developer ID"
I have been going in circles for the last two days. I have tried all the stuff several articles here at SO mentioned (e.g. this one), or the Apple Dev Forums (for example this one). I have tried deleting certs, regenerating them, adding them again, downloading "manual profiles" again, going to the building settings and changing certs and options...
It doesn't matter. It goes nowhere.
Here's the thing, if I go back to the version without iCloud, all is good. Same with any iOS app that uses iCloud. All builds, and all goes to Apple without any issues. It's only this app.
For the sake of testing I created a new macOS app, and from the get go I enabled CloudKit. Same error.
So, what's missing? Is there any special foo or incantation I need to utter to get an macOS app to use iCloud and be able to be distributed?
Thanks
I asked this on StackOverflow, but maybe you know the solution.
My app implements a static quick action. When you call it, a sheet is shown and you enter a new item. Simple.
I noticed that when you call the quick action (by long pressing its icon and selecting the action), and you send the app to the background (by going to the home screen or going to another app), and you come back to the app, the quick action is triggered again. As if the quick action was cached and never cleared. This happens when the app is closed and you opened it with a quick action, or when the app was already opened, in the background and you call the quick action from the app icon.
I looked at my code and I thought that maybe I needed to set the quick action to nil once I was done. I tried but no luck.
Here's the code and what I have tried as well.
Declaration of the static quick action on info.plist:
<key>UIApplicationShortcutItems</key>
<array>
<dict> <key>UIApplicationShortcutItemIconSymbolName</key>
<string>square.and.pencil</string>
<key>UIApplicationShortcutItemTitle</key>
<string>New Item</string>
<key>UIApplicationShortcutItemType</key>
<string>NewItem</string>
</dict>
</array>
Main app:
import SwiftUI
@main
struct MyApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
@Environment(\.scenePhase) var scenePhase
private let quickActionService = QuickActionService()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(quickActionService)
}
.onChange(of: scenePhase) { scenePhase in
switch scenePhase {
case .active:
guard let shortcutItem = appDelegate.shortcutItem else { return }
quickActionService.action = QuickAction(rawValue: shortcutItem.type)
print(">>>>quick action type: " + shortcutItem.type)
default:
return
}
}
}
}
Main view:
struct ContentView: View {
@EnvironmentObject var quickActions: QuickActionService
@State private var sheetNewItem = false
var body: some View {
NavigationView {
List {
Text("Hello World")
}
.sheet(isPresented: $sheetNewItem) {
SheetViewNewItem()
}
// triggers the quick action when the app is closed
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) {
checkQuickAction()
}
}
// triggers the quick action when the app comes back from he background
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in
DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) {
checkQuickAction()
}
}
}
}
func checkQuickAction() {
if let quickAction = quickActions.action {
print("Selected Quick Action: \(quickAction.rawValue)")
self.sheetNewItem.toggle()
}
}
}
The class for the quick action:
import Foundation
enum QuickAction: String {
case NewItem
}
final class QuickActionService: ObservableObject {
@Published var action: QuickAction?
init(initialValue: QuickAction? = nil) {
action = initialValue
}
}
And the app delegate:
import UIKit
import CoreData
final class AppDelegate: NSObject, UIApplicationDelegate {
var shortcutItem: UIApplicationShortcutItem? { AppDelegate.shortcutItem }
fileprivate static var shortcutItem: UIApplicationShortcutItem?
func application(
_ application: UIApplication,
configurationForConnecting connectingSceneSession: UISceneSession,
options: UIScene.ConnectionOptions
) -> UISceneConfiguration {
if let shortcutItem = options.shortcutItem {
AppDelegate.shortcutItem = shortcutItem
}
let sceneConfiguration = UISceneConfiguration(
name: "Scene Configuration",
sessionRole: connectingSceneSession.role
)
sceneConfiguration.delegateClass = SceneDelegate.self
return sceneConfiguration
}
}
private final class SceneDelegate: UIResponder, UIWindowSceneDelegate {
func windowScene(
_ windowScene: UIWindowScene,
performActionFor shortcutItem: UIApplicationShortcutItem,
completionHandler: @escaping (Bool) -> Void
) {
AppDelegate.shortcutItem = shortcutItem
completionHandler(true)
}
}
First I thought I needed to add code for handling when the app went into the background, setting the quick action to 'nil', but the problems lingered.
.onChange(of: scenePhase) { scenePhase in
switch scenePhase {
case .active:
guard let shortcutItem = appDelegate.shortcutItem else { return }
quickActionService.action = QuickAction(rawValue: shortcutItem.type)
case .background:
print(">>>>app in background, setting quick action to nil")
quickActionService.action = nil
default:
return
}
}
So I thought maybe I should set it to nil on receive:
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in
DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) {
checkQuickAction()
quickActions.action = nil
print(">> coming back: \(quickActions.action.debugDescription)")
}
}
I also tried on the function itself:
func checkQuickAction() {
if let quickAction = quickActions.action {
print("Selected Quick Action: \(quickAction.rawValue)")
self.sheetNewItem.toggle()
quickActions.action = nil
}
}
And in spite of quickActions.action now being nil it is still triggering the quick action once it comes from the background.
At this point I figured that maybe this is not the way, or maybe there is something lingering that I'm not seeing. I searched for examples of quick actions, and everything I have found is similar to my code, with the difference being that the examples call a setting of dynamic action when the app goes to the background. I don't need this, but either way, I tried, and the problem still remained. Some of the more educational articles are this, this, this, and this (this one also teaches you how to debug a quick action).
And here I am. I'm going in circles and I don't know whether this is a bug on SwiftUI, or whether I'm not doing something right.
Do you have any ideas how to prevent this from happening?
I asked this on Stack Overflow,, and here,, but so far no answers.
I don't know if this is a bug on the implementation of .searchable on macOS, or it's the expected behavior, but when you add a .searchable to a NavigationView, with the expected behavior of the search field being placed at the right most part of the toolbar, a focus issue happens.
When you type the text you want to search, I can filter the list with that text, but when I place the mouse cursor on the first item on the list and try to move down the list with the arrow key, with each arrow key press, the focus goes back to the search field, making it impossible to navigate up and down the list with the keyboard. The same happens if I select each item with the mouse.
Maybe I'm not implementing it right, or maybe it doesn't work yet with macOS, either way, this is the code I'm using. This is part of a three panel view, with this one being the middle view (right next to the side bar):
struct AllNotes: View {
@EnvironmentObject private var data: DataModel
@State var selectedNoteId: UUID?
@State var searchText: String = ""
var body: some View {
NavigationView {
List(data.notes.filter { searchText.isEmpty ? true : $0.text.localizedCaseInsensitiveContains(searchText) }) { note in
NavigationLink(
destination: NoteView(note: note),
tag: note.id,
selection: $selectedNoteId
) {
VStack(alignment: .leading) {
Text(getFirstLine(noteText: note.text)).font(.body).fontWeight(.bold)
}
}
}
.listStyle(InsetListStyle())
.toolbar {
// a few other buttons
}
}
.searchable(
text: $searchText,
placement: .toolbar,
prompt: "Search..."
)
}
}
I asked this on Stack Overflow, but I'll ask here as well since there is no answers there yet.
I'm trying to move away from having a TextField in the toolbar as my search field by using the new .searchable. But there seems to be a problem I can't solve.
When you type the text you want to search, I can filter the list with that text, but when I place the mouse cursor on the first item and try to move down the list with the arrow key, with each arrow key press, the focus goes back to the search field, making it impossible to navigate up and down the list with the keyboard.
Maybe I'm not implementing it right, or maybe it doesn't work yet with macOS, either way, this is the code I'm using. This is part of a three panel view, with this one being the middle view (right next to the side bar):
struct AllNotes: View {
@EnvironmentObject private var data: DataModel
@State var selectedNoteId: UUID?
@State var searchText: String = ""
var body: some View {
NavigationView {
List(data.notes.filter { searchText.isEmpty ? true : $0.text.localizedCaseInsensitiveContains(searchText) }) { note in
NavigationLink(
destination: NoteView(note: note, text: note.text),
tag: note.id,
selection: $selectedNoteId
) {
VStack(alignment: .leading) {
Text(getFirstLine(noteText: note.text)).font(.body).fontWeight(.bold)
}
}
}
.searchable(
text: $searchText,
placement: .toolbar,
prompt: "Search..."
)
.toolbar {
// a few other buttons
}
}
}
}
The DataModel is simple a struct of NoteItem:
struct NoteItem: Codable, Hashable, Identifiable {
let id: UUID
var text: String
var changed: Bool = false
}
Am I missing anything? Am I implementing this right?