How to reduce complication extension executable size?

Hello! I'm working on the watchOS app that provides user with multiple fancy complication sets. There are about 50 widgets in our WidgetBundle now. With this amount we reached memory limit and the app crashes:

Thread 1: EXC_RESOURCE (RESOURCE_TYPE_MEMORY: high watermark memory limit exceeded) (limit=15 MB)

I inspected complicationExtension.appex content and find out that most volume is occupied by the executable:

  • Localized strings (12 langs) altogether - 165 KB
  • Assets.car - 1.1 MB
  • Imported fonts TTF files altogether - 866 KB
  • complicationsExtension executable file - 8.2 MB

Why it takes so much space and how can it be made smaller?

Replies

Further exploration. I looked into intermediate objects in DerivedData... /Debug-watchsimulator/complicationsExtension.build/Objects-normal/arm64. The arm64 folder itself takes 25 MB. The fattest files are complicationsExtension.swiftmodule (1 MB) and complicationsExtension-master.priors (500 KB), everything else is hundreds of object files .o and other compiled stuff. Some complications are short files but they produce heavy objects. For example ClassicComplPulse.o is a 70-line SwiftUI view (with previews) and it results in 200 KB. The source code takes only 3 KB. Looks like SwiftUI views get significantly expanded after compilation.

Looks like EXC_RESOURCE RESOURCE_TYPE_MEMORY has nothing to do with the bundle size, probably there's a memory leak or something.

Update: I added onAppear and onDisappear to every complication. Turns out that right before the memory crash, onAppear called for all of them, even for those that aren't present on any face. Clearly, the reason for memory crash is that all Views are loaded into memory simultaneously. I submitted Technical Support Issue to Apple.

  • I mean I added print to onAppear and onDisappear

Add a Comment

The common stack traces:

Thread 1 Queue : com.apple.main-thread (serial)
#0	0x507c8864 in lzfseDecode ()
#1	0x507c788c in lzfse_decode_buffer ()
#2	0x507c645c in compression_decode_buffer ()
#3	0x52200ff0 in Deepmap2DecodeDefault ()
#4	0x5221d184 in DecodeTiledImage ()
#5	0x521ea5a4 in vImageDeepmap2Decode ()
#6	0x41ea601c in __CUIUncompressDeepmap2ImageData_block_invoke ()
#7	0x41ea074c in CUIUncompressDeepmap2ImageData ()
#8	0x41ea4548 in -[_CSIRenditionBlockData expandCSIBitmapData:fromSlice:makeReadOnly:] ()
#9	0x41ea1d48 in __csiCompressImageProviderCopyImageBlockSetWithOptions ()
#10	0x2d7cdcd8 in IIOImagePixelDataProvider::getBytesImageProvider(void*, unsigned long) ()
#11	0x2d7dac6c in PNGWritePlugin::writePNG(IIOImagePixelDataProvider*, IIODictionary*) ()
#12	0x2d7e1d64 in PNGWritePlugin::writeAll() ()
#13	0x2d7da558 in PNGWritePlugin::WriteProc(void*, void*, void*, void*) ()
#14	0x2d7cd044 in IIOImageDestination::finalizeDestination() ()
#15	0x2d807770 in CGImageDestinationFinalize ()
#16	0x244cd564 in ___lldb_unnamed_symbol212136 ()
#17	0x244cd0ec in ___lldb_unnamed_symbol212135 ()
#18	0x244cebfc in ___lldb_unnamed_symbol212193 ()
#19	0x2402f64c in ___lldb_unnamed_symbol166857 ()
#20	0x2402c180 in ___lldb_unnamed_symbol166740 ()
#21	0x2402cdec in ___lldb_unnamed_symbol166744 ()
#22	0x2402ca94 in ___lldb_unnamed_symbol166742 ()
#23	0x244cc404 in ___lldb_unnamed_symbol212112 ()
#24	0x244cce08 in ___lldb_unnamed_symbol212134 ()
#25	0x23b3f110 in ___lldb_unnamed_symbol123404 ()
#26	0x241fed00 in ___lldb_unnamed_symbol183123 ()
#27	0x241fce70 in ___lldb_unnamed_symbol183102 ()
#28	0x241fcf54 in ___lldb_unnamed_symbol183102 ()
#29	0x241fcf54 in ___lldb_unnamed_symbol183102 ()
#30	0x241fcf54 in ___lldb_unnamed_symbol183102 ()
#31	0x241fcf54 in ___lldb_unnamed_symbol183102 ()
#32	0x23c0e600 in ___lldb_unnamed_symbol130598 ()
#33	0x23c10a20 in ___lldb_unnamed_symbol130761 ()
#34	0x242f05e4 in ___lldb_unnamed_symbol193041 ()
#35	0x23c0dbf0 in ___lldb_unnamed_symbol130593 ()
#36	0x23c0e888 in ___lldb_unnamed_symbol130605 ()
#37	0x23c0e7ac in ___lldb_unnamed_symbol130604 ()
#38	0x23c0e9e8 in ___lldb_unnamed_symbol130607 ()
#39	0x27b5dc48 in ___lldb_unnamed_symbol5926 ()
#40	0x27ae5124 in ___lldb_unnamed_symbol2483 ()
#41	0x27ae4558 in ___lldb_unnamed_symbol2482 ()
#42	0x27b9f844 in ___lldb_unnamed_symbol7756 ()
#43	0x26214138 in _dispatch_call_block_and_release ()
#44	0x262159ac in _dispatch_client_callout ()
#45	0x2622246c in _dispatch_main_queue_drain ()
#46	0x262220c4 in _dispatch_main_queue_callback_4CF ()
#47	0x1d0aede4 in __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ ()
#48	0x1d08274c in __CFRunLoopRun ()
#49	0x1d0c76bc in CFRunLoopRunSpecific ()
#50	0x1baf5d78 in -[NSRunLoop(NSRunLoop) runMode:beforeDate:] ()
#51	0x1bb2ec14 in -[NSRunLoop(NSRunLoop) run] ()
#52	0x50520c3c in _xpc_objc_main ()
#53	0x50522d64 in _xpc_main ()
#54	0x50522f24 in xpc_main ()
#55	0x1bb66208 in -[NSXPCListener resume] ()
#56	0x3351142c in -[_EXRunningExtension resume] ()
#57	0x335112dc in -[_EXRunningExtension startWithArguments:count:] ()
#58	0x3352e52c in EXExtensionMain ()
#59	0x1c2644b8 in NSExtensionMain ()
#60	0x4e69167c in start ()

𝟮.

#0	0x4f0c6014 in _platform_memmove ()
#1	0x4f21e6c0 in ___lldb_unnamed_symbol110 ()
#2	0x4f21ddd4 in ___lldb_unnamed_symbol105 ()
#3	0x4f21bc14 in ___lldb_unnamed_symbol102 ()
#4	0x4f21b390 in deflate ()
#5	0x2c34be8c in png_compress_IDAT ()
#6	0x2c34c3ec in png_write_find_filter ()
#7	0x2c431d58 in _cg_png_write_row_sized ()
#8	0x2c34ae5c in PNGWritePlugin::writePNG(IIOImagePixelDataProvider*, IIODictionary*) ()
#9	0x2c351d64 in PNGWritePlugin::writeAll() ()
#10	0x2c34a558 in PNGWritePlugin::WriteProc(void*, void*, void*, void*) ()
#11	0x2c33d044 in IIOImageDestination::finalizeDestination() ()
#12	0x2c377770 in CGImageDestinationFinalize ()
#13	0x2303d564 in ___lldb_unnamed_symbol212136 ()
#14	0x2303d0ec in ___lldb_unnamed_symbol212135 ()
#15	0x2303ebfc in ___lldb_unnamed_symbol212193 ()
#16	0x22b9f64c in ___lldb_unnamed_symbol166857 ()
#17	0x22b9c180 in ___lldb_unnamed_symbol166740 ()
#18	0x22b9cdec in ___lldb_unnamed_symbol166744 ()
#19	0x22b9ca94 in ___lldb_unnamed_symbol166742 ()
#20	0x2303c404 in ___lldb_unnamed_symbol212112 ()
#21	0x2303ce08 in ___lldb_unnamed_symbol212134 ()
#22	0x226af110 in ___lldb_unnamed_symbol123404 ()
#23	0x22d6ed00 in ___lldb_unnamed_symbol183123 ()
#24	0x22d6ce70 in ___lldb_unnamed_symbol183102 ()
#25	0x22d6cf54 in ___lldb_unnamed_symbol183102 ()
#26	0x22d6cf54 in ___lldb_unnamed_symbol183102 ()
#27	0x22d6cf54 in ___lldb_unnamed_symbol183102 ()
#28	0x22d6cf54 in ___lldb_unnamed_symbol183102 ()
#29	0x2277e600 in ___lldb_unnamed_symbol130598 ()
#30	0x22780a20 in ___lldb_unnamed_symbol130761 ()
#31	0x22e605e4 in ___lldb_unnamed_symbol193041 ()
#32	0x2277dbf0 in ___lldb_unnamed_symbol130593 ()
#33	0x2277e888 in ___lldb_unnamed_symbol130605 ()
#34	0x2277e7ac in ___lldb_unnamed_symbol130604 ()
#35	0x2277e9e8 in ___lldb_unnamed_symbol130607 ()
#36	0x266cdc48 in ___lldb_unnamed_symbol5926 ()
#37	0x26655124 in ___lldb_unnamed_symbol2483 ()
#38	0x26654558 in ___lldb_unnamed_symbol2482 ()
#39	0x2670f844 in ___lldb_unnamed_symbol7756 ()
#40	0x24d84138 in _dispatch_call_block_and_release ()
#41	0x24d859ac in _dispatch_client_callout ()
#42	0x24d9246c in _dispatch_main_queue_drain ()
#43	0x24d920c4 in _dispatch_main_queue_callback_4CF ()
#44	0x1bc1ede4 in __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ ()
#45	0x1bbf274c in __CFRunLoopRun ()
#46	0x1bc376bc in CFRunLoopRunSpecific ()
#47	0x1a665d78 in -[NSRunLoop(NSRunLoop) runMode:beforeDate:] ()
#48	0x1a69ec14 in -[NSRunLoop(NSRunLoop) run] ()
#49	0x4f090c3c in _xpc_objc_main ()
#50	0x4f092d64 in _xpc_main ()
#51	0x4f092f24 in xpc_main ()
#52	0x1a6d6208 in -[NSXPCListener resume] ()
#53	0x3208142c in -[_EXRunningExtension resume] ()
#54	0x320812dc in -[_EXRunningExtension startWithArguments:count:] ()
#55	0x3209e52c in EXExtensionMain ()
#56	0x1add44b8 in NSExtensionMain ()
#57	0x4d20167c in start ()

з. Isolated test app with complications only

