Figment.so
Mac hostingWebsite hostingGuidesHow to usePricing

Debug Core Data Issues on a Rented Remote Mac

Recommendation: enable -com.apple.CoreData.SQLDebug 1 and -com.apple.CoreData.ConcurrencyDebug 1 through an Xcode test plan, run xcodebuild test on a rented Mac, and sort the output with awk. If your scenario reproduces on x86_64 simulators, Figment's $29/month virtual Intel macOS environment is the cheapest place to iterate. If you specifically need the arm64 iOS Simulator slice to match a shipping device, a Figment M-series Mac (from $95/month, exact chip/RAM/disk confirmed in the quote at /mac-hosting/start) is the right tier.

Core Data exposes three runtime launch arguments that print a verbose trace without a single code change: SQLDebug, MigrationDebug, and ConcurrencyDebug. On a remote Mac you want them driven from the terminal, because opening the Xcode UI over a browser desktop session is slow. A small .xctestplan file plus xcodebuild test gets you there. Reference material: Apple's Core Data documentation and the Xcode guide Diagnosing and resolving bugs in your running app.

Core Data debug flags and what each one logs

ArgumentValueWhat it logs
-com.apple.CoreData.SQLDebug1–3SQL issued to the SQLite store. Level 1 prints statement text; higher levels add binding parameters and row counts.
-com.apple.CoreData.MigrationDebug1Lightweight migration steps: entity mapping, attribute transforms, model hashes.
-com.apple.CoreData.ConcurrencyDebug1Faults when a managed object is touched from the wrong queue; crashes with a stack trace on misuse.

Start with SQLDebug 1 and ConcurrencyDebug 1. Add MigrationDebug 1 only while investigating a model change; otherwise it buries the SQL trace.

Inject the arguments through a test plan

xcodebuild test reads launch arguments from the scheme or from a .xctestplan file. A checked-in test plan is the cleanest way to get repeatable runs on a remote Mac. Create CoreDataDebug.xctestplan at the project root:

{
  "configurations": [
    {
      "id": "coredata-sql-debug",
      "name": "Core Data SQL Debug",
      "options": {
        "commandLineArguments": [
          { "argument": "-com.apple.CoreData.SQLDebug 1",         "enabled": true },
          { "argument": "-com.apple.CoreData.ConcurrencyDebug 1", "enabled": true }
        ]
      }
    }
  ],
  "defaultOptions": {},
  "testTargets": [],
  "version": 1
}

Leave testTargets empty to inherit the scheme's targets, or list specific target references to scope the run. Confirm the field format in your own .xctestplan that Xcode generates from the IDE; the schema is not formally documented and has changed between versions.

List simulators on the remote Mac, then run tests. Use your project's real scheme name:

xcrun simctl list devices available | grep -i iphone

xcodebuild test \
  -project YourApp.xcodeproj \
  -scheme "$YOUR_SCHEME" \
  -destination 'platform=iOS Simulator,name=iPhone 16' \
  -testPlan CoreDataDebug \
  2>&1 | tee coredata-test.log

Swap -project for -workspace YourApp.xcworkspace if you use CocoaPods or SwiftPM via a workspace. tee keeps the log so analysis does not require a second test run.

A note on where to run this. Xcode 26 and earlier run on Intel; Xcode 27 and later require Apple silicon, per Apple's Porting your macOS apps to Apple silicon note. The $29 Figment virtual Intel macOS plan can run Intel-compatible Xcode toolchains for x86_64 simulator work. For arm64 simulators that match an iPhone 16's execution profile, request an M-series Mac at /mac-hosting/start.

Rank the slowest fetches with awk

Core Data prints two lines per executed statement: an sql: line and an annotation: line with the time. Pair them and sort:

awk '
  /CoreData: sql:/        { stmt = substr($0, index($0, "sql: ") + 5) }
  /annotation: sql execution time/ {
    t = $6; sub(/s,?$/, "", t)
    printf "%8.4fs  %s\n", t+0, stmt
  }
