es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Cómo recargar widgets desde la hoja de intercambio, a través de una Extensión de Acción.

Mi aplicación tiene una extensión de acción que permite a los usuarios recopilar y guardar enlaces en todo el sistema. También tiene un widget para mostrar estos enlaces en la pantalla de inicio.

Cuando agrego enlaces desde la aplicación principal, llamo a WidgetCenter.shared.reloadAllTimelines() para recargar todos los widgets, y todo funciona como se espera.

Pero cuando agrego enlaces desde la extensión de acción, los widgets no se recargan, incluso después de llamar a WidgetCenter.shared.reloadAllTimelines(). Solo cuando voy a la aplicación principal es cuando los widgets se recargan.

¿Cómo puedo actualizar mis widgets para los cambios realizados a través de la extensión de acción desde la hoja de compartición?

Tags:  , ,

Answer

  1. Avatar for davy.ai

    Para actualizar los widgets con los cambios realizados mediante la Extensión de Acciones, debes usar la propiedad URL .widgetURL en tu extensión. Cuando un usuario guarda un enlace desde la extensión de acción, establece la propiedad widgetURL correspondiente con la URL del contenedor compartido donde se almacenan los datos. El widget se actualizará automáticamente cuando cambien los datos.

    En la línea de tiempo del widget, en la implementación de TimelineProvider, deberás verificar si la propiedad widgetURL ha cambiado desde la última actualización, y si lo ha hecho, deberás obtener los nuevos datos y devolver una nueva TimelineEntry con la información actualizada.

    Aquí tienes un ejemplo de implementación:

    struct Link {
        let title: String
        let url: URL
    }
    
    class DataManager {
        static let shared = DataManager()
    
        private var links = [Link(title: "Enlace de ejemplo", url: URL(string: "https://example.com")!)]
    
        func addLink(link: Link) {
            links.append(link)
        }
    
        func getAllLinks() -> [Link] {
            return links
        }
    
        func getSharedContainerURL() -> URL {
            guard let containerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.your-app.bundle-id") else {
                fatalError("No se pudo obtener la URL del contenedor compartido")
            }
            return containerURL
        }
    }
    
    struct LinkWidgetEntry: TimelineEntry {
        let date: Date
        let links: [Link]
    }
    
    struct LinkWidgetProvider: TimelineProvider {
        typealias Entry = LinkWidgetEntry
    
        func placeholder(in context: Context) -> LinkWidgetEntry {
            return LinkWidgetEntry(date: Date(), links: [Link(title: "", url: URL(string: "")!)])
        }
    
        func getSnapshot(in context: Context, completion: @escaping (LinkWidgetEntry) -> ()) {
            let entry = LinkWidgetEntry(date: Date(), links: DataManager.shared.getAllLinks())
            completion(entry)
        }
    
        func getTimeline(in context: Context, completion: @escaping (Timeline<linkwidgetentry>) -> ()) {
            let entry = LinkWidgetEntry(date: Date(), links: DataManager.shared.getAllLinks())
            let timeline = Timeline(entries: [entry], policy: .atEnd)
            completion(timeline)
        }
    }
    
    struct LinkWidget: Widget {
        let kind: String = "LinkWidget"
    
        var body: some WidgetConfiguration {
            StaticConfiguration(kind: kind, provider: LinkWidgetProvider()) { entry in
                VStack(alignment: .leading) {
                    ForEach(entry.links, id: \.url) { link in
                        LinkRow(link: link)
                    }
                }
            }
            .configurationDisplayName("Enlaces")
            .description("Ver una lista de enlaces guardados.")
            .supportedFamilies([.systemSmall, .systemMedium, .systemLarge])
        }
    }
    
    struct LinkRow: View {
        let link: Link
    
        var body: some View {
            VStack(alignment: .leading) {
                Text(link.title)
                    .font(.headline)
    
                Link(destination: link.url) {
                    Text(link.url.absoluteString)
                        .foregroundColor(.blue)
                }
            }
        }
    }
    
    struct LinkWidgetView: View {
        var body: some View {
            LinkWidget()
                .previewContext(WidgetPreviewContext(family: .systemMedium))
        }
    }
    
    struct LinkWidget_Previews: PreviewProvider {
        static var previews: some View {
            LinkWidgetView()
        }
    }
    

    En tu extensión de acción, establece la propiedad widgetURL con la URL del contenedor compartido al guardar un enlace de la siguiente manera:

    let link = Link(title: "Enlace de ejemplo", url: URL(string: "https://example.com")!)
    DataManager.shared.addLink(link: link)
    
    guard let sharedContainerURL = WidgetCenter.shared.sharedContainerURL else {
        return
    }
    let linkData = try! NSKeyedArchiver.archivedData(withRootObject: link, requiringSecureCoding: false)
    let linkFileURL = sharedContainerURL.appendingPathComponent("link.data")
    try? linkData.write(to: linkFileURL)
    
    let widgetURL = sharedContainerURL.appendingPathComponent("widget.link")
    try? NSKeyedArchiver.archivedData(withRootObject: linkFileURL, requiringSecureCoding: false).write(to: widgetURL)
    
    WidgetCenter.shared.reloadAllTimelines()
    

    En la implementación de TimelineProvider, deberás verificar si la propiedad widgetURL ha cambiado desde la última actualización, y si lo ha hecho, deberás obtener los nuevos datos y devolver una nueva TimelineEntry con la información actualizada:

    “`swift
    struct LinkWidgetEntry: TimelineEntry {
    let date: Date
    let link: Link

    static var placeholder: LinkWidgetEntry {
        return LinkWidgetEntry(date: Date(), link: Link(title: "", url: URL(string: "")!))
    }
    

    }

    struct LinkWidgetProvider: TimelineProvider {
    typealias Entry = LinkWidgetEntry

    private func loadLink(from url: URL) -> Link? {
        guard let linkFileURLData = try? Data(contentsOf: url),
              let linkFileURL = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(linkFileURLData) as? URL,
              let linkData = try? Data(contentsOf: linkFileURL),
              let link = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(linkData) as? Link else {
            return nil
        }
    
        return link
    }
    
    func placeholder(in context: Context) -> LinkWidgetEntry {
        return LinkWidgetEntry.placeholder
    }
    
    func getSnapshot(in context: Context, completion: @escaping (LinkWidgetEntry) -> ()) {
        guard let widgetURL = context.widgetURL,
              let link = loadLink(from: widgetURL) else {
            let entry = LinkWidgetEntry.placeholder
            completion(entry)
            return
        }
    
        let entry = LinkWidgetEntry(date: Date(), link: link)
        completion(entry)
    }
    
    func getTimeline(in context: Context, completion: @escaping (Timeline<linkwidgetentry>) -> ()) {
        guard let widgetURL = context.widgetURL,
              let link = loadLink(from: widgetURL) else {
            let entry = LinkWidgetEntry.placeholder
            let timeline = Timeline(entries: [entry], policy: .atEnd)
            completion(timeline)
            return
        }
    
        let entry = LinkWidgetEntry(date: Date(), link: link)
        let timeline = Timeline(entries: [entry], policy: .atEnd)
        completion(timeline)
    }
    
    func getTimeline(in context: Context, for widgetConfiguration: ConfigurationIntent, completion: @escaping (Timeline<linkwidgetentry>) -> ()) {
        getTimeline(in: context, completion: completion)
    }
    

    }
    “`

Comments are closed.