fastlane: Persisting plist updating keys using symbols not working, while string constants work fine

I’m using update_info_plist action like so:

update_info_plist(plist_path: INFO_PLIST, xcodeproj: XCODEPROJ_PATH, block: lambda { |plist|
  plist['working'] = 'foo'
  plist[:not_working] = 'bar'
}

First change is being persisted to the file, second change is not. It appears there is a bug in Xcodeproj::Plist.write_to_path.

I’m using rvm with ruby 2.2.2 Please let me know if I can provide any more information to solve this.

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 1
  • Comments: 15 (12 by maintainers)

Most upvoted comments

Using your project I was able to reproduce the problem. Thank you! 👍

Using a symbol as the key will work, but only if that key is not present in the Plist at the time it is read. So, effectively, it will only work the first time 😫 All the reasons are exactly as @frisocki said above.

Because this problem has a simple work-around, I think I’m going to leave this here.

If we wanted to provide a little more safety, instead of passing the hash to the block directly, we could pass something that proxied for the hash and called to_s on all provided keys before updating the hash. If you’re interested in making such a PR, I’d be happy to review it!

Thanks!

Thanks for the example project, that’s 💯 ! I will probably get to have a look tomorrow.

Oh yes we fixed the issue by replacing everything with strings. But I don’t mind to experiment to get to the bottom of this. Will try to do it shortly On Wed, Aug 31, 2016 at 9:36 PM, Michael Furtak notifications@github.com wrote:

OK, that may be an interesting hint as to why I can’t reproduce.

If I’m reading their code correctly, Xcodeproj internally chooses between two possible implementations for writing plist data. One attempts to call CoreFoundation and DevToolsCore code directly through FFI, and the other uses another gem called plist.

If you’re still interested in experimenting, I might suggest trying to gem install ffi and then re-run your tests to see if the results are different. If so, that might suggest that the two transitive plist writing implementations have differences.

In either case, is this something that is still blocking you? Does the answer “use string keys for maximum safety” seem like a reasonable resolution for now? If so, are you aware of any documentation we should update for correctness?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/fastlane/fastlane/issues/5905#issuecomment-243858767, or mute the thread https://github.com/notifications/unsubscribe-auth/ACkHJtt8nWxLcBGSO4psSBK9Sr_HZO_kks5qlcmRgaJpZM4JuRHv .

@zats

When the existing plist file with the key CFBundleDisplayName is read, it is stored in the hash as the string literal CFBundleDisplayName. When you attempt to update that value with the symbol :CFBundleDisplayName, 2 entries are created in the hash:

  1. String Literal CFBundleDisplayName
  2. Symbol :CFBundleDisplayName

Then when the plist file is written, it attempts to write out both keys and values. Each symbol must be converted to a string, at which point there are basically duplicate keys in the hash.

The underlying implementation ultimately calls (in projects outside of fastlane)

RubyHashToCFDictionary

hash.each do |key, value|
    key = RubyStringToCFString(key.to_s)
    value = RubyValueToCFTypeRef(value)
    CFDictionaryAddValue(result, key, value)
  end

CFDictionaryAddValue

Adds a key-value pair to a dictionary if the specified key is not already present.

The 1st CFDictionaryAddValue will work, subject to how hash.each decides to enumerate the keys and values. The 2nd will silently fail.

Any reason why you are using a symbol as the hash key and not the string literal? Using a string literal should solve your issue.

The reason it works the 1st time around is there is no collision in the hash key naming.