Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to add an Embed App Clip phase #753

Closed
swwol opened this issue Mar 31, 2023 · 4 comments
Closed

Unable to add an Embed App Clip phase #753

swwol opened this issue Mar 31, 2023 · 4 comments

Comments

@swwol
Copy link

swwol commented Mar 31, 2023

I'm trying to write a script to embed an AppClip into a project.
There are 2 steps.. Adding the AppClip as a dependency of the main app target, and adding a Copy Files build phase that embeds the AppClip.

Step 1 appears to be working ok, but step 2 - The Copy Files phase is created but doesn't have any content.

This is what I have so far:

func addAppClipEmbedPhase(project: PBXProj, target: PBXTarget) throws {
  guard !target.buildPhases.contains(where: { $0.name() == "Embed App Clip" }) else {
    throw ProjectEditError.error("Target already has a build phase called Embed App Clip")
  }

  let mainGroup = project.projects.first!.mainGroup

  let fileReference = PBXFileReference(
    sourceTree: .buildProductsDir,
    explicitFileType: "wrapper.application",
    path: "AppClip.app",
    includeInIndex: false
  )

  mainGroup!.children.append(fileReference)

  let buildFile = PBXBuildFile(file: fileReference, settings: ["ATTRIBUTES": "RemoveHeadersOnCopy"])

  let phase = PBXCopyFilesBuildPhase(dstPath: "$(CONTENTS_FOLDER_PATH)/AppClips",
                                            dstSubfolderSpec: .productsDirectory,
                                            name: "Embed App Clip",
                                            files: [buildFile],
                                            runOnlyForDeploymentPostprocessing:false)

  project.add(object: phase)
  target.buildPhases.append(phase)
}

I suspect I may be missing a step and the buildFile needs to be added somewhere else? But not sure where? Thanks!

@kwridan
Copy link
Collaborator

kwridan commented Mar 31, 2023

I think the missing bit is adding the file reference and build file objects to the project.

project.add(object: fileReference)
project.add(object: buildFile)

XcodeGen and Tuist can be useful references as they both leverage XcodeProj.

https://github.com/yonaskolb/XcodeGen/blob/5a34c489e16eed6b7d43ee0064b3525b0d11f1c0/Sources/XcodeGenKit/PBXProjGenerator.swift#L1192

https://github.com/tuist/tuist/blob/4fdcf9d2b75fc16a4426a3f9dd7f30ca9d0dc3b6/Sources/TuistGenerator/Generator/BuildPhaseGenerator.swift#L541

Hope this helps

@swwol
Copy link
Author

swwol commented Apr 3, 2023

Thanks for this - those are helpful references. I am still having an issue however. The problem seems to be with the PBXFileReference. And it's not clear from those code excerpts how this should be created. Without adding the file reference I can create an empty buildphase, but as soon as I try to add the fileReference to the buildphase I get a corrupted Xcode project with the error message:
"Exception: -[__NSCFString countByEnumeratingWithState:objects:count:]: unrecognized selector sent to instance 0x60009b278930".

I am creating the file reference and adding it to the project. I have also tried adding it to the mainGroup.children array as without this step it gets an identifier prefixed with 'TEMP'. However both with and without that step, I am getting the corrupted project issue. Current iteration of code below - any thoughts much appreciated!

func addAppClipEmbedPhase(pbxProj: PBXProj, target: PBXTarget) throws {
  guard !target.buildPhases.contains(where: { $0.name() == "Embed App Clip" }) else {
    throw ProjectEditError.error("Target already has a build phase called Embed App Clip")
  }

  let mainGroup = pbxProj.projects.first!.mainGroup

  let embedAppClipsBuildPhase = PBXCopyFilesBuildPhase(
    dstPath: "$(CONTENTS_FOLDER_PATH)/AppClips",
    dstSubfolderSpec: .productsDirectory,
    name: "Embed App Clip"
  )

  pbxProj.add(object: embedAppClipsBuildPhase)
  target.buildPhases.append(embedAppClipsBuildPhase)

  let fileReference = PBXFileReference(
    sourceTree: .buildProductsDir,
    explicitFileType: "wrapper.application",
    path: "AppClip.app",
    includeInIndex: false
  )
  pbxProj.add(object: fileReference)

  mainGroup!.children.append(fileReference)
  let buildFile = PBXBuildFile(file: fileReference, settings: ["ATTRIBUTES": "RemoveHeadersOnCopy"])
  pbxProj.add(object: buildFile)
  embedAppClipsBuildPhase.files = [buildFile]
}

@kwridan
Copy link
Collaborator

kwridan commented Apr 4, 2023

I've tried out the snippet in a sample and hit the same error, looks like there's a small type error in the build file attributes, where it should be an array of string flags rather than a single string/

 let buildFile = PBXBuildFile(file: fileReference, settings: ["ATTRIBUTES": ["RemoveHeadersOnCopy"]])

Note, if you are modifying a project that already has both targets within it, existing file references can be used rather than creating new ones:

func addAppClipEmbedPhase(pbxProj: PBXProj, appTarget: PBXTarget, appClipTarget: PBXTarget) throws {
    guard !appTarget.buildPhases.contains(where: { $0.name() == "Embed App Clip" }) else {
        throw ProjectEditError.error("Target already has a build phase called Embed App Clip")
    }

    let embedAppClipsBuildPhase = PBXCopyFilesBuildPhase(
        dstPath: "$(CONTENTS_FOLDER_PATH)/AppClips",
        dstSubfolderSpec: .productsDirectory,
        name: "Embed App Clip"
    )

    pbxProj.add(object: embedAppClipsBuildPhase)
    appTarget.buildPhases.append(embedAppClipsBuildPhase)

    guard let appClipFileReference = appClipTarget.product else {
        throw ProjectEditError.error("AppClip file reference is not available")
    }

    let buildFile = PBXBuildFile(file: appClipFileReference, settings: ["ATTRIBUTES": ["RemoveHeadersOnCopy"]])
    pbxProj.add(object: buildFile)
    embedAppClipsBuildPhase.files = [buildFile]
}

@swwol
Copy link
Author

swwol commented Apr 4, 2023

Thank you so much! Just tried that and its working perfectly now

@swwol swwol closed this as completed Apr 4, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants