WatchOS3 Complication that launches App

后端 未结 3 865
夕颜
夕颜 2020-12-30 14:49

I would like to create a complication for watchOS 3 that will simply launch my App. I have used XCode to create the ComplicationController:

class Complicatio         


        
相关标签:
3条回答
  • 2020-12-30 15:35

    Apple Watch 4 new graphic complications looks like this:

    func getCurrentTimelineEntry(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTimelineEntry?) -> Void) {
        // Call the handler with the current timeline entry
        switch complication.family {
        case .graphicCorner:
            if #available(watchOSApplicationExtension 5.0, *) {
                let template = CLKComplicationTemplateGraphicCornerCircularImage()
                let image = UIImage(named: "Complication/Graphic Corner")!
                template.imageProvider = CLKFullColorImageProvider(fullColorImage: image)
                let timelineEntry = CLKComplicationTimelineEntry(date: Date(), complicationTemplate: template)
                handler(timelineEntry)
            } else {
                handler(nil)
            }
        case .graphicCircular:
            if #available(watchOSApplicationExtension 5.0, *) {
                let template = CLKComplicationTemplateGraphicCircularImage()
                let image = UIImage(named: "Complication/Graphic Circular")!
                template.imageProvider = CLKFullColorImageProvider(fullColorImage: image)
                let timelineEntry = CLKComplicationTimelineEntry(date: Date(), complicationTemplate: template)
                handler(timelineEntry)
            } else {
                handler(nil)
            }
        default:
            handler(nil)
        }
    }
    
    func getLocalizableSampleTemplate(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTemplate?) -> Void) {
        // This method will be called once per supported complication, and the results will be cached
        switch complication.family {
        case .graphicCorner:
            if #available(watchOSApplicationExtension 5.0, *) {
                let template = CLKComplicationTemplateGraphicCornerCircularImage()
                let image = UIImage(named: "Complication/Graphic Corner")!
                template.imageProvider = CLKFullColorImageProvider(fullColorImage: image)
                handler(template)
            } else {
                handler(nil)
            }
        case .graphicCircular:
            if #available(watchOSApplicationExtension 5.0, *) {
                let template = CLKComplicationTemplateGraphicCircularImage()
                let image = UIImage(named: "Complication/Graphic Circular")!
                template.imageProvider = CLKFullColorImageProvider(fullColorImage: image)
                handler(template)
            } else {
                handler(nil)
            }
        default:
            handler(nil)
        }
    }
    
    0 讨论(0)
  • 2020-12-30 15:36

    Add Apple Watch 4 new graphic complications (watchOS 5.0+) and prevent crash on Apple Watch Series 1, 2 and 3.

    Important: the code from Zoltan will crash on Apple Watch series 3 or lower.

    According to the Apple documentation, the new graphic complications require watchOS 5.0 or higher and a Watch Series 4 or later:

    Note Watch faces that support graphic templates are available only on Apple Watch Series 4 or later.

    This means that a Watch Series 3 with watchOS 5 or 6 (due to if #available(watchOSApplicationExtension 5.0, *)) will attempt to load the complication image from the asset catalogue. However, since App Thinning is enabled by default, the Watch Series 3 doesn't have the image in it's binary and therefor the following line will crash the app:

    let image = UIImage(named: "Complication/Graphic Corner")!

    We discovered this when we found thousands of crash reports in Xcode:

    1. Open Xcode
    2. Window -> Organizer
    3. Select tab "Crashes"
    4. Select an App Store version

    We found a crash report for each of the 2 Graphic complications that we support, all were with watchOS 5 or 6 and Watch Series 2 or 3, like:

    Solution

    Embed loading of the graphic asset within an IF statement and return nil as template, so it won't crash on devices without the asset.

    The example above from Zoltan will be:

    case .graphicCorner:
            if #available(watchOSApplicationExtension 5.0, *) {
                let template = CLKComplicationTemplateGraphicCornerCircularImage()
                if let image = UIImage(named: "Complication/Graphic Corner") {
                    template.imageProvider = CLKFullColorImageProvider(fullColorImage: image)
                    let timelineEntry = CLKComplicationTimelineEntry(date: Date(), complicationTemplate: template)
                    handler(timelineEntry)
                } else {
                    handler(nil)
                }
            } else {
                handler(nil)
            }
    

    For efficient and maintainable code, we created a reusable function templateForComplication() that is used in all 3 mandatory delegate functions:

    class ComplicationController: NSObject, CLKComplicationDataSource {
        
        // MARK: Mandatory Delegate Methods
        
        func getSupportedTimeTravelDirections(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTimeTravelDirections) -> Void) {
            // Turn off time travelling:
            handler([])
        }
        
        func getCurrentTimelineEntry(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTimelineEntry?) -> Void) {
            let template = templateForComplication(complication: complication)
            let timelineEntry = CLKComplicationTimelineEntry(date: Date(), complicationTemplate: template!)
            handler(timelineEntry)
        }
        
        func getPlaceholderTemplate(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTemplate?) -> Void) {
            // This method will be called once per supported complication, and the results will be cached
            handler(templateForComplication(complication: complication))
        }
        
        func getLocalizableSampleTemplate(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTemplate?) -> Void) {
            handler(templateForComplication(complication: complication))
        }
        
        
        // MARK: Helper Methods
        
        private func templateForComplication(complication: CLKComplication) -> CLKComplicationTemplate? {
            // Init default output:
            var template: CLKComplicationTemplate? = nil
            
            // Graphic Complications are only availably since watchOS 5.0:
            if #available(watchOSApplicationExtension 5.0, *) {
                // NOTE: Watch faces that support graphic templates are available only on Apple Watch Series 4 or later. So the binary on older devices (e.g. Watch Series 3) will not contain the images.
                if complication.family == .graphicCircular {
                    let imageTemplate = CLKComplicationTemplateGraphicCircularImage()
                    // Check if asset exists, to prevent crash on non-supported devices:
                    if let fullColorImage = UIImage(named: "Complication/Graphic Circular") {
                        let imageProvider = CLKFullColorImageProvider.init(fullColorImage: fullColorImage)
                        imageTemplate.imageProvider = imageProvider
                        template = imageTemplate
                    }
                }
                else if complication.family == .graphicCorner {
                    let imageTemplate = CLKComplicationTemplateGraphicCornerCircularImage()
                    // Check if asset exists, to prevent crash on non-supported devices:
                    if let fullColorImage = UIImage(named: "Complication/Graphic Corner") {
                        let imageProvider = CLKFullColorImageProvider.init(fullColorImage: fullColorImage)
                        imageTemplate.imageProvider = imageProvider
                        template = imageTemplate
                    }
                }
            }
            
            // For all watchOS versions:
            if complication.family == .circularSmall {
                let imageTemplate = CLKComplicationTemplateCircularSmallSimpleImage()
                let imageProvider = CLKImageProvider(onePieceImage: UIImage(named: "Complication/Circular")!)
                imageProvider.tintColor = UIColor.blue
                imageTemplate.imageProvider = imageProvider
                template = imageTemplate
            }
            else if complication.family == .modularSmall {
                let imageTemplate = CLKComplicationTemplateModularSmallSimpleImage()
                let imageProvider = CLKImageProvider(onePieceImage: UIImage(named: "Complication/Modular")!)
                imageProvider.tintColor = UIColor.blue
                imageTemplate.imageProvider = imageProvider
                template = imageTemplate
            }
            else if complication.family == .utilitarianSmall {
                let imageTemplate = CLKComplicationTemplateUtilitarianSmallSquare()
                let imageProvider = CLKImageProvider(onePieceImage: UIImage(named: "Complication/Utilitarian")!)
                imageProvider.tintColor = UIColor.blue
                imageTemplate.imageProvider = imageProvider
                template = imageTemplate
            }
            
            return template
        }
    }
    
    0 讨论(0)
  • 2020-12-30 15:40

    These code changes are required:

    func getSupportedTimeTravelDirections(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTimeTravelDirections) -> Void)
    {
        handler([])
    }
    
    
    func getCurrentTimelineEntry(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTimelineEntry?) -> Void)
    {
        if complication.family == .circularSmall
        {
    
            let template = CLKComplicationTemplateCircularSmallRingImage()
            template.imageProvider = CLKImageProvider(onePieceImage: UIImage(named: "Circular")!)
            let timelineEntry = CLKComplicationTimelineEntry(date: Date(), complicationTemplate: template)
            handler(timelineEntry)
    
        } else if complication.family == .utilitarianSmall
        {
    
            let template = CLKComplicationTemplateUtilitarianSmallRingImage()
            template.imageProvider = CLKImageProvider(onePieceImage: UIImage(named: "Utilitarian")!)
            let timelineEntry = CLKComplicationTimelineEntry(date: Date(), complicationTemplate: template)
            handler(timelineEntry)
    
        } else if complication.family == .modularSmall
        {
    
            let template = CLKComplicationTemplateModularSmallRingImage()
            template.imageProvider = CLKImageProvider(onePieceImage: UIImage(named: "Modular")!)
            let timelineEntry = CLKComplicationTimelineEntry(date: Date(), complicationTemplate: template)
            handler(timelineEntry)
    
        } else {
    
            handler(nil)
    
        }
    
    }
    
    
    func getLocalizableSampleTemplate(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTemplate?) -> Void)
    {        
        switch complication.family
        {
            case .circularSmall:
                let image: UIImage = UIImage(named: "Circular")!
                let template = CLKComplicationTemplateCircularSmallSimpleImage()
                template.imageProvider = CLKImageProvider(onePieceImage: image)
                handler(template)
            case .utilitarianSmall:
                let image: UIImage = UIImage(named: "Utilitarian")!
                let template = CLKComplicationTemplateUtilitarianSmallSquare()
                template.imageProvider = CLKImageProvider(onePieceImage: image)
                handler(template)
            case .modularSmall:
                let image: UIImage = UIImage(named: "Modular")!
                let template = CLKComplicationTemplateModularSmallSimpleImage()
                template.imageProvider = CLKImageProvider(onePieceImage: image)
                handler(template)
            default:
                handler(nil)
        }
    }
    

    Plus you need to provide the images as assets in the extension.

    0 讨论(0)
提交回复
热议问题