ccios 5.1.0 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dc6f5c81e7b9f2abf46e0f373717896e51762b3565f616205bd6a666d888fc65
4
- data.tar.gz: 0f8661b91001f98ebe5e6435e8572bf1d671d871a22c120d85d29783d294cb36
3
+ metadata.gz: 27b359e8339dcd511d85488eb93f75d19e7b03bc6ed7cceedebdd721c3b9ad03
4
+ data.tar.gz: db07b4baa0b8143fcec3f70c3ecbd45b9e4a26b1885659128da67a52ffb1afc3
5
5
  SHA512:
6
- metadata.gz: 7113bdee060f5d5808bb43951a0e0485b45cc058d857ccd0c64e36702a6418ed893e62a6a1dc9695ffa2c770c177931b9705a533cf2f2f8727bbabbbe0c25244
7
- data.tar.gz: b0d3109396e17b9d8d3b4979901b3c2b6183de19da7df9f60ad784d6e0c7b26b6da9a730f6f0c607f4fd344634d8ca9f1ae505207f86131e677261fee451a4a1
6
+ metadata.gz: 25e62f91e331fe7b2e1e8b58dd1e44559faefe40be02b61a8d3858a0ba174618af52d897d9e87fa2b91b7df21e82a6130c8891d51bb0bc706764142c2b8916a4
7
+ data.tar.gz: c924d07dc088ca07efddc97de3222411f8fa564935b28e971480856bce1c08f88e3323134725f721d385e061980c62d50083057d9291a55c6c399db5b4ec0ca2
data/CHANGELOG.md CHANGED
@@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [5.2.0]
8
+
9
+ ### Added
10
+
11
+ - Add support for Swift Packages and Xcode 16 synchronized folders
12
+ - Add new optional variable nammed "project_type" which accepts 2 values: "xcode" and "filesystem". When missing ccios use "xcode" by default.
13
+ - When "project_type" is set to "filesystem" ccios will not try to update a pbxproj and will only generate files.
14
+ - When "project_type" is set to "filesystem" multi target definition is no longer supported for generated files.
15
+ - When generating files for an filesystem project, the target name in the header is either: the target defined in the template variables, the target defined in `.ccios.yml`, or it will try to guess the name when using SPM by searching the target name inside the standard naming scheme of: "Sources/<target_name>/".
16
+
7
17
  ## [5.1.0]
8
18
 
9
19
  ### Changed
data/Gemfile.lock CHANGED
@@ -1,10 +1,10 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ccios (5.1.0)
4
+ ccios (5.2.0)
5
5
  activesupport (> 4)
6
6
  mustache (~> 1.0)
7
- xcodeproj (~> 1.4)
7
+ xcodeproj (~> 1.27)
8
8
 
9
9
  GEM
10
10
  remote: https://rubygems.org/
@@ -13,7 +13,7 @@ GEM
13
13
  base64
14
14
  nkf
15
15
  rexml
16
- activesupport (8.0.1)
16
+ activesupport (8.0.2)
17
17
  base64
18
18
  benchmark (>= 0.3)
19
19
  bigdecimal
@@ -27,22 +27,22 @@ GEM
27
27
  tzinfo (~> 2.0, >= 2.0.5)
28
28
  uri (>= 0.13.1)
29
29
  atomos (0.1.3)
30
- base64 (0.2.0)
31
- benchmark (0.4.0)
32
- bigdecimal (3.1.9)
30
+ base64 (0.3.0)
31
+ benchmark (0.4.1)
32
+ bigdecimal (3.2.2)
33
33
  claide (1.1.0)
34
34
  colored2 (3.1.2)
35
35
  concurrent-ruby (1.3.5)
36
- connection_pool (2.5.0)
37
- drb (2.2.1)
36
+ connection_pool (2.5.3)
37
+ drb (2.2.3)
38
38
  i18n (1.14.7)
39
39
  concurrent-ruby (~> 1.0)
40
- logger (1.6.6)
41
- minitest (5.25.4)
40
+ logger (1.7.0)
41
+ minitest (5.25.5)
42
42
  mustache (1.1.1)