' coredata-test.log | sort -rn | head -20

What to act on:

  • > 10 ms per fetch on a simulator usually means a table scan. Mark the attribute as Indexed in the model editor and re-run.
  • The same SELECT ... WHERE Z_PK = ? repeated in a loop is the classic N+1. Set relationshipKeyPathsForPrefetching on the fetch request.

Group identical query templates to count repeats:

grep 'CoreData: sql: SELECT' coredata-test.log \
  | sed 's/ WHERE Z_PK = ?.*/ WHERE .../' \
  | sort | uniq -c | sort -rn | head -10

A worked cost worksheet for this task

Decide the tier before you rent. Fill the blanks with your own numbers; the table below is a cost comparison at list, not a benchmark.

OptionHeadline price12-month outlayNotes
Figment virtual Intel macOS$29/month$348 at listx86_64 simulator only; browser-console access.
Figment M-series (entry)from $95/monthfrom $1,140 at listSpecific chip/RAM/disk confirmed in the quote; request at /mac-hosting/start.
CloudMac M4/16GB/256GB$99.99/month$999/year prepaid (cloudmac.org) = $83.25/month effective256GB row shows Reserve now; 512GB/1TB currently "fully reserved".
MacStadium M4.S$149/month (16GB/256GB)monthly in advance; multi-year via salesIncludes support and network per the pricing page.

Read the trade-off honestly: CloudMac's prepaid annual is $141 below 12 × $95 at list, but $95 is an unspecified M-series entry, not a confirmed M4/16GB. If you need a committed M4 spec on a published annual, CloudMac's $999/year is the cheaper equivalent-hardware option on paper. Figment's value sits in the managed tiers — the Managed Build Mac ($249/month) adds CI setup, backups, monitoring, and 30 minutes of monthly support on one M-series Mac, and the Managed Mac Team ($999/month) covers two Macs, two CI runners, team setup, backups, monitoring, and one hour of support. Ask for a quote if you want that work done for you.

Instruments for the timeline view

For fault firing and save-operation timelines, open Instruments on the remote Mac's desktop via the browser console session and pick the Core Data template. The template shows each fetch request as a bar scaled to its duration, each fault fire as a discrete event, and save() durations per context. Line it up with Time Profiler to attribute UI stutter to faults firing on the main thread.

Observing merge conflicts on purpose

With ConcurrencyDebug 1 on, a cross-queue managed-object access crashes with a stack trace, which is the behaviour you want in CI. To exercise merge conflicts deliberately, save the same object from two contexts without merging:

try backgroundContext.save()   // succeeds; stamps the row version
try mainContext.save()         // throws NSManagedObjectMergeError

mainContext.save() returns NSError in NSCocoaErrorDomain. Look the numeric code up in the Core Data error header in your installed SDK rather than hard-coding one; a few codes have shifted across SDK versions. Set mainContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy and re-run to confirm the policy resolves it.

Validating a migration before you ship

Before raising a model version, run a migration against a representative seed store on the remote simulator. Boot the simulator, install the app, copy the seed .sqlite into the app container, then run the test plan with MigrationDebug 1 added. Grep the log for CoreData: migration: lines and any error: lines; a lightweight success prints a "Successfully migrated" entry, and a mismatch between source and destination model hashes needs a mapping model.

If this workflow is going to live on a long-running build host, the Figment Managed Build Mac handles the CI plumbing and leaves the test plan in your repository. See Figment Mac Hosting for the plan matrix or go straight to /mac-hosting/start for a quote.


Related: XCTest Performance Testing on a Remote Mac · UI Testing on a Remote Mac with Xcode · GitHub Actions Self-Hosted Runner on Mac


Subscribe for Figma design and website publishing guides.


Figment.so

Contact

Twitter

Website pricing

Compute index

About

Privacy

Terms