invalid mode 'kCFRunLoopCommonModes' provided to CFRunLoopRunSpecific - break on _CFRunLoopError_RunCalledWithInvalidMode to debugI get this warning when I tap either of the switches shown below. I've tried capturing the switch state in a var and using that to trigger the do/catch statement but no joy. I've even tried pulling the do/catch into separate functions and I still get the warning. Has anybody else run into this and how did you fix it?@IBAction func greetingFormat_Tapped(_ sender: UISwitch)
{
let theQuery = theTable_Settings.filter(settingID == 1)
if sender.isOn
{
do {
if try Database.shared.databaseConnection!.run(theQuery.update(greeting_Format <- "true")) > 0
{
greetingFormatLabel_Outlet.text = NSLocalizedString("HelloMrSmith_String", comment: "")
} else {
print("greeting format true not found")
}
} catch {
print("greeting format true update failed! Error: \(error)")
}
} else {
do {
if try Database.shared.databaseConnection!.run(theQuery.update(greeting_Format <- "false")) > 0
{
greetingFormatLabel_Outlet.text = NSLocalizedString("HiJoe_String", comment: "")
} else {
print("greeting format false not found")
}
} catch {
print("greeting format false update failed! Error: \(error)")
}
}
}@IBAction func nonrefundableSwitch_Tapped(_ sender: UISwitch)
{
let theQuery = theTable_Settings.filter(settingID == 1)
var itsOn: String = ""
if sender.isOn
{
itsOn = "true"
} else {
itsOn = "false"
}
if itsOn == "true"
{
do {
if try Database.shared.databaseConnection!.run(theQuery.update(nonRefundable_Bool <- "true")) > 0
{
depositDueLabel_Outlet.text = NSLocalizedString("nonRefunddepositisdue_String", comment: "")
} else {
print("nonRefundable true not found")
}
} catch {
print("nonRefundable true update failed! Error: \(error)")
}
} else {
do {
if try Database.shared.databaseConnection!.run(theQuery.update(nonRefundable_Bool <- "false")) > 0
{
depositDueLabel_Outlet.text = NSLocalizedString("depositisdue_String", comment: "")
} else {
print("nonRefundable false not found")
}
} catch {
print("nonRefundable false update failed! Error: \(error)")
}
}
}
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
When I execute a restore on my in-app purchase I'm getting a warning, however the restore is successfully executed. I think this is something new with Xcode 14.3. My test device is running iOS 16.4
This is the warning:
<SKPaymentQueue: 0x283708a80>: No observers found that respond to "paymentQueue:shouldAddStorePayment:forProduct:", will not check for purchase intents
It fires at this point in the code. If I comment out the first line, I don't get the warning however, the restore doesn't execute. Is anybody else seeing this or do I have something wrong in my code? I know it's only a warning but any help would be appreciated.
@IBAction func restoreButtonTapped(_ sender: UIBarButtonItem)
{
SKPaymentQueue.default().add(self)
SKPaymentQueue.default().restoreCompletedTransactions()
}
I've included the rest of the code just for a completeness.
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction])
{
for transaction in transactions
{
switch transaction.transactionState
{
case .purchasing:
//print("Purchase in progress...")
break
case .purchased:
//print("Purchase Successful")
SKPaymentQueue.default().finishTransaction(transaction)
//print("Transaction Complete")
// Hide the restore button
navigationItem.setRightBarButton(nil, animated: true)
//Set the BaseVerion in the Db to true
IAPHandler.set_BaseVersion_To_Purchased()
//Also hide the Purchase button
UIView.animate(withDuration: 1.0, animations: { [weak self] in
self?.purchaseButton.alpha = 0
}) { [weak self] (success) in
self?.selector_Top_Constraint.constant = 30
}
case .failed:
if let error = transaction.error
{
let errorDescription = error.localizedDescription
print("Transaction failed due to error: \(errorDescription)")
}
case .restored:
SKPaymentQueue.default().finishTransaction(transaction)
//print("Transaction Complete")
// Hide the restore button
navigationItem.setRightBarButton(nil, animated: true)
// Set the BaseVerion in the Db to true
IAPHandler.set_BaseVersion_To_Purchased()
// Also hide the Purchase button
UIView.animate(withDuration: 1.0, animations: { [weak self] in
self?.purchaseButton.alpha = 0
}) { [weak self] (success) in
self?.selector_Top_Constraint.constant = 30
}
case .deferred:
//print("Purchase Deferred")
break
@unknown default:
if let error = transaction.error
{
let errorDescription = error.localizedDescription
print("Transaction failed due to error: \(errorDescription)")
}
break
}
}
}
@IBAction func purchaseButtonTapped(_ sender: UIButton)
{
let theAlert = UIAlertController.init(title: K.Titles.pleaseChoose, message: nil, preferredStyle: .actionSheet)
let theCancleAction = UIAlertAction(title: K.Titles.cancel, style: .cancel)
let thePurchaseAction = UIAlertAction(title: K.DefaultList_Buttons.purchase_BaseVersion_Btn, style: .default) { [weak self] (action2) in
if SKPaymentQueue.canMakePayments()
{
// User can make payments
let paymentRequest = SKMutablePayment()
paymentRequest.productIdentifier = self!.base_Product_ID
SKPaymentQueue.default().add(self!)
SKPaymentQueue.default().add(paymentRequest)
} else {
// User cannot make payments
print("User cannot make payments")
}
}
theAlert.addAction(thePurchaseAction)
theAlert.addAction(theCancleAction)
theAlert.setValue(NSAttributedString(string: theAlert.title ?? "", attributes: [.font : UIFont.systemFont(ofSize: (gDefaultTextSize - 2), weight: UIFont.Weight.semibold)]), forKey: "attributedTitle")
let popOver = theAlert.popoverPresentationController
popOver?.sourceView = sender
popOver?.sourceRect = sender.bounds
popOver?.permittedArrowDirections = .any
present(theAlert, animated: true)
}
On Jan 2, 2024 I uploaded and got released 13 new apps. The problem I'm having is that only one of the new apps is showing data in analytics, the other 12 show nothing. They are all very similar apps with pretty much the same search criteria as the rest of my apps, 42 total. All of the other apps show plenty of analytic data.
I've done searches and the apps do show up in the search.
Is there a limit on the number of apps you can have?
Is there a way to have Apple look and see if I've done something wrong that's causing the problem?
Thank in advance
My apps are set up to store data in a SQLite database on the device. The user is also able to add images and those are also stored on the device. The database and images are stored in the apps documents folder. The database is set up with four tables, one of them containing a list of selectable items so the information in that table is constant. The other three are read/write to the user. The database also contains a field, which contains true/false as to whether the app has been purchased or not.
My thought behind was that this would make the users data private and secure.
My apps are set up using UIKit so SwiftData is not an option unless I rewrite the entire app in SwiftUI. Or is there a good way to use SwiftData in UIKit?
Is there a way to store/move this information into the cloud so that the data can be synced across multiple devices?
Or maybe set up an import/export scenario using a CSV file for the database using Dropbox?
Any help or advice would be appreciated.
Thanks in advance.
I use the code below for a non-consumable in-app purchase in my apps. Has anybody worked out how to handle this without using any of the deprecated items?
SKPaymentQueue - deprecated,
SKPayment - deprecated,
SKProduct - deprecated,
transactionState - deprecated,
SKPaymentTransaction - deprecated,
finishTransaction - deprecated
func paymentQueue(_ queue: SKPaymentQueue, shouldAddStorePayment payment: SKPayment, for product: SKProduct) -> Bool
{
true
}
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction])
{
for transaction in transactions
{
switch transaction.transactionState
{
case .purchasing:
break
case .purchased:
SKPaymentQueue.default().finishTransaction(transaction)
// Hide the restore button
navigationItem.setRightBarButton(nil, animated: true)
// Set the ProVerion in the Db to true
IAPHandler.setProVersionToPurchased()
// Also hide the Purchase button
UIView.animate(withDuration: 1.0, animations: { [weak self] in
self?.purchaseBtn_Outlet.alpha = 0
}) { [weak self] (success) in
if self!.theDevice.isOneOf(K.Device_Groups.SE_3_iPhone8) {
self?.segControlTop_Constraint.constant = 10
} else if self!.theDevice.isPhone {
self?.segControlTop_Constraint.constant = 30
}
}
case .failed:
if let error = transaction.error
{
let errorDescription = error.localizedDescription
print("Transaction failed due to error: \(errorDescription)")
}
case .restored:
SKPaymentQueue.default().finishTransaction(transaction)
// Hide the restore button
navigationItem.setRightBarButton(nil, animated: true)
// Set the ProVerion in the Db to true
IAPHandler.setProVersionToPurchased()
// Also hide the Purchase button
UIView.animate(withDuration: 1.0, animations: { [weak self] in
self?.purchaseBtn_Outlet.alpha = 0
}) { [weak self] (success) in
if self!.theDevice.isOneOf(K.Device_Groups.SE_3_iPhone8) {
self?.segControlTop_Constraint.constant = 10
} else if self!.theDevice.isPhone {
self?.segControlTop_Constraint.constant = 30
}
}
case .deferred:
break
@unknown default:
if let error = transaction.error
{
let errorDescription = error.localizedDescription
print("Transaction failed due to error: \(errorDescription)")
}
break
}
}
}
// Sets the purchase to true in the Db
class IAPHandler: NSObject {
//Get the ProVersion Status
static func isProVersionPurchased() -> Bool
{
let VC_String = "IAPHandler"
var theStatus = false
do {
let settings = try Database.shared.databaseConnection!.read { db in
try My_Settings.fetchOne(db)
}
let theStatusText = settings?.ProVersion ?? "false"
theStatus = theStatusText == "true" ? true : false
} catch {
print("Getting the ProVersion Status failed! \(VC_String) \(error)")
}
return theStatus
}
// Set ProVersion to true.
static func setProVersionToPurchased()
{
let VC_String = "IAPHandler"
do {
try Database.shared.databaseConnection!.write { db in
try db.execute(sql: "UPDATE My_Settings SET ProVersion = :proVersion WHERE Settings_ID = :id",
arguments: ["proVersion": "true", "id": 1])
}
} catch {
print("Update set pro version, failed! \(VC_String)s \(error)")
}
}
}// End of class
I have an app that was written in UIKit. It's too large, and it would be much too time consuming at this point to convert it to SwiftUI.
I want to incorporate the new limited contacts into this app. The way it's currently written everything works fine except for showing the limited contacts in the contact picker.
I have downloaded and gone though the Apple tutorial app but I'm having trouble thinking it through into UIKit. After a couple of hours I decided I need help.
I understand I need to pull the contact IDs of the contacts that are in the limited contacts list. Not sure how to do that or how to get it to display in the picker. Any help would be greatly appreciated.
func requestAccess(completionHandler: @escaping (_ accessGranted: Bool) -> Void)
{
switch CNContactStore.authorizationStatus(for: .contacts)
{
case .authorized:
completionHandler(true)
case .denied:
showSettingsAlert(completionHandler)
case .restricted, .notDetermined:
CNContactStore().requestAccess(for: .contacts) { granted, error in
if granted
{
completionHandler(true)
} else {
DispatchQueue.main.async { [weak self] in
self?.showSettingsAlert(completionHandler)
}
}
}
// iOS 18 only
case .limited:
completionHandler(true)
@unknown default: break
}
}
// A text field that displays the name of the chosen contact
@IBAction func contact_Fld_Tapped(_ sender: TextField_Designable)
{
sender.resignFirstResponder()
// The contact ID that is saved to the Db
getTheCurrentContactID()
let theAlert = UIAlertController(title: K.Titles.chooseAContact, message: nil, preferredStyle: .actionSheet)
// Create a new contact
let addContact = UIAlertAction(title: K.Titles.newContact, style: .default) { [weak self] _ in
self?.requestAccess { _ in
let openContact = CNContact()
let vc = CNContactViewController(forNewContact: openContact)
vc.delegate = self // this delegate CNContactViewControllerDelegate
DispatchQueue.main.async {
self?.present(UINavigationController(rootViewController: vc), animated: true)
}
}
}
let getContact = UIAlertAction(title: K.Titles.fromContacts, style: .default) { [weak self] _ in
self?.requestAccess { _ in
self?.contactPicker.delegate = self
DispatchQueue.main.async {
self?.present(self!.contactPicker, animated: true)
}
}
}
let editBtn = UIAlertAction(title: K.Titles.editContact, style: .default) { [weak self] _ in
self?.requestAccess { _ in
let store = CNContactStore()
var vc = CNContactViewController()
do {
let descriptor = CNContactViewController.descriptorForRequiredKeys()
let editContact = try store.unifiedContact(withIdentifier: self!.oldContactID, keysToFetch: [descriptor])
vc = CNContactViewController(for: editContact)
} catch {
print("Getting contact to edit failed: \(self!.VC_String) \(error)")
}
vc.delegate = self // delegate for CNContactViewControllerDelegate
self?.navigationController?.isNavigationBarHidden = false
self?.navigationController?.navigationItem.hidesBackButton = false
self?.navigationController?.pushViewController(vc, animated: true)
}
}
let cancel = UIAlertAction(title: K.Titles.cancel, style: .cancel) { _ in }
if oldContactID.isEmpty
{
editBtn.isEnabled = false
}
theAlert.addAction(getContact) // Select from contacts
theAlert.addAction(addContact) // Create new contact
theAlert.addAction(editBtn) // Edit this contact
theAlert.addAction(cancel)
let popOver = theAlert.popoverPresentationController
popOver?.sourceView = sender
popOver?.sourceRect = sender.bounds
popOver?.permittedArrowDirections = .any
present(theAlert,animated: true)
}
func requestAccess(completionHandler: @escaping (_ accessGranted: Bool) -> Void)
{
switch CNContactStore.authorizationStatus(for: .contacts)
{
case .authorized:
completionHandler(true)
case .denied:
showSettingsAlert(completionHandler)
case .restricted, .notDetermined:
CNContactStore().requestAccess(for: .contacts) { granted, error in
if granted
{
completionHandler(true)
} else {
DispatchQueue.main.async { [weak self] in
self?.showSettingsAlert(completionHandler)
}
}
}
// iOS 18 only
case .limited:
completionHandler(true)
@unknown default: break
}
}
// MARK: - Contact Picker Delegate
extension AddEdit_Quote_VC: CNContactPickerDelegate
{
func contactPicker(_ picker: CNContactPickerViewController, didSelect contact: CNContact)
{
selectedContactID = contact.identifier
let company: String = contact.organizationName
let companyText = company == "" ? K.Titles.noCompanyName : contact.organizationName
contactNameFld_Outlet.text = CNContactFormatter.string(from: contact, style: .fullName)!
companyFld_Outlet.text = companyText
save_Array[0] = K.AppFacing.true_App
setSaveBtn_AEQuote()
}
}
extension AddEdit_Quote_VC: CNContactViewControllerDelegate
{
func contactViewController(_ viewController: CNContactViewController, shouldPerformDefaultActionFor property: CNContactProperty) -> Bool
{
return false
}
func contactViewController(_ viewController: CNContactViewController, didCompleteWith contact: CNContact?)
{
selectedContactID = contact?.identifier ?? ""
if selectedContactID != ""
{
let company: String = contact?.organizationName ?? ""
let companyText = company == "" ? K.Titles.noCompanyName : contact!.organizationName
contactNameFld_Outlet.text = CNContactFormatter.string(from: contact!, style: .fullName)
companyFld_Outlet.text = companyText
getTheCurrentContactID()
if selectedContactID != oldContactID
{
save_Array[0] = K.AppFacing.true_App
setSaveBtn_AEQuote()
}
}
dismiss(animated: true, completion: nil)
}
}