diff --git a/lib/xcodeproj/project/object.rb b/lib/xcodeproj/project/object.rb index ce582c0a..bfb55165 100644 --- a/lib/xcodeproj/project/object.rb +++ b/lib/xcodeproj/project/object.rb @@ -414,7 +414,12 @@ def nested_object_for_hash(object, method) end def ascii_plist_annotation - " #{display_name} " + name = display_name + + if name && name.is_a?(String) && (name.encoding == Encoding::UTF_8 || name.encoding == Encoding::US_ASCII) + name = name.unicode_normalize(:nfd) + end + " #{name} " end def to_ascii_plist diff --git a/lib/xcodeproj/project/object/build_phase.rb b/lib/xcodeproj/project/object/build_phase.rb index 340dee23..90a38902 100644 --- a/lib/xcodeproj/project/object/build_phase.rb +++ b/lib/xcodeproj/project/object/build_phase.rb @@ -351,6 +351,20 @@ def pretty_print }, } end + + def to_hash_as(method = :to_hash) + hash_as = super + included_keys_for_serialization_when_empty.each do |key| + if hash_as[key].nil? + hash_as[key] = [] + end + end + hash_as + end + + def included_keys_for_serialization_when_empty + %w(inputPaths outputPaths) + end end #-----------------------------------------------------------------------# diff --git a/lib/xcodeproj/project/object/file_reference.rb b/lib/xcodeproj/project/object/file_reference.rb index 2e32e93f..d41631f1 100644 --- a/lib/xcodeproj/project/object/file_reference.rb +++ b/lib/xcodeproj/project/object/file_reference.rb @@ -131,6 +131,11 @@ class PBXFileReference < AbstractObject # needed. # def display_name + # For folder references with SOURCE_ROOT, use full path like Xcode + if last_known_file_type&.start_with?('folder') && source_tree == 'SOURCE_ROOT' + return path if path + end + if name name elsif (class << GroupableHelper; self; end)::SOURCE_TREES_BY_KEY[:built_products] == source_tree diff --git a/lib/xcodeproj/project/object/file_system_synchronized_exception_set.rb b/lib/xcodeproj/project/object/file_system_synchronized_exception_set.rb index 9e61e10f..5e166b0f 100644 --- a/lib/xcodeproj/project/object/file_system_synchronized_exception_set.rb +++ b/lib/xcodeproj/project/object/file_system_synchronized_exception_set.rb @@ -35,7 +35,7 @@ class PBXFileSystemSynchronizedBuildFileExceptionSet < AbstractObject attribute :platform_filters_by_relative_path, Hash def display_name - "Exceptions for \"#{GroupableHelper.parent(self).display_name}\" folder in \"#{target.name}\" target" + 'PBXFileSystemSynchronizedBuildFileExceptionSet' end end @@ -49,12 +49,16 @@ class PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet < AbstractO # attribute :membership_exceptions, Array + # @return [Hash] The files with specific attributes. + # + attribute :attributes_by_relative_path, Hash + # @return [Hash] The files with a platform filter. # attribute :platform_filters_by_relative_path, Hash def display_name - "Exceptions for \"#{GroupableHelper.parent(self).display_name}\" folder in \"#{build_phase.name}\" build phase" + 'PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet' end end end diff --git a/lib/xcodeproj/project/object/file_system_synchronized_root_group.rb b/lib/xcodeproj/project/object/file_system_synchronized_root_group.rb index 4a26bc6a..466ebd54 100644 --- a/lib/xcodeproj/project/object/file_system_synchronized_root_group.rb +++ b/lib/xcodeproj/project/object/file_system_synchronized_root_group.rb @@ -68,6 +68,21 @@ def display_name return path if path super end + + def to_hash_as(method = :to_hash) + hash_as = super + excluded_keys_for_serialization_when_empty.each do |key| + if !hash_as[key].nil? && hash_as[key].empty? + hash_as.delete(key) + end + end + hash_as + end + + # @return [Array] array of keys to exclude from serialization when the value is empty + def excluded_keys_for_serialization_when_empty + %w(exceptions) + end end end end diff --git a/lib/xcodeproj/project/object/swift_package_local_reference.rb b/lib/xcodeproj/project/object/swift_package_local_reference.rb index aa5ae7f4..7c7a9031 100644 --- a/lib/xcodeproj/project/object/swift_package_local_reference.rb +++ b/lib/xcodeproj/project/object/swift_package_local_reference.rb @@ -19,7 +19,7 @@ class XCLocalSwiftPackageReference < AbstractObject #--------------------------------------# def ascii_plist_annotation - " #{isa} \"#{File.basename(display_name)}\" " + " #{isa} \"#{display_name}\" " end # @return [String] the path of the local Swift package reference. diff --git a/lib/xcodeproj/project/object/swift_package_remote_reference.rb b/lib/xcodeproj/project/object/swift_package_remote_reference.rb index 5abdaabb..9f652890 100644 --- a/lib/xcodeproj/project/object/swift_package_remote_reference.rb +++ b/lib/xcodeproj/project/object/swift_package_remote_reference.rb @@ -18,7 +18,8 @@ class XCRemoteSwiftPackageReference < AbstractObject #--------------------------------------# def ascii_plist_annotation - " #{isa} \"#{File.basename(display_name, '.git')}\" " + name = extract_package_name(display_name) + " #{isa} \"#{name}\" " end # @return [String] the name of the remote Swift package reference. @@ -27,6 +28,36 @@ def display_name return repositoryURL if repositoryURL super end + + private + + # Extracts package name from repository URL + # Handles different URL formats: + # - https://github.com/owner/repo.git -> repo + # - https://github.com/owner/socket.io-client-swift -> socket + # - git@github.com:owner/repo.git -> repo + # - github.com/owner/repo -> repo + # - domain.xyz (custom registry) -> xyz + def extract_package_name(url) + return url unless url + + # Remove .git extension first + name = url.sub(/\.git$/, '') + + # Check if it's a URL with path (contains /) + if name.include?('/') + # Extract last path component (handle both / and :) + name = name.split(/[\/:]/).last + # If name contains a dot, use only the part before the first dot + # e.g., socket.io-client-swift -> socket + name = name.split('.').first if name.include?('.') + elsif name.include?('.') + # For other URLs (e.g., custom registry like domain.xyz), split by . and use last component + name = name.split('.').last + end + + name + end end end end diff --git a/spec/project/object/file_reference_spec.rb b/spec/project/object/file_reference_spec.rb index cc2f2cd9..306661d6 100644 --- a/spec/project/object/file_reference_spec.rb +++ b/spec/project/object/file_reference_spec.rb @@ -83,6 +83,32 @@ module ProjectSpecs @target.build_phases[0].files.should.be.empty end + describe 'concerning folder references' do + it 'returns full path for display_name when lastKnownFileType is folder with SOURCE_ROOT' do + folder = @project.new(PBXFileReference) + folder.last_known_file_type = 'folder' + folder.path = 'NaverMap_v5Tests/NaviSearch/Fixtures' + folder.source_tree = 'SOURCE_ROOT' + folder.display_name.should == 'NaverMap_v5Tests/NaviSearch/Fixtures' + end + + it 'returns basename for display_name for folder with source tree' do + folder = @project.new(PBXFileReference) + folder.last_known_file_type = 'folder' + folder.path = 'Path/To/Folder' + folder.source_tree = '' + folder.display_name.should == 'Folder' + end + + it 'returns basename for display_name for folder.assetcatalog with ' do + folder = @project.new(PBXFileReference) + folder.last_known_file_type = 'folder.assetcatalog' + folder.path = 'Resources/Assets.xcassets' + folder.source_tree = '' + folder.display_name.should == 'Assets.xcassets' + end + end + describe 'concerning proxies' do it 'returns that it is not a proxy' do @file.should.not.be.a.proxy diff --git a/spec/project/object/swift_package_remote_reference_spec.rb b/spec/project/object/swift_package_remote_reference_spec.rb index a199c2d2..d04d1f94 100644 --- a/spec/project/object/swift_package_remote_reference_spec.rb +++ b/spec/project/object/swift_package_remote_reference_spec.rb @@ -24,5 +24,15 @@ module ProjectSpecs @proxy.repositoryURL = 'github.com/swift/package.git' @proxy.ascii_plist_annotation.should == ' XCRemoteSwiftPackageReference "package" ' end + + it 'returns the ascii plist annotation with the part before the first dot for URLs with dotted names' do + @proxy.repositoryURL = 'https://github.com/socketio/socket.io-client-swift' + @proxy.ascii_plist_annotation.should == ' XCRemoteSwiftPackageReference "socket" ' + end + + it 'returns the ascii plist annotation with the part before the first dot for git@ URLs with dotted names' do + @proxy.repositoryURL = 'git@github.com:socketio/socket.io-client-swift.git' + @proxy.ascii_plist_annotation.should == ' XCRemoteSwiftPackageReference "socket" ' + end end end