Open Any Folder in Xcode

p3l6.dev|

A short shell utility to open folders as an Xcode workspace.

In addition to being a great IDE, Xcode is a great general purpose text editor. However, I still always find myself opening up projects in vscode, simply because Xcode cannot open folders. Xcode can only open projects and workspaces, that is, data files it specifically creates with project metadata.

On the other hand, vscode can open any folder, and search and explore the contents. I finally realized this was a simple problem to solve: I only needed a small shell script to generate Xcode workspaces containing single folders! The only thing left to verify was the structure of an xcode workspace. I created a blank workspace, and then added a single folder reference path to it. Then jumped into a terminal to cat the contents:

<?xml version="1.0" encoding="UTF-8"?>
<Workspace
   version = "1.0">
   <FileRef
      location = "group:/Users/paul/tmp/SomeFolderToOpen">
   </FileRef>
</Workspace>

Excellent, these files are sufficiently trivial to create on the fly from a shell script. And so doing that, I saved this script as x, and added it to my $PATH. Invoking it without arguments opens the current directory in a new Xcode workspace, and with arguments, any folder or files.

The generated workspaces are saved inside the opened folder, under a .xcode directory. I added this pattern to my global gitignore file, since I create the workspaces with absolute paths to the referenced contents. At first, I attempted setting group as a relative path: location = "group:../", which almost worked, except that Xcode used .. as the name of the root folder.

Below is the script:

#!/bin/zsh

setopt extendedglob

# Show usage and exit
if [[ "$1" = "-h" ]] ; then
  cat <<EOF
===== ===== ========= ======
Xcode magic workspace opener
===== ===== ========= ======

Specify any number of paths as arguments:
- Files will be opened in individual windows
- Folders will be opened as projects
  | If the folder already contains an xcode project, that will be opened.
  | Otherwise, a new workspace will be created for you with the folder added.
  | There will only be one folder per workspace, if multiple folders are
  | specified, multiple windows will be opened.

Without any arguments, the current working directory will be opened
EOF
exit 0
fi

openItem() {
  A=$(realpath $1)
  basename=$(basename $A)

  if [[ -f $A ]] ; then
    echo Opening file: $1
    open -a Xcode.app $1

  elif [[ -d $A ]] ; then
    # First try to open the path's project file
    if [[ -n $A/*.xcodeproj(#qN) ]] ; then
      echo Opening project in: $1
      open -a Xcode.app $A/*.xcodeproj

    # Then, try an existing xcode workspace
    elif [[ -n $A/*.xcworkspace(#qN) ]] ; then
      echo Opening workspace in: $1
      open -a Xcode.app $A/*.xcworkspace

    # Check if the folder is a swift package
    elif [[ -f $A/Package.swift ]] ; then
      echo Opening package in: $1
      open -a Xcode.app $A/Package.swift

    # Check to see if the `x` tool has already been used in this folder
    elif [[ -d $A/.xcode/$basename.xcworkspace ]] ; then
      echo Opening folder: $1
      open -a Xcode.app $A/.xcode/$basename.xcworkspace

    # Lastly, create a new workspace and open it
    else
      echo Wrapping: $basename as $A/.xcode/$basename.xcworkspace
      mkdir -p $A/.xcode/$basename.xcworkspace/
      cat <<EOF > $A/.xcode/$basename.xcworkspace/contents.xcworkspacedata
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
   version = "1.0">
   <FileRef
      location = "group:$A">
   </FileRef>
</Workspace>
EOF
      echo Opening folder: $1
      open -a Xcode.app $A/.xcode/$basename.xcworkspace
    fi

  else
    echo Not a file or folder: $1
  fi
}

for arg in "$@" ; do
  openItem $arg
done

if [[ "$#" = 0 ]] ; then
  echo 'An arg of $PWD implied when args are omitted'
  echo Use -h for more help
  openItem $PWD
fi

Any updates to the script can be found in my dotfiles repository.