0

Added a WidgetBuilder to my extension so I could include a second type of widget for my app. I keep getting this error in a popup when building it onto my device.

Details

SendProcessControlEvent:toPid: encountered an error: Error Domain=com.apple.dt.deviceprocesscontrolservice Code=8 "Failed to show Widget 'com.identifier' error: Error Domain=SBAvocadoDebuggingControllerErrorDomain Code=2 "Please specify the widget kind in the scheme's Environment Variables using the key '_XCWidgetKind' to be one of: 'QuoteWidget', 'RandomWidget'" UserInfo={NSLocalizedDescription=Please specify the widget kind in the scheme's Environment Variables using the key '_XCWidgetKind' to be one of: 'QuoteWidget', 'RandomWidget'}." UserInfo={NSLocalizedDescription=Failed to show Widget 'com.identifiert' error: Error Domain=SBAvocadoDebuggingControllerErrorDomain Code=2 "Please specify the widget kind in the scheme's Environment Variables using the key '_XCWidgetKind' to be one of: 'QuoteWidget', 'RandomWidget'" UserInfo={NSLocalizedDescription=Please specify the widget kind in the scheme's Environment Variables using the key '_XCWidgetKind' to be one of: 'QuoteWidget', 'RandomWidget'}., NSUnderlyingError=0x12f915290 {Error Domain=SBAvocadoDebuggingControllerErrorDomain Code=2 "Please specify the widget kind in the scheme's Environment Variables using the key '_XCWidgetKind' to be one of: 'QuoteWidget', 'RandomWidget'" UserInfo={NSLocalizedDescription=Please specify the widget kind in the scheme's Environment Variables using the key '_XCWidgetKind' to be one of: 'QuoteWidget', 'RandomWidget'}}}
Domain: DTXMessage
Code: 1
--

Heres the before and after code.

Before adding the second widget:

import WidgetKit
import SwiftUI
import Foundation

let testBook = Book(id: UUID(), name: "Name", author: "Author", genre: "Error", page: "0", total: "77")

public enum AppGroup: String {
  case Livre = "group.com.idetifier"

  public var containerURL: URL {
    switch self {
    case .Livre:
      return FileManager.default.containerURL(
      forSecurityApplicationGroupIdentifier: self.rawValue)!
    }
  }
}

struct Provider: TimelineProvider {
    func placeholder(in context: Context) -> SimpleEntry {
        SimpleEntry(date: Date(), book: testBook)
    }

    func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) {
        let entry = SimpleEntry(date: Date(), book: testBook)
        completion(entry)
    }

    func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
        let currentDate = Date()
        let entryDate = Calendar.current.date(byAdding: .minute, value: 30, to: currentDate)!
        let books: [Book] = self.load("list")
        let entry = SimpleEntry(date: currentDate, book: books.randomElement() ?? testBook)
        let timeline = Timeline(entries: [entry], policy: .after(entryDate))
        completion(timeline)
    
    }
    
    func load<T: Decodable>(_ filename: String) -> T {
        let groupDirectory = AppGroup.Livre.containerURL
        let groupURL = groupDirectory
            .appendingPathComponent(filename)
            .appendingPathExtension("json")

        return try! JSONDecoder().decode(T.self, from: Data(contentsOf: groupURL))
    }
}

struct SimpleEntry: TimelineEntry {
    let date: Date
    let book: Book
}

struct PlaceholderView: View {
    var body: some View {
        RandomWidgetView(book: testBook)
            .redacted(reason: .placeholder)
    }
}

struct LivreWidgetEntryView : View {
    
    @Environment(\.widgetFamily) var family
    
    var entry: Provider.Entry
    
    @ViewBuilder
    var body: some View {
        switch family {
        case .systemSmall:
            RandomWidgetView(book: entry.book, size: .small)
        case .systemMedium:
            RandomWidgetView(book: entry.book, size: .medium)
        case .systemLarge:
            RandomWidgetView(book: entry.book, size: .large)
        default:
            RandomWidgetView(book: entry.book, size: .small)
        }
    }
}

@main
struct LivreWidget: Widget {
    let kind: String = "LivreWidget"

    var body: some WidgetConfiguration {
        StaticConfiguration(kind: kind, provider: Provider()) { entry in
            LivreWidgetEntryView(entry: entry)
        }
        .configurationDisplayName("Progress of a random book")
        .description("This widget shows the progress from a random book in your Library")
        .supportedFamilies([.systemSmall, .systemMedium])
    }
}

and after adding the second widget:

import WidgetKit
import SwiftUI
import Foundation

@main
struct LivreWidgetBuilder: WidgetBundle {
    init() {}
    
    @WidgetBundleBuilder
    var body: some Widget {
        RandomWidget()
        QuoteWidget()
    }
}

let testBook = Book(id: UUID(), name: "Name", author: "Author", genre: "Error", page: "0", total: "77", quotes: ["Quote"])

public enum AppGroup: String {
  case Livre = "group.com.identifier"

  public var containerURL: URL {
    switch self {
    case .Livre:
      return FileManager.default.containerURL(
      forSecurityApplicationGroupIdentifier: self.rawValue)!
    }
  }
}

//MARK: Random book widget
struct RandomProvider: TimelineProvider {
    func placeholder(in context: Context) -> RandomSimpleEntry {
        RandomSimpleEntry(date: Date(), book: testBook)
    }

    func getSnapshot(in context: Context, completion: @escaping (RandomSimpleEntry) -> ()) {
        let entry = RandomSimpleEntry(date: Date(), book: testBook)
        completion(entry)
    }

    func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
        let currentDate = Date()
        let entryDate = Calendar.current.date(byAdding: .minute, value: 30, to: currentDate)!
        let books: [Book] = self.load("list")
        let entry = RandomSimpleEntry(date: currentDate, book: books.randomElement() ?? testBook)
        let timeline = Timeline(entries: [entry], policy: .after(entryDate))
        completion(timeline)
    
    }
    
    func load<T: Decodable>(_ filename: String) -> T {
        let groupDirectory = AppGroup.Livre.containerURL
        let groupURL = groupDirectory
            .appendingPathComponent(filename)
            .appendingPathExtension("json")

        return try! JSONDecoder().decode(T.self, from: Data(contentsOf: groupURL))
    }
}

struct RandomSimpleEntry: TimelineEntry {
    let date: Date
    let book: Book
}

struct RandomPlaceholderView: View {
    var body: some View {
        RandomWidgetView(book: testBook)
            .redacted(reason: .placeholder)
    }
}

struct RandomWidgetEntryView : View {
    
    @Environment(\.widgetFamily) var family
    
    var entry: RandomProvider.Entry
    
    @ViewBuilder
    var body: some View {
        switch family {
        case .systemSmall:
            RandomWidgetView(book: entry.book, size: .small)
        case .systemMedium:
            RandomWidgetView(book: entry.book, size: .medium)
        case .systemLarge:
            RandomWidgetView(book: entry.book, size: .large)
        default:
            RandomWidgetView(book: entry.book, size: .small)
        }
    }
}

struct RandomWidget: Widget {
    let kind: String = "RandomWidget"

    var body: some WidgetConfiguration {
        StaticConfiguration(kind: kind, provider: RandomProvider()) { entry in
            RandomWidgetEntryView(entry: entry)
        }
        .configurationDisplayName("Random book progress")
        .description("This widget shows the progress from a random book in your Library")
        .supportedFamilies([.systemSmall, .systemMedium])
    }
}

//MARK: Quote Widget
struct QuoteProvider: TimelineProvider {
    func placeholder(in context: Context) -> QuoteSimpleEntry {
        QuoteSimpleEntry(date: Date(), book: testBook)
    }

    func getSnapshot(in context: Context, completion: @escaping (QuoteSimpleEntry) -> ()) {
        let entry = QuoteSimpleEntry(date: Date(), book: testBook)
        completion(entry)
    }

    func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
        let currentDate = Date()
        let entryDate = Calendar.current.date(byAdding: .minute, value: 30, to: currentDate)!
        let books: [Book] = self.load("list")
        let entry = QuoteSimpleEntry(date: currentDate, book: books.randomElement() ?? testBook)
        let timeline = Timeline(entries: [entry], policy: .after(entryDate))
        completion(timeline)
    
    }
    
    func load<T: Decodable>(_ filename: String) -> T {
        let groupDirectory = AppGroup.Livre.containerURL
        let groupURL = groupDirectory
            .appendingPathComponent(filename)
            .appendingPathExtension("json")

        return try! JSONDecoder().decode(T.self, from: Data(contentsOf: groupURL))
    }
}

struct QuoteSimpleEntry: TimelineEntry {
    let date: Date
    let book: Book
}

struct QuotePlaceholderView: View {
    var body: some View {
        QuoteWidgetView(book: testBook)
            .redacted(reason: .placeholder)
    }
}

struct QuoteWidgetEntryView : View {
    
    @Environment(\.widgetFamily) var family
    
    var entry: QuoteProvider.Entry
    
    var body: some View {
        QuoteWidgetView(book: entry.book, size: .small)
    }
}

struct QuoteWidget: Widget {
    let kind: String = "QuoteWidget"

    var body: some WidgetConfiguration {
        StaticConfiguration(kind: kind, provider: QuoteProvider()) { entry in
            QuoteWidgetEntryView(entry: entry)
        }
        .configurationDisplayName("Quote widget")
        .description("This widget shows a random quote from a random book in your Library")
        .supportedFamilies([.systemMedium])
    }
}

The widgets seem to work, but I'm trying to find out why i'm getting this popup error.

Arnav Motwani
  • 707
  • 7
  • 26
  • Relevant thread on Apple dev forums: https://developer.apple.com/forums/thread/657599 – jnpdx May 20 '21 at 16:50
  • Other SO question dealing with this: https://stackoverflow.com/questions/63451273/failed-to-launch-home-screen-widget-in-ios-14-simulator – jnpdx May 20 '21 at 16:50

0 Answers0