xcodeprojでxcodeをいじる

#xcodeprojとは？
xcodeprojはRubyのgemであり、xcodeないの設定などをrubyでいじるために使われる物である。
例をあげるとschemeなどの作成やターゲットの作成、buildsettingやgroupの作成などxcodeに関係する様々な設定を操作可能にします。

##使用例
※以下コードはアプリケーションのマスク替えを効率化する事を目的に記述されています。

```
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)

# 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
```
それではまず下記のコードの解説をしていきます。
まずインストールしたgemを使用するために、requireで必要gemの使用を可能にします。
そしてnameにアプリケーション名を代入し、projに各当プロジェクトの名称を代入します。
次にsrc_targetにコピー元のターゲットを代入します。

```
require 'rubygems'

require 'xcodeproj'

name = 'test_copy'

proj = Xcodeproj::Project.open('test.xcodeproj')
src_target = proj.targets.find { |item| item.to_s == 'test' }
```
次にターゲットの作成を行います。
まず先ほど定義したsrc_target（コピー元ファイル）のsymbol_type(例　:application,:frameworkなど）を設定し、名前の設定し、platform_nameは:iosか:osxを設定します。devekopment_targetは調べてもよくわからなかったので、nilを指定してあげるとターゲットがbuildされました。
説明が不十分な点ご了承ください。
下記にこのメソッドの公式リファレンスを貼っておきます。
https://www.rubydoc.info/github/CocoaPods/Xcodeproj/Xcodeproj%2FProject%2FProjectHelper.new_target

```
# create target
target = proj.new_target(src_target.symbol_type, name, src_target.platform_name, src_target.deployment_target)
```
次にスチーマのbuildを行います。
設置は下記の通りで、スチーマにadd_build_targetで各当のターゲットを設定します。
set_launch_targetに関しては必要性がわからなかったのですが、なくても問題なくターゲットも設定されたスチーマがbuildされていました。気になる方は検証してみてください。
最後にsave_asメソッドでプロジェクトのパスとスチーマ名を設定し保存で完了です。

```
# create scheme
scheme = Xcodeproj::XCScheme.new
scheme.add_build_target(target)
scheme.set_launch_target(target)
scheme.save_as(proj_path, name)
```
次にbuild_phasesのコピーを行います。
まずphasesにsrc_targetのbuild_phases内のrun_script（Xcodeproj::Project::Object::PBXShellScriptBuildPhase）をrejectメソッドで取り除き必要な項目だけを残して代入します。
次にsrcにコピー元のbuild_phasesを見つけ代入します。そしてdstに先ほどbuildしたターゲットのbuild_phasesを見つけ代入します。そしてunless文でdstにない項目を特定し、ないものを加えます。
次に加えられた項目をremove_from_projectで前のプロジェクト情報を取り除きます。
そして次に、各ファイルに新しいプロジェクト情報や名前、パスなどをsrc（元ターゲット）のファイルに設定していきます。
そしてbuild_fileに新しく空のfile_refを作り、そこに先ほど再設定した元ファイルのfile_refを代入します。
最後にこれを新ターゲットのファイルに加えることで、ターゲット情報が変わった新ファイルを生成することができます。

```
# 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
```

次は新しいファイルを加えるための処理を行なっていきます。
まず最初にclassesにファイルを作成するグループを代入します。
次に各当ターゲットのbuild_phasesをsourcesに代入します。
そしてfile_refにclassesに新しくファイルを生成し代入し、build.fileにfile_refに代入し、sourcesのファイルの方にも加えて完了です。

```
# 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
```

##最後に
私自身まだまだ理解が足りていないので、説明が曖昧な箇所多々あると思いますがご了承ください。
さらに理解を深めたい方は下記に参考リンクを載せておくので、ご利用ください。

##参考文献
Github
https://github.com/CocoaPods/Xcodeproj

公式リファレンス
https://www.rubydoc.info/gems/xcodeproj/Xcodeproj/Project

参考記事
https://gist.github.com/ratazzi/f6d9217654d6605450a0