Currently, I have achieve shadow and corner effect for UICollectionViewListCell, using the following code.
UICollectionViewListCell
class NoteCell: UICollectionViewListCell {
override func awakeFromNib() {
super.awakeFromNib()
initShadow()
initCorner()
}
private func updateShadowColor() {
// Determine the shadow color based on the current interface style
let shadowUIColor = UIColor.label
self.layer.shadowColor = shadowUIColor.cgColor
}
private func initShadow() {
// https://www.hackingwithswift.com/example-code/uikit/how-to-add-a-shadow-to-a-uiview
self.layer.shadowOpacity = 0.3
self.layer.shadowOffset = CGSize(width: 0.5, height: 0.5)
self.layer.shadowRadius = 2
self.layer.masksToBounds = false
self.updateShadowColor()
// Remove the following two lines if you experience any issues with shadow rendering:
self.layer.shouldRasterize = true
self.layer.rasterizationScale = UIScreen.main.scale
}
private func initCorner() {
var backgroundConfig = UIBackgroundConfiguration.listPlainCell()
backgroundConfig.backgroundColor = .systemBackground
backgroundConfig.cornerRadius = 16
self.backgroundConfiguration = backgroundConfig
}
layout
private func layoutConfig() -> UICollectionViewCompositionalLayout {
let layout = UICollectionViewCompositionalLayout { section, layoutEnvironment in
var config = UICollectionLayoutListConfiguration(appearance: .plain)
config.headerMode = .none
config.footerMode = .none
config.showsSeparators = false
config.headerTopPadding = 0
config.backgroundColor = nil
config.trailingSwipeActionsConfigurationProvider = { [weak self] indexPath in
guard let self = self else { return nil }
// Knowing what we are tapping at.
var snapshot = dataSource.snapshot()
let sectionIdentifier = snapshot.sectionIdentifiers[indexPath.section]
let itemIdentifiers = snapshot.itemIdentifiers(inSection: sectionIdentifier)
let itemIdentifier: NoteWrapper = itemIdentifiers[indexPath.item]
let deleteHandler: UIContextualAction.Handler = { action, view, completion in
completion(true)
// TODO:
//snapshot.reloadItems([itemIdentifier])
}
let deleteAction = UIContextualAction(style: .normal, title: "Trash", handler: deleteHandler)
var swipeActionsConfiguration = UISwipeActionsConfiguration(actions: [
deleteAction,
])
deleteAction.image = UIImage(systemName: "trash")
deleteAction.backgroundColor = UIColor.systemRed
swipeActionsConfiguration.performsFirstActionWithFullSwipe = false
return swipeActionsConfiguration
}
// https://developer.apple.com/forums/thread/759987
let layoutSection = NSCollectionLayoutSection.list(using: config, layoutEnvironment: layoutEnvironment)
layoutSection.interGroupSpacing = 16 // Distance between item.
layoutSection.contentInsets = NSDirectionalEdgeInsets(
top: 16, // Distance between 1st item and its own header.
leading: 16,
bottom: 16, // Distance of last item and other header/ bottom edge.
trailing: 16
)
return layoutSection
}
return layout
}
This is the outcome.
However, when I perform swipe action, the shadow effect is gone.
Do you have any idea how I can resolve such? Thanks.
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
After waiting for few hours, I am still not able to edit the poster frame of app preview.
Can anyone from App Store Connect assist me on this?
Thank you.
Topic:
App Store Distribution & Marketing
SubTopic:
App Store Connect
Tags:
App Store
App Store Connect
This documentation describes what kind of data we should be sending to Apple server, once we are receiving CONSUMPTION_REQUEST
https://developer.apple.com/documentation/appstoreserverapi/consumptionrequest
But, it doesn't describe what kind of data we are receiving, when we are receiving CONSUMPTION_REQUEST?
May I know, is such a document available?
Thank you.
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
App Store Server Notifications
App Store Server API
App Store Server Library
Hi,
I am using Apple Search Ads (Basic), and I’ve noticed that my bank account has been debited weekly.
However, when I recently tried to access the reports, they appear empty. Please see the attached screenshots for reference.
Is there a customer support representative from Apple Search Ads who can assist me with this issue?
Thank you.
Topic:
App Store Distribution & Marketing
SubTopic:
General
Tags:
App Store
Marketing
Apple Search Ads
We offer a 3-day free trial, and our paywall clearly states that users will be charged after the trial ends.
However, some users request refunds after the charge - even after fully using our app for days or even weeks. In some cases, refunds are approved despite the users having consumed our AI processing services for up to a month.
Since our app relies on backend AI processing, each user session incurs a real cost. To prevent losses, we utilize RevenueCat’s CONSUMPTION_REQUEST system and have set our refundPreference to: "2. You prefer that Apple declines the refund".
Until recently, Apple typically respected this preference, and 90% of refund requests were declined as intended.
However, starting about a week ago, we observed a sudden reversal: Apple is now approving around 90% of refund requests, despite our refund preference. As a result, we are operating at a loss and have had to halt both our marketing campaigns and our 3-day free trial.
We’re trying to understand whether this shift is due to a change in Apple’s refund policy, or if we need to handle CONSUMPTION_REQUEST differently on our end.
Has anyone else experienced similar changes? Any insights would be greatly appreciated.
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
Subscriptions
StoreKit
App Store Server Notifications
App Store Server Library
I requested an expedited review, and the app was approved within a few hours.
However, the in-app purchase subscription plan is still under review.
Is there anything I can do to speed up the approval process for the in-app purchase, or do I just have to wait?
Thanks.
We're using XCode 26.1.1
We do not have resource to adopt Liquid Glass design. Hence, we are using the following workaround
<key>UIDesignRequiresCompatibility</key>
<true/>
This is our Storyboard.
Pre XCode 26
Before XCode 26.1.1, the bottom toolbar looks great.
In XCode 26
However, in XCode 26.1.1, the bottom toolbar buttons seems to "Squish together".
Do anyone have any idea, how I can make UIToolbar works by enabling UIDesignRequiresCompatibility?
Thanks.
This problem has buzzed me quite a while. So far, I still haven't founded a good solution.
Currently, I have an entity with a Bool column named pinned. I use it as the sectionNameKeyPath for NSFetchedResultsController.
So, my UICollectionView will always have 1 sections (All pinned = true, or All pinned = false), or 2 sections (Some pinned = true, and some pinned = false)
When I toggle the pinned value from true to false, or false to true, I expect FRC shall fire a "move" callback. (Due to item has moved to a new section) This happen most of the time.
However, sometimes, randomly, instead of firing "move" callback, FRC will fire "update" callback.
When such incident happen, I will exam the content of fetchedResultsController.sections. Then, I will notice the entity item stays in wrong section.
My FRC looks pretty straightforward.
lazy var fetchedResultsController: NSFetchedResultsControllerNSPlainNote = {
// Create a fetch request for the Quake entity sorted by time.
let fetchRequest = NSFetchRequestNSPlainNote(entityName: "NSPlainNote")
fetchRequest.sortDescriptors = [
NSSortDescriptor(key: "pinned", ascending: false)
]
// Create a fetched results controller and set its fetch request, context, and delegate.
let controller = NSFetchedResultsController(fetchRequest: fetchRequest,
managedObjectContext: CoreDataStack.INSTANCE.persistentContainer.viewContext,
sectionNameKeyPath: "pinned",
cacheName: nil
)
controller.delegate = fetchedResultsControllerDelegate
// Perform the fetch.
do {
try controller.performFetch()
} catch {
fatalError("Unresolved error \(error)")
}
return controller
}()
This is how I update the pinned column using background thread.
func updatePinned(_ objectID: NSManagedObjectID, _ pinned: Bool) {
let coreDataStack = CoreDataStack.INSTANCE
let backgroundContext = coreDataStack.backgroundContext
backgroundContext.perform {
let nsPlainNote = try! backgroundContext.existingObject(with: objectID) as! NSPlainNote
nsPlainNote.pinned = pinned
RepositoryUtils.saveContextIfPossible(backgroundContext)
}
}
I am not really sure, whether this can caused by my background thread.
As, if I replace the backgroundContext with viewContext, I haven't observed the random problem so far.
But, even so, I am not confident to conclude using backgroundContext is the culprit to this problem.
The setup of my background thread is also pretty straightforward. I cannot see how it can went wrong.
class CoreDataStack {
public static let INSTANCE = CoreDataStack()
private init() {
}
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "xxx")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
// TODO:
container.viewContext.automaticallyMergesChangesFromParent = true
//container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
//container.viewContext.undoManager = nil
//container.viewContext.shouldDeleteInaccessibleFaults = true
return container
}()
lazy var backgroundContext: NSManagedObjectContext = {
let backgroundContext = persistentContainer.newBackgroundContext()
// TODO:
//backgroundContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
//backgroundContext.undoManager = nil
return backgroundContext
}()
}
I was wondering, have anyone of you encounter similar problem when trying to utilize sectionNameKeyPath or FRC? Do you know what? Do you have any workaround/ solution for that?
Thank you!
Introduction
I expect when I perform "update" on 1 of the CoreData entity content, corresponding UICollectionViewCell should updated seamlessly.
But, the thing doesn't work as expected.
After debugging, I notice that instead of updating visible UICollectionViewCell on screen, an invisible offscreen UICollectionViewCell has been created and all update operations are performed on the invisible UICollectionViewCell?!
Simple hookup between NSFetchedResultsController and Diffable Data Source
The hookup is pretty straightforward
extension ViewController: NSFetchedResultsControllerDelegate {
func controller(_ fetchedResultsController: NSFetchedResultsController<NSFetchRequestResult>, didChangeContentWith snapshotReference: NSDiffableDataSourceSnapshotReference) {
guard let dataSource = self.dataSource else {
return
}
let snapshot = snapshotReference as NSDiffableDataSourceSnapshot<Int, NSManagedObjectID>
print("dataSource.apply")
dataSource.apply(snapshot, animatingDifferences: true) {
}
}
}
Simple data structure
import Foundation
import CoreData
extension NSTabInfo {
@nonobjc public class func fetchRequest() -> NSFetchRequest<NSTabInfo> {
return NSFetchRequest<NSTabInfo>(entityName: "NSTabInfo")
}
@NSManaged public var name: String?
@NSManaged public var order: Int64
}
extension NSTabInfo : Identifiable {
}
Simple data source to update cell
private func initDataSource() {
let dataSource = DataSource(
collectionView: collectionView,
cellProvider: { [weak self] (collectionView, indexPath, objectID) -> UICollectionViewCell? in
guard let self = self else { return nil }
guard let cell = collectionView.dequeueReusableCell(
withReuseIdentifier: "cell",
for: indexPath) as? CollectionViewCell else {
return nil
}
guard let nsTabInfo = self.getNSTabInfo(indexPath) else { return nil }
cell.label.text = nsTabInfo.name
print("Memory for cell \(Unmanaged.passUnretained(cell).toOpaque())")
print("Content for cell \(cell.label.text)\n")
return cell
}
)
self.dataSource = dataSource
}
Updating code doesn't work
We perform update on the 1st cell using the following code
@IBAction func updateClicked(_ sender: Any) {
let backgroundContext = self.backgroundContext
backgroundContext.perform {
let fetchRequest = NSFetchRequest<NSTabInfo>(entityName: "NSTabInfo")
fetchRequest.sortDescriptors = [
NSSortDescriptor(key: "order", ascending: true)
]
do {
let nsTabInfos = try fetchRequest.execute()
if !nsTabInfos.isEmpty {
// Perform update on the first cell
nsTabInfos[0].name = "\(Int(nsTabInfos[0].name!)! + 1)"
if backgroundContext.hasChanges {
try backgroundContext.save()
}
}
} catch {
print("\(error)")
}
}
}
Nothing is changed on the screen. However, if we look at the print output. We can see the initial content ("0") of the 1st cell is updated to "1". But, all of these are being done in an invisible same instance cell.
dataSource.apply
Memory for cell 0x0000000138705cd0
Content for cell Optional("1")
Memory for cell 0x0000000138705cd0
Content for cell Optional("1")
Memory for cell 0x0000000138705cd0
Content for cell Optional("2")
Memory for cell 0x0000000138705cd0
Content for cell Optional("3")
Ordering work without issue
Changing the ordering of the cell works as expected. The following is the code for charging ordering.
@IBAction func moveClicked(_ sender: Any) {
let backgroundContext = self.backgroundContext
backgroundContext.perform {
let fetchRequest = NSFetchRequest<NSTabInfo>(entityName: "NSTabInfo")
fetchRequest.sortDescriptors = [
NSSortDescriptor(key: "order", ascending: true)
]
do {
let nsTabInfos = try fetchRequest.execute()
for (index, element) in nsTabInfos.reversed().enumerated() {
element.order = Int64(index)
}
if backgroundContext.hasChanges {
try backgroundContext.save()
}
} catch {
print("\(error)")
}
}
}
Do you have idea why such problem occur? I prefer not to have collectionView.reloadData as workaround, as it will create more issue (like resetting scroll position, cell press state, ...)
I posted a complete demo at https://github.com/yccheok/problem-update-frc-diffable
Thank you
Currently, I have the following core data backed collection view, which enable users to perform add/ delete/ modify/ reordering.
It will be much more convenient to achieve what I, if I let UICollectionViewCell, to hold its corresponding NSManagedObject. So that I can each perform add/ delete/ modify/ reordering.
I was wondering, should I mark the NSManagedObject as weak, to avoid from interferencing with how CoreData manage its memory?
Using weak
class TabInfoSettingsCell: UICollectionViewCell {
// NSManagedObject. Use weak, as we do not want to inteference with how CoreData manage its memory.
private weak var nsTabInfo : NSTabInfo?
Use strong
Or, such concern is invalid. We can simply use strong
class TabInfoSettingsCell: UICollectionViewCell {
// NSManagedObject.
private var nsTabInfo : NSTabInfo?
Use struct
Or, such concern is valid and using weak is unsafe (as it can become nil in some edge case?). We can use a struct, which contains everything including objectID, but excluding NSManagedObjectContext.
class TabInfoSettingsCell: UICollectionViewCell {
// struct. Contains everything including objectID, but excluding NSManagedObjectContext.
private var tabInfo : TabInfo?
This come with the cost of having to create a struct out from NSManagedObject each time, in cellForItemAt.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
...
tabInfoSettingsCell.update(nsTabInfo.toTabInfo())
...
}
May I know, which is the correct design, as far as safe memory management is concerned?
NSAsynchronousFetchRequest seems like an answer to read large amount of data from CoreData, without blocking main thread.
But, it is lacking some key features, provided by NSFetchedResultsController. The key features are :-
In NSFetchedResultsControllerDelegate didChange and controllerDidChangeContent, we automatically receive a complete change information of persistence store. This enables us to implement the animation of add/delete/move/modify in collection view.
Always listen to DB changes. Whenever there is changes being written into the persistence store, NSFetchedResultsControllerDelegate didChange and controllerDidChangeContent will be triggered automatically. This enables us to ensure our UI is in sync with DB.
But, NSAsynchronousFetchRequest doesn't come with NSAsynchronousFetchedResultsController :(
I was wondering, if you were using NSAsynchronousFetchRequest, how do you implement
Animation changes on collection view?
Always listen to DB change?
My initial thought for animation changes on collection view, it seems we can utilise UICollectionViewDiffableDataSource.
But, I notice it might be highly memory inefficient to do so. We need to keep ALL NSManagedObject in memory, fire all faults during comparison. It looks like we will run out of memory, if there are many rows.
May I know, how do you achieve the following features, if you ever apply NSAsynchronousFetchRequest in production app?
Animation changes on collection view?
Always listen to DB change?
Thanks.
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.
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.
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.
I still fail to understand, what is the problem NSPersistentHistoryTransaction is trying to solve, in the CoreDataCloudKitDemo WWDC 2019 "Using Core Data with CloudKit"
https://github.com/software123inc/CoreDataCloudKitDemo/blob/master/CoreDataCloudKitDemo/DataProvider/CoreDataStack.swift#L161
I want to see, what problem will occur, if processPersistentHistory is not executed.
By making the processPersistentHistory empty, I try to do the following testing.
Run 2 simulators simultaneously in the same machine.
Add an item to simulator A.
Since, there is no way for simulator B to receive push notification, I press the home button for simulator B.
In simulator B, I tap on the app icon to launch the app again.
In simulator B, I can observe controllerDidChangeContent is being called. My guess is that, because the backed SQLite is seamlessly updated by CloudKit background task, NSFetchedResultController will be notified the SQLite DB change, and subsequently update the UI. Check the "Download CloudKit Changes into Core Data" of https://developer.apple.com/documentation/coredata/mirroring_a_core_data_store_with_cloudkit/syncing_a_core_data_store_with_cloudkit
In simulator B, due to controllerDidChangeContent is being triggered correctly, I can observe the UI change perform by NSFetchResultController without issue.
Hence, I am not clear, on what problem processPersistentHistory is trying to solve in the demo code.
May I know what kind of test case I can perform, to understand the problem solved by processPersistentHistory?
Based on "Integrate Store Changes Relevant to the Current View"
https://developer.apple.com/documentation/coredata/mirroring_a_core_data_store_with_cloudkit/syncing_a_core_data_store_with_cloudkit
Your app receives remote change notifications when the local store updates from CloudKit. However, it’s unnecessary to update your UI in response to every notification, because some changes may not be relevant to the current view.
Analyze the persistent history to determine whether the changes are relevant to the current view before consuming them in the user interface. Inspect the details of each transaction, such as the entity name, its updated properties, and the type of change, to decide whether to act.
For more information about persistent history tracking, see Consuming Relevant Store Changes.
This part is getting confusing. Our NSFetchedResultController is receiving relevant entity change event due to SQLite, and subsequently able to update the UI correct. If that is so, why do we still need persistent history?