xcodemacossafari-extensionsafari-web-extension

Safari Web Extension works when archived but not when run from Xcode — ‘Unable to find popup.html’ error”


I'm developing a Safari Web Extension for Mac using Xcode, and I'm encountering a frustrating issue that I can't seem to resolve.

The Problem:

When I archive and export my app and install it, the extension works flawlessly in Safari—no errors, and all resources load correctly. However, when I run the app directly from Xcode (for development and debugging), Safari shows the following error in the extension preferences:

Sometimes it also shows:

Additionally, the extension doesn't appear in Safari's Extensions list when running from Xcode, even though the app launches.

This used to work and then it just stopped working randomly when I exported a copy of the app by archiving and opened that app to test it. After that I was not able to get it to work by running from Xcode.

What I've Tried So Far:

Additional Observations:

Questions:

Environment:

Any insights or suggestions would be greatly appreciated!


Solution

  • Adding this as a workaround until Apple fixes this issue.

    Rather than relying on the Copy Bundle Resources build phase I just manually copied the necessary files by modifying an existing custom shell script which was getting called before Copy Bundle Resources.

    I'll include the entire script here so other people can use it as a starting point and modify it as needed but I can't guarantee that it'll work without modifications.

    Add this shell script into your Resources folder and name it whatever you want.

    Ex: CopyFiles.sh

    #!/bin/bash
    
    set -e # Exit if any command fails
    
    # Function to copy additional resources from source to destination directory since Xcode build phase fails to copy them
    copy_additional_resources() {
        local src_dir="$1"
        local dest_dir="$2"
        
        echo "Copying additional resources from ${src_dir} to ${dest_dir}"
        
        # Copy directories (recursive)
        echo "Copying directories..."
        cp -R "${src_dir}/_locales" "${dest_dir}/"
        cp -R "${src_dir}/images" "${dest_dir}/"
        
        # Copy individual files
        echo "Copying files..."
        cp "${src_dir}/manifest.json" "${dest_dir}/"
        cp "${src_dir}/popup.css" "${dest_dir}/"
        cp "${src_dir}/popup.html" "${dest_dir}/"
    
        # Add additional files/dirs if needed
        
        # List contents to verify
        echo "Contents of destination directory after copying:"
        ls -la "${dest_dir}/"
    }
    
    echo "Environment variable SRCROOT is set to '${SRCROOT}'"
    SRC_RESOURCES="${SRCROOT}/{ENTER_YOUR_EXTENSION_TARGET_DIR_HERE}/Resources"
    echo "SRC_RESOURCES is set to '${SRC_RESOURCES}'"
    
    echo "Environment variable CONTENTS_FOLDER_PATH is set to '${CONTENTS_FOLDER_PATH}'"
    
    # Determine if this is a Mac build by checking if CONTENTS_FOLDER_PATH contains "Contents"
    IS_MAC_BUILD=false
    if [[ "${CONTENTS_FOLDER_PATH}" == *"Contents"* ]]; then
        IS_MAC_BUILD=true
        DEST_DIR="${BUILT_PRODUCTS_DIR}/${CONTENTS_FOLDER_PATH}/Resources"
    else
        DEST_DIR="${BUILT_PRODUCTS_DIR}/${CONTENTS_FOLDER_PATH}"
    fi
    
    if [ "${CONFIGURATION}" = "DEBUG" ]; then
        echo "Running Debug build: Copying original JS files."
    
        # For debug builds, handle directory creation based on platform
        if [ "$IS_MAC_BUILD" = true ]; then
            echo "Creating Resources directory for Mac build at ${DEST_DIR}."
            mkdir -p "${DEST_DIR}"
        fi
    
        echo "Copying original JS files to destination directory."
        cp "${SRC_RESOURCES}/content.js" "${DEST_DIR}/content.js"
        cp "${SRC_RESOURCES}/background.js" "${DEST_DIR}/background.js"
        cp "${SRC_RESOURCES}/popup.js" "${DEST_DIR}/popup.js"
        
        # List copied files
        echo "Verifying copied JS files in destination directory:"
        ls -la "${DEST_DIR}/"
    
        # Ensure JS files are not executable in debug builds as well
        echo "Removing execute permissions from JS files."
        chmod -x "${DEST_DIR}/content.js"
        chmod -x "${DEST_DIR}/background.js"
        chmod -x "${DEST_DIR}/popup.js"
        
        # Manually additional files and directories
        copy_additional_resources "${SRC_RESOURCES}" "${DEST_DIR}"
    fi
    
    echo "JS files processing completed successfully."
    
    

    Then add a new Build Phase (Run Script phase)

    enter image description here

    Make sure you add the proper input files and output files as seen in the screenshot:

    enter image description here

    Obviously, change the string "Protego Extension" to the name of your extension target's directory

    I left the Copy Bundle Resources build phase at the end of the list since everything was working as expected.

    The primary purpose of my script is to minify my js scripts so I tried to remove the minification logic. Feel free to refactor the script as needed.