When testing In-App Purchases in Xcode with a .storekit file, I can delete past purchase transactions, so I can re-test the purchase experience.
I've switched to using a Sandbox tester and made purchases. However, I cannot find how to delete previous purchase transactions made in the sandbox so I can re-run the tests.
Is this possible?
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
Created
I'm using TestFlight to test an app with payment/subscription functionality. I created sandbox accounts in AppStore Connect accordingly to be able to test the subscriptions. I'm logged in with the sandbox account.
When I try to subscribe in the App the wrong account (this is my actual real AppleID) is used for the subscription although it is recognized that this is just a sandbox subscription.
I tried:
logging off/on into the sandbox account
creating a totally new sandbox account
trying to trigger the payment with no logged in sandbox account
The result is always: in the payment popup it is stated that the purchase account will be my original AppleID and not a sandbox account.
How can I switch the accounts? Is this a bug at Apple's side somehow?
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
Subscriptions
TestFlight
In-App Purchase
StoreKit
I have an existing iOS app with shortcuts support, and I am trying to bring the same shortcuts to my Mac app in macOS Monterey. In my case, I have added the same intents definition file to my Mac target app, added "Intents eligible for in-app handling" to my Info.plust file and added the intent names, and made sure all the intent handling code is part of both iOS and Mac targets. Still, when I build and run the app on macOS Monterey, the new shortcuts don't show in the shortcut editor at all. I've tried closing and restarting the Shortcuts app, but no luck. The build logs do show the intents being built, but they're just not showing up in the Shortcuts app.
I tried 'donating' one of the intents in my Mac app code, but got an error:
Cannot donate interaction with intent that has no valid shortcut types
Not sure what to try to make it work.
Thanks.
Topic:
App & System Services
SubTopic:
Automation & Scripting
Tags:
wwdc21-10232
App Intents
Shortcuts
On an app that was using the old API for In-App Purchases (StoreKit 1). The app is already published on the App Store. The purchase is non-consumable.
While trying to migrate to StoreKit 2, I'm unable to restore purchases.
Specifically displaying and purchasing products works as expected, but when deleting and reinstalling the app, and then trying to restore purchases I can't do it.
I'm trying to restore them using the new APIs but it doesn't seem to be working.
What I have tried so far:
I'm listening for transaction updates during the whole lifetime of the app, with:
Task.detached {
for await result in Transaction.updates {
if case let .verified(safe) = result {
}
}
}
I have a button that calls this method, but other than prompting to log in again with the Apple ID it doesn't seem to have any effect at all:
try? await AppStore.sync()
This doesn't return any item
for await result in Transaction.currentEntitlements {
if case let .verified(transaction) = result {
}
}
This doesn't return any item
for await result in Transaction.all {
if case let .verified(transaction) = result {
}
}
As mentioned before I'm trying this after purchasing the item and deleting the app. So I'm sure it should be able to restore the purchase.
Am trying this both with a Configuration.storekit file on the simulator, and without it on a real device, in the Sandbox Environment.
Has anyone being able to restore purchases using StoreKit 2?
PD: I already filed a feedback report on Feedback Assistant, but so far the only thing that they have replied is:
Because StoreKit Testing in Xcode is a local environment, and the data is tied to the app, when you delete the app you're also deleting all the transaction data for that app in the Xcode environment. The code snippets provided are correct usage of the API.
So yes, using a Configuration.storekit file won't work on restoring purchases, but if I can't restore them on the Sandbox Environment I'm afraid that this won't work once released, leaving my users totally unable to restore what they have already purchased.
Can i use apple pay integration into my web iframe?In my situation, canMakePayment() returns null when i check browser support apple pay or not in Iframe
In our Mac application, we are creating a web-socket connection using NWConnection and we are able to successfully establish the connection and read/write data from both sides. We have auth tokens which are sent in headers of NWProtocolWebSocket.Options to the server. If token is good, server accepts the web-socket connection. As per RFC 6455, if server does not want to accept the connection for any reason during web-socket handshake, it returns 403 status code. In our case, if cookies are not valid, server returns 403 during web-socket handshake.
However, we could not find a way to read this status code in Network.framework. We are only getting failed state with NWErrorwhich is .posix(53) but there is no indication of the status code 403. We tried looking into protocol metadata on NWConnection object and they are nil.
We tested the same using URLSessionWebSocketTask where in failure callback method, we could see 403 status code on task.response which means client is getting the code correctly from server.
So, is there a way to read the HTTP status code returned by server during web-socket handshake using Network.framework?
I have received two strange crash reports from an iPad11,7 running iPadOS 15.1 and an iPad11,2 running iPadOS 15.2.
On both occasions, the crashed thread calls CFURLRequestSetMainDocumentURL, which in turn calls _dispatch_source_set_runloop_timer_4CF in libdispatch, after which the application crashes with SIGSEGV and SEGV_MAPERR.
The crashed thread's call stack is displayed below. Full crash logs are attached as well. What could this be?
Exception Type: SIGSEGV
Exception Codes: SEGV_MAPERR at 0x21d
Crashed Thread: 20
Thread 20 Crashed:
0 libdispatch.dylib 0x00000001829e1784 _dispatch_source_set_runloop_timer_4CF + 36
1 CFNetwork 0x00000001834fc824 CFURLRequestSetMainDocumentURL + 2240
2 CFNetwork 0x00000001836b89a8 _CFNetworkErrorGetLocalizedDescription + 693652
3 CFNetwork 0x00000001834fdb1c CFURLRequestSetMainDocumentURL + 7096
4 CFNetwork 0x00000001834f3c34 CFURLRequestSetURL + 9668
5 libdispatch.dylib 0x00000001829ca914 _dispatch_call_block_and_release + 28
6 libdispatch.dylib 0x00000001829cc660 _dispatch_client_callout + 16
7 libdispatch.dylib 0x00000001829d3de4 _dispatch_lane_serial_drain + 668
8 libdispatch.dylib 0x00000001829d498c _dispatch_lane_invoke + 440
9 libdispatch.dylib 0x00000001829d5c74 _dispatch_workloop_invoke + 1792
10 libdispatch.dylib 0x00000001829df1a8 _dispatch_workloop_worker_thread + 652
11 libsystem_pthread.dylib 0x00000001f1eea0f4 _pthread_wqthread + 284
12 libsystem_pthread.dylib 0x00000001f1ee9e94 start_wqthread + 4
second_crashlog.txt
report-2517628380750009999-e4d7ea06-6f22-4b7e-b129-045599e1dee5.txt
I'm using NSPersistentCloudKitContainer with Core Data and I receive errors because my iCloud space is full. The errors printed are the following: <CKError 0x280df8e40: "Quota Exceeded" (25/2035); server message = "Quota exceeded"; op = 61846C533467A5DF; uuid = 6A144513-033F-42C2-9E27-693548EF2150; Retry after 342.0 seconds>.
I want to inform the user about this issue, but I can't find a way to access the details of the error. I'm listening to NSPersistentCloudKitContainer.eventChangedNotification, I receive a error of type .partialFailure. But when I want to access the underlying errors, the partialErrorsByItemID property on the error is nil.
How can I access this Quota Exceeded error?
import Foundation
import CloudKit
import Combine
import CoreData
class SyncMonitor {
fileprivate var subscriptions = Set<AnyCancellable>()
init() {
NotificationCenter.default.publisher(for: NSPersistentCloudKitContainer.eventChangedNotification)
.sink { notification in
if let cloudEvent = notification.userInfo?[NSPersistentCloudKitContainer.eventNotificationUserInfoKey] as? NSPersistentCloudKitContainer.Event {
guard let ckerror = cloudEvent.error as? CKError else {
return
}
print("Error: \(ckerror.localizedDescription)")
if ckerror.code == .partialFailure {
guard let errors = ckerror.partialErrorsByItemID else {
return
}
for (_, error) in errors {
if let currentError = error as? CKError {
print(currentError.localizedDescription)
}
}
}
}
} // end of sink
.store(in: &subscriptions)
}
}
I've got an iOS app with lots of extensions, some of them complex and doing a lot of stuff.
After a bug I'd like to be able to use OSLogStore to get a holistic picture of logging for the app and its extensions and send that to a debugging server to retrospectively view logs for the app and its extensions.
The constructor is OSLogStore.init(scope: OSLogStore.Scope), however scope only has one value .currentProcessIdentifier.
Implying if that is called from within the app it can only get access to logging for its process only. I tried it out to confirm this is the case - if I log something in an extension (using Logger), then run the app with code like this:
let logStore = try! OSLogStore(scope: .currentProcessIdentifier)
let oneHourAgo = logStore.position(date: Date().addingTimeInterval(-3600))
let allEntries = try! logStore.getEntries(at: oneHourAgo)
for entry in allEntries {
look at the content of the entry
Then none of the entries are from the extension.
Is there anyway from within the app I can access logging made within an extension?
I was playing around a bit with the new AttributedString and a few questions came up.
I saw this other forum question "JSON encoding of AttributedString with custom attributes", but I did not completely understand the answer and how I would need to use it.
I created my custom attribute where I just want to store additional text like this:
enum AdditionalTextAttribute: CodableAttributedStringKey, MarkdownDecodableAttributedStringKey {
typealias Value = AttributedString
static let name = "additionalText"
}
I then extended the AttributeScopes like this:
extension AttributeScopes {
struct MyAppAttributes: AttributeScope {
let additionalText: AdditionalTextAttribute
let swiftUI: SwiftUIAttributes
}
var myApp: MyAppAttributes.Type { MyAppAttributes.self }
}
and I also implemented the AttributeDynamicLookup like this:
extension AttributeDynamicLookup {
subscript<T: AttributedStringKey>(dynamicMember keyPath: KeyPath<AttributeScopes.MyAppAttributes, T>) -> T { self[T.self] }
}
So next I created my AttributedString and added some attributes to it:
var attStr = AttributedString("Hello, here is some text.")
let range1 = attStr.range(of: "Hello")!
let range2 = attStr.range(of: "text")!
attStr[range1].additionalText = AttributedString("Hi")
attStr[range2].foregroundColor = .blue
attStr[range2].font = .caption2
Next I tried to create some JSON from my string and took a look at it like this:
let jsonData = try JSONEncoder().encode(attStr)
print(String(data: jsonData, encoding: .utf8) ?? "no data")
//print result: ["Hello",{},", here is some ",{},"text",{"SwiftUI.ForegroundColor":{},"SwiftUI.Font":{}},".",{}]
I guess it makes sense, that both SwiftUI.ForegroundColor and SwiftUI.Font are empty, because they both do not conform to Codable protocol.
My first question would be: Why does my additionalText attribute not show up here?
I next tried to extend Color to make it codable like this:
extension Color: Codable {
enum CodingKeys: CodingKey {
case red, green, blue, alpha
case desc
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
guard let cgColor = self.cgColor,
let components = cgColor.components else {
if description.isEmpty { throw CodingErrors.encoding }
try container.encode(description, forKey: .desc)
return
}
try container.encode(components[0], forKey: .red)
try container.encode(components[1], forKey: .green)
try container.encode(components[2], forKey: .blue)
try container.encode(components[3], forKey: .alpha)
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if let description = try container.decodeIfPresent(String.self, forKey: .desc) {
if description == "blue" {
self = Color.blue
return
}
throw CodingErrors.decoding
}
let red = try container.decode(CGFloat.self, forKey: .red)
let green = try container.decode(CGFloat.self, forKey: .green)
let blue = try container.decode(CGFloat.self, forKey: .blue)
let alpha = try container.decode(CGFloat.self, forKey: .alpha)
self.init(CGColor(red: red, green: green, blue: blue, alpha: alpha))
}
}
But it looks like even though Color is now codable, the encoding function does not get called when I try to put my attributed string into the JSONEncoder.
So my next question is: Does it just not work? Or do I also miss something here?
Coming to my last question: If JSONEncoder does not work, how would I store an AttributedString to disk?
I'm developing a per-app VPN iOS app with Wireguard. For that, I created a configuration file with payload type "com.apple.vpn.managed.applayer". Using the MDM server I installed some apps which need to use the VPN connection. But when I open these apps, I could see the VPN getting enabled in the device. The VPN icon appears on the notification bar but no internet connection. The VPN and internet is working correctly if I change the payload type to "com.apple.vpn.managed" in configuration file.
I am testing App Clip on Testflight to show App Clip Card but it only shows a white Card with the message: “This app clip is not currently available in your country or region” (if using Local Expreriences, it shows normally)
I have fully installed apple-app-site-association, App Clip Experience, Domain URL Status also validated ... don't understand why, is the app "Redy For Sale" new to show the Card?. I want to let customers test show App Clip Card without using Local Expreriences on Testflight
If anyone knows, please help, thank you.
What does APS stand for in APS Environment Entitlement?
When my app starts it loads data (of vehicle models, manufacturers, ...) from JSON files into CoreData. This content is static.
Some CoreData entities have fields that can be set by the user, for example an isFavorite boolean field.
How do I tell CloudKit that my CoreData objects are 'static' and must not be duplicated on other devices (that will also load it from JSON files).
In other words, how can I make sure that the CloudKit knows that the record created from JSON for vehicle model XYZ on one device is the same record that was created from JSON on any other device?
I'm using NSPersistentCloudKitContainer.
I have two call directory extensions, each with InfoPlist.strings in en.lproj and nb.lproj directories. In these files I've defined CFBundleDisplayName for both locales.
These names are displayed under Settings -> Phone -> Call Blocking & Identification. On iOS 12.2 the names are displayed correctly in both Norwegian and English.
Testing on iOS 15.3 the English names are displayed even when device language is set to Norwegian.
Worth noting: When updating the English versions of CFBundleDisplayName this is immediately reflected in Call Blocking & Identification page with Norwegian device language.
As this feature requires a real device, I'm unable to test on iOS 13 and 14.
Hello!
I make use of the new iOS 15.4 SKAdNetwork.updatePostbackConversionValue feature:
SKAdNetwork.updatePostbackConversionValue(0) { error in
if let error = error {
print(error.localizedDescription)
}
}
I am not sure why, but I always see this error message in the console:
SKAdNetwork: Error while updating conversion value: Error Domain=SKANErrorDomain Code=10 "(null)"
The operation couldn’t be completed. (SKANErrorDomain error 10.)
Any idea what’s going on there? What does Error Code 10 mean? Couldn't find anything in the documentation about that so far.
I have the NSAdvertisingAttributionReportEndpoint key with domain (https://api2.branch.io/v1/skadnetwork/advertiser_app) in my .plist.
@Quinn,
The application which is not made with Xcode, has a provision profile, but App Store Connect says "Not Available for Testing".
My Googlefu appears weak as I can't seem to figure out why this is, except that it mentions you need to be using Xcode 13 or newer.
Am guessing Xcode is adding some meta data to the Info.plist file which TestFlight requires.
Is it possible to know which keys and values are required to satisfy TestFlight?
If it's not plist keys, is there something else that's needed, that can be shared? We can do this privately if desired.
I'm trying to use task_for_pid in a project but I keep getting error code 5 signaling some kind of signing error. Even with this script I cant seem to get it to work.
#include <mach/mach_types.h>
#include <stdlib.h>
#include <mach/mach.h>
#include <mach/mach_error.h>
#include <mach/mach_traps.h>
#include <stdio.h>
int main(int argc, const char * argv[]) {
task_t task;
pid_t pid = argc >= 2 ? atoi(argv[1]) : 1;
kern_return_t error = task_for_pid(mach_task_self(), pid, &task);
printf("%d -> %x [%d - %s]\n", pid, task, error, mach_error_string(error));
return error;
}
I've tried signing my executables using codesign and also tried building with Xcode with the "Debugging Tool" box checked under hardened runtime. My Info.plist file includes the SecTaskAccess key with the values "allowed" and "debug." Hoping someone can point me towards what I'm missing here. Thanks!
I am having problems on Xcode13.3.1, Monterey 12.2.1, Apple M1 Max.
Sending an UpdateApplicationContext update from a paired iPhone simulator is not received on the paired Apple Watch Simulator in the didRecieveApplicationContext. However, sendMessage from the apple watch simulator does update the iphone simulator app properly. It is however, not possible to send anything from the paired iPhone simulator to the paired Apple Watch Simulator.
When working with actual devices everything works properly with WatchConnectivity with passing information back and forth via updateapplicationcontext and sendmessage calls.
Can anyone confirm this is a bug or if there is something wrong with my setup?
Topic:
App & System Services
SubTopic:
Networking
Tags:
watchOS
WatchKit
Watch Connectivity
Simulator
The unified system log on Apple platforms gets a lot of stick for being ‘too verbose’. I understand that perspective: If you’re used to a traditional Unix-y system log, you might expect to learn something about an issue by manually looking through the log, and the unified system log is way too chatty for that. However, that’s a small price to pay for all its other benefits.
This post is my attempt to explain those benefits, broken up into a series of short bullets. Hopefully, by the end, you’ll understand why I’m best friends with the system log, and why you should be too!
If you have questions or comments about this, start a new thread and tag it with OSLog so that I see it.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
Your Friend the System Log
Apple’s unified system log is very powerful. If you’re writing code for any Apple platform, and especially if you’re working on low-level code, it pays to become friends with the system log!
The Benefits of Having a Such Good Friend
The public API for logging is fast and full-featured.
And it’s particularly nice in Swift.
Logging is fast enough to leave log points [1] enabled in your release build, which makes it easier to debug issues that only show up in the field.
The system log is used extensively by the OS itself, allowing you to correlate your log entries with the internal state of the system.
Log entries persist for a long time, allowing you to investigate an issue that originated well before you noticed it.
Log entries are classified by subsystem, category, and type. Each type has a default disposition, which determines whether that log entry is enable and, if it is, whether it persists in the log store. You can customise this, based on the subsystem, category, and type, in four different ways:
Install a configuration profile created by Apple (all platforms) [2].
Add an OSLogPreferences property to your app’s Info.plist (all platforms).
Run the log tool with the config command (macOS only)
Create and install a custom configuration profile with the com.apple.system.logging payload (macOS only).
When you log a value, you may tag it as private. These values are omitted from the log by default but you can configure the system to include them. For information on how to do that, see Recording Private Data in the System Log.
The Console app displays the system log. On the left, select either your local Mac or an attached iOS device. Console can open and work with log snapshots (.logarchive). It also supports surprisingly sophisticated searching. For instructions on how to set up your search, choose Help > Console Help.
Console’s search field supports copy and paste. For example, to set up a search for the subsystem com.foo.bar, paste subsystem:com.foo.bar into the field.
Console supports saved searches. Again, Console Help has the details.
Console supports viewing log entries in a specific timeframe. By default it shows the last 5 minutes. To change this, select an item in the Showing popup menu in the pane divider. If you have a specific time range of interest, select Custom, enter that range, and click Apply.
Instruments has os_log and os_signpost instruments that record log entries in your trace. Use this to correlate the output of other instruments with log points in your code.
Instruments can also import a log snapshot. Drop a .logarchive file on to Instruments and it’ll import the log into a trace document, then analyse the log with Instruments’ many cool features.
The log command-line tool lets you do all of this and more from Terminal.
The log stream subcommand supports multiple output formats. The default format includes column headers that describe the standard fields. The last column holds the log message prefixed by various fields. For example:
cloudd: (Network) [com.apple.network:connection] nw_flow_disconnected …
In this context:
cloudd is the source process.
(Network) is the source library. If this isn’t present, the log came from the main executable.
[com.apple.network:connection] is the subsystem and category. Not all log entries have these.
nw_flow_disconnected … is the actual message.
There’s a public API to read back existing log entries, albeit one with significant limitations on iOS (more on that below).
Every sysdiagnose log includes a snapshot of the system log, which is ideal for debugging hard-to-reproduce problems. For more details on that, see Using a Sysdiagnose Log to Debug a Hard-to-Reproduce Problem. For general information about sysdiagnose logs, see Bug Reporting > Profiles and Logs.
But you don’t have to use sysdiagnose logs. To create a quick snapshot of the system log, run the log tool with the collect subcommand. If you’re investigating recent events, use the --last argument to limit its scope. For example, the following creates a snapshot of log entries from the last 5 minutes:
% sudo log collect --last 5m
For more information, see:
os > Logging
OSLog
log man page
os_log man page (in section 3)
os_log man page (in section 5)
WWDC 2016 Session 721 Unified Logging and Activity Tracing
[1] Well, most log points. If you’re logging thousands of entries per second, the very small overhead for these disabled log points add up.
[2] These debug profiles can also help you focus on the right subsystems and categories. Imagine you’re investigating a CryptoTokenKit problem. If you download and dump the CryptoTokenKit debug profile, you’ll see this:
% security cms -D -i "CTK_iOS_Logging.mobileconfig" | plutil -p -
{
…
"PayloadContent" => [
0 => {
…
"Subsystems" => {
"com.apple.CryptoTokenKit" => {…}
"com.apple.CryptoTokenKit.APDU" => {…}
}
}
]
…
}
That’s a hint that log entries relevant to CryptoTokenKit have a subsystem of either com.apple.CryptoTokenKit and com.apple.CryptoTokenKit.APDU, so it’d make sense to focus on those.
Foster Your Friendship
Good friendships take some work on your part, and your friendship with the system log is no exception. Follow these suggestions for getting the most out of the system log.
The system log has many friends, and it tries to love them all equally. Don’t abuse that by logging too much. One key benefit of the system log is that log entries persist for a long time, allowing you to debug issues with their roots in the distant past. But there’s a trade off here: The more you log, the shorter the log window, and the harder it is to debug such problems.
Put some thought into your subsystem and category choices. One trick here is to use the same category across multiple subsystems, allowing you to track issues as they cross between subsystems in your product. Or use one subsystem with multiple categories, so you can search on the subsystem to see all your logging and then focus on specific categories when you need to.
Don’t use too many unique subsystem and context pairs. As a rough guide: One is fine, ten is OK, 100 is too much.
Choose your log types wisely. The documentation for each OSLogType value describes the default behaviour of that value; use that information to guide your choices.
Remember that disabled log points have a very low cost. It’s fine to leave chatty logging in your product if it’s disabled by default.
Some app extension types have access to extremely sensitive user data and thus run in a restricted sandbox, one that prevents them from exporting any data. For example, an iOS Network Extension content filter data provider runs in such a sandbox. While I’ve never investigated this for other app extension types, an iOS NE content filter data provider cannot record system log entries.
This restriction only applies if the provider is distribution signed. A development-signed provider can record system log entries.
Apple platforms have accumulated many different logging APIs over the years. All of these are effectively deprecated [1] in favour of the system log API discussed in this post. That includes:
NSLog (documented here)
CFShow (documented here)
Apple System Log (see the asl man page)
syslog (see the syslog man page)
Most of these continue to work, simply calling through to the underlying system log. However, there are good reasons to move on to the system log API directly:
It lets you control the subsystem and category, making it much easier to track down your log entries.
It lets you control whether data is considered private or public.
In Swift, the Logger API is type safe, avoiding the classic bug of mixing up your arguments and your format specifiers.
[1] Some formally and some informally.
No Friend Is Perfect
The system log API is hard to wrap. The system log is so efficient because it’s deeply integrated with the compiler. If you wrap the system log API, you undermine that efficiency. For example, a wrapper like this is very inefficient:
-*-*-*-*-*- DO NOT DO THIS -*-*-*-*-*-
void myLog(const char * format, ...) {
va_list ap;
va_start(ap, format);
char * str = NULL;
vasprintf(&str, format, ap);
os_log_debug(sLog, "%s", str);
free(str);
va_end(ap);
}
-*-*-*-*-*- DO NOT DO THIS -*-*-*-*-*-
This is mostly an issue with the C API, because the modern Swift API is nice enough that you rarely need to wrap it.
If you do wrap the C API, use a macro and have that pass the arguments through to the underlying os_log_xyz macro.
Note If you’re curious about why adding a wrapper is bad, see my explanation on this thread.
iOS has very limited facilities for reading the system log. Currently, an iOS app can only read entries created by that specific process, using .currentProcessIdentifier scope. This is annoying if, say, the app crashed and you want to know what it was doing before the crash. What you need is a way to get all log entries written by your app (r. 57880434).
There are two known bugs with the .currentProcessIdentifier scope. The first is that the .reverse option doesn’t work (r. 87622922). You always get log entries in forward order. The second is that the getEntries(with:at:matching:) method doesn’t honour its position argument (r. 87416514). You always get all available log entries.
Xcode 15 beta has a shiny new console interface. For the details, watch WWDC 2023 Session 10226 Debug with structured logging. For some other notes about this change, search the Xcode 15 Beta Release Notes for 109380695.
In older versions of Xcode the console pane was not a system log client (r. 32863680). Rather, it just collected and displayed stdout and stderr from your process. This approach had a number of consequences:
The system log does not, by default, log to stderr. Xcode enabled this by setting an environment variable, OS_ACTIVITY_DT_MODE. The existence and behaviour of this environment variable is an implementation detail and not something that you should rely on.
Xcode sets this environment variable when you run your program from Xcode (Product > Run). It can’t set it when you attach to a running process (Debug > Attach to Process).
Xcode’s Console pane does not support the sophisticated filtering you’d expect in a system log client.
When I can’t use Xcode 15, I work around the last two by ignoring the console pane and instead running Console and viewing my log entries there.
If you don’t see the expected log entries in Console, make sure that you have Action > Include Info Messages and Action > Include Debug Messages enabled.
The system log interface is available within the kernel but it has some serious limitations. Here’s the ones that I’m aware of:
Prior to macOS 14.4, there was no subsystem or category support (r. 28948441).
There is no support for annotations like {public} and {private}.
Adding such annotations causes the log entry to be dropped (r. 40636781).
The system log interface is also available to DriverKit drivers. For more advice on that front, see this thread.
Metal shaders can log using the interface described in section 6.19 of the Metal Shading Language Specification.
Revision History
2025-08-19 Added information about effectively deprecated logging APIs, like NSLog.
2025-08-11 Added information about the restricted sandbox applied to iOS Network Extension content filter data providers.
2025-07-21 Added a link to a thread that explains why wrapping the system log API is bad.
2025-05-30 Fixed a grammo.
2025-04-09 Added a note explaining how to use a debug profile to find relevant log subsystems and categories.
2025-02-20 Added some info about DriverKit.
2024-10-22 Added some notes on interpreting the output from log stream.
2024-09-17 The kernel now includes subsystem and category support.
2024-09-16 Added a link to the the Metal logging interface.
2023-10-20 Added some Instruments tidbits.
2023-10-13 Described a second known bug with the .currentProcessIdentifier scope. Added a link to Using a Sysdiagnose Log to Debug a Hard-to-Reproduce Problem.
2023-08-28 Described a known bug with the .reverse option in .currentProcessIdentifier scope.
2023-06-12 Added a call-out to the Xcode 15 Beta Release Notes.
2023-06-06 Updated to reference WWDC 2023 Session 10226. Added some notes about the kernel’s system log support.
2023-03-22 Made some minor editorial changes.
2023-03-13 Reworked the Xcode discussion to mention OS_ACTIVITY_DT_MODE.
2022-10-26 Called out the Showing popup in Console and the --last argument to log collect.
2022-10-06 Added a link WWDC 2016 Session 721 Unified Logging and Activity Tracing.
2022-08-19 Add a link to Recording Private Data in the System Log.
2022-08-11 Added a bunch of hints and tips.
2022-06-23 Added the Foster Your Friendship section. Made other editorial changes.
2022-05-12 First posted.