3

I would like to automate the process of adding a new target to my iOS application and write a script which would speed up the whole process. Is there any way to duplicate Xcode iOS application target, then rename it, add new icon specific for this target, change bundle id, app name etc. and finally build the app? I've tried using AppleScript to achieve this, but with no luck.

The workaround I am using now is to create one additional target (using Xcode GUI) and customize it during the build time (every time I want to build the app). The biggest downside of this approach is that targets' configurations are not saved anywhere and the parameters of the target have to be specified for every build of the app. This can lead to unrepeatable builds and other undebuggable issues. That's why I am looking for a method or a tool which would allow me to duplicate and configure new target from command line script

fragon
  • 3,391
  • 10
  • 39
  • 76
  • 1
    Which part of target configuration do you need to customize? You are aware that you can easily override all build settings when you trigger an Xcode build from command line, are you? So if that's all you need, instead of creating multiple targets, how about a script that builds a single target multiple time with different overridden settings? – Mecki Mar 30 '17 at 17:34
  • @Mecki I'm using such solution right now. I just want to know if there is any way to add/"save" created target in the project, so that anyone opening the project in the future will see all of the created targets. – fragon Mar 30 '17 at 18:53
  • 1
    There is no way to automate the setup of a target. There exists a very advance Ruby library to parse and modify Xcode projects: https://github.com/CocoaPods/Xcodeproj But you will then still have to write your own Ruby scripts. However, this will give you static targets and it's a hell to keep these in sync over the time. I prefer to just have one target and have build settings rather overridden during build time, that scales much better over years and a dozen of developers. – Mecki Mar 31 '17 at 09:11

1 Answers1

5

You can use this ruby script to duplicate any target, but you need to install github.com/CocoaPods/Xcodeproj first :

#!/usr/bin/env ruby

require 'rubygems'
require 'xcodeproj'

puts "Project path:"
proj_path = gets
puts "New target name:"
name = gets
puts "New target bundle identifer"
bundleIdentifier = gets
puts "Witch target to clone?"
srcTargetName =  gets

name = name.chomp
bundleIdentifier = bundleIdentifier.chomp
proj_path = proj_path.chomp
srcTargetName = srcTargetName.chomp

proj = Xcodeproj::Project.open(proj_path)
src_target = proj.targets.find { |item| item.to_s == srcTargetName }
#proj_path = "/testingTest.xcodeproj"

# 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, shared = true)

# copy build_configurations
target.build_configurations.map do |item|
  item.build_settings.update(src_target.build_settings(item.name))
end

target.build_configurations.each do |config|
  config.build_settings['PRODUCT_BUNDLE_IDENTIFIER'] = bundleIdentifier
  config.build_settings['PRODUCT_NAME'] = "$(TARGET_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
Michael Maher
  • 210
  • 4
  • 12
  • Is there a way we can generate new build configurations? I wanted to duplicate one but I cant regenrate the UUID/Hash of the configuration since its the same as what I have duplicated. – MetaSnarf May 29 '23 at 15:00