Storyboards are rather a royal pain from a git workflow perspective when multiple people are collaborating on them. For example, the XML in the .storyboard file has its starting <document>
tag's toolsVersion
and systemVersion
attributes altered by whatever configuration the most recent file manipulator happens to be running. Synchronizing everybody's Xcode versions precisely seems to help with toolsVersion
, but systemVersion
changes no matter what, depending on the specific Mac and/or OS X version the developer is running.
This is idiotic, but mostly harmless. What worries us, though, is that at other times some other changes are automatically made to the storyboard just by opening them after a git pull
. That is to say, Alice makes changes to a storyboard, commits and pushes them to the repository. Bob then pulls Alice's changes and opens up the storyboard to make further changes. The moment he opens the storyboard, the file icon immediately changes to a modified-but-unsaved state, and a git status
shows that any number of weird changes have occurred. All this without Bob having changed anything or saved the file himself.
The most common automated change we're seeing is the disappearance or reappearance of the entire <classes>
tag hierachy near the end of a storyboard file. We haven't figured out what is causing this. We may have several localized versions of a storyboard in various .lproj directories, and when opening them inside Interface Builder, the class hierarchy may spontaneously be removed from some and added into others, or left alone in some. This causes a lot of noise in git diff
, but it doesn't actually break any functionality. We will often selectively add the actual changes we made into git's index, commit those, and then just discard the spontaneous, nonsensical <classes>
changes. This is to keep commits small and nice, as they should be. Eventually, though, it just becomes too much to bother with since Xcode keeps re-doing the changes, and someone just ragecommits them along with some other stuff... which is fine until someone else's Xcode decides to want to change them back for no apparent reason. (Our commit history has a lot of swearing over this.)
Is anyone else seeing this behaviour? Is this an Xcode bug or a configuration issue on one or more of our developer Macs? We've seen some similar behaviour when collaborating with XIB files, but storyboards seem more susceptible to this.
This is not a bug, this is a consequence of how Xcode processes storyboard files. I am writing a diff and merge program for storyboard files (GitHub link) and I have spent hours analyzing the storyboard files logic and how Xcode processes it. This is what I discovered:
Why do weird changes occur in storyboard files?
Xcode uses the NSXML API to parse storyboard files into some NSSet
-based logical tree structure. When Xcode needs to write changes it creates an NSXMLDocument
based on the logical tree structure, clears the storyboard file and calls XMLDataWithOptions:
to fill the file again. Because sets do not preserve the order of their elements, even the slightest modification could change the whole storyboard XML file.
Why does the class tag disappear or reappear randomly?
The <class>
section is nothing more than an internal Xcode cache. Xcode use it to cache information about classes. The cache changes often. Elements are added when class .h/.m
files are opened and removed when Xcode suspects they are outdated (at least older Xcodes behave like this). When you save the storyboard, the current version of the cache is dumped, which is why the <class>
section often changes or even disappears.
I have not reverse-engineered Xcode; I made these observations by experimenting with Xcode and storyboard files. Nevertheless, I am almost 100% sure it works this way.
Conclusions:
MyController1
view controller in a storyboard document. Open the storyboard file, and find something like this
<viewController id=”ory-XY-OBM” sceneMemberID=”MyController1”>
.
You can safely commit only changes in this section and ignore everything else. If you changed segues or constraints, also commit anything that has “ory-XY-OBM”
inside. Simple!