Thread 1 Queue : com.apple.main-thread (serial)
#0	0x4bf13134 in madvise ()
#1	0x467592a8 in -[_CSIRenditionBlockData _allocateImageBytes] ()
#2	0x467ae188 in -[_CSIRenditionBlockData initWithPixelWidth:pixelHeight:sourceRowbytes:pixelFormat:] ()
#3	0x4674dd04 in __csiCompressImageProviderCopyImageBlockSetWithOptions ()
#4	0x32079cd8 in IIOImagePixelDataProvider::getBytesImageProvider(void*, unsigned long) ()
#5	0x32086c6c in PNGWritePlugin::writePNG(IIOImagePixelDataProvider*, IIODictionary*) ()
#6	0x3208dd64 in PNGWritePlugin::writeAll() ()
#7	0x32086558 in PNGWritePlugin::WriteProc(void*, void*, void*, void*) ()
#8	0x32079044 in IIOImageDestination::finalizeDestination() ()
#9	0x320b3770 in CGImageDestinationFinalize ()
#10	0x28d79564 in ___lldb_unnamed_symbol212136 ()
#11	0x28d790ec in ___lldb_unnamed_symbol212135 ()
#12	0x28d7abfc in ___lldb_unnamed_symbol212193 ()
#13	0x288db64c in ___lldb_unnamed_symbol166857 ()
#14	0x288d8180 in ___lldb_unnamed_symbol166740 ()
#15	0x288d8dec in ___lldb_unnamed_symbol166744 ()
#16	0x288d8a94 in ___lldb_unnamed_symbol166742 ()
#17	0x28d78404 in ___lldb_unnamed_symbol212112 ()
#18	0x28d78e08 in ___lldb_unnamed_symbol212134 ()
#19	0x283eb110 in ___lldb_unnamed_symbol123404 ()
#20	0x28aaad00 in ___lldb_unnamed_symbol183123 ()
#21	0x28aa8e70 in ___lldb_unnamed_symbol183102 ()
#22	0x28aa8f54 in ___lldb_unnamed_symbol183102 ()
#23	0x28aa8f54 in ___lldb_unnamed_symbol183102 ()
#24	0x28aa8f54 in ___lldb_unnamed_symbol183102 ()
#25	0x28aa8f54 in ___lldb_unnamed_symbol183102 ()
#26	0x28aa8f54 in ___lldb_unnamed_symbol183102 ()
#27	0x284ba600 in ___lldb_unnamed_symbol130598 ()
#28	0x284bca20 in ___lldb_unnamed_symbol130761 ()
#29	0x28b9c5e4 in ___lldb_unnamed_symbol193041 ()
#30	0x284b9bf0 in ___lldb_unnamed_symbol130593 ()
#31	0x284ba888 in ___lldb_unnamed_symbol130605 ()
#32	0x284ba7ac in ___lldb_unnamed_symbol130604 ()
#33	0x284ba9e8 in ___lldb_unnamed_symbol130607 ()
#34	0x2c409c48 in ___lldb_unnamed_symbol5926 ()
#35	0x2c391124 in ___lldb_unnamed_symbol2483 ()
#36	0x2c390558 in ___lldb_unnamed_symbol2482 ()
#37	0x2c44b844 in ___lldb_unnamed_symbol7756 ()
#38	0x2aac0138 in _dispatch_call_block_and_release ()
#39	0x2aac19ac in _dispatch_client_callout ()
#40	0x2aace46c in _dispatch_main_queue_drain ()
#41	0x2aace0c4 in _dispatch_main_queue_callback_4CF ()
#42	0x2195ade4 in __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ ()
#43	0x2192e74c in __CFRunLoopRun ()
#44	0x219736bc in CFRunLoopRunSpecific ()
#45	0x203a1d78 in -[NSRunLoop(NSRunLoop) runMode:beforeDate:] ()
#46	0x203dac14 in -[NSRunLoop(NSRunLoop) run] ()
#47	0x54dccc3c in _xpc_objc_main ()
#48	0x54dced64 in _xpc_main ()
#49	0x54dcef24 in xpc_main ()
#50	0x20412208 in -[NSXPCListener resume] ()
#51	0x37dbd42c in -[_EXRunningExtension resume] ()
#52	0x37dbd2dc in -[_EXRunningExtension startWithArguments:count:] ()
#53	0x37dda52c in EXExtensionMain ()
#54	0x20b104b8 in NSExtensionMain ()
#55	0x52f3d67c in start ()

Mods, would you please rename the thread to "Complications - extensive memory usage"

Updates regarding onAppear being called for every complication. Based on the information provided by Apple support: app archives all of the timelines and makes snapshots of all complications.

Further investigations. Extended memory growth happens inplaceholder(in:) func of a TimelineProvider. In getSnapshot(in:completion:) and getTimeline(in:completion:) memory doesn't grow. Maybe the key difference here is that placeholder(in:) is a syncrhonous function, while two others use completions. I suppose internal WidgetKit implementation runs a for loop through all the widgets and accumulates placeholder images array. Then writes it to disk and memory decreases.