Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix issues with variant group, versioned, and asset catalog resources #4635

Merged
merged 1 commit into from
Dec 13, 2015

Conversation

efirestone
Copy link
Contributor

Addresses issue #1653.

Summary
Xcode represents certain resources specially in its project structure, and without that special representation they will fail to be compiled correctly. The notable special cases are:

  • General localized files - en.lproj/image.png and de.lproj/image.png are represented as a "variant group" named image.png with two localizations within the Xcode project.
  • Localized interface files (XIB, Storyboard) using Base Internationalization - Base.lproj/Main.xib and en.lproj/Main.strings are represented as a variant group named Main.xib
  • Core Data Models - On disk Core Data models have a parent xcdatamodeld directory, and within them have individual versions with an xcdatamodel extension. These are represented in the Xcode project as a "versioned group"
  • Asset Catalogs - On disk an asset catalog is a directory, but within the Xcode project they're represented as a single file reference to the top level xcassets directory.

Because of these special cases, the previous naive copying caused issues when using a recursive glob for collecting resources. Notably, you usually had two options:

  • Use Resources/* (non-recursive), which would copy the .lproj directories into the resources directly. Because the internals of these directories were ignored, strings, XIBs, and other resources weren't compiled.
  • Use Resources/**/* (recursive), which would add all the files, but use the default CocoaPods behavior of flattening everything into a single level (like the internals of xcdatamodeld directories), thus breaking these resources.

Changes

  • Change variant group name to match the resource rather than stripping the extension. Ex: Localizable.strings rather than just Localizable
  • Special case interface files to use the XIB or Storyboard name for the variant group when using Base Internationalization.
  • Stop naively grouping things with the same name and different extensions. Ex: no longer groups image.strings with image.png. The specific case where this is needed is Base Internationalization, which is handled specially (see previous item).
  • Stop adding file references to the project for anything within an xcdatamodeld or xcassets directory. Xcode only wants a reference to the root and will handle the rest.
  • Stop adding items within a variant group to the resources build phase. Only the variant group itself should be added.

Other Considerations

  • Legacy Usage - To avoid breaking legacy usage of Resources/*, lproj directories are still added directly to the project if none of their children are matched in the glob. In general, everyone should move to the more correct Resources/**/* glob, which now works correctly.
  • Nested Non-Localized Resources - Localized resources are handled correctly, but as the CocoaPods default behavior is to flatten everything, nested directories such as Resources/Nested/Image.png will become just Image.png in the resulting bundle. This wasn't previously a problem because recursive globs didn't work and weren't used. This is consistent with how source and header files are handled, and so I think is a separate issue. A proper fix here might be to add something like header_mappings_dir for resources.

@segiddins
Copy link
Member

@efirestone this looks absolutely awesome! I'm swamped with finals now so it might take me a bit to get around to reviewing this, but thank you for opening the PR :D

@efirestone
Copy link
Contributor Author

No problem at all. Let me know if there's anything I can do to help, and good luck with finals!

#
# @return [Array<Pathname>] The paths which can be added to the Xcode project
#
def allowable_project_paths(paths)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is done as a separate pass because it needs to determine which .lproj directories are only added directly (without adding any of its children), and therefore needs access to the entire paths list. This was to preserve the existing behavior of non-recursive Resources/* globs. If we don't care/stop caring about that behavior then this filtering can be added in-line in the paths.each on L181 and just remove all items ending in .lproj. I think as-implemented is the right behavior and likely always will be.

@segiddins
Copy link
Member

Biggest help would be a project / podspec I can eventually turn into an integration spec (ideally just a showcase of all the different cases you've addressed)

@@ -98,6 +98,7 @@ def add_files_to_build_phases
resource_refs = file_accessor.resources.flatten.map do |res|
project.reference_for_path(res)
end
resource_refs.compact!
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some resource files in the glob no longer have direct corresponding file references. This includes things like files within an xcdatamodeld directory or xcassets directory.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a comment to that effect would be excellent!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do. I commented on the one just below this, but worth repeating here.

@efirestone
Copy link
Contributor Author

For the sample project: I added all the sample cases I could think of to the BananaLib fixture. You would just need to change from s.resource = 'Resources/*' to s.resource_bundle = 'BananaResources' => 'Resources/**/*' like some of the new tests do.

path_str = path.to_s.downcase

# We add the directory for a Core Data model, but not the items in it.
next false if path_str =~ /.*xcdatamodeld\/.+/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all of these next false can just be next

@efirestone efirestone force-pushed the firestone/variant-groups branch 2 times, most recently from 3c6c093 to d386c79 Compare December 7, 2015 21:55
@segiddins
Copy link
Member

Looks good! @neonichu / @manuyavuz want to have a stab at this?

@neonichu
Copy link
Member

neonichu commented Dec 8, 2015

Looks good from my end. Especially like the detailed comments, great work, @efirestone!

An additional integration spec would indeed be great, though, maybe you can share the example(s) you used to test against Xcode?

@efirestone
Copy link
Contributor Author

The BananaLib tarball now includes all of them, and you can see the differences here

Namely, the following cases are important:

Base Internationalization of XIB/Storyboards - If files exist named Base.lproj/<name>.xib or Base.lproj/<name>.storyboard and files named <language>.lproj/<name>.strings then: (1) A variant group should exist named <name>.xib/storyboard that contains all of these files. (2) None of those files should exist in the Xcode project outside of the variant group. (3) Only the variant group should be in the Resources build phase and none of the child files should. If done correctly Xcode will show <FileName> in Resources/(Localization).lproj in the build phase list (it literally says (Localization)).

General Localized Files - If files exist with the same name in different localization folders (like en.lproj/image.png and de.lproj/image.png then the results should look similar to the case above: single variant group, files are only within that variant group, resources build phase only includes the variant group.

Nested Localized Files - If directories exist with the same name in different localization folders (like en.lproj/images/<files> and de.lproj/images/<files> then the results should look similar to the case above, but the directory itself is the nested item and it should be a "file system reference" type item (blue folder icon in Xcode). Xcode will have no idea of the <files> within the directories and the final resource bundle should contain direct copies of the directories, still within their appropriate lproj directories.

Core Data Models with Multiple Versions - Create a Core Data model, then add a version by going to Editor > Add Model Version…. Results should be similar to the above: (1) A versioned group should exist named <ModelName>.xcdatamodeld (2) None of the version files (xcdatamodel, no d) within the directory should exist in the project outside of the versioned group. (3) Only the versioned group should be in the Source Files build phase.

Image Asset Catalog - Create a standard asset catalog. This is actually just a PBXFileReference in the xcode project and not a group. None of the files within the catalog should show up in the file tree anywhere (clicking it gives the standard Asset Catalog UI on the right). Only the asset catalog directory file reference should show up in the resources build phase.

@efirestone
Copy link
Contributor Author

After actually going through that, let me check that core data models are being put into the right build phase. I believe they may be added to Copy Bundle Resources instead of Compile Sources right now.

@efirestone
Copy link
Contributor Author

CoreData models are indeed going into Resources and not Compile Sources. This isn't what Xcode does via the UI, but they do get built correctly.

Thoughts @segiddins & @neonichu? I can add a special case in the "add resources" code to add these files to the compile phase instead of resources (matches Xcode), or we can just leave them as being copied into the resources phase (less code to maintain). Both work equally well.

file_ref.should.be.not.nil
end

it "doesn't add file references for files within Asset Catalogs" do
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be inside 'Installation With Recursive Resources Glob' section? BananaLib adds resources non-recursively by default, also explicitly sets xcassets path. This scenario seems to not testing your aim because there will be no resource paths for Pods/BananaLib/Resources/Images.xcassets/Logo.imageset/logo.png for example. Am I missing sth?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, good catch. Will move.

@manuyavuz
Copy link
Member

@efirestone this is great, really liked the good documentation and test coverage!

I have one little concern that I commented on the code, other than that, one more LGTM.

@efirestone
Copy link
Contributor Author

Thanks guys! Everything's updated and should be ready to merge.

There's the one outstanding question of whether we should automatically be adding Core Data models to the Compile Sources phase rather than the Copy Bundle Resources phase. As mentioned, functionally this makes no difference.

Arguments for Compile Sources:

  • It's what Xcode does if you add a Core Data model using the UI.

Arguments for Copy Bundle Resources:

  • It's one fewer edge case to maintain.
  • It's consistent with how XIBs, Asset Catalogs, and strings are handled. They're also compiled, but live in the resources phase.
  • It's slightly more straightforward for podspec creators. They're specifying this item as part of resources, so you'd expect it to show up in resources.

My slight personal inclination is to leave it in Copy Bundle Resources for simplicity sake, but I can definitely see where the weight of the "that's how Xcode does it" argument is overpowering. If we do want to move it to the Compile Sources phase, does it make sense to modify Xcodeproj's native target add_resources method, or to find all the places in CocoaPods that this is called and branch for Core Data models? My thought would be to catch all the places in CocoaPods as this seems like a strong unintended behavior for add_resources. That does of course introduce more code.

@segiddins
Copy link
Member

Let's leave it for now, but add a @note comment in the relevant method with that above comment

config.sandbox.project = @project

@spec = fixture_spec('banana-lib/BananaLib.podspec')
@spec.resource_bundle = { 'banana_bundle' => ['Resources/**/*'] }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we should also set resources attribute to nil because current state will cause resources to be added multiple times to the project, which is not the case you want to test.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done and done.

@efirestone
Copy link
Contributor Author

@segiddins Added @note for the relevant sections.

@manuyavuz
Copy link
Member

Great! 🛥

@efirestone
Copy link
Contributor Author

Anything left before we can hit the merge button?

@segiddins
Copy link
Member

I'll do a final review in the next couple of days

@efirestone
Copy link
Contributor Author

👍

path_str = path.to_s.downcase

# We add the directory for a Core Data model, but not the items in it.
next if path_str =~ /.*xcdatamodeld\/.+/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.*\.xc ? (for the next line as well)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated, along with the xcassets regex below it.

@segiddins
Copy link
Member

👍 other than that one nitpick

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants