OK, I finally succeeded to submit my app to iTunesConnect for validation :-)
Here are the changes I made in juce_ProjectExport_XCode.h, see comments starting with (pca) and bold lines. These changes prevent the errors ITMS-90032 (due to deprecated icon declaration), ITMS-90475 (forcing full-screen to be compliant with the new split screen allowing two apps to run simultaneously), and ITMS-90096 (missing launchImages).
Around line 670, next to createiOSIconFiles():
// (pca) added this method to create a set of black launch images
void createiOSLaunchImageFiles (File launchImageSet) const
{
const Array<ImageType> types (getiOSLaunchImageTypes());
for (int i = 0; i < types.size(); ++i)
{
const ImageType type = types.getUnchecked(i);
// Create default black launch image
Image image (Image::ARGB, type.width, type.height, true);
Graphics g(image);
g.fillAll(Colours::black);
MemoryOutputStream pngData;
PNGImageFormat pngFormat;
pngFormat.writeImageToStream (image, pngData);
overwriteFileIfDifferentOrThrow (launchImageSet.getChildFile (type.filename), pngData);
}
}
Around line 606:
void writeInfoPlistFile() const
{
if (! xcodeCreatePList)
return;
.....
addPlistDictionaryKey (dict, "CFBundleExecutable", "${EXECUTABLE_NAME}");
// (pca) Removed this line because it causes a validation error during app publishing (error ITMS-90032)
// addPlistDictionaryKey (dict, "CFBundleIconFile", iconFile.exists() ? iconFile.getFileName() : String::empty);
addPlistDictionaryKey (dict, "CFBundleIdentifier", project.getBundleIdentifier().toString());
addPlistDictionaryKey (dict, "CFBundleName", projectName);
addPlistDictionaryKey (dict, "CFBundlePackageType", xcodePackageType);
addPlistDictionaryKey (dict, "CFBundleSignature", xcodeBundleSignature);
......
if (iOS)
{
// (pca) needed to make the app compatible with new iOS multi-threading feature.
// Forcing full screen allows to keep default behavior by disabling split screen feature (to prevent the error ITMS-90475)
addPlistDictionaryKeyBool (dict, "UIRequiresFullScreen", true);
static const char* kDefaultiOSOrientationStrings[] =
{
"UIInterfaceOrientationPortrait",
"UIInterfaceOrientationPortraitUpsideDown",
"UIInterfaceOrientationLandscapeLeft",
"UIInterfaceOrientationLandscapeRight",
nullptr
};
.....
Around line 1390:
static Array<AppIconType> getiOSAppIconTypes()
{
AppIconType types[] =
{
{ "iphone", "29x29", "Icon-Small.png", "1x", 29 },
{ "iphone", "29x29", "Icon-Small@2x.png", "2x", 58 },
// (pca) Added this image
{ "iphone", "29x29", "Icon-Small@3x.png", "3x", 87 },
{ "iphone", "40x40", "Icon-Spotlight-40@2x.png", "2x", 80 },
// (pca) Added this image
{ "iphone", "40x40", "Icon-Spotlight-40@3x.png", "3x", 120 },
{ "iphone", "57x57", "Icon.png", "1x", 57 },
{ "iphone", "57x57", "Icon@2x.png", "2x", 114 },
.....
Around line 1440:
.....
images.append (var (d));
}
return getiOSAssetContents (images);
}
// (pca) removed this structure definition from getiOSLaunchImageContents to mimic AppIcon way to fill the asset
struct ImageType
{
const char* orientation;
const char* idiom;
// (pca) Added subtype, needed to distinguish iPhone Retina4
const char* subtype;
const char* extent;
const char* scale;
// (pca) Added filename and image resolution
const char* filename;
const int width;
const int height;
};
// (pca) removed this array definition from getiOSLaunchImageContents to mimic AppIcon way to fill the asset
static Array<ImageType> getiOSLaunchImageTypes()
{
ImageType types[] =
{
// (pca) Added subtype, filename and image resolution
{ "portrait", "iphone", NULL, "full-screen", "2x", "LaunchImage-iphone-2x.png", 640, 960 },
{ "portrait", "iphone", "retina4", "full-screen", "2x", "LaunchImage-iphone-retina4.png", 640, 1136 },
{ "portrait", "ipad", NULL, "full-screen", "1x", "LaunchImage-ipad-portrait-1x.png", 768, 1024 },
{ "landscape","ipad", NULL, "full-screen", "1x", "LaunchImage-ipad-landscape-1x.png", 1024, 768 },
{ "portrait", "ipad", NULL, "full-screen", "2x", "LaunchImage-ipad-portrait-2x.png", 1536, 2048 },
{ "landscape","ipad", NULL, "full-screen", "2x", "LaunchImage-ipad-landscape-2x.png", 2048, 1536 }
};
return Array<ImageType> (types, numElementsInArray (types));
}
// (pca) Modified to mimic getiOSAppIconContents
String getiOSLaunchImageContents() const
{
const Array<ImageType> types = getiOSLaunchImageTypes();
var images;
for (size_t i = 0; i < types.size(); ++i)
{
ImageType type = types.getUnchecked(i);
DynamicObject::Ptr d = new DynamicObject();
d->setProperty ("orientation", type.orientation);
d->setProperty ("idiom", type.idiom);
if (type.subtype) d->setProperty ("subtype", type.subtype);
d->setProperty ("extent", type.extent);
d->setProperty ("minimum-system-version", "7.0");
d->setProperty ("scale", type.scale);
d->setProperty ("filename", type.filename);
images.append (var (d));
}
return getiOSAssetContents (images);
}
void createiOSAssetsFolder() const
{
.....
And finally around line 1500,
void createiOSAssetsFolder() const
{
File assets (getTargetFolder().getChildFile (project.getProjectFilenameRoot()).getChildFile ("Images.xcassets"));
overwriteFileIfDifferentOrThrow (assets.getChildFile ("AppIcon.appiconset").getChildFile ("Contents.json"), getiOSAppIconContents());
createiOSIconFiles (assets.getChildFile ("AppIcon.appiconset"));
overwriteFileIfDifferentOrThrow (assets.getChildFile ("LaunchImage.launchimage").getChildFile ("Contents.json"), getiOSLaunchImageContents());
// (pca) create the launch images
createiOSLaunchImageFiles (assets.getChildFile ("LaunchImage.launchimage"));
RelativePath assetsPath (assets, getTargetFolder(), RelativePath::buildTargetFolder);
addFileReference (assetsPath.toUnixStyle());
resourceIDs.add (addBuildFile (assetsPath, false, false));
resourceFileRefs.add (createFileRefID (assetsPath));
}
That's it. Hope this can help some of you :-)