43
43
  nanaimo (0.4.0)
44
44
  nkf (0.2.0)
45
- rake (13.2.1)
45
+ rake (13.3.0)
46
46
  rexml (3.4.1)
47
47
  securerandom (0.4.1)
48
48
  tzinfo (2.0.6)
data/MAINTAINER.md CHANGED
@@ -6,3 +6,7 @@
6
6
  - Update the version in ccios.gemspec & run `bundle install`
7
7
  - Create a Pull Request
8
8
  - Once merged, create a tag `A.B.C` on the merged commit
9
+ - Create release in GitHub
10
+ - Publish release to RubyGem:
11
+ - `make build`
12
+ - `gem push ccios-A.B.C.gem`
data/README.md CHANGED
@@ -137,6 +137,7 @@ templates_collection: ccios/templates
137
137
 
138
138
  # Global overrides of variables [Optional]
139
139
  variables:
140
+ project_type: xcode
140
141
  project: Project.xcodeproj
141
142
  target: SomeDefaultTarget
142
143
 
@@ -221,6 +222,9 @@ parameters:
221
222
  # List of templates variables that is used to generate files in an xcode project. [Optional]
222
223
  # Those variables can be overridden in config file, see section "Variable hierarchy" for more informations.
223
224
  variables:
225
+ # Type of project "filesystem" or "xcode", will be considered as "xcode" if not specified. [Optional]
226
+ # You want to use "filesystem" if you want to generate files for an SPM project, or if your Xcode project uses the new synchronized group from Xcode 16.
227
+ project_type: filesystem
224
228
  # The name of the xcode project. "*.xcodeproj" will use the first it finds. [required]
225
229
  project: "*.xcodeproj"
226
230
  # The base path used to generate an element. This variable must be defined once here, or on each elements below.
@@ -297,3 +301,16 @@ Element will use variables in this order (first in this list is used): (For file
297
301
  - Config Template variables
298
302
  - Default templates variables
299
303
  - Config Global variables
304
+
305
+ # How to develop
306
+
307
+ 1. install the gem locally using
308
+ ```bash
309
+ make install
310
+ ```
311
+ 2. Run use the locally installed ccios gem on your project
312
+ ```bash
313
+ ccios presenter MyNewPresenterStack
314
+ ```
315
+
316
+ Note: ensure that you use the same ruby version when building, installing and running this local version of ccios.
data/ccios.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'ccios'
3
- s.version = '5.1.0'
3
+ s.version = '5.2.0'
4
4
  s.executables << 'ccios'
5
5
  s.date = '2016-08-03'
6
6
  s.summary = "Clean Code iOS Generator"
@@ -12,7 +12,7 @@ Gem::Specification.new do |s|
12
12
  s.homepage = 'http://rubygems.org/gems/hola'
13
13
  s.license = 'MIT'
14
14
  s.add_dependency 'activesupport', '> 4'
15
- s.add_dependency 'xcodeproj', '~> 1.4'
15
+ s.add_dependency 'xcodeproj', '~> 1.27'
16
16
  s.add_dependency "mustache", "~> 1.0"
17
17
 
18
18
  s.add_development_dependency 'rake', '~> 13.2'
@@ -3,18 +3,6 @@ require 'fileutils'
3
3
  require 'logger'
4
4
  require 'xcodeproj'
5
5
 
6
- class Xcodeproj::Project::Object::PBXGroup
7
-
8
- def pf_new_group(associate_path_to_group:, name:, path:)
9
- # When using "Group with folder" we only provide a path
10
- # When using "Group without folder" we only provide a name
11
- new_group(
12
- associate_path_to_group ? nil : name,
13
- associate_path_to_group ? path : nil
14
- )
15
- end
16
- end
17
-
18
6
  class Xcodeproj::Project
19
7
 
20
8
  def project_name_from_path
@@ -37,10 +25,18 @@ class FileCreator
37
25
  full_username: git_username,
38
26
  date: DateTime.now.strftime("%d/%m/%Y"),
39
27
  }
