Hello,
I would like to draw your attention to the following imperfection. For validating purchases of my paid application Guru Maps Pro, I use the download id. This is a unique ID that can replace the Transaction ID for paid applications. However, with the release of the new AppTransaction API, this field is no longer present in the data. I tried parsing the receipt, but that field is absent there as well. The only way to obtain the download id is to send the receipt to the deprecated /verifyReceipt endpoint. This deprecated status concerns me, because at some point it might stop working.
Let me explain a little about why I need this. My users have a guru-account, which they can use both in the web version and on Android. When a user purchases the paid version of the application, they can access the paid features on both web and Android. This works great for in-app purchases, where there is a transaction ID, but it may soon stop working for paid applications because there is no way to determine any ID associated with the purchase. Transaction ID or Download ID – I don't mind which.
Delve into the world of built-in app and system services available to developers. Discuss leveraging these services to enhance your app's functionality and user experience.
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
Hello,
I write a Bachelor-Theses about Geolocation on an iPhone. I have a Signal-generator from R&S to simulate GPS-Data. I write an App on an Android-Phone and can readout the GPS-ID and the strength from the signal and the time and Position for Geolocation zb. Island or Africa like this.
Now my thesis is about the iPhone and I write an App for get the location manager Geolocation and to save it to a sqlite-database with longitude, latitude and time.
But the App recognizes only the real world for geolocation on GPS (LTE and Wlan are disabled!). With my Radio-Generator it would not recognize any Geolocation like the Android phone.?
So I need some fast help for my thesis, where are my problems?
I allready have a function like this:
func updateAccuracy(highAccuracy: Bool) {
locationManager.desiredAccuracy = highAccuracy ? kCLLocationAccuracyBestForNavigation : kCLLocationAccuracyHundredMeters
print("🎯 GPS-Genauigkeit geändert: (highAccuracy ? "Hoch" : "Plane-Genauigkeit")")
}
but nothing happens?
Best regards
Topic:
App & System Services
SubTopic:
Maps & Location
Hello everyone,
I'm encountering a strange location authorization issue in the iOS simulator, and I'm hoping someone can help me analyze it.
Problem Description:
When my app runs for the first time in the simulator, it requests location permissions.
I select "Deny" for the authorization.
Then, I go to the simulator's "Settings" -> "Privacy & Security" -> "Location Services" and enable location permissions for my app.
However, when I return to the app, CLLocationManager.authorizationStatus still returns .notDetermined, and the authorization request pop-up does not appear again.
This issue persists even after resetting the simulator settings multiple times.
import CoreLocation
@Observable
final class LocationManager: NSObject, CLLocationManagerDelegate {
var locationManager = CLLocationManager()
var currentLocation: CLLocationCoordinate2D?
override init() {
super.init()
locationManager.delegate = self
}
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
let status = manager.authorizationStatus
print("Authorize Status: \(status)")
switch status {
case .authorizedWhenInUse, .authorizedAlways:
locationManager.startUpdatingLocation()
case .denied, .restricted:
stopLocation()
case .notDetermined:
locationManager.requestWhenInUseAuthorization()
print("Location permission not determined.")
@unknown default:
break
}
}
func requestLocation() {
let status = locationManager.authorizationStatus
if status == .authorizedWhenInUse || status == .authorizedAlways {
locationManager.requestLocation()
} else {
locationManager.requestWhenInUseAuthorization()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let newLocation = locations.first else { return }
currentLocation = newLocation.coordinate
print("Updated location: \(newLocation.coordinate)")
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Location update failed with error: \(error.localizedDescription)")
currentLocation = nil
}
func stopLocation() {
locationManager.stopUpdatingLocation()
print("Stopped updating location")
}
}
Topic:
App & System Services
SubTopic:
Maps & Location
Tags:
Core Location
Maps and Location
Simulator
I'm currently developing an app with mapkit which I may submit to the Swift Student Challenge. So anyways, the app will be used completely offline. I was wondering if there was any way to include very low resolution map tiles with the app files. That way, the app never needs wifi. However, I'm not sure if this is possible, especially since the map would need to be under 20 mb. Thanks.
Topic:
App & System Services
SubTopic:
Maps & Location
Is there any way I can get updates when I change CarPlay style settings?
I've tried CPSessionConfigurationDelegate.contentStyleChanged and CPTemplateApplicationSceneDelegate.contentStyleDidChange, but they always produce the same result.
When I choose:
Automatic -> I receive light in case of daylight;
Always Dark and Always Show Dark Map toggle on -> dark
Always Dark and Always Show Dark Map toggle off -> light.
But it seems to be wrong, b/c CarPlay's toolbar is still dark, and I receive light.
Is there a way to get a dark style when choosing Always Dark and Always Show Dark Map toggle off? Or at least get updates when the Always Show Dark Map toggle changes?
While reviewing the Apple Documentation, I came across a potential issue in one of the examples that I believe is worth addressing.
The example appears to compare strings instead of integers, which could lead to unexpected behavior in production environments. Specifically, in the line where originalMajorVersion (a string) is compared with newBusinessModelMajorVersion (also a string) using <:
if originalMajorVersion < newBusinessModelMajorVersion
This comparison performs a lexicographical check rather than evaluating the numerical values of the strings. As a result, strings like "10" would incorrectly be considered less than "2", which is not the desired behaviour when comparing version numbers.
I have reported this via the Feedback assistant (FB16432337) but at the time of posting this there has been no reply at all (23 days)
Supporting business model changes by using the app transaction
do {
// Get the appTransaction.
let shared = try await AppTransaction.shared
if case .verified(let appTransaction) = shared {
// Hard-code the major version number in which the app's business model changed.
let newBusinessModelMajorVersion = "2"
// Get the major version number of the version the customer originally purchased.
let versionComponents = appTransaction.originalAppVersion.split(separator: ".")
let originalMajorVersion = versionComponents[0]
if originalMajorVersion < newBusinessModelMajorVersion {
// This customer purchased the app before the business model changed.
// Deliver content that they're entitled to based on their app purchase.
}
else {
// This customer purchased the app after the business model changed.
}
}
}
catch {
// Handle errors.
}
Topic:
App & System Services
SubTopic:
StoreKit
Is it possible to integrate a button in an app that displays advertisements to support charities -without offering any direct reward to the user?
When I quit the app from the background task list, CLLocationUpdate.liveUpdates does not resume properly and start location updates. However, if I kill the app directly, it can recover and start location updates.
Topic:
App & System Services
SubTopic:
Maps & Location
I am developing an app to add Discover cards to Apple Wallet. Unlike Visa, MasterCard, etc., Discover does not have APIs that return activationData, encryptedPassData and ephemeralPublicKey for a given card, so I have created a backend server to handle this. In my server, I am unsure how to generate the ephemeralPublicKey. Do I need to use the merchant certificate? If so, how do I use it to generate the ephemeralPublicKey?
I would appreciate it if someone could provide me with a step-by-step guide on how to generate ephemeralPublicKey for provisioning a card.
Hey,
I would love to access the users Contact (ie. the Me Card)
Apple recently released the Invites app and within this app you can find the users Contacts Photo. I tried to replicate this behaviour but I currently need to match based on a name or email or something else. I have no idea how to receive this data because when using the Invites app I only remember the app asking for Contacts permission and nothing else so far.
let store = CNContactStore()
let keysToFetch = [CNContactImageDataAvailableKey, CNContactImageDataKey, CNContactThumbnailImageDataKey] as [CNKeyDescriptor]
let email = "test@test.de"
let predicate = CNContact.predicateForContacts(matchingEmailAddress: email)
do {
let contacts = try store.unifiedContacts(matching: predicate, keysToFetch: keysToFetch)
let imageDatas: [Data] = contacts.compactMap { $0.imageData }
self.images = imageDatas.map { UIImage(data: $0) ?? UIImage() }
} catch {
print("Error fetching contacts: \(error)")
}
This is how I am retrieving the Image. MAYBE someone can help me out.
Thank you so far
~ Flo
Hello,
I recently implemented a conditional debounce publisher using Swift's Combine.
If a string with a length less than 2 is passed, the event is sent downstream immediately without delay. If a string with a length of 2 or more is passed, the event is emitted downstream with a 0.2-second delay.
While writing test logic related to this, I noticed a strange phenomenon: sometimes the publisher, which should emit events with a 0.2-second delay, does not emit an event.
The test code below should have all indices from 1 to 100 in the array, but sometimes some indices are missing, causing the assertion to fail. Even after observing completion, cancel, and output events through handleEvents, I couldn't find any cause. Am I using Combine incorrectly, or is there a bug in Combine?
I would appreciate it if you could let me know.
import Foundation
import Combine
var cancellables: Set<AnyCancellable> = []
@MainActor func text(index: Int, completion: @escaping () -> Void) {
let subject = PassthroughSubject<String, Never>()
let textToSent = "textToSent"
subject
.map { text in
if text.count >= 2 {
return Just<String>(text)
.delay(for: .seconds(0.2), scheduler: RunLoop.main)
.eraseToAnyPublisher()
} else {
return Just<String>(text)
.eraseToAnyPublisher()
}
}
.switchToLatest()
.sink {
if $0.count >= 2 {
completion()
}
}.store(in: &cancellables)
for i in 0..<textToSent.count {
let stringIndex = textToSent.index(textToSent.startIndex, offsetBy: i)
let stringToSent = String(textToSent[textToSent.startIndex...stringIndex])
subject.send(stringToSent)
}
}
var array = [Int]()
for i in 1...100 {
text(index: i) {
array.append(i)
}
}
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
for i in 1...100 {
assert(array.contains(i))
}
}
RunLoop.main.run(until: .now + 10)
Hello Apple Developers,
I’m developing an iOS app that requires access to the user’s contacts. I understand that starting from iOS 14, Apple introduced two levels of contact permissions:
1️⃣ Full Access – The app can read all contacts.
2️⃣ Limited Access – The app can only access contacts selected by the user.
My Question:
If a user initially grants Limited Access, is it allowed by Apple’s guidelines to request Full Access later?
🔹 I understand that re-requesting permission immediately after the user denies it is against Apple’s policies.
🔹 However, if the user has already granted Limited Access, and we first show an explanation modal explaining why Full Access is beneficial, can we then prompt them to change their settings via openAppSettings()?
🔹 Alternatively, can we use Permission.contacts.request() again to re-prompt the user for Full Access, or will iOS prevent the permission prompt from appearing again?
How We Handle This in the App:
1️⃣ If a user selects Limited Access, we respect their choice and only access their selected contacts.
2️⃣ Later, if Full Access is necessary for an enhanced experience, we display a clear explanation modal explaining why the app needs Full Access.
3️⃣ If the user agrees, we attempt to guide them to openAppSettings(), allowing them to manually change the permission.
4️⃣ However, if Permission.contacts.request() can be used to directly request Full Access again, we would like to know if this is acceptable.
We want to ensure that our implementation follows Apple’s privacy guidelines while providing the best user experience.
Any official guidance or best practices on this matter would be greatly appreciated.
Thank you in advance!
I am currently developing a custom-protocol VPN application for iOS using PacketTunnelProvider. I have also integrated an HTTP proxy service, which is launched via a dylib.
The overall flow is as follows:
App -> VPN TUN -> Local HTTP Proxy -> External Network
I have a question:
I am capturing all traffic, and normally, requests sent out by the HTTP proxy are also captured again by the VPN. However, when I send requests using createUdpSession in my code, they are not being captured by the virtual interface (TUN).
What could be the reason for this?
override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) {
let tunnelNetworkSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "192.168.18.0")
tunnelNetworkSettings.mtu=1400
let ipv4Settings = NEIPv4Settings(addresses: ["192.169.10.10"], subnetMasks: ["255.255.255.0"])
ipv4Settings.includedRoutes=[NEIPv4Route.default()]
ipv4Settings.excludedRoutes = [NEIPv4Route(destinationAddress: "10.0.0.0", subnetMask: "255.0.0.0"),
NEIPv4Route(destinationAddress: "172.16.0.0", subnetMask: "255.240.0.0"),
NEIPv4Route(destinationAddress: "192.168.0.0", subnetMask: "255.255.0.0"),
NEIPv4Route(destinationAddress:"127.0.0.0", subnetMask: "255.0.0.0"),
]
tunnelNetworkSettings.ipv4Settings = ipv4Settings
// Configure proxy settings
let proxySettings = NEProxySettings()
proxySettings.httpEnabled = true
proxySettings.httpServer = NEProxyServer(address: "127.0.0.1", port: 7890)
proxySettings.httpsEnabled = true
proxySettings.httpsServer = NEProxyServer(address: "127.0.0.1", port: 7890)
proxySettings.excludeSimpleHostnames = true
proxySettings.exceptionList=["localhost","127.0.0.1"]
tunnelNetworkSettings.proxySettings = proxySettings
setTunnelNetworkSettings(tunnelNetworkSettings) { [weak self] error in
if error != nil {
completionHandler(error)
return
}
completionHandler(nil)
let stack = TUNInterface(packetFlow: self!.packetFlow)
RawScoketFactory.TunnelProvider=self
stack.register(stack: UDPDirectStack())
stack.register(stack: TCPDirectStack())
stack.start()
}
}
NWUdpSession.swift
//
// NWUDPSocket.swift
// supervpn
//
// Created by TobbyQuinn on 2025/2/3.
//
import Foundation
import NetworkExtension
import CocoaLumberjack
public protocol NWUDPSocketDelegate: AnyObject{
func didReceive(data:Data,from:NWUDPSocket)
func didCancel(socket:NWUDPSocket)
}
public class NWUDPSocket:NSObject{
private let session:NWUDPSession
private let timeout:Int
private var pendingWriteData: [Data] = []
private var writing = false
private let queue:DispatchQueue=QueueFactory.getQueue()
public weak var delegate:NWUDPSocketDelegate?
public init?(host:String,port:UInt16,timeout:Int=Opt.UDPSocketActiveTimeout){
guard let udpSession = RawScoketFactory.TunnelProvider?.createUDPSession(to: NWHostEndpoint(hostname: host, port: "\(port)"), from: nil) else{
return nil
}
session = udpSession
self.timeout=timeout
super.init()
session.addObserver(self, forKeyPath: #keyPath(NWUDPSession.state),options: [.new], context: nil)
session.setReadHandler({ dataArray, error in
self.queueCall{
guard error == nil, let dataArray = dataArray else {
print("Error when reading from remote server or connection reset")
return
}
for data in dataArray{
self.delegate?.didReceive(data: data, from: self)
}
}
}, maxDatagrams: 32)
}
/**
Send data to remote.
- parameter data: The data to send.
*/
public func write(data: Data) {
pendingWriteData.append(data)
checkWrite()
}
public func disconnect() {
session.cancel()
}
public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard keyPath == "state" else {
return
}
switch session.state {
case .cancelled:
queueCall {
self.delegate?.didCancel(socket: self)
}
case .ready:
checkWrite()
default:
break
}
}
private func checkWrite() {
guard session.state == .ready else {
return
}
guard !writing else {
return
}
guard pendingWriteData.count > 0 else {
return
}
writing = true
session.writeMultipleDatagrams(self.pendingWriteData) {_ in
self.queueCall {
self.writing = false
self.checkWrite()
}
}
self.pendingWriteData.removeAll(keepingCapacity: true)
}
private func queueCall(block:@escaping ()->Void){
queue.async {
block()
}
}
deinit{
session.removeObserver(self, forKeyPath: #keyPath(NWUDPSession.state))
}
}
I haven't been able to find a definitive answer to this online. Is NSUbiquitousKeyValueStore end-to-end encrypted with Advanced Data Protection turned on? It’s not specifically called out here https://support.apple.com/en-us/102651.
Topic:
App & System Services
SubTopic:
iCloud & Data
Hey everyone,
I wanted to check if anyone else has faced extreme delays when requesting access to Apple Pay Wallet APIs. It was Oct 11 2024 a year ago since we first applied to enable in-app provisioning for virtual cards in our app and we made 1% progress.
For context, we already got access from Google for Google Wallet—it was smooth, professional, and timely. But with Apple… it’s been nothing but an endless cycle of waiting.
We followed every step, submitted everything correctly, and even called Apple Developer Support multiple times. Their response? "We've escalated it." Again and again. But there’s no real progress. We’re rerouted, ignored, and left in limbo.
At this point, I don’t even know if anyone is actually reviewing these requests. If a business like ours—fully compliant and ready to integrate—can’t even get a response in 150 day, how is this process supposed to work?
I’m posting this here because I can’t be the only one. Has anyone else faced this? If you finally got access, how did you do it? Because right now, it feels like Apple Pay in-app provisioning is an impossible goal.
Hoping someone from Apple sees this and realizes how broken this process is. We’re just trying to innovate and offer Apple users a great experience—why is it so difficult?
Looking forward to hearing from anyone in the community who can help, Thanks! 🙏
Crashed: com.apple.libcache.memorypressure
0 libsystem_platform.dylib 0x4ce4 _os_unfair_lock_recursive_abort + 36
1 libsystem_platform.dylib 0x1e1c _os_unfair_lock_lock_slow + 308
2 libcache.dylib 0x3a50 cache_remove_all + 56
3 CoreFoundation 0xd8d18 -[NSCache dealloc] + 84
4 Photos 0x505e4 -[PHSafeNSCacheDelegateReflector cache:willEvictObject:] + 216
5 CoreFoundation 0xbe524 __NSCacheCallDelegateWillEvictObjectCallbackWithValue + 76
6 CoreFoundation 0xbe43c __NSCacheValueRelease + 104
7 libcache.dylib 0x3998 _value_entry_remove + 120
8 libcache.dylib 0x3898 _entry_evict + 188
9 libcache.dylib 0x37c8 _evict_last + 108
10 libcache.dylib 0x3108 _cache_enforce_limits + 104
11 libcache.dylib 0x16f4 ___cache_handle_memory_pressure_event_block_invoke + 92
12 libdispatch.dylib 0x13394 _dispatch_block_async_invoke2 + 148
13 libdispatch.dylib 0x3fa8 _dispatch_client_callout + 20
14 libdispatch.dylib 0xd76c _dispatch_workloop_invoke + 2172
15 libdispatch.dylib 0x1738c _dispatch_root_queue_drain_deferred_wlh + 288
16 libdispatch.dylib 0x16bd8 _dispatch_workloop_worker_thread + 540
17 libsystem_pthread.dylib 0x3680 _pthread_wqthread + 288
18 libsystem_pthread.dylib 0x1474 start_wqthread + 8
Topic:
App & System Services
SubTopic:
Core OS
Hello,
I have been implementing NEAppPushProvider class to establish my own protocol to directly communicate with our provider server without the need to rely on APNs for background push notifications.
I am at a stage where I am able to establish a tcp communicator and receive messages back and forth but I noticed that when I disconnect from the WIFI I've set up by setting a given SSID, I am not getting hit on the Stop method. Below is briefly how I load and save preferences.
NEAppPushManager appPushManager = new NEAppPushManager();
appPushManager.LoadFromPreferences((error) =>
{
if (error != null)
{
Console.WriteLine($"Error loading NEAppPushManager preferences: {error.LocalizedDescription}");
return;
}
if (!enable)
{
Console.WriteLine("Disabling Local Push Provider...");
appPushManager.Enabled = false;
// ✅ Immediately update UserDefaults before saving preferences
userDefaults.SetBool(false, Constants.IsLocalPushEnabled);
userDefaults.Synchronize();
appPushManager.SaveToPreferences((saveError) =>
{
if (saveError != null)
{
Console.WriteLine($"Error disabling Local Push: {saveError.LocalizedDescription}");
}
else
{
Console.WriteLine("Local Push successfully disabled.");
}
});
return;
}
// ✅ Now we can safely enable Local Push
Console.WriteLine($"Enabling Local Push for SSID: {_currentSSID}");
appPushManager.MatchSsids = new string[] { _currentSSID };
appPushManager.LocalizedDescription = "LocalPushProvider";
appPushManager.ProviderBundleIdentifier = Constants.LocalPushExtensionBundleId;
appPushManager.Enabled = true;
appPushManager.SaveToPreferences((saveError) =>
{
if (saveError != null)
{
Console.WriteLine($"Error saving Local Push settings: {saveError.LocalizedDescription}");
}
else
{
Console.WriteLine("✅ Local Push successfully registered.");
userDefaults.SetBool(true, Constants.IsLocalPushEnabled);
userDefaults.Synchronize();
}
});
});
I've read through documentation and was expecting the Stop method to be hit when I turn off Wifi. Am I missing anything? Please let me know if I should provide more info. Currently I just have a console writeline method inside the Stop method to see if it actually gets hit.
Hi everyone,
I've recently implemented CKSyncEngine in my app, and I have two questions regarding its behavior:
Duplicate FetchedRecordZoneChanges After Sending Changes:
I’ve noticed that the engine sometimes receives a FetchedRecordZoneChanges event containing modifications and deletions that were just sent by the same device a few moments earlier. This event arrives after the SentRecordZoneChanges event, and both events share the same recordChangeTag, which results in double-handling the record.
Is this expected behavior? I’d like to confirm if this is how CKSyncEngine works or if I might be overlooking something.
Handling Initial Sync with a "Sync Screen":
When a user opens the app for the first time and already has data stored in iCloud, I need to display a "Sync Screen" temporarily to prevent showing partial data or triggering abrupt, rapid UI changes.
I’ve found that canceling current operations, then awaiting sendChanges() and fetchChanges() works well to ensure data is fully synced before dismissing the sync screen:
displaySyncScreen = true
await syncEngine.cancelOperations()
try await syncEngine.sendChanges()
try await syncEngine.fetchChanges()
displaySyncScreen = false
However, I’m unsure if canceling operations like this could lead to data loss or other issues. Is this a safe approach, or would you recommend a better strategy for handling this initial sync state?
where can we find documentation on the following fields included in payloads? They're not listed alongside the other fields in the documentation linked below:
https://developer.apple.com/documentation/weatherkitrestapi/hourweatherconditions
precipitationIntensity
snowfallAmount
Or if we can get the data type, unit used, and description here that would be great
Hello. Recently, the users of our APP have reported using our products, and they can normally receive push in Hong Kong. However, when traveling to Japan or some countries and regions in Europe, push will not be received.
we checked that
-the cert is valid
-the server get 'sent successfully' response
-used pushtry to test the cert and token and it can receive push in app