Do complication images have to come from xcassets?

I've managed to get a simple text-only complication working and now want to add an Image Provider to it.


From a couple of the posts I've read, it looks like the preferred (and maybe required) way to do this is to add the image assets to images.xcassets and then load them with a name derived from the template size (e.g., Complication/Modular).


That seems to imply a single image will be used whenever the complication is displayed, as xcassets only gives one space for each sized image. In my case, the image will change frequently, each time we generate a new timeline entry we will check to see which image should be displayed. So this implies I'd just be loading them from the file system ( so something like UIImage(named:"myimage.png"), in theory, but that's returning nil at the moment so I may have the image in the wrong place or be missing something else).


Is that a valid approach? (I'm planning to have someone else provide the images so I've got nothing to test with at the moment, although I suppose I should find or create something to play in the short term).


If I'm just creating a UIImage from a .png in the file system, I am guessing things like '@2x' naming convention are irrelevant. I'm not sure if something would fail if I didn't return the exact expected image size -- but I'd like to make everything 'pixel perfect', so if I'm doing my own image loading, is there a way to check the device size so I know whether to return the image sized for a 38mm vs 42mm device?

Accepted Reply

I've got this working now, with one or two things I'm not sure I understand, but I'll document what works for the next person to get stumped by it.


  • You can load the images from a xcassets bundle, but it's the bundle in the WatchKit Extension, not the one in the WatchKit App. I kept trying to put images in the WatchKit App's bundle and was never able to load from there.
  • You can create as many "Complication" items as you want in xcassets, but it doesn't seem to matter - anything you put in any but the initial "Complications" folder will fail to load.
  • (This may be a bug?) Whenever you create a 'New Watch Complication' folder in the asset editor, that complication then shows up as a selectable item for WatchKit Extension build target / Complications Configuration / Complications Group
    • Even Complication folders you create on the WatchKit App will appear here and being selectable, making it seem like loading from the app would be supported.
    • As far as I can tell, it makes absolutely no difference what 'Complications Group' you select here; complication icons will only be loaded from the set named "Complication" in the WatchKit Extension
  • When you create the "Complication" folder, it is pre-populated with 3 items - Circular, Modular, Utilitarian. These cannot be deleted. They can be renamed, but the loading for them doesn't match the pattern for other sets you may create (Specifically, they seem to not require a Complication/ prefix in the name, where any sets you create yourself must be prefixed with Complication.
  • For my prototype, I created two new Image Sets - Star.ModLarge and Star.ModSmall. Each was then populated with appropriately sized images for 38mm and 42mm. In my code, I can create images from these as shown in the code sample below


The code that is working for me:

