Skip to content

Instantly share code, notes, and snippets.

@ratazzi
Last active December 22, 2022 14:09
Show Gist options
  • Save ratazzi/f6d9217654d6605450a0 to your computer and use it in GitHub Desktop.
Save ratazzi/f6d9217654d6605450a0 to your computer and use it in GitHub Desktop.
Duplicate Xcode Project Target with Ruby
#!/usr/bin/env ruby
require 'rubygems'
require 'xcodeproj'
name = 'test_copy'
proj = Xcodeproj::Project.open('test.xcodeproj')
src_target = proj.targets.find { |item| item.to_s == 'test' }
# create target
target = proj.new_target(src_target.symbol_type, name, src_target.platform_name, src_target.deployment_target)
target.product_name = name
# create scheme
scheme = Xcodeproj::XCScheme.new
scheme.add_build_target(target)
scheme.set_launch_target(target)
scheme.save_as(proj_path, name)
# copy build_configurations
target.build_configurations.map do |item|
item.build_settings.update(src_target.build_settings(item.name))
end
# copy build_phases
phases = src_target.build_phases.reject { |x| x.instance_of? Xcodeproj::Project::Object::PBXShellScriptBuildPhase }.collect(&:class)
phases.each do |klass|
src = src_target.build_phases.find { |x| x.instance_of? klass }
dst = target.build_phases.find { |x| x.instance_of? klass }
unless dst
dst ||= proj.new(klass)
target.build_phases << dst
end
dst.files.map { |x| x.remove_from_project }
src.files.each do |f|
file_ref = proj.new(Xcodeproj::Project::Object::PBXFileReference)
file_ref.name = f.file_ref.name
file_ref.path = f.file_ref.path
file_ref.source_tree = f.file_ref.source_tree
file_ref.last_known_file_type = f.file_ref.last_known_file_type
file_ref.fileEncoding = f.file_ref.fileEncoding
begin
file_ref.move(f.file_ref.parent)
rescue
end
build_file = proj.new(Xcodeproj::Project::Object::PBXBuildFile)
build_file.file_ref = f.file_ref
dst.files << build_file
end
end
# add files
classes = proj.main_group.groups.find { |x| x.to_s == 'Group' }.groups.find { |x| x.name == 'Classes' }
sources = target.build_phases.find { |x| x.instance_of? Xcodeproj::Project::Object::PBXSourcesBuildPhase }
file_ref = classes.new_file('test.m')
build_file = proj.new(Xcodeproj::Project::Object::PBXBuildFile)
build_file.file_ref = file_ref
sources.files << build_file
proj.save
@eggsy84
Copy link

eggsy84 commented Mar 27, 2017

Thanks for sharing this - very useful!

@ListenXunInNight
Copy link

哥们,我使用你的代码,遇到了这种问题,知道怎么解决吗?***.rb:14:in <main>': undefined method symbol_type' for nil:NilClass (NoMethodError)

@alez007
Copy link

alez007 commented Sep 5, 2017

minor bug:
scheme.save_as(proj_path, name) should be scheme.save_as(proj.path, name)
also, probably the xcode sdk version is different but file_ref.fileEncoding triggers a undefined method error.

Thank you for sharing this! The main point is to use the documentation found here to improve the script as you see fit.

@Liestambeur
Copy link

Liestambeur commented Sep 19, 2017

classes in nill when I run the project:
59:in '<main>': undefined method 'new_file' for nil:NilClass (NoMethodError)

@sachinpandey44
Copy link

Hi,

I am also getting 'file_ref.fileEncoding - undefined method error. I am using Xcode 8.3.3 and xcodeproj --version 1.5.2
Can someone suggest a way to fix it ?

@cytryn
Copy link

cytryn commented Dec 14, 2017

also have this issue for .fileEncoding

@prathmeshranaut
Copy link

Line 24-54 can be replaced by:

# Copy the build phases
src_target.build_phases.each do |phase|
  target.build_phases << phase
end

This fixes and .fileEncoding issues. File encoding error is caused when the script tries copying storyboard files which do not have a file encoding.

@cytryn
Copy link

cytryn commented Jul 27, 2018

@prathmeshranaut File encoding error is caused when the script tries copying storyboard files which do not have a file encoding.

how do you fix this and give them file encoding?

@cytryn
Copy link

cytryn commented Jul 27, 2018

@prathmeshranaut

# Copy the build phases
src_target.build_phases.each do |phase|
  target.build_phases << phase
end

I tried your suggestion but I get error:

undefined method `new_file' for nil:NilClass (NoMethodError)

@zhuodream
Copy link

@prathmeshranaut
This method can make the xcode 9.4.1 crash when you delete the new target in xcode. I meet the error. Maybe we can't use the same uuid file in different target.

@Michael-Maher-BBN
Copy link

can we duplicate xcworkspace targets using this approach ?

@owurman
Copy link

owurman commented Dec 18, 2019

This was very useful, thank you. For those just joining us, the bit under "# add files" is an example in case you have new files to add to the dst target, it's not "working code". And yes, it's proj.name not proj_name.

@spiridonkopicl
Copy link

I had issues with PBXFileReference for some reason its parameters where nil, so I needed to introduce conditions before assigning the values and it works. It is strange thought that I used this script before and added just condition for fileEncoding and it worked. So if anyone has explanation how properties like name, path, source_tree, last_known_file_type became nil I would really appreciate. In docs name can be missing: the name of the reference, often not present. but what about others.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment