Post

Replies

Boosts

Views

Activity

SwifUI quick actions using NavigationView and NavigationLink
Hello all! I'm sorry to cross post this question (I've already done on stackoverflow: https://stackoverflow.com/questions/67349440/swifui-quick-actions-using-navigationview-and-navigationlink), but here I hope to find more fortune than there. Code created so far: QuickActionViewModel.swift import Foundation class QuickActionsViewModel: ObservableObject { enum Action: Hashable { case one case two case three } @Published var action: Action? = nil } QATestApp.swift import CoreData import SwiftUI import UIKit var shortcutItemToHandle: UIApplicationShortcutItem? let quickActionsViewModel = QuickActionsViewModel() @main enum MainApp { static func main() { QATestApp.main() } } struct QATestApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate @Environment(\.scenePhase) var lifeCycle var body: some Scene { WindowGroup { ContentView() .environmentObject(quickActionsViewModel) } .onChange(of: lifeCycle) { newLifeCyclePhase in if newLifeCyclePhase == .active { guard let name = shortcutItemToHandle?.type else { return } switch name { case "OneAction": quickActionsViewModel.action = .one case "TwoAction": quickActionsViewModel.action = .two case "ThreeAction": quickActionsViewModel.action = .three default: break } } } } } class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) - UISceneConfiguration { let sceneConfiguration = UISceneConfiguration(name: "Custom Configuration", sessionRole: connectingSceneSession.role) sceneConfiguration.delegateClass = SceneDelegate.self return sceneConfiguration } } class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { if let shortcutItem = connectionOptions.shortcutItem { handleShortcutItem(shortcutItem) } } func windowScene(_ windowScene: UIWindowScene, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) - Void) { shortcutItemToHandle = shortcutItem } private func handleShortcutItem(_ shortcutItem: UIApplicationShortcutItem, completionHandler: ((Bool) - Void)? = nil) {} } ContentView.swift import Combine import Defaults import SwiftUI struct ContentView: View { @EnvironmentObject var quickActionsViewModel: QuickActionsViewModel var body: some View { NavigationView { VStack { Home() // quick actions links NavigationLink(destination: Text("One"), tag: .one, selection: self.$quickActionsViewModel.action) { EmptyView() } NavigationLink(destination: Text("Two"), tag: .two, selection: self.$quickActionsViewModel.action) { EmptyView() } NavigationLink(destination: Text("Three"), tag: .three, selection: self.$quickActionsViewModel.action) { EmptyView() } } } .navigationViewStyle(StackNavigationViewStyle()) .onReceive(self.quickActionsViewModel.$action, perform: { action in print(action.debugDescription) }) } } Home() has some stuff to see when the app opens. I've statically create Shortcut items in the info.plist keyUIApplicationShortcutItems/key array dict keyUIApplicationShortcutItemIconSymbolName/key stringbook.fill/string keyUIApplicationShortcutItemTitle/key stringOne/string keyUIApplicationShortcutItemType/key stringOneAction/string /dict dict keyUIApplicationShortcutItemIconSymbolName/key stringbook.fill/string keyUIApplicationShortcutItemTitle/key stringTwor/string keyUIApplicationShortcutItemType/key stringTwoAction/string /dict dict keyUIApplicationShortcutItemIconSymbolName/key stringbook.fill/string keyUIApplicationShortcutItemTitle/key stringThree/string keyUIApplicationShortcutItemType/key stringThreeAction/string /dict /array Problem number one: If I navigate away from home, send the app to background and then use one of the quick actions, none of them work. I've added .onReceive(self.quickActionsViewModel.$action, perform: { action in print(action.debugDescription) }) to check and every time prints a value Problem number two: if I trigger one of the quick actions the app opens and show correctly the related view. If I send on bg again and trigger another quick action, the app open to the old one (correct, I sent to background with this view open), shows the Home(), show the selected view and return to the home almost istantly, with onreceive printing NIL. Any help is really appreciated
0
0
648
May ’21
Objective C to Swift with CoreData and Cloud Documents
I have this old application written in Objective C that uses CoreData, backed with ICloud Documents. Here's the setup for the old app @property (nonatomic, strong) PersistentStack* persistentStack; ... - (NSManagedObjectContext *)managedObjectContext { if (!self.persistentStack) { NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"MyApp" withExtension:@"momd"]; self.persistentStack = [[PersistentStack alloc] initWithStoreURL:self.storeURL modelURL:modelURL]; } return self.persistentStack.managedObjectContext; } - (NSURL*)storeURL { NSURL* documentsDirectory = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:NULL]; return [documentsDirectory URLByAppendingPathComponent:@"MyApp.sqlite"]; } _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; _managedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy; _managedObjectContext.persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel]; [option addEntriesFromDictionary:@{ NSPersistentStoreUbiquitousContentNameKey : @"iCloudStore" }]; [self.managedObjectContext.persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:self.storeURL options:option error:&error]; This function fetches all the data. The entity defined in the .xcdatamodeld is called Nota +(NSMutableArray*)getNoteInContext:(NSManagedObjectContext*)context { if(context==nil) context = [AppDelegate mainManagedObjectContext]; NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; [fetchRequest setReturnsObjectsAsFaults:NO]; NSEntityDescription *entity = [NSEntityDescription entityForName:@"Nota" inManagedObjectContext:context]; [fetchRequest setEntity:entity]; NSSortDescriptor *sortDescriptor1 = [[NSSortDescriptor alloc] initWithKey:@"timestamp" ascending:NO]; NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor1, nil]; [fetchRequest setSortDescriptors:sortDescriptors]; NSError *error; NSArray *risposta = [context executeFetchRequest:fetchRequest error:&error]; return [NSMutableArray arrayWithArray:risposta]; } Here's the "new" app, written in Swift (SwiftUI) I defined a class to manage the core data stuff final class CoreDataManager { private(set) lazy var managedObjectContext: NSManagedObjectContext = { let managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType) managedObjectContext.persistentStoreCoordinator = self.persistentStoreCoordinator return managedObjectContext }() private lazy var managedObjectModel: NSManagedObjectModel = { guard let modelURL = Bundle.main.url(forResource: "MyApp", withExtension: "momd") else { fatalError("Unable to Find Data Model") } guard let managedObjectModel = NSManagedObjectModel(contentsOf: modelURL) else { fatalError("Unable to Load Data Model") } return managedObjectModel }() private lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = { let persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel) let fileManager = FileManager.default let storeName = "MyApp.sqlite" do { let documentsDirectoryURL = try fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true) let persistentStoreURL = documentsDirectoryURL.appendingPathComponent(storeName) try persistentStoreCoordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: persistentStoreURL, options: nil) } catch { fatalError("Unable to Load Persistent Store") } return persistentStoreCoordinator }() } and here is how is consumed struct Home: View { @State var notes: [Nota]? var body: some View { VStack { Text("Entries: \(self.notes?.count.description ?? "No entries at all")") .padding(.top) }.onAppear { let moc = CoreDataManager().managedObjectContext let fetchRequest = NSFetchRequest<Nota>() fetchRequest.returnsObjectsAsFaults = false let entity = NSEntityDescription.entity(forEntityName: "Nota", in: moc) fetchRequest.entity = entity let sortDescriptor1 = NSSortDescriptor(key: "timestamp", ascending: false) let sortDescriptors = [sortDescriptor1] fetchRequest.sortDescriptors = sortDescriptors do { self.notes = try moc.fetch(fetchRequest) } catch { fatalError("Failed to fetch notes: \(error)") } } } } In the new app, having the same CFBundleIdentifier, the same Developer account and the same Apple ID in either simulator or physical device, the data saved in the "Old" App is not available in the "New" one. If I delete the old app and reinstall it, ICloud syncs correcly show me the data from the cloud. Even after a complete reset of the device (just in case...) What is missing? Or what I've done wrong? Thanks in advance!
6
0
1.1k
Oct ’21
Command Center / Dynamic Island missing icons and animations
hello all! I'm setting up a really simple media player in my swiftui app. the code is the following: import AVFoundation import MediaPlayer class AudioPlayerProvider { private var player: AVPlayer init() { self.player = AVPlayer() self.player.automaticallyWaitsToMinimizeStalling = false self.setupAudioSession() self.setupRemoteCommandCenter() } private func setupAudioSession() { do { try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default) try AVAudioSession.sharedInstance().setActive(true) } catch { print("Failed to set up audio session: \(error.localizedDescription)") } } private func setupRemoteCommandCenter() { let commandCenter = MPRemoteCommandCenter.shared() commandCenter.playCommand.addTarget { [weak self] _ in guard let self = self else { return .commandFailed } self.play() return .success } commandCenter.pauseCommand.addTarget { [weak self] _ in guard let self = self else { return .commandFailed } self.pause() return .success } } func loadAudio(from urlString: String) { guard let url = URL(string: urlString) else { return } let asset = AVAsset(url: url) let playerItem = AVPlayerItem(asset: asset) self.player.pause() self.player.replaceCurrentItem(with: playerItem) NotificationCenter.default.addObserver(self, selector: #selector(self.streamFinished), name: .AVPlayerItemDidPlayToEndTime, object: self.player.currentItem) } func setMetadata(title: String, artist: String, duration: Double) { var nowPlayingInfo = [ MPMediaItemPropertyTitle: title, MPMediaItemPropertyArtist: artist, MPMediaItemPropertyPlaybackDuration: duration, MPNowPlayingInfoPropertyPlaybackRate: 1.0, ] as [String: Any] MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo } @objc private func streamFinished() { self.player.seek(to: .zero) try? AVAudioSession.sharedInstance().setActive(false) MPNowPlayingInfoCenter.default().playbackState = .stopped } func play() { MPNowPlayingInfoCenter.default().playbackState = .playing self.player.play() } func pause() { MPNowPlayingInfoCenter.default().playbackState = .paused self.player.pause() } } pretty scholastic. The code works when called on views. It also shows up within the lock screen / dynamic island (when in background), but here lies the problems: The play/pause button do not appear neither in the Command Center nor in the dynamic island. If I tap on the position these button should show up, the command works. Just the icons are not appearing. the waveform animation does not animate when playing. Many audio apps are working just fine so is my code lacking something. But I don't know why. What is missing? Thanks in advance!
1
0
728
May ’24
Swift 6 and NSPersistentCloudKitContainer
Hello all! I'm porting a ios15+ swiftui app to be compatible with Swift 6 and enabling strict concurrency checking gave me a warning that will be an error when switching to swift 6. I'm initializing a persistence controller for my cloud kit container: import CoreData struct PersistenceController { static let shared = PersistenceController() let container: NSPersistentCloudKitContainer init() { container = NSPersistentCloudKitContainer(name: "IBreviary") container.loadPersistentStores(completionHandler: { _, error in if let error = error as NSError? { fatalError("Unresolved error \(error), \(error.userInfo)") } }) container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy container.viewContext.automaticallyMergesChangesFromParent = true } } The warning is on the merge policy: Reference to var 'NSMergeByPropertyObjectTrumpMergePolicy' is not concurrency-safe because it involves shared mutable state; this is an error in the Swift 6 language mode I have no idea how to make this concurrency safe, nor I found a documentation entry to help me with this. Anyone have idea how to solve this? Thanks in advance V.
2
0
388
Sep ’24
Request authorization for the notification center crash iOS app on Swift 6
Hey all! During the migration of a production app to swift 6, I've encountered a problem: when hitting the UNUserNotificationCenter.current().requestAuthorization the app crashes. If I switch back to Language Version 5 the app works as expected. The offending code is defined here class AppDelegate: NSObject, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -&gt; Bool { FirebaseApp.configure() FirebaseConfiguration.shared.setLoggerLevel(.min) UNUserNotificationCenter.current().delegate = self let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound] UNUserNotificationCenter.current().requestAuthorization(options: authOptions) { _, _ in } application.registerForRemoteNotifications() Messaging.messaging().delegate = self return true } } The error is depicted here: I have no idea how to fix this. Any help will be really appreciated thanks in advance
13
2
1.2k
Sep ’24
MPMediaItemPropertyArtwork crashes on Swift 6
Hey all! in my personal quest to make future proof apps moving to Swift 6, one of my app has a problem when setting an artwork image in MPNowPlayingInfoCenter Here's what I'm using to set the metadata func setMetadata(title: String? = nil, artist: String? = nil, artwork: String? = nil) async throws { let defaultArtwork = UIImage(named: "logo")! var nowPlayingInfo = [ MPMediaItemPropertyTitle: title ?? "***", MPMediaItemPropertyArtist: artist ?? "***", MPMediaItemPropertyArtwork: MPMediaItemArtwork(boundsSize: defaultArtwork.size) { _ in defaultArtwork } ] as [String: Any] if let artwork = artwork { guard let url = URL(string: artwork) else { return } let (data, response) = try await URLSession.shared.data(from: url) guard (response as? HTTPURLResponse)?.statusCode == 200 else { return } guard let image = UIImage(data: data) else { return } nowPlayingInfo[MPMediaItemPropertyArtwork] = MPMediaItemArtwork(boundsSize: image.size) { _ in image } } MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo } the app crashes when hitting MPMediaItemPropertyArtwork: MPMediaItemArtwork(boundsSize: defaultArtwork.size) { _ in defaultArtwork } or nowPlayingInfo[MPMediaItemPropertyArtwork] = MPMediaItemArtwork(boundsSize: image.size) { _ in image } commenting out these two make the app work again. Again, no clue on why. Thanks in advance
6
0
510
Sep ’24