40
- if targets.count == 1
41
- defaults["project_name"] = targets[0].display_name
28
+ if project.nil?
29
+ if targets.count == 1
30
+ defaults["project_name"] = targets[0]
31
+ else
32
+ raise "A file outside an xcode project cannot require multiple targets"
33
+ end
42
34
  else
43
- defaults["project_name"] = project.project_name_from_path
35
+ if targets.count == 1
36
+ defaults["project_name"] = targets[0].display_name
37
+ else
38
+ defaults["project_name"] = project.project_name_from_path
39
+ end
44
40
  end
45
41
  defaults
46
42
  end
@@ -69,10 +65,7 @@ class FileCreator
69
65
  file.puts(file_content)
70
66
 
71
67
  file.close
72
- file_ref = group.new_reference(file_path)
73
- targets.each do |target|
74
- target.add_file_references([file_ref])
75
- end
68
+ group.register_file_to_targets(file_path, targets)
76
69
  end
77
70
 
78
71
  def print_file_content_using_template(filename, template_path, context)
@@ -83,8 +76,12 @@ class FileCreator
83
76
  logger.info file_content
84
77
  end
85
78
 
86
- def create_empty_directory(group)
79
+ def create_empty_directory_for_group(group)
87
80
  dirname = group.real_path
81
+ create_empty_directory(dirname)
82
+ end
83
+
84
+ def create_empty_directory(dirname)
88
85
  FileUtils.mkdir_p dirname unless File.directory?(dirname)
89
86
 
90
87
  git_keep_path = File.join(dirname, ".gitkeep")
@@ -1,6 +1,6 @@
1
1
  require_relative 'code_templater'
2
2
  require_relative 'file_creator'
3
- require_relative 'pbxproj_parser'
3
+ require_relative 'xcode_group_representation'
4
4
 
5
5
  class FileTemplateDefinition
6
6
  def initialize(file_template_definition_hash)
@@ -33,20 +33,34 @@ class FileTemplateDefinition
33
33
  base_path = merged_variables["base_path"]
34
34
  raise "Missing base_path variable" if base_path.nil?
35
35
 
36
- base_group = project[base_path]
37
- raise "Base path \"#{base_path}\" is missing" if base_group.nil?
38
-
39
36
  target_name = merged_variables["target"]
40
- if target_name.is_a?(String) || target_name.nil?
41
- target = parser.target_for(project, target_name)
42
- raise "Unable to find target \"#{target_name}\"" if target.nil?
43
- elsif target_name.is_a?(Array)
44
- target_name.each do |target_name|
37
+ if project.nil?
38
+ if target_name.is_a?(String)
39
+ target = target_name
40
+ elsif target_name.nil?
41
+ guessed_name = guess_target_name_from_path(base_path)
42
+ raise "Unable to guess the target from the base path \"#{base_path}\", please specify the target in your config file" if guessed_name.nil?
43
+ target = guessed_name
44
+ elsif target_name.is_a?(Array)
45
+ raise "A template generating files in an filesystem project type cannot specify multiple targets"
46
+ else
47
+ raise "Invalid target in template #{@name}"
48
+ end
49
+ else
50
+ base_group = XcodeGroupRepresentation.findGroup(base_path, project)
51
+ raise "Base path \"#{base_path}\" is missing" if base_group.nil?
52
+
53
+ if target_name.is_a?(String) || target_name.nil?
45
54
  target = parser.target_for(project, target_name)
46
55
  raise "Unable to find target \"#{target_name}\"" if target.nil?
56
+ elsif target_name.is_a?(Array)
57
+ target_name.each do |target_name|
58
+ target = parser.target_for(project, target_name)
59
+ raise "Unable to find target \"#{target_name}\"" if target.nil?
60
+ end
61
+ else
62
+ raise "Invalid target in template #{@name}"
47
63
  end
48
- else
49
- raise "Invalid target in template #{@name}"
50
64
  end
51
65
 
52
66
  end
@@ -55,32 +69,30 @@ class FileTemplateDefinition
55
69
  merged_variables = config.variables_for_template_element(template_definition, @name, @variables)
56
70
 