switch complication.family {
case .ModularSmall:
   let modSmall = CLKComplicationTemplateModularSmallSimpleImage()
   let ms_image = UIImage(named: "Complication/Star.ModSmall"
   modSmall.imageProvider = CLKImageProvider(onePieceImage: ms_image!)
   template = modSmall
case .ModularLarge
   let modLarge = CLKComplicationTemplateModularLargeTable()
   let ml_image = UIImage(named: "Complication/Star.ModLarge"
   modLarge.headerImageProvider = onePieceImage: ml_image!)
   // assigning various text providers omitted 
   template = modLarge
// other cases omitted

Replies

Found the answer to at least a small piece of it ... UIImage(named: ) looks in the WatchKit Extension, not the WatchKit App, so when I moved the file I'm now getting something. (What I'm getting is just a white square at the moment, but I think that's because I'm using a full color image rather than just the alpha information ... I'm only using a placeholder image at the moment so once I get 'real' artwork I'll need to figure out how to get it into the needed format.

Worked on this a good bit yesterday, and still quite confused by it.


It seems that the same method invocation - UIImage(named: "foo") -- in some cases will load from the files found in the WatchKit extension, and in other cases will load from the contents of images.xcassets of the WatchKit App. How does it decide which? How do I specify where I want the image to come from? I can't seem to find any clear documentation that says exactly how UIImage(named: ) finds the image to load. It doesn't appear the UIImage constructor that takes an 'inBundle' parameter is available on watchOS. I would think it would search the asset catalog first, but I feel like I've configured and referenced the asset catalog correctly and I'm still getting nil images. Hopefully it's just a dumb error on my part that someone can point out to me.


The only thing I've been successful in making work is to place my image file in whe Watchkit extension, and then specifically reference it by the full name

let goodImage = UIImage(named: "8-Pointed Star.42mm.png")


That works, but then it's up to me to load the right device size, and icons are being transferred over from the extension rather than include in the app bundle.


If I copy that same file into my WatchKit App (I created a Complication Icons group for it), I can then add it into my images.xcassets. Eventually I want to have my own app-specific image sets, but for a simple test case I just created a Complication image set, and added my appropriately-sized images to the "Modular" set.


So my images.xcassets has an 'AppIcon' set, and a 'Complication' folder.

Within the Complication folder are 3 pre-created image sets - Circular, Modular, and Utilitarian.

I've added my images (by drag and drop from the files copied into the WatchKit App) to the targets of the Modular image set. If I look at the inspector panel, the images have 'watchOS' checked under devices, and have the appropriate sizes (24x24 for 42mm, 22x22 for 32mm). (These are the sizes needed for a header image in the 'CLKComplicationTemplateModularLargeTemplate', not all Modular templates require the same sizes so I'm going to need to create additional image sets eventually to support ModularLarge and ModularSmall variations of my icon).


I then try to load this as follows, and get nil:

let nilimage = UIImage(named: "Complication/Modular")


And it fails, returning nil.


What else do I need to check?

I'm pretty sure that the "Complication/Modular" images are *only* used when you've called the getComplication handler with "nil"; I could never get anything from that kind of asset to show up in my code, but I do see them for the nil responses. Standard Image assets worked fine in code. Just make sure you've only got "Apple Watch" checkbox selected on the rightmost IB inspector pane; it lets the app optimize which icon to use without scaling.

(I'm also not certain the sizes IB insists on for the Complication assets are right; when those images appear they look scaled.)

I've got this working now, with one or two things I'm not sure I understand, but I'll document what works for the next person to get stumped by it.


  • You can load the images from a xcassets bundle, but it's the bundle in the WatchKit Extension, not the one in the WatchKit App. I kept trying to put images in the WatchKit App's bundle and was never able to load from there.
  • You can create as many "Complication" items as you want in xcassets, but it doesn't seem to matter - anything you put in any but the initial "Complications" folder will fail to load.
  • (This may be a bug?) Whenever you create a 'New Watch Complication' folder in the asset editor, that complication then shows up as a selectable item for WatchKit Extension build target / Complications Configuration / Complications Group
    • Even Complication folders you create on the WatchKit App will appear here and being selectable, making it seem like loading from the app would be supported.
    • As far as I can tell, it makes absolutely no difference what 'Complications Group' you select here; complication icons will only be loaded from the set named "Complication" in the WatchKit Extension
  • When you create the "Complication" folder, it is pre-populated with 3 items - Circular, Modular, Utilitarian. These cannot be deleted. They can be renamed, but the loading for them doesn't match the pattern for other sets you may create (Specifically, they seem to not require a Complication/ prefix in the name, where any sets you create yourself must be prefixed with Complication.
  • For my prototype, I created two new Image Sets - Star.ModLarge and Star.ModSmall. Each was then populated with appropriately sized images for 38mm and 42mm. In my code, I can create images from these as shown in the code sample below


The code that is working for me:

switch complication.family {
case .ModularSmall:
   let modSmall = CLKComplicationTemplateModularSmallSimpleImage()
   let ms_image = UIImage(named: "Complication/Star.ModSmall"
   modSmall.imageProvider = CLKImageProvider(onePieceImage: ms_image!)
   template = modSmall
case .ModularLarge
   let modLarge = CLKComplicationTemplateModularLargeTable()
   let ml_image = UIImage(named: "Complication/Star.ModLarge"
   modLarge.headerImageProvider = onePieceImage: ml_image!)
   // assigning various text providers omitted 
   template = modLarge
// other cases omitted

In my case , I put below code in both "getLocalizableSampleTemplate" & "getCurrentTimelineEntry"

        case .graphicRectangular:
            let templ = CLKComplicationTemplateGraphicRectangularLargeImage()
            templ.textProvider = CLKSimpleTextProvider(text: "Test name matching")
            guard let img = UIImage(named: "Complication/NON-EXISTED-NAME") else {
                  fatalError("Unable to load image")
            }
            templ.imageProvider = CLKFullColorImageProvider(fullColorImage:img)

And put a suitable placeholder image to Assets.xcassets default Complication/Graphic Large Rectangular set.


After running the WatchOS simulator (6.1.1) , I chosed the GraphicRectangular complication for my App. I found there is no error has been raised even I deliberately make a fatal error in my code.


According to the test result I belive CLKComplicationTemplateGraphicRectangularLargeImage class (maybe with other CLKComplicationTemplates) will ignore imageProvider attribute we assigned to it.


Is it a WatchKit bug ?