Post

Replies

Boosts

Views

Activity

Trouble with getting a background refresh task working
I am able to setup and schedule a background refresh task as well as manually trigger it via Xcode and the simulator tied to my iPhone 11 Pro test phone using the e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier: However, it won't execute on the app on it's own. And yes, the pinfo.list entries are correct and match! I know the scheduler is not exact on timing but it's just not executing on its own. Since I can trigger manually it I'm pretty sure the code is good but I must be missing something. I created an observable object for this code and the relevant parts look like this: class BackgroundTaskHandler: ObservableObject { static let shared = BackgroundTaskHandler() var taskState: BackgroundTaskState = .idle let backgroundAppRefreshTask = "com.opexnetworks.templateapp.shell.V1.appRefreshTask" func registerBackgroundTask() { BGTaskScheduler.shared.register(forTaskWithIdentifier: backgroundAppRefreshTask, using: nil) { task in self.handleAppRefresh(task: task as! BGAppRefreshTask) } self.updateTaskState(to: .idle, logMessage: "✅ Background app refresh task '\(backgroundAppRefreshTask)' registered.") BGTaskScheduler.shared.register(forTaskWithIdentifier: backgroundTaskIdentifier, using: nil) { task in self.handleProcessingTask(task: task as! BGProcessingTask) } self.updateTaskState(to: .idle, logMessage: "✅ Background task identifier '\(backgroundTaskIdentifier)' registered.") } // Handle the app refresh task private func handleAppRefresh(task: BGAppRefreshTask) { self.updateTaskState(to: .running, logMessage: "🔥 app refresh task is now running.") PostNotification.sendNotification(title: "Task Running", body: "App refresh task is now running.") let queue = OperationQueue() queue.maxConcurrentOperationCount = 1 let operation = BlockOperation { self.doSomeShortTaskWork() } task.expirationHandler = { self.updateTaskState(to: .expired, logMessage: "💀 App refresh task expired before completion.") PostNotification.sendNotification(title: "Task Expired", body: "App refresh task expired before completion \(self.formattedDate(Date())).") operation.cancel() } operation.completionBlock = { if !operation.isCancelled { self.taskState = .completed } task.setTaskCompleted(success: !operation.isCancelled) let completionDate = Date() UserDefaults.standard.set(completionDate, forKey: "LastBackgroundTaskCompletionDate") self.updateTaskState(to: .completed, logMessage: "🏁 App refresh task completed at \(self.formattedDate(completionDate)).") PostNotification.sendNotification(title: "Task Completed", body: "App refresh task completed at: \(completionDate)") self.scheduleAppRefresh() // Schedule the next one } queue.addOperation(operation) } func scheduleAppRefresh() { // Check for any pending task requests BGTaskScheduler.shared.getPendingTaskRequests { taskRequests in let refreshTaskIdentifier = self.backgroundAppRefreshTask let refreshTaskAlreadyScheduled = taskRequests.contains { $0.identifier == refreshTaskIdentifier } if refreshTaskAlreadyScheduled { self.updateTaskState(to: .pending, logMessage: "⚠️ App refresh task '\(refreshTaskIdentifier)' is already pending.") // Iterate over pending requests to get details for taskRequest in taskRequests where taskRequest.identifier == refreshTaskIdentifier { let earliestBeginDate: String if let date = taskRequest.earliestBeginDate { earliestBeginDate = self.formattedDate(date) } else { earliestBeginDate = "never" } self.updateTaskState(to: .pending, logMessage: "⚠️ Pending Task: \(taskRequest.identifier), Earliest Begin Date: \(earliestBeginDate)") } // Optionally, show a warning message to the user in your app PostNotification.sendNotification(title: "Pending Tasks", body: "App refresh task is already pending. Task scheduling cancelled.") return } else { // No pending app refresh task, so schedule a new one let request = BGAppRefreshTaskRequest(identifier: refreshTaskIdentifier) request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60) // Earliest in 15 minutes do { try BGTaskScheduler.shared.submit(request) self.taskState = .scheduled self.updateTaskState(to: .scheduled, logMessage: "✅ App refresh task '\(refreshTaskIdentifier)' successfully scheduled for about 15 minutes later.") PostNotification.sendNotification(title: "Task Scheduled", body: "App refresh task has been scheduled to run in about 15 minutes.") } catch { print("Could not schedule app refresh: \(error)") self.taskState = .failed self.updateTaskState(to: .failed, logMessage: "❌ Failed to schedule app refresh task.") } } } } // Short task work simulation private func doSomeShortTaskWork() { print("Doing some short task work...") // Simulate a short background task (e.g., fetching new data from server) sleep(5) print("Short task work completed.") } In my AppDelegate I trigger the registerBackground task in the didFinishLaunchingWithOptions here: BackgroundTaskHandler.shared.registerBackgroundTask() And I scheduled it here in the launch view under a task when visible: .task { BackgroundTaskHandler.shared.scheduleAppRefresh() } I've also tried the last in the AppDelegate after registering. either way the task schedules but never executes.
4
0
322
Sep ’24
Renaming SwiftData model causing an error
I'm still working on version 1.0.0 of my app so don't yet need to migrate swiftdata versions. I renamed my data model(s) but I get this error when running the app after changing the name of the model from SectionsSD to HelpSection error: the replacement path doesn't exist: "/var/folders/fk/m2x_2qzj44508m9zzcll2j_w0000gn/T/swift-generated-sources/@__swiftmacro_11OpExShellV112FilteredListV11helpSection5QueryfMa_.swift" Despite cleaning the build, deleting the derived data and deleting the app and data on the simulator (and resetting it) I still get the replacement path error whenever I run the app in the simulator. When I run this code against the model I get the error: @Query var helpSection: [HelpSection] let searchString: String let isExpanded: Bool var filteredSections: [HelpSection] { helpSection.filter { section in section.toArticles?.contains { $0.search.contains(searchString) } == true } .sorted { $0.rank < $1.rank } } deleting derived data, cleaning builds and restarts do not resolve the issue.
2
2
536
Sep ’24
FaceID changes in iOS 18
I currently do FaceID validation in my apps but it looks like Apple is offering FaceID ad the App level. Does this mean we still need to or can code for it in iOS 18 apps? Right now I've been working on migrating to iOS 18 using beta but my swift code just returns an "unknown error". From a developer perspective I can't find any examples or guidance on how handle FaceID currently in iOS 18 or going forward. Anyone have any insights or resources. This is the code that used to work but now under iOS 18 returns the error. Maybe the simulator and swift have not caught up but I don't think so given that it's been two beta release that I know of where this has not worked. class biometric { class func authenticateUser() async -> (Bool, Error?) { let context = LAContext() var error: NSError? if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) { let biometryType = context.biometryType var reason = "Authenticate with \(biometryType)" if biometryType == .faceID { reason = "Authenticate with Face ID" } else if biometryType == .touchID { reason = "Authenticate with Touch ID" } do { let success = try await context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) LogEvent.print(module: "Authentication.biometric.authenticateUser", message: "Biometric authentication. success: \"\(success)\".") return (success, nil) } catch let evaluationError as LAError { LogEvent.print(module: "Authentication.biometric.authenticateUser", message: "Biometric authentication failed. evaluationError: \"\(evaluationError.localizedDescription)\"") handleEvaluationError(evaluationError) I do get past the .canEvaluatePolicy but fail on the .evaluatePolicy
11
1
2.1k
Aug ’24
CLLocationUpdate isStationary issue
Hi, Per Apple the isStationary value is supposed to set to true when the device is stationary. I am trying to get a better understanding of when and how this status is changed. When and how does Apple decide when to set this to true and what is the threshold by which it is set to false. Right now when I start my app and use it is set from true to false instantly. let updates = CLLocationUpdate.liveUpdates() for try await update in updates { self.isStationary = update.isStationary I would love to know by what criteria it sets it to true otherwise I'm collecting a lot of zero speed very slight to no movements in latitude and longitude that I have to make some assumptions about filtering out of what I capture. I can't seem to find any mention of this or use case examples in any of the usual sources for examples despite having been introduced at the last WWDC 2023. Any help here would be appreciated.
0
0
431
Apr ’24
Background location tracking challenge
I have a driving tracking app I'm working on that properly starts tracking and logging location when the app is in the foreground or background. I use a buffer/queue to keep recent locations so when a trip ramps up to driving speed I can record that to work back to the start location just before the trip starts. This works great, however, in background mode when the user does not have the app open it will record locations but not until a significant location change is detected. The buffering I do is lost and the location only starts tracking several hundred yards or more after the trip has started. Does anyone have any suggestions or strategies to handle this chicken and the egg scenario?
3
0
1.2k
Apr ’24
Issues with NWPathMonitor
With SCNetworkReachabilityCreateWithAddress now depreciated I'm having to use NWPathMonitor to determine if I have a good connection to access some HTML and JSON files. I have this working just fine if the network connection is down but if the network connection comes back up it detects an event but still shows the status as unsatisfied. I can't seem to capture or pickup that when I've got a good connection again so I can proceed with the right status. Here is my current code. Any suggestions would be appreciated: import Network import Combine class NetworkStatus: ObservableObject { static let shared = NetworkStatus() private var monitor: NWPathMonitor? private var queue = DispatchQueue(label: "NetworkMonitor") @Published var isConnected: Bool = false private init() { monitor = NWPathMonitor() startMonitoring() } deinit { stopMonitoring() } func startMonitoring() { monitor?.pathUpdateHandler = { [weak self] path in DispatchQueue.main.async { let status = path.status == .satisfied self?.isConnected = status print(">>> \(path.status)") } } monitor?.start(queue: queue) } func stopMonitoring() { monitor?.cancel() monitor = nil } }
1
0
1.1k
Mar ’24
CoreData/SwiftData persistent history error after adding additional models
I'm trying to add two more data models SectionsSD andArticles SD to an existing project using SwiftData where I had one model and am running into a variety of CoreData errors when I try to run my app. I am running Xcode 15.2 and the latest iOS simulator at iOS 17.2 Here is my container let container: ModelContainer = { let schema = Schema([ LocationData.self, SectionsSD.self, ArticlesSD.self ]) let container = try! ModelContainer(for: schema, configurations: []) return container }() After I add these two models I get the following errors and I can't load data into the SectionsSD and ArticlesSD models. If I comment out my LocataionData model that works w/o error or if I run just LocationData it runs without error but run all together and I get the following errors in the log: error: Error: Persistent History (11) has to be truncated due to the following entities being removed: ( SectionsSD, ArticlesSD ) CoreData: error: Error: Persistent History (11) has to be truncated due to the following entities being removed: ( SectionsSD, ArticlesSD ) warning: Warning: Dropping Indexes for Persistent History CoreData: warning: Warning: Dropping Indexes for Persistent History warning: Warning: Dropping Transactions prior to 11 for Persistent History CoreData: warning: Warning: Dropping Transactions prior to 11 for Persistent History warning: Warning: Dropping Changes prior to TransactionID 11 for Persistent History Any advice here would be appreciated. These are initial models so migration is not really a need yet. Seems to just **** if you add more models.
3
0
1.3k
Feb ’24
Store location data when app is not running using Xcode 15.1 and SwiftUI
I'm close but can't seem to get my app to store location data when not running. I'm trying to collect driver data. When I start the App it asks for location permission while using the App. At no time does the app ask me to give permission for allowing the app to collect information when I am not using the app. I think the problem revolves around this. I've also tried going into iOS settings for the app and set to Always but that didn't help. I'm likely missing something here within the app to get it to collect the data when the app is not running. Any help is appreciated. Here is what I've got. For Signing I have Background Modes enabled with "Location updates". For plist.info I have the following set with descriptions. Privacy - Location Location Always Usage Description Privacy - Location When in Use Usage Description Privacy - LocationAways and When in Use Usage Description I also have in the Info file: Required background modes with Item 0 set with "App registers for location updates" for code I have the the following in the AppDelegate: import UIKit import CoreLocation class AppDelegate: NSObject, UIApplicationDelegate { static let shared = AppDelegate() func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { // Add any custom setup code here return true } } extension AppDelegate { func requestLocationAuthorization() { let locationManager = CLLocationManager() locationManager.requestAlwaysAuthorization() } } For my app here is how I trigger the app delegate and starupt the location manager import SwiftUI import SwiftData @main struct ZenTracApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate @Environment(\.scenePhase) var scenePhase @StateObject var locationManager = LocationManager() ... for code I have the following LocationManager: import CoreLocation import SwiftData @MainActor class LocationManager: NSObject, ObservableObject { @Published var location: CLLocation? //@Published var region = MKCoordinateRegion() private let locationManager = CLLocationManager() /// Override exiting init override init() { /// Bring in the normal init super.init() AppDelegate.shared.requestLocationAuthorization() locationManager.desiredAccuracy = kCLLocationAccuracyBest locationManager.distanceFilter = kCLDistanceFilterNone locationManager.requestAlwaysAuthorization() locationManager.startUpdatingLocation() locationManager.delegate = self locationManager.allowsBackgroundLocationUpdates = true locationManager.showsBackgroundLocationIndicator = true locationManager.startUpdatingLocation() } } extension LocationManager: CLLocationManagerDelegate { /// iOS triggers whenever the location changes func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { guard let newLocation = locations.last else {return} /// Check if the location has changed significantly before updating in this case more than 10 meters if self.location == nil || location!.distance(from: newLocation) > 10 { self.location = newLocation // save the location info in this function saveEntry(location: self.location!) } } }
1
0
767
Dec ’23
Accessing a completion variable outside the function
I am retrieving a value from a remote json file through task in a function with a completion code (as recommended practice) where I retrieve a date. I then need to use that date in subsequent processing. In this case, return the date and continue my processing. I can work with the date with in the function (see below) but I can't get the value outside the function. Any help would be appreciated. case .remote: var fetchDate: Date? fetchRemoteArticlesDate { date in fetchDate = date // finds the correct date print("* fetchDate1: \(String(describing: fetchDate))" ) } // How can I receive the date from fetchRemoteArticlesData so I can return it? print("* fetchDate2: \(fetchDate ?? DateInfo.zeroDate)") return fetchDate ?? DateInfo.zeroDate class func fetchRemoteArticlesDate(completion: @escaping (Date) -> Void) { let urlString = AppValues.articlesLocation.remote let url = URL(string: urlString)! let session = URLSession.shared let task = session.dataTask(with: url) { (data, response, error) in if let error = error { LogEvent.print(module: "Articles.fetchArticles", message: "Error saving articles.json: \(error)") DispatchQueue.main.sync { completion(DateInfo.zeroDate) } return } guard let myData = data else { LogEvent.print(module: "Articles.fetchArticles", message: "no JSON data received") DispatchQueue.main.async { completion(DateInfo.zeroDate) } return } let date = decodeArticlesDateV2(myData) LogEvent.print(module: "Articles.articlesDate", message: "\(UserSettings.init().articlesLocation.description) date: \(date)") completion(date) } task.resume() }
2
0
452
Nov ’23
Can't remove GitHub repository
I give up and need some help. I picked up another GitHub repository into my project when adding a file from another project through Xcode add file/copy and so on. I can't seem to find out what file it's pointing to despite deleting it from the project. Also, I can't seem to get rid of the extra repository. Any help in how to clean this up would be appreciated. I want to get rid of the ex-login-1 GitHub in this project. That said I do not want to get rid of ex-login-1 as it's own GitHub Xcode project. I just need to get these two separated. On the ex-login-1 Xcode project it has no reference to the Zentastic project so this is one way.
4
0
1.2k
Aug ’23
How to form a search predicate using swift data with a one to many model
I am working in Xcode 15 (beta) migrating to SwiftData and am having a hard time figuring out how to form a search predicate for my one to many model. The desired result is to return a query that returns only the articles and sections where the article "search" field contains the string a user is searching for. Here is my current model: @Model class SectionsSD { @Attribute(.unique) var id: String var section: String var rank: String var toArticles: [ArticlesSD]? init(id:String, section: String, rank: String) { self.id = id self.section = section self.rank = rank } } @Model class ArticlesSD { var id: String var title: String var summary: String var search: String var section: String var body: String @Relationship(inverse: \SectionsSD.toArticles) var toSection: SectionsSD? init(id: String, title: String, summary: String, search: String, section: String, body: String) { self.id = id self.title = title self.summary = summary self.search = search self.section = section self.body = body } } In CoreData I was able to do the following in my code to form and pass the search predicate ("filter" being the user input search text, "SectionsEntity" being my old CoreData model): _fetchRequest = FetchRequest<SectionsEntity>(sortDescriptors: [SortDescriptor(\.rank)], predicate: NSPredicate(format: "toArticles.search CONTAINS %@", filter)) I can't find any examples or info anywhere that explains how to form a predicate in SwiftData to achieve the same search results. I can't seem to find a way to represent toArticles.search properly assuming the capability is there. Here is what I've tried but Xcode complains about the $0.toArticles?.contains(filter) with errors about "Cannot convert value of type 'Bool?' to closure result type 'Bool'" and "Instance method 'contains' requires the types 'ArticlesSD' and 'String.Element' (aka 'Character') be equivalent" let searchPredicate = #Predicate<SectionsSD> { $0.toArticles?.contains(filter) } _sectionsSD = Query(filter: searchPredicate) I've tried $0.toArticles?.search.contains(filter) but Xcode can't seem to find its way there like it did using CoreData Any suggestions and examples on how to form a predicate in this use case would be appreciated.
7
1
5.6k
Jun ’23