Currently, if we perform multiple selection on PHPickerViewController, a tick icon is being used to indicate selection.
https://i.imgur.com/KCaSF9p.png
However, such information doesn't convey information for the selection ordering.
Is it every possible, to display the ordering number? This is an example from 3rd party library - https://github.com/mikaoj/BSImagePicker
As you can see, the 3rd party library is using 1,2,3... to convey selection ordering information
https://i.imgur.com/YoQVS4v.png
Can we achieve the same behavior in PHPickerViewController? We prefer to use PHPickerViewController, because it doesn't need to request permission to access photo library.
Thanks.
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
I wish to login simulator using a sandbox account so that I can test on in-app purchase and subscription.
I have created a new apple id account using a new email.
I have confirmed the new apple id + password is correct, by login into https://appleid.apple.com/#!&page=signin
I also added the new apple id account into App Store Connect as sandbox tester -
However, I am still not able login to simulator. I am keep getting error - "Username or password is incorrect"
You can see in the simulator background. I can login to https://appleid.apple.com/#!&page=signin using the new apple id + password via web browser. But, I am not sure why I am not able login into the simulator itself.
Can anyone assist me on this? Thank you.
Topic:
App Store Distribution & Marketing
SubTopic:
App Store Connect
Tags:
App Store Connect
Simulator
I am using "checkmark.circle.fill"
I wish to have a blue filled circle with the checkmark being solid black.
For the blue filled circle, I can choose the tinted color as blue.
But, the default SF Symbols "checkmark.circle.fill", its checkmark is transparent.
It looks as follow
So, I try to use "SF Symbols beta", to create custom icon.
During editing, the thing seems like what I want
Blue filled circle
Solid black color for checkmark
Then, I use "Export Symbol...", using Template Version 2.0 (XCode cannot recognise Template Version 3.0)
It seems like the checkmark fall back to transparent color.
When I import it to XCode
It seems like the "solid black color" on the checkmark is gone. When I use this custom icon in my app, it gave same result as "checkmark.circle.fill" (the checkmark still remain as transparent)
Doesn't anyone know how to resolve this issue? Thanks.
Due to privacy concern, I believe some users prefer their data not to sync with iCloud.
Hence, we plan to provide a switch option within the app, so that user can choose, whether to sync their data with iCloud.
Such feature can be achieved via the implementation suggested at https://developer.apple.com/forums/thread/118924?login=true
However, once user chooses to disable sync with iCloud, we should not perform purging on history tracking transactions.
Reason is that, if few months later/ few years later, user decides to turn on sync with iCloud again, lack of sufficient history tracking transactions, will cause sync with iCloud operation fail.
This is causing a dilemma. If we never clean the history tracking transactions, will it cause disk full issue?
If that is so, may I know, what is the correct way, to handle history tracking transactions purging, if we intent to provide CloutKit sync enable/ disable feature?
Thank you.
After using CloudKit enabled CoreData app for a while, I have the following observation.
I have been actively use a CloudKit enabled CoreData app, for 2 weeks.
Then, I get another fresh device. Once I hook up the fresh device to internet, based on the UI updating sequence, it seems like CoreData is replaying the entire transaction history, starting from 2 weeks ago till now.
So, my questions is, if we are using the CloudKit enabled CoreData app for 5 years.
Then, when I get a fresh new device, will the CoreData replaying the entire transaction history starting from 5 years ago?
Isn't that is highly inefficient, and cause the new device sync extremely slow? Is that is so, how can we avoid such slowness, when user is using the app long time enough, and then decide to sync to a newly bought device?
Thank you
I am using XCode 14.
The following is the UIImageView, using SFSymbol face.smiling
UIImageView background is pink color
UIImageView tint is black color
When I switch my app to dark mode, the smiling face which use to be transparent color, has became solid black color.
The face line (eye, mouth, face border) used to be solid black color, had became transparent color.
It seems like the image has changed from "face.smiling" to "face.smiling.fill" ?
I would like to avoid such outcome. Is there a way, to force UIImageView load SFSymbol in light mode, even though the entire app is using dark mode?
(This happens same when I test using real device and simulator)
Thanks.
Our widget extension appearance, is dependent on the main app content.
The only way for us to keep the widget(s) up-to-date, is to perform the following during app inactive.
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
func sceneDidEnterBackground(_ scene: UIScene) {
// Refresh home widgets.
WidgetCenter.shared.reloadAllTimelines()
This is always called regardless whether there is widget being placed.
I was wondering, is this consider a good practice?
I do not wish to perform unnecessary operation, which will drain up user battery.
Before iOS17, when we implement "Edit widget" feature with dynamic data, we are using the following ways.
Using an intent definition file
Using an intent extension
Here's the outcome.
A searchable view controller
Multi sectioned data view controller
Here is the implementation
intent definition file + intent extension
class IntentHandler: INExtension, ConfigurationIntentHandling {
func provideStickyNoteWidgetItemOptionsCollection(for intent: ConfigurationIntent, with completion: @escaping (INObjectCollection<StickyNoteWidgetItem>?, Error?) -> Void) {
var stickyNoteWidgetItems = [StickyNoteWidgetItem]()
var archivedStickyNoteWidgetItems = [StickyNoteWidgetItem]()
let allStickyNoteWidgetItems = NSPlainNoteRepository.getStickyNoteWidgetItemsWithoutTrash()
for allStickyNoteWidgetItem in allStickyNoteWidgetItems {
if allStickyNoteWidgetItem.archived == 1 {
archivedStickyNoteWidgetItems.append(allStickyNoteWidgetItem)
} else {
stickyNoteWidgetItems.append(allStickyNoteWidgetItem)
}
}
var sections = [INObjectSection<StickyNoteWidgetItem>]()
if !stickyNoteWidgetItems.isEmpty {
let section = INObjectSection(title: nil, items: stickyNoteWidgetItems)
sections.append(section)
}
if !archivedStickyNoteWidgetItems.isEmpty {
let archivedSection = INObjectSection(title: "archive".localized, items: archivedStickyNoteWidgetItems)
sections.append(archivedSection)
}
let collection = INObjectCollection(sections: sections)
completion(collection, nil)
}
override func handler(for intent: INIntent) -> Any {
// This is the default implementation. If you want different objects to handle different intents,
// you can override this and return the handler you want for that particular intent.
return self
}
}
However, if I were using AppIntent in iOS17, I can only achieve the following
Not searchable.
Not section-able.
Using AppIntent
import Foundation
import AppIntents
@available(iOS 16.0, macOS 13.0, watchOS 9.0, tvOS 16.0, *)
struct StickyNoteWidgetItemAppEntity: AppEntity {
static var typeDisplayRepresentation = TypeDisplayRepresentation(name: "StickyNoteWidgetItem")
@Property(title: "archived")
var archived: Bool?
struct StickyNoteWidgetItemAppEntityQuery: EntityQuery {
func entities(for identifiers: [StickyNoteWidgetItemAppEntity.ID]) async throws -> [StickyNoteWidgetItemAppEntity] {
// TODO: return StickyNoteWidgetItemAppEntity entities with the specified identifiers here.
return []
}
func suggestedEntities() async throws -> [StickyNoteWidgetItemAppEntity] {
// TODO: return likely StickyNoteWidgetItemAppEntity entities here.
// This method is optional; the default implementation returns an empty array.
return [
StickyNoteWidgetItemAppEntity(id: "id0", displayString: "note 0"),
StickyNoteWidgetItemAppEntity(id: "id1", displayString: "note 1"),
StickyNoteWidgetItemAppEntity(id: "id2", displayString: "note 2")
]
}
}
static var defaultQuery = StickyNoteWidgetItemAppEntityQuery()
var id: String // if your identifier is not a String, conform the entity to EntityIdentifierConvertible.
var displayString: String
var displayRepresentation: DisplayRepresentation {
DisplayRepresentation(title: "\(displayString)")
}
init(id: String, displayString: String) {
self.id = id
self.displayString = displayString
}
}
By using AppIntent, I would like to achieve what is previously achievable using intent defination file + intent extension
Is that ever possible?
Thanks.
For PHPickerViewController, we know we can perform simple filtering by
var config = PHPickerConfiguration()
config.filter = PHPickerFilter.images
But, how about we only want to show images with format JPG & PNG, but excluding GIF? This is because our app doesn't support GIF.
Is it possible to do so?
Currently, we are using PHPickerViewController + CGImage for efficient memory usage consideration - https://christianselig.com/2020/09/phpickerviewcontroller-efficiently/
However, we are getting "unsupported file format 'org.webmproject.webp'" error, while trying to save CGImage in webp format.
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true)
guard !results.isEmpty else { return }
for result in results {
result.itemProvider.loadFileRepresentation(forTypeIdentifier: UTType.image.identifier) { (url, error) in
guard let url = url else { return }
let options: [CFString: Any] = [
kCGImageSourceCreateThumbnailFromImageAlways: true,
kCGImageSourceCreateThumbnailWithTransform: true,
kCGImageSourceShouldCacheImmediately: true,
kCGImageSourceThumbnailMaxPixelSize: 512
]
guard let imageSource = CGImageSourceCreateWithURL(url as NSURL, nil) else { return }
let image = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options as CFDictionary)
//
// No issue in dealing with UTType.jpeg.identifier and UTType.png.identifier.
// destUrl is type URL.
//
guard let destination = CGImageDestinationCreateWithURL(destUrl as CFURL, UTType.webP.identifier, 1, nil) else { return }
CGImageDestinationAddImage(destination, image!, nil)
CGImageDestinationFinalize(destination)
}
}
We face no issue in saving CGImage in UTType.jpeg.identifier format and UTType.png.identifier format.
May I know how can we save CGImage in webp format without issue? Thank you.
Every of our data row, contains an unique uuid column.
Previously, before adopting CloudKit, the uuid column has a unique constraint. This enables us to prevent data duplication.
Now, we start to integrate CloudKit, into our existing CoreData. Such unique constraint is removed. The following user flow, will cause data duplication.
Steps to cause data duplication when using CloudKit
Launch the app for the first time.
Since there is empty data, a pre-defined data with pre-defined uuid is generated.
The pre-defined data is sync to iCloud.
The app is uninstalled.
The app is re-installed.
Launch the app for the first time.
Since there is empty data, a pre-defined data with pre-defined uuid is generated.
Previous old pre-defined data from step 3, is sync to the device.
We are now having 2 pre-defined data with same uuid! :(
I was wondering, is there a way for us to prevent such duplication?
In step 8, we wish we have a way to execute such logic before written into CoreData
Check whether such uuid exists in CoreData. If not, write to CoreData.
If not, we will pick the one with latest update date, then overwrite
the existing data.
I once try to insert the above logic into https://developer.apple.com/documentation/coredata/nsmanagedobject/1506209-willsave . To prevent save, I am using self.managedObjectContext?.rollback(). But it just crash.
Do you have any idea, what are some reliable mechanism I can use, to prevent data duplication in CoreData CloudKit?
Additional info:
Before adopting CloudKit
We are using using the following CoreData stack
class CoreDataStack {
static let INSTANCE = CoreDataStack()
private init() {
}
private(set) lazy var persistentContainer: NSPersistentContainer = {
precondition(Thread.isMainThread)
let container = NSPersistentContainer(name: "xxx", managedObjectModel: NSManagedObjectModel.wenote)
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// This is a serious fatal error. We will just simply terminate the app, rather than using error_log.
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
// So that when backgroundContext write to persistent store, container.viewContext will retrieve update from
// persistent store.
container.viewContext.automaticallyMergesChangesFromParent = true
// TODO: Not sure these are required...
//
//container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
//container.viewContext.undoManager = nil
//container.viewContext.shouldDeleteInaccessibleFaults = true
return container
}()
Our CoreData data schema has
Unique constraint.
Deny deletion rule for relationship.
Not having default value for non-null field.
After adopting CloudKit
class CoreDataStack {
static let INSTANCE = CoreDataStack()
private init() {
}
private(set) lazy var persistentContainer: NSPersistentContainer = {
precondition(Thread.isMainThread)
let container = NSPersistentCloudKitContainer(name: "xxx", managedObjectModel: NSManagedObjectModel.wenote)
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// This is a serious fatal error. We will just simply terminate the app, rather than using error_log.
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
// So that when backgroundContext write to persistent store, container.viewContext will retrieve update from
// persistent store.
container.viewContext.automaticallyMergesChangesFromParent = true
// TODO: Not sure these are required...
//
//container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
//container.viewContext.undoManager = nil
//container.viewContext.shouldDeleteInaccessibleFaults = true
return container
}()
We change the CoreData data schema to
Not having unique constraint.
Nullify deletion rule for relationship.
Having default value for non-null field.
Based on a feedback of a Developer Technical Support engineer from https://developer.apple.com/forums/thread/699634?login=true , hen mentioned we can
Detecting Relevant Changes by Consuming Store Persistent History
Removing Duplicate Data
But, it isn't entirely clear on how it should be implemented, as the github link provided in broken.
I install and test my own app, in real device, from TestFlight.
I want to able to keep testing on the in-app purchase/ subscription feature.
I notice once I have purchase an item success (I was prompted to enter password during purchase), it will forever be marked as purchased.
In simulator, if I uninstall then re-install again the app, the in-app purchase status will all be reset.
However, for TestFlight in real device, I do not find a way to reset my purchase. (So that I can keep testing on the purchase process)
Does anyone know, how I can reset/ cancel my in-app purchase status from TestFlight account?
I have developed an interactive widget which looks as the following
It is using CoreData.
The view is implemented as the following
struct widget_extensionEntryView : View {
var body: some View {
if let nsTodoList = nsTodoList {
VStack(alignment: .leading, spacing: 1) {
...
ForEach(0..<prefixGoodNSTodoArray.count, id: \.self) { index in
...
let todoIntent = TodoIntent(objectIDAsString: objectIDAsString, parentOrder: parentOrder)
Button(intent: todoIntent) {
}
}
}
}
}
}
The AppIntent is implemented as the following
import Foundation
import AppIntents
import WidgetKit
import CoreData
struct TodoIntent: AppIntent {
static var title: LocalizedStringResource = "Complete Task"
static var description: IntentDescription = IntentDescription("Complete selected task")
@Parameter(title: "objectIDAsString")
var objectIDAsString: String
@Parameter(title: "parentOrder")
var parentOrder: Int
init() { }
init(objectIDAsString: String, parentOrder: Int) {
self.objectIDAsString = objectIDAsString
self.parentOrder = parentOrder
}
func perform() async throws -> some IntentResult {
guard let objectID = NSManagedObjectID.from(objectIDAsString) else { return .result() }
guard let nsTodoList = NSTodoListRepository.INSTANCE.getNSTodoList(objectID) else { return .result() }
nsTodoList.toggleChecked(context: CoreDataStack.INSTANCE.viewContext, parentOrder: Int64(parentOrder))
RepositoryUtils.saveContextIfPossible(CoreDataStack.INSTANCE.viewContext)
TodoWidgetOptions.isWrittenByWidgetExtension = true
// Refresh all home widgets.
// TODO: https://developer.apple.com/forums/thread/721937
WidgetCenter.shared.reloadAllTimelines()
return .result()
}
}
The interactive widget works pretty well.
However, tapping on the widget has no response in the following situations:
After an overnight, we turn on the iPhone's screen and tap on the widget.
After a few hours of idle time, we turn on the iPhone's screen and tap on the widget.
One of the steps below will make the widget workable again:
Launch and close the main app again. The main app will call WidgetCenter.shared.reloadAllTimelines() during sceneDidEnterBackground.
Press and hold on the widget, choose 'Edit widget', and select the desired Todo list.
One thing to take note of is that I am using IntentTimelineProvider instead of AppIntentTimelineProvider. The reason I am using 'older tech' is due to the limitation mentioned in https://developer.apple.com/forums/thread/741053
However, I am not sure whether this is the root cause of the problem.
Does anyone have any idea why such a problem occurs?
Thanks.
I notice that, normal implementation for UICollectionView + UISearchController, will not able to achieve smooth scrolling animation when hiding search bar.
As you can see in the below video, when we scroll upward the page, it seems like "the page has slipped up suddenly".
If we compare the animation with search bar in iOS Settings page, the problem is more obvious. iOS Settings page able to have a smooth scrolling experience.
Implementation
The following is our implementation. The complete workable project is found at : https://github.com/yccheok/demo-uicollectionview-uisearchcontroller
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
@IBOutlet weak var collectionView: UICollectionView!
let reuseIdentifier = "cell" // also enter this string as the cell identifier in the storyboard
var items = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48","1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48","1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "bye"]
private lazy var searchController: UISearchController = {
let searchController = UISearchController(searchResultsController: UIViewController())
searchController.searchResultsUpdater = self
searchController.obscuresBackgroundDuringPresentation = true
searchController.searchBar.placeholder = "search_todos"
searchController.searchBar.delegate = self
return searchController
}()
override func viewDidLoad() {
super.viewDidLoad()
collectionView.dataSource = self
collectionView.delegate = self
navigationItem.searchController = searchController
}
// MARK: - UICollectionViewDataSource protocol
// tell the collection view how many cells to make
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.items.count
}
// make a cell for each cell index path
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// get a reference to our storyboard cell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! MyCollectionViewCell
// Use the outlet in our custom class to get a reference to the UILabel in the cell
cell.label.text = self.items[indexPath.item]
cell.backgroundColor = .yellow
return cell
}
// MARK: - UICollectionViewDelegate protocol
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// handle tap events
print("You selected cell #\(indexPath.item)!")
}
}
class MyCollectionViewCell: UICollectionViewCell {
@IBOutlet weak var label: UILabel!
}
extension ViewController: UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
}
}
extension ViewController: UISearchBarDelegate {
}
Do you have any idea, how we can achieve such a smooth scrolling animation? Thanks.
In my recent endeavor, I aimed to introduce new Fetch Index Elements to the Core Data model of my iOS application. To achieve this, I followed a process of lightweight migration, detailed as follows:
Navigate to Editor > Add Model Version to create a new version of the data model.
Name the new version with a sequential identifier (e.g., MyAppModelV3.xcdatamodel) based on the naming convention of previous models.
Select the newly created version, MyAppModelV3.xcdatamodel, as the active model.
Mark this new version as the "Current" model in the Xcode properties panel on the right.
In the new version of the model, MyAppModelV3.xcdatamodel, and add the new Fetch Index Elements there. Also, insert "v3" in the Versioning Hash Modifier field of affected entity, to indicate this modification.
Upon reflection, I realized that creating a new version of the xcdatamodel might not have been necessary for this particular case. However, it appears to have caused no adverse effects on the application's functionality.
During testing, I executed the application in a simulated environment, initially running an older version of the app to inspect the database content with SQLite DB Browser. I then upgraded to the latest app version to verify that the migration was successfully completed without causing any crashes. Throughout this testing phase, I employed the -com.apple.CoreData.MigrationDebug 1 flag to monitor all SQL operations, ensuring that indexes were appropriately dropped and recreated for the affected entity.
Following thorough testing, I deployed the update to production. The majority of users were able to upgrade to the new app version seamlessly. However, a small fraction reported crashes at startup, indicated by the following error message:
Fatal error: Unresolved error Error Domain=NSCocoaErrorDomain Code=134110 "An error occurred during persistent store migration." UserInfo={NSUnderlyingError=0x2820ad3e0 {Error Domain=NSCocoaErrorDomain Code=134100 "The managed object model version used to open the persistent store is incompatible with the one that was used to create the persistent store." UserInfo={metadata={ NSPersistenceFrameworkVersion = 1338; NSStoreModelVersionChecksumKey = "qcPf6+DfpsPrDQ3j1EVXcBIrFe1O0R6IKd30sJf4IrI="; NSStoreModelVersionHashes = { NSAttachment = {length = 32, ...
Strangely, the only way I could replicate this issue in the simulator was by running the latest version of the app followed by reverting to an older version, a scenario unlikely to occur in a real-world setting. This raises the question: How could this situation arise with actual users, considering they would typically move from an old to a new version rather than the reverse?
I am reaching out to the community for insights or advice on this matter. Has anyone else encountered a similar problem during the Core Data migration process? How did you resolve it?