iOS resource management

There are facilities in Cmake for hinting source code to be treated as resources for an iOS App, and that works modestly well. When you generate an Xcode project, the code behavior is close to being right. You get a project that moves icon data and storyboard data and so forth into the final bundle in the right locations. You can even set a property tells the app to use a specific icon group as it’s main icon (which would be good, but I haven’t been able to make it work). However, modern iOS apps need to compile the images and icons into an asset catalog and the .storyboard files need to be compiled into .storyboards files and stored in a Base.lproj directory for later localization (don’t even get me started on trying to engineer further localization via Cmake). So I have been able to move the UI Resources (images and storyboards) to the right folder layout with the RESOURCE target property and the MACOSX_PACKAGE_LOCATION source code property, but I am definitely not communicating to Cmake that they need to be “compiled” into the form that iOS is expecting to consume them at runtime.

So here are some possible directions to go:

1.) stop using MACOS_PACKAGE_LOCATION and instead use add_custom_command to do what Xcode does at build time with all these things. That’s good, because, I’m probably going to have to do that anyway to make the ninja code work, assuming I meed both paths to work (which is right now, not a hard requirement of my project). What I don’t like about that is that Apple changes its build tool behaviors more often than I’d like, and they’re mot too interested in backward compatibility, so my custom command will be frozen in time.

2.) Leave things the way they are (more or less) and post process the generated Xcode project in python to bless all the files to be treated in the appropriate way. This is good because it’s surprisingly easy to do, but bad because it involves reverse engineering the XML of the project format, generating UUID’s, and all kinds of error-prone and unsupported behaviors.

3.) figure out how to properly hint the resource files, so that CMake generates an Xcode project that simply knows how to treat the files appropriately. This would be ideal, but it’s also very arcane and brittle. I founds some really weird behaviors, like failing to add the x86_64 architecture doesn’t produce a project that only works on devices, it produces a project that fails at build time (???). Nonetheless, I’d like to try this approach.

So I’m looking to the community to for wisdom in doing number 3 (numbers 1 or 2, I know how to do). Here’s some partial code for the sake of discussion:

add_executable (FTApp
    test_fw/test_fw.h
    AppDelegate.h
    AppDelegate.m
    SceneDelegate.h
    SceneDelegate.m
    ViewController.h
    ViewController.m
    main.m
    ${RESOURCE_FILES}
    ${FRAMEWORK_PATH}
)

set_target_properties (FTApp PROPERTIES
    MACOSX_BUNDLE YES
    MACOSX_BUNDLE_INFO_PLIST Info.plist
    XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER com.airtime.FTApp
    XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS @executable_path/Frameworks
    XCODE_ATTRIBUTE_ASSETCATALOG_COMPILER_APPICON_NAME "AppIcon"
    ${CODE_SIGN_DATA}
    XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC YES
    OSX_ARCHITECTURES "arm64;x86_64"
    COMPILE_OPTIONS "-F ${FRAMEWORK_FOLDER}"
    #  RESOURCE "${RESOURCE_FILES}"
)

The RESOURCE property is commented out, because if you properly apply the source code property MACOSX_PACKAGE_LOCATION on the files in RESOURCE_FILES, they will be moved to their proper destinations in the bundle, the information in the RESOURCE target property is implicit (for general purpose resources, this is exactly the thing to do). The problem is that this mechanism ONLY knows how to move files that are source code into destinations within the bundle. It obviously doesn’t understand how to properly process files of various specific kinds into type-specific formats. I used to use the RESOURCE target property alone, and had some success, but it was very hard to target the outputs to a standard location. I’m thinking the path forward is to mix and match. For example, I noticed that treating the folder with the storyboard files as a source file, and blessing it with its own MACOSX_PACKAGE_LOCATION had some desirable results, but when I switch to using the property on the individual storyboard files, it seemed to be redundant. Maybe it’s not. Maybe one says “compile these as resources” and the other says “put the compiled outputs here”. You don’t always need both, but in the case of storyboards you do. You would use the GENERATED key word and bless the speculative output of the compilation with MACOSX_PACKAGE_LOCATION and specify the folder they’re in as a RESOURCE, so it has the side effect of creating the compiled versions. I’ll experiment and see.

Anyone solve some of these problems yet? Is this just a missing feature, and I’m whistling in the wind to hope that this works? There are some future properties that are supposed to handle this mentioned in some feature requests and they are found in the source for version 3.16.4, but they are currently ignored.