57
71
  base_path = merged_variables["base_path"]
58
- base_group = project[base_path]
72
+ base_group = XcodeGroupRepresentation.findGroup(base_path, project)
59
73
  file_path = CodeTemplater.new.render_string(@path, context)
60
74
 
61
75
  intermediates_groups = file_path.split("/")[0...-1]
62
76
  generated_filename = file_path.split("/")[-1]
63
77
 
64
- group = base_group
65
- associate_path_to_group = !base_group.path.nil?
66
-
67
- intermediates_groups.each do |group_name|
68
- new_group_path = File.join(group.real_path, group_name)
69
- existing_group = group.groups.find { |g| g.display_name == group_name }
70
- group = existing_group || group.pf_new_group(
71
- associate_path_to_group: associate_path_to_group,
72
- name: group_name,
73
- path: new_group_path
74
- )
75
- end
78
+ group = base_group.create_groups_if_needed_for_intermediate_groups(intermediates_groups)
76
79
 
77
80
  target_name = merged_variables["target"]
78
81
 
79
82
  targets = []
80
- if target_name.is_a?(String) || target_name.nil?
81
- targets = [parser.target_for(project, target_name)]
82
- elsif target_name.is_a?(Array)
83
- targets = target_name.map { |name| parser.target_for(project, name) }
83
+ if project.nil?
84
+ if target_name.is_a?(String)
85
+ targets = [target_name]
86
+ elsif target_name.nil?
87
+ guessed_name = guess_target_name_from_path(base_path)
88
+ targets = [guessed_name]
89
+ end
90
+ else
91
+ if target_name.is_a?(String) || target_name.nil?
92
+ targets = [parser.target_for(project, target_name)]
93
+ elsif target_name.is_a?(Array)
94
+ targets = target_name.map { |name| parser.target_for(project, name) }
95
+ end
84
96
  end
85
97
 
86
98
  FileCreator.new.create_file_using_template_path(
@@ -92,4 +104,15 @@ class FileTemplateDefinition
92
104
  context
93
105
  )
94
106
  end
95
- end
107
+
108
+ private def guess_target_name_from_path(path)
109
+ # SPM standard format
110
+ parts = path.split(File::SEPARATOR)
111
+ sources_index = parts.index("Sources")
112
+ if sources_index && sources_index + 1 < parts.length
113
+ return parts[sources_index + 1]
114
+ else
115
+ return nil
116
+ end
117
+ end
118
+ end
@@ -1,6 +1,5 @@
1
1
  require_relative 'code_templater'
2
2
  require_relative 'file_creator'
3
- require_relative 'pbxproj_parser'
4
3
 
5
4
  class GroupTemplateDefinition
6
5
  def initialize(group_template_definition_hash)
@@ -22,7 +21,7 @@ class GroupTemplateDefinition
22
21
  base_path = merged_variables["base_path"]
23
22
  raise "Missing base_path variable" if base_path.nil?
24
23
 
25
- base_group = project[base_path]
24
+ base_group = XcodeGroupRepresentation.findGroup(base_path, project)
26
25
  raise "Base path \"#{base_path}\" is missing" if base_group.nil?
27
26
  end
28
27
 
@@ -30,25 +29,13 @@ class GroupTemplateDefinition
30
29
  merged_variables = config.variables_for_template_element(template_definition, @name, @variables)
31
30
 
32
31
  base_path = merged_variables["base_path"]
33
- base_group = project[base_path]
32
+ base_group = XcodeGroupRepresentation.findGroup(base_path, project)
34
33
  group_path = CodeTemplater.new.render_string(@path, context)
35
34
 
36
- group_path = group_path.split("/")
37
-
38
- group = base_group
39
- associate_path_to_group = !base_group.path.nil?
40
-
41
- group_path.each do |group_name|
42
- new_group_path = File.join(group.real_path, group_name)
43
- existing_group = group.groups.find { |g| g.display_name == group_name }
44
- group = existing_group || group.pf_new_group(
45
- associate_path_to_group: associate_path_to_group,
46
- name: group_name,
47
- path: new_group_path
48
- )
49
- end
35
+ group_path_components = group_path.split("/")
36
+ group = base_group.create_groups_if_needed_for_intermediate_groups(group_path_components)
50
37
 
