In main app, I can override dark/ light theme based on user preference, so that I can retrieve correct color information based on named color.
if (user preference) {
overrideUserInterfaceStyle = .light
} else {
overrideUserInterfaceStyle = .dark
}
// Dark theme/ light theme automatic aware color.
SwiftUI.Color("purpleColor")
However, how can I override the theme of a WidgetKit, so that my WidgetKit can interpret named color correctly?
I know in WidgetKit, I can read what is current system wide theme settings using
@Environment(\.colorScheme)
But, that is not what I want.
I want the ability to override theme of a WidgetKit based on user preference, then able to retrieve correct named color.
Thanks.
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
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'm using the UISheetPresentationController to present a view, as shown in the following code:
@IBAction func click(_ sender: Any) {
let whiteViewController = WhiteViewController.instanceFromMainStoryBoard()
if let sheet = whiteViewController.sheetPresentationController {
sheet.detents = [.medium()]
sheet.prefersScrollingExpandsWhenScrolledToEdge = false
sheet.prefersGrabberVisible = false
// Remove dim effect.
////sheet.largestUndimmedDetentIdentifier = .medium
}
self.present(whiteViewController, animated: true)
}
The outcome is as follow
This code results in a presentation with:
A dimmed background.
A 'Touch anywhere to dismiss' behavior.
However, I want to remove the dim background while keeping the 'Touch anywhere to dismiss' functionality. When I add the line:
sheet.largestUndimmedDetentIdentifier = .medium
The dim background is indeed removed, but the touch-to-dismiss behavior is gone too, as illustrated:
Is there a way to achieve both – removing the dim background and retaining the touch-to-dismiss functionality?
Thank you.
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?
I have an app which is in the app store since 2022.
It is an app with combined features of note taking, todo list and calendar.
My most recent app update is rejected with the following message.
Guideline 4.3(a) - Design - Spam
We noticed your app shares a similar binary, metadata, and/or concept as apps submitted to the App Store by other developers, with only minor differences.
Submitting similar or repackaged apps is a form of spam that creates clutter and makes it difficult for users to discover new apps.
Next Steps
Since we do not accept spam apps on the App Store, we encourage you to review your app concept and submit a unique app with distinct content and functionality.
Resources
Some factors that contribute to a spam rejection may include:
Submitting an app with the same source code or assets as other apps already submitted to the App Store
Creating and submitting multiple similar apps using a repackaged app template
Purchasing an app template with problematic code from a third party
Submitting several similar apps across multiple accounts
Learn more about our requirements to prevent spam in App Review Guideline 4.3(a).
Support
Reply to this message in your preferred language if you need assistance. If you need additional support, use the Contact Us module.
Consult with fellow developers and Apple engineers on the Apple Developer Forums.
Help improve the review process or identify a need for clarity in our policies by suggesting guideline changes.
I reply the reviewer with the following message.
Hi,
I am writing to appeal the rejection of my app, XXX. I would like to emphasize the unique aspects of XXX that distinguish it from other apps in the market:
Integrated Functionality: Unlike most apps that are standalone note-taking, to-do, or calendar apps, XXX uniquely combines all three functions into a single app.
Extensive Calendar Features: XXX includes a comprehensive calendar feature covering holidays in 250 countries and 3,680 states and provinces, a rare and valuable feature.
Chinese Lunar Calendar: Our app includes a Chinese Lunar Calendar, which is highly appreciated in the Taiwanese market, adding a culturally specific feature not commonly found in other apps.
Organizational Tools: XXX allows unlimited tabs and colors for organizing notes, surpassing many competitors who offer limited color options.
Flexible Viewing and Sorting Modes: Users can choose from four viewing modes (Grid, Compact Grid, List, Compact List) and five sorting modes (Modified Time, Created Time, Alphabet, Color, Check, Reminder), enhancing the user experience by providing flexibility and customization.
No Character Limit: Our design supports notes of any length without character limitations, unlike many apps that impose restrictions.
Organized To-Do Lists: Completed to-do items are automatically moved to the bottom, keeping the list clean and organized, a feature that sets us apart from other apps.
Rich Attachments: XXX supports unlimited picture attachments, drawing attachments, and voice recordings for notes, offering more versatility than many other apps.
Advanced Security: XXX offers 3 different kinds of password lock mechanisms to protect note privacy, ensuring users have multiple options for securing their information.
Powerful Reminders: Our app includes highly customizable reminders that can repeat daily, weekly, monthly, or yearly, making it easy for users to stay on top of their tasks and appointments.
Due to the character limits of this form, I cannot list all the unique features of XXX. Please refer to our product description for a comprehensive overview.
We are proud of our 5k user reviews with an average rating of 4.8, reflecting user satisfaction with XXX's unique features.
Here's are some of the recent reviews. (United states only. Please kindly contact me to obtain full list of the reviews)
by ??? – Jun 14, 2024
omg i cannot explain how much i love XXX like unlike the boring notes app this one is actually fun to use and rlly easy and i rlly like the fonts, color, to do list, calandar that can remind you like every day, week, month, and year like omg?!?!? i love XXX and this app is going in like my "I 💖U" folder like tysm for making XXX idk what i would do without it like omg tysm i cant explain in words how much i love it💖💖💖💖💖💖💖💖💖💖💖💖💖💖
by ??? – May 12, 2024
I love it sooo much I give it a 5 because there is a password so no one can look
by ??? – Apr 30, 2024
Perfect app. All in one
by ??? – Mar 26, 2024
I just downloaded this app a few days ago and I love it!! 😃 Up til recently I ALWAYS used paper sticky notes that I would have on my desk - and the reason why is because I could see everything all at once at all times; it’s very convenient - but it also produces clutter and anxiety because every subject is all in one place. XXX fixed that!! 😀 With tabs and the list or grid view mode, I can have more organization of subjects and still see almost everything all at once! I love it! 🤩 suggestion I would really love to have more format options for the text! 🙂 Like a button to indent, and be able to have sub notes with points, dashes, or numbers. It makes everything look more tidy and neat. Thank you for this wonderful app! 😆
I am available at 123-456-789 to discuss further and provide additional detail. Thank you.
Till to date, I haven't received reply from the reviewer.
May I know, what are some other actionable items I can do, so that I can get through "Guideline 4.3(a) - Design - Spam" app update rejection?
Thank you so much!
Topic:
App Store Distribution & Marketing
SubTopic:
App Review
Tags:
App Store
App Review
App Store Connect
App Submission
Currently, I am using UICollectionViewCompositionalLayout to achieve the following list layout with spacing:
We were thinking of trying out UICollectionLayoutListConfiguration due to its ability to support swipe actions.
We would like to specify the spacing between each item. However, I do not find a way to specify item spacing in UICollectionLayoutListConfiguration.
Does UICollectionLayoutListConfiguration not support item spacing, or have I missed something?
Thanks.
Sorting is an important feature in my app. I am using integers to represent the ordering sequence.
Items: A, B, D, E, H
Order number: 0, 1, 2, 3, 4
When I insert a new item "C", here's the outcome:
Items: A, B, C, D, E, H
Order number: 0, 1, 2, 3, 4, 5
Here's the write operation required on existing order numbers:
D: 2 -> 3
E: 3 -> 4
H: 4 -> 5
I wish to reduce the number of write operations because CoreData is pretty slow at writing. The problem becomes more significant when my users start to have a few thousand items in CoreData.
Here's my current workaround: leaving gaps between each order number. For instance:
Items: A, B, D, E, H
Order number: 0, 10, 20, 30, 40
When I insert a new item "C", here's the outcome:
Items: A, B, C, D, E, H
Order number: 0, 10, 11, 20, 30, 40
No write operations are required on existing order numbers.
Every 1 or 2 weeks, when my users close the app, I run background tasks to re-arrange the gaps between order numbers so that when users insert new items, fewer existing order numbers will be affected.
Items: A, B, C, D, E, H
Order number: 0, 10, 20, 30, 40, 50
Since sorting is pretty common, I was thinking some of you might have a better idea on how to reduce write operations on existing order numbers.
If you have a better idea, do you mind to share it with us? Thanks.
I am following the guideline at
https://developer.apple.com/documentation/apple_search_ads/implementing_oauth_for_the_apple_search_ads_api#3733114
However, I am getting an empty page like this :
I do not find a way upload my public key. Can someone help? Thanks.
UIKit and SwiftUI each have their own strengths and weaknesses:
UIKit: More performant (e.g., UICollectionView).
SwiftUI: Easier to create shiny UI and animations.
My usual approach is to base my project on UIKit and use UIHostingController whenever I need to showcase visually rich UI or animations (such as in an onboarding presentation).
So far, this approach has worked well for me—it keeps the project clean while solving performance concerns effectively.
However, I was wondering: Has anyone tried the opposite approach?
Creating a project primarily in SwiftUI, then embedding UIKit when performance is critical.
If so, what has your experience been like? Would you recommend this approach?
I'm considering this for my next project but am unsure how well it would work in practice.
Hi,
Currently, instead of using a real device and test account for in-app purchase testing, we are using Products.storekit with the Simulator.
Our app offers a subscription plan with a 3-day free trial.
We would like to simulate the following test scenarios:
User cancels the subscription within the 3-day free trial period.
User cancels the subscription after the 3-day free trial period.
However, in Xcode, under Debug > StoreKit > Manage Transactions..., we couldn’t find an option to simulate a subscription cancellation.
There is an option to refund the purchase, but we believe this is not the same as a cancellation.
Do you have any idea how we can simulate these two cases using Products.storekit and the Simulator?
Thanks in advance!
Apple has shown us how to perform heavy write operation using background thread (by using newBackgroundContext) in their official earth quake example - https://github.com/yccheok/earthquakes-WWDC20
But, what about heavy read operation? (millions of rows for stress test purpose)
We would also like our app UI to be responsiveness, when we are launching the app for first time, and the app is reading a large amount of data from CoreData.
The following is the code snippet which is using NSFetchedResultController.
[UI is not responsiveness if there are a lot of rows] }
[UI is not responsiveness if there are a lot of rows]
We try to perform controller.performFetch() using background thread. Still, but not sure why, the UI is still not responsiveness. My guess is that, after NSFetchedResultsController is occupied UI main thread, to perform some time consuming I/O read operation. }
[UI is responsive now. But the solution is incorrect...]
I guess, we need to place NSFetchedresultController under background thread too. Hence, we do the following modification. }
The UI is responsiveness during fetching, and data is able to be fetched and shown after some time.
However, if we investigate further by using the launching argument
com.apple.CoreData.ConcurrencyDebug 1
We will experience the following crash after controller.performFetch() finish executed.
CoreData`+[NSManagedObjectContext __Multithreading_Violation_AllThatIsLeftToUsIsHonor__]:
May I know, is it ever possible to make UI responsive, when we are using NSFetchedResultController to load a large amount of data (few millions rows as per testing)?
Can NSFetchedResultController ever operated under background thread?
I was wondering, when you use CoreData, do you usually create another equivalent struct based data class, to compliment the NSManagedObject?
The struct based data class, will act as the bridge, between UI layer, and CoreData data layer.
For instance, I have the following CoreData model data class.
@objc(NSTabInfo)
public class NSTabInfo: NSManagedObject {
}
extension NSTabInfo {
@nonobjc public class func fetchRequest() -> NSFetchRequest<NSTabInfo> {
return NSFetchRequest<NSTabInfo>(entityName: "NSTabInfo")
}
@NSManaged public var colorIndex: Int32
@NSManaged public var customColor: Int32
@NSManaged public var name: String?
@NSManaged public var order: Int64
@NSManaged public var typeValue: Int32
@NSManaged public var syncedTimestamp: Int64
@NSManaged public var uuid: UUID
}
extension NSTabInfo : Identifiable {
}
We need a UI, to represent such model data object. Initially, we have the following UI class
class TabInfoCell: UICollectionViewCell {
private var tabInfo: NSTabInfo?
func update(_ tabInfo: NSTabInfo) {
self.tabInfo = tabInfo
}
}
But, we just feel not comfortable with such design.
Letting TabInfoCell (UI class) to hold NSTabInfo (CoreData model data class) doesn't feel right, as NSTabInfo contains CoreData's context. I do not feel comfortable, to expose CoreData's context to an UI.
Will holding a class reference to NSTabInfo in UI, affect CoreData memory allocation/ deallocation strategy? Using weak reference might solve the issue. But, what should the UI do when the weak reference become nil?
With such concern, We have the following design
struct TabInfo {
enum Kind: Int {
case All = 0
case Calendar
case Custom
case Settings
}
let kind: Kind
var name: String?
var colorIndex: Int
var customColor: Int
var order: Int
var syncedTimestamp: Int64
var uuid: UUID
}
@objc(NSTabInfo)
public class NSTabInfo: NSManagedObject {
convenience init(context: NSManagedObjectContext, tabInfo: TabInfo) {
self.init(context: context)
self.colorIndex = Int32(tabInfo.colorIndex)
self.customColor = Int32(tabInfo.customColor)
self.name = tabInfo.name
self.order = Int64(tabInfo.order)
self.typeValue = Int32(tabInfo.kind.rawValue)
self.syncedTimestamp = tabInfo.syncedTimestamp
self.uuid = tabInfo.uuid
}
func toTabInfo() -> TabInfo {
return TabInfo(
kind: TabInfo.Kind(rawValue: Int(self.typeValue))!,
name: self.name,
colorIndex: Int(self.colorIndex),
customColor: Int(self.customColor),
order: Int(self.order),
syncedTimestamp: self.syncedTimestamp,
uuid: uuid
)
}
}
@nonobjc public class func fetchRequest() -> NSFetchRequest<NSTabInfo> {
return NSFetchRequest<NSTabInfo>(entityName: "NSTabInfo")
}
@NSManaged public var colorIndex: Int32
@NSManaged public var customColor: Int32
@NSManaged public var name: String?
@NSManaged public var order: Int64
@NSManaged public var typeValue: Int32
@NSManaged public var syncedTimestamp: Int64
@NSManaged public var uuid: UUID
}
extension NSTabInfo : Identifiable {
}
Then, in our UI class, it looks like
class TabInfoCell: UICollectionViewCell {
private var tabInfo: TabInfo?
func update(_ tabInfo: TabInfo) {
self.tabInfo = tabInfo
}
}
I was wondering, does such design philosophy make sense? Do you usually have a struct based data class, as the bridge between your UI layer, and the CoreData data layer?
Thanks.
I was wondering did anyone of you come across this strange situation?
I tried to index a property in entity.
For instance, for entity named NSPlainNote, I try to create an index named index_plain_note_label, on property label.
This is how it looks like.
However, if I inspect the content of the generated SQLite file.
It seems that 2 same indices with different name are created.
CREATE INDEX Z_NSPlainNote_index_plain_note_label ON ZNSPLAINNOTE (ZLABEL COLLATE BINARY ASC)
CREATE INDEX Z_NSPlainNote_label ON ZNSPLAINNOTE (ZLABEL COLLATE BINARY ASC)
If we observe carefully, such duplication also happen in all other index with 1 property like
NSPlainNote's order
NSPlainNote's sticky
NSTabInfo's order
Such duplication does not happen on index with 2 properties, or index with unique constraint.
May I know why it is so? Is there any step which I have done incorrectly?
So far, here's are the code snippets that almost work for NSFetchedResultsController + UICollectionView, based on the information provided
https://developer.apple.com/videos/play/wwdc2018/225 (Starting at 36:50)
https://stackoverflow.com/questions/38597804/how-to-order-moves-inserts-deletes-and-updates-in-a-uicollectionview-performb/53271562#53271562
Please note that, there are 2 [BlockOperation], as reloadItems and moveItem doesn't play well within single performBatchUpdates. Based on the workaround proposed in the video, we have to call reloadItems in a separate performBatchUpdates.
We also do not follow 100% methods (Perform reloadItems typed performBatchUpdates first, followed by insert/ move/ delete typed performBatchUpdates) proposed in the video.
This is because we notice that it doesn't work well even for simple case. Some strange behaviour including reloadItems will cause duplicated cell UI to be shown on screen. The "almost" work method we found are
Perform performBatchUpdates for insert, move and delete
At completion handler of performBatchUpdates, perform another performBatchUpdates for reloadItems
NSFetchedResultsController + UICollectionView integration
private var blockOperations: [BlockOperation] = []
// reloadItems and moveItem do not play well together. We are using the following workaround proposed at
// https://developer.apple.com/videos/play/wwdc2018/225/
private var blockUpdateOperations: [BlockOperation] = []
extension DashboardViewController: NSFetchedResultsControllerDelegate {
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
if type == NSFetchedResultsChangeType.insert {
print(">> insert")
blockOperations.append(
BlockOperation(block: { [weak self] in
if let self = self {
self.collectionView!.insertItems(at: [newIndexPath!])
}
})
)
}
else if type == NSFetchedResultsChangeType.update {
print(">> update")
blockUpdateOperations.append(
BlockOperation(block: { [weak self] in
if let self = self, let indexPath = indexPath {
self.collectionView.reloadItems(at: [indexPath])
}
})
)
}
else if type == NSFetchedResultsChangeType.move {
print(">> move")
blockOperations.append(
BlockOperation(block: { [weak self] in
if let self = self, let newIndexPath = newIndexPath, let indexPath = indexPath {
self.collectionView.moveItem(at: indexPath, to: newIndexPath)
}
})
)
}
else if type == NSFetchedResultsChangeType.delete {
print(">> delete")
blockOperations.append(
BlockOperation(block: { [weak self] in
if let self = self {
self.collectionView!.deleteItems(at: [indexPath!])
}
})
)
}
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
if type == NSFetchedResultsChangeType.insert {
print(">> section insert")
blockOperations.append(
BlockOperation(block: { [weak self] in
if let self = self {
self.collectionView!.insertSections(IndexSet(integer: sectionIndex))
}
})
)
}
else if type == NSFetchedResultsChangeType.update {
print(">> section update")
blockOperations.append(
BlockOperation(block: { [weak self] in
if let self = self {
self.collectionView!.reloadSections(IndexSet(integer: sectionIndex))
}
})
)
}
else if type == NSFetchedResultsChangeType.delete {
print(">> section delete")
blockOperations.append(
BlockOperation(block: { [weak self] in
if let self = self {
self.collectionView!.deleteSections(IndexSet(integer: sectionIndex))
}
})
)
}
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
if blockOperations.isEmpty {
performBatchUpdatesForUpdateOperations()
} else {
collectionView.performBatchUpdates({ [weak self] () -> Void in
guard let self = self else { return }
for operation: BlockOperation in self.blockOperations {
operation.start()
}
self.blockOperations.removeAll(keepingCapacity: false)
}, completion: { [weak self] (finished) -> Void in
print("blockOperations completed")
guard let self = self else { return }
self.performBatchUpdatesForUpdateOperations()
})
}
}
private func performBatchUpdatesForUpdateOperations() {
if blockUpdateOperations.isEmpty {
return
}
collectionView.performBatchUpdates({ [weak self] () -> Void in
guard let self = self else { return }
for operation: BlockOperation in self.blockUpdateOperations {
operation.start()
}
self.blockUpdateOperations.removeAll(keepingCapacity: false)
}, completion: { [weak self] (finished) -> Void in
print("blockUpdateOperations completed")
guard let self = self else { return }
})
}
}
The above way, works "almost" well when no "section" operations involved.
If there is item moved operation between different section, the following logging will be printed
blockOperations completed
>> move
blockOperations completed
>> move
blockOperations completed
However, if there are item being moved and section being added/ removed, the following logging will be printed
>> section delete
>> move
>> section insert
>> move
This means the completion handler block is not executed! Does anyone know why it is so, and how I can workaround with this issue? Thanks.
Currently, I am porting several SQLite statement to CoreData.
"SELECT id, \"order\" FROM plain_note WHERE archived = :archived AND trashed = :trashed " +
"ORDER BY " +
"CASE WHEN color_index = -1 THEN 12 + ABS(custom_color) ELSE " +
"color_index END DESC, " +
"\"order\" ASC"
"SELECT id, \"order\" FROM plain_note WHERE archived = :archived AND trashed = :trashed " +
"ORDER BY " +
"title COLLATE NOCASE DESC, " +
"CASE WHEN locked = 1 THEN NULL ELSE " +
"CASE WHEN type = 0 THEN body ELSE searched_string END " +
"END COLLATE NOCASE DESC, " +
"\"order\" ASC"
But, I am clueless on how I can make CoreData have the following CASE WHEN...END behavior, during sorting.
CASE WHEN color_index = -1 THEN 12 + ABS(custom_color) ELSE color_index END DESC
CASE WHEN locked = 1 THEN NULL ELSE
CASE WHEN type = 0 THEN body ELSE searched_string END
END COLLATE NOCASE DESC
I was wondering, is there any possible technique I can use, so that NSSortDescriptor can have the similar behavior?