Bartłomiej Semańczyk
Bartłomiej Semańczyk

Reputation: 61774

How to debug an iOS extension (.appex)?

How to make log prints appear in Xcode's lldb debugger from extension?

Upvotes: 22

Views: 16459

Answers (5)

Serhii Petrenko
Serhii Petrenko

Reputation: 340

In my experience the simplest way to get your logs, when debugger fails - is to use Logger with custom category + Console. Example:

import os

class MyViewController: UIViewController {
    
    private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "MyAmazingLogger")
    
    override func viewDidLoad() {
        let content = "content"
        logger.notice("MyViewController viewDidLoad \(content, privacy: .public)")
    }
} 

After that open your Console app, filter output for your category and proceed with living your life

Upvotes: 0

Jens Alfke
Jens Alfke

Reputation: 1983

As of Xcode 14:

  1. Choose menu command Window > Devices And Simulators.
  2. Select the iOS device
  3. Press the View Device Logs button to view crash logs, or Open Console to view other logs.

Upvotes: 0

Bartłomiej Semańczyk
Bartłomiej Semańczyk

Reputation: 61774

Simple answer:

  • log messages are not printed, however you can stop at breakpoints, and then print everything using lldb.
  1. Run your app
  2. While the app is running, go to Debug -> Attach to process by PID or name

enter image description here

  1. Write the name of your extension (as bundle-id does not work!?), and click "Attach".

enter image description here

  1. Then run your extension with any way you can do this on your device.
  2. Wait for Xcode's debugger to stop the extension at breakpoint, but some may need to call waitForDebugger (which is a custom function, see logic below).

Wait For Debugger example

public static func isDebuggerAttached() -> Bool {
    // Buffer for "sysctl(...)" call's result.
    var info = kinfo_proc()
    // Counts buffer's size in bytes (like C/C++'s `sizeof`).
    var size = MemoryLayout.stride(ofValue: info)
    // Tells we want info about own process.
    var mib : [Int32] = [CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()]
    // Call the API (and assert success).
    let junk = sysctl(&mib, UInt32(mib.count), &info, &size, nil, 0)
    assert(junk == 0, "sysctl failed")
    // Finally, checks if debugger's flag is present yet.
    return (info.kp_proc.p_flag & P_TRACED) != 0
}

@discardableResult
public static func waitForDebugger(_ timeout: Int = 30000) -> Bool {
    var now: UInt64 = DispatchTime.now().uptimeNanoseconds
    let begin = now
    repeat {
        if isDebuggerAttached() {
            // Wait a little bit longer,
            // because early breakpoints may still not work.
            Thread.sleep(forTimeInterval: 3.0)
            return true
        }
        Thread.sleep(forTimeInterval: 0.1)
        now = DispatchTime.now().uptimeNanoseconds
    } while Double(now - begin) / 1000000.0 < Double(timeout);
    return false;
}

Upvotes: 51

Jim Ingham
Jim Ingham

Reputation: 27110

You shouldn't need to attach to your app extension by hand like this. Xcode should take care of all this automatically.

Look at the run scheme editor for your extension scheme. The executable will either be set to your app, or to "Ask on Launch". In either case, running the extension target will end up launching the app you have chosen.

Go to that app on your device, create/choose whatever it is in the app that you want to share, click the share icon, choose your extension in the list of active sharing extensions. Then when your sharing extension starts up the debugger will automatically attach to it. This may take a couple of seconds, but you'll see your extension with all its threads show up in the Debug Navigator, and if you set any breakpoints it should stop at them.

If you do it this way, the debugger will also hook up to stdout so you'll see your log messages in the debugger console.

Upvotes: 12

Dan Loughney
Dan Loughney

Reputation: 4677

If you are debugging on a device you can open the Device Manager using Command, Shift, 2 and view the console messages there. Select your device.

If you are debugging on a simulator, the messages go to the system log. You can open that from the Simulator using Command / or "Open System Log" from the Simulator Debug menu.

Upvotes: 0

Related Questions