51
38
  file_creator = FileCreator.new
52
- file_creator.create_empty_directory(group)
39
+ file_creator.create_empty_directory_for_group(group)
53
40
  end
54
- end
41
+ end
@@ -33,4 +33,4 @@ class PBXProjParser
33
33
  project.targets.find { |t| t.name == target_name }
34
34
  end
35
35
  end
36
- end
36
+ end
@@ -59,10 +59,17 @@ class TemplateDefinition
59
59
  raise "Error: invalid template name" unless @name.is_a? String
60
60
 
61
61
  merged_variables = config.variables_for_template(self)
62
- project_path = merged_variables["project"]
63
-
64
- project = parser.project_for(project_path)
65
- raise "Error: Unable to find project \"#{project_path}\"" if project.nil?
62
+ project_type = merged_variables["project_type"] || "xcode"
63
+ case project_type
64
+ when "xcode"
65
+ project_path = merged_variables["project"]
66
+ project = parser.project_for(project_path)
67
+ raise "Error: Unable to find project \"#{project_path}\"" if project.nil?
68
+ when "filesystem"
69
+ project = nil
70
+ else
71
+ raise "Invalid project_type given \"#{project_type}\", only \"xcode\" and \"fiilesystem\" are supported"
72
+ end
66
73
 
67
74
  @template_file_source.each do |file_template_name, path|
68
75
  raise "Missing template source file for \"#{file_template_name}\"" unless File.exist?(self.template_source_file(file_template_name))
@@ -93,9 +100,16 @@ class TemplateDefinition
93
100
  options = agrument_transformed_options(options)
94
101
 
95
102
  merged_variables = config.variables_for_template(self)
96
- project_path = merged_variables["project"]
97
103
 
98
- project = parser.project_for project_path
104
+ project_type = merged_variables["project_type"] || "xcode"
105
+ case project_type
106
+ when "xcode"
107
+ project_path = merged_variables["project"]
108
+ project = parser.project_for project_path
109
+ when "filesystem"
110
+ project = nil
111
+
112
+ end
99
113
 
100
114
  @generated_elements.each do |element|
101
115
  element.generate(parser, project, options, self, config)
@@ -0,0 +1,107 @@
1
+ require_relative 'pbxproj_parser'
2
+
3
+ # This object handles Xcode groups, folder reference and synchronized folder reference
4
+ class XcodeGroupRepresentation
5
+ def self.findGroup(path, project)
6
+ intermediates_groups = path.split("/")
7
+
8
+ if project.nil?
9
+ return self.new nil, nil, intermediates_groups
10
+ end
11
+
12
+ deepest_group = project.main_group
13
+ additional_path = []
14
+
15
+ intermediates_groups.each do |group_name|
16
+ if deepest_group.is_a?(Xcodeproj::Project::Object::PBXFileSystemSynchronizedRootGroup)
17
+ additional_path.append(group_name)
18
+ elsif deepest_group.is_a?(Xcodeproj::Project::Object::PBXGroup)
19
+ deepest_group = deepest_group.find_subpath(group_name)
20
+ return nil if deepest_group.nil?
21
+ else
22
+ raise "Unsupported element found with name \"#{group_name}\": #{deepest_group}"
23
+ end
24
+ end
25
+ self.new project, deepest_group, additional_path
26
+ end
27
+
28
+ def initialize(project, xcode_group, additional_path = [])
29
+ if project.nil?
30
+ throw "Unexpected xcode_group when project is nil, we should be in an filesystem context" unless xcode_group.nil?
31
+ else
32
+ throw "Unsupported group type" unless xcode_group.is_a?(Xcodeproj::Project::Object::PBXFileSystemSynchronizedRootGroup) || xcode_group.is_a?(Xcodeproj::Project::Object::PBXGroup)
33
+ if !additional_path.empty? && !xcode_group.is_a?(Xcodeproj::Project::Object::PBXFileSystemSynchronizedRootGroup)
34
+ throw "additional_path can only be specified for a synchronized file system group"
35
+ end
36
+ end
37
+ # This represent the xcode project if present, if nil we should be generating files in an Swift package
38
+ @project = project
39
+ # This represents the deepest group or folder reference in the project
40
+ @xcode_deepest_group = xcode_group
41
+ # This represents the additional filesystem path after `xcode_deepest_group` as an Array of strings. This should be non empty only when deepest group is a synchronized group
42
+ @additional_path = additional_path
43
+ end
44
+
45
+ def real_path
46
+ if @xcode_deepest_group.nil?
47
+ @additional_path.join("/")
48
+ else
49
+ Xcodeproj::Project::Object::GroupableHelper.real_path(@xcode_deepest_group) + @additional_path.join("/")
50
+ end
51
+ end
52
+
53
+
54
+ def register_file_to_targets(file_path, targets)
55
+ if @xcode_deepest_group.nil?
56
+ return
57
+ end
58
+ if @xcode_deepest_group.is_a?(Xcodeproj::Project::Object::PBXGroup)
59
+ file_ref = @xcode_deepest_group.new_reference(file_path)
60
+ targets.each do |target|
61
+ target.add_file_references([file_ref])
62
+ end
63
+ else
64
+ # no file to register, unless exceptions needs to be made
65
+ # TODO: Handle synchronized groups (no new reference, but use exceptions)
66
+ targets.each do |taget|
67
+ puts "Unsupported target mismatch between \"#{file_path}\" and synchronized group #{ @xcode_deepest_group.display_name }" unless taget.file_system_synchronized_groups.index(@xcode_deepest_group) != nil
68
+ end
69
+ end
70
+ end
71
+
72
+ # @return [XcodeGroupRepresentation] the created group or self if the intermediates_groups is empty
73
+ def create_groups_if_needed_for_intermediate_groups(intermediates_groups)
74
+ return self if intermediates_groups.empty?
75
+
76
+ new_deepest_group = @xcode_deepest_group
77
+ new_additional_path = @additional_path
78
+
79
+ intermediates_groups.each do |group_name|
80
+ if new_deepest_group.nil? || new_deepest_group.is_a?(Xcodeproj::Project::Object::PBXFileSystemSynchronizedRootGroup)
81
+ new_additional_path.append(group_name)
82
+ elsif new_deepest_group.is_a?(Xcodeproj::Project::Object::PBXGroup)
83
+ existing_child = new_deepest_group.find_subpath(group_name)
84
+ new_group_path = File.join(new_deepest_group.real_path, group_name)
85
+ new_deepest_group = existing_child || new_deepest_group.pf_new_group(
86
+ associate_path_to_group: !new_deepest_group.path.nil?,
87
+ name: group_name,
88
+ path: new_group_path
89
+ )
90
+ else
91
+ raise "Unsupported element found for \"#{group_name}\": #{new_deepest_group}"
92
+ end
93
+ end
94
+ XcodeGroupRepresentation.new @project, new_deepest_group, new_additional_path
95
+ end
96
+ end
97
+
98
+ # Private utility method
99
+
100
+ class Xcodeproj::Project::Object::PBXGroup
101
+ def pf_new_group(associate_path_to_group:, name:, path:)
102
+ new_group(
103
+ associate_path_to_group ? nil : name,
104
+ associate_path_to_group ? path : nil
105
+ )
106
+ end
107
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ccios
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.1.0
4
+ version: 5.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pierre Felgines
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '1.4'
33
+ version: '1.27'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '1.4'
40
+ version: '1.27'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: mustache
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -130,6 +130,7 @@ files:
130
130
  - lib/ccios/templates/Repository/repository_implementation.mustache
131
131
  - lib/ccios/templates/Repository/template.yml
132
132
  - lib/ccios/templates_loader.rb
133
+ - lib/ccios/xcode_group_representation.rb
133
134
  - templates_library/async/Coordinator/coordinator.mustache
134
135
  - templates_library/async/Coordinator/template.yml
135
136
  - templates_library/async/Presenter/dependency_provider.mustache