The thing is this: you can’t be successful building an iOS application without these behaviors working and people have been successful in creating iOS apps with Cmake. However the examples I’ve seen on, say, GitHub generally use some combination of methods 1 and 2 above, and I would greatly like to avoid that. Still if the Cmake developers got even the most basic “hello world” to work on iOS, they would have to address this issue. Another thing I’m looking at is using another source code property (XCODE_EXPLICIT_FILE_TYPE for example) to make clear to Cmake certain special folders, like *.xcassets, should be handled a specific way (though Xcode should derive that from the extension.

All thoughts and ideas are much appreciated. You should know that I have scoured the Internet for wisdom before joining this forum and pleading for help, but I’m currently a bit stalled.

@alcroito Is there something in this that the Qt folks are also wrestling with, or do you have your own way of handling these things?

I didn’t hear of anything. Mostly due to the fact that up till a few months ago, you couldn’t use CMake + Qt for iOS out of the box, and most customers still use qmake.

Oh. Just to be clearer, I didn’t use Qt, though you raise an interesting point. I have some experience using Qt with Cmake to produce iOS (though through a horrible cobbling together of techniques). In order for that to work, you would think there’s a way forward. On the other hand, when I did in my last job, I actually delegated most of the heavy lifting with respect to iOS behavior TO qmake (maybe that’s why it worked).

Yes, I only pinged the Qt folks because I know they’ve been more actively working on CMake of late, including some iOS-related things.

Thanks for following up. So that question is sort of all in the mix of my confusion. So by experimentation, I solved half of my question above. I got graphical elements (icons and launch-screen) to be properly set up to “compile” (which during development really means just sorted for the destination device, collected in the right location - they call it compiling, for short). I have yet to solve a similar problem for the layout elements - the storyboards. Those undergo something more like real compiling, i.e. a processed binary output is placed in the bundle in place of the textual XML source, and the runtime system searches for that “compiled” form when the app is launched. So you ask if I have my own way of doing this. The answer is “not by choice”. I’d like to use
CMakeLists.txt in whatever way implies to Cmake to produce an Xcode project that “does the right thing” with storyboard assets. As I mentioned above, I JUST now got it to work with the image assets. Perhaps I can generalize from that. I think what you’re really asking is “have I discovered the first-class CMake way of accomplishing what I’m attempting and is THAT not working.” The answer to that is “No”. I’m not yet sure what the proper way of indicating to Cmake that foo.storyboard needs to be compiled into foo.storyboardc (the compiled form) and placed in the place in the bundle that the runtime expects it. In the case of the images, the answer turned out to be that a level of declaration was required that I thought was redundant. I had to add the constituent image folder to the source code for the add_application specification as if it were a source file in addition to the two sub-folders with icons and images, as if each of them were a source file too. That’s a little odd. Typically source files have to be have to be, y’know, files. For example, you can’t (I think) just throw in a directory path that contains all your program source files into the specification for an add_application call and expect it to work. I think the semantic distinction may be that the distinction between directories and files on MacOS is a little squishy, due to frameworks and bundles and so forth. But even so, having Assets.xcassets in the source list AND each sub-folder that contains constituent images and then NOT specifying all of the individual images themselves just seems to break what few sane models there are in this process. The only reason I thought to do it is that I noticed that Xcode itself seems to treat them each a little differently when it generates a project for itself. And it has the side effect of adding all the constituent images to the projects as a side-effect. Oh and then you have to specify it redundantly in the RESOURCES property. It’s just counter-intuitive, but it works, and I’m sure it makes sense to someone. I would be happy to find a counter-intuitive-but-effective way of getting my storyboard files to compile, but I have not yet. However the answer may simply lie in generalizing the observations about images. Images exist in folder hierarchies that compile down to run-time binary “blobs”. Storyboards exist in a similar context. I think I may be close. Thanks for drilling down on this a bit with me. I’ll post my project here if I get it to work. It’s a completely generic toy project I threw together just to get the general principles in place for a project that requires automatic generation of CMake to create Xcode projects. The one I’m playing with is purely for educational purposes (my own education).

In a project I work on, I have the following code:

  set(bundle_resources ${JUCER_ICON_FILE})
  if(IOS)
    list(APPEND bundle_resources
      ${JUCER_XCASSETS} ${JUCER_LAUNCH_STORYBOARD_FILE}
    )
  endif()

  target_sources(${target} PRIVATE ${bundle_resources})
  set_source_files_properties(${bundle_resources} PROPERTIES
    MACOSX_PACKAGE_LOCATION "Resources"
  )
  source_group("Resources" FILES ${bundle_resources})

where:

  • JUCER_ICON_FILE is the path to an.icns icon file,
  • JUCER_XCASSETS is the path to an .xcassets folder containing an AppIcon.iconset folder and a LaunchImage.launchimage folder,
  • JUCER_LAUNCH_STORYBOARD_FILE is the path to a .storyboard file.

When I run cmake .. -GXcode -DCMAKE_SYSTEM_NAME=iOS and then build the project in Xcode (for the simulator because I don’t own any iOS device), I have the following in the build report:

The asset catalog and the storyboard file seem to be processed as expected, but I’m not an iOS expert at all, so maybe there is still something missing.

I hope this helps!