Post not yet marked as solved
Project structure is:
App target + widget extension + widget intent extension
All share a common appgroup group.com.x.y and all file handling is done using
FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.x.y")
so that only the shared container is used.
Using the Main app target, a font "Chewy-Regular.ttf" is downloaded and saved to the shared AppGroup container.
Font can now be loaded via
CTFontManagerRegisterFontsForURL
and displayed in a Main App Text view
Text("Testing...").font(Font.custom("Chewy-Regular", size: 20))
Now add a Widgetkit widget instance that uses this font.
In 'getTimeLine() and getSnapShot() of IntentTimelineProvider we load the font again via CTFontManagerRegisterFontsForURL (this needs to happen again probably because widget runs in a separate process from the main app?).
On simulator, the widget will show the correct font.
BUT
On iPhone7 real device, the widget will show the 'redacted placeholder view'. It seems that something is crashing.
I see in the device console :
error 14:39:07.567120-0800 chronod No configuration found for configured widget identifier: D9BF75EE-4A04-441A-8C85-1507F7ECE379
fault 14:39:07.625600-0800 widgetxExtension -[EXSwiftUI_Subsystem beginUsing:withBundle:] unexpectedly called multiple times.
error 14:39:07.672733-0800 chronod Encountered an error reading the view archive for <private>; error: <private>
error 14:39:07.672799-0800 chronod [co.appevolve.onewidget.widgetx:widgetx:small:1536744920620481560@148.0/148.0/20.2] reload: could not decode view
error 14:39:07.674984-0800 kernel Sandbox: chronod(2128) deny(1) file-read-metadata /private/var/mobile/Containers/Shared/AppGroup/9B524570-1765-4C24-9E0C-15BC3982F0DC/downloadedFonts/Chewy/Chewy-Regular.ttf
error 14:39:07.675762-0800 kernel Sandbox: chronod(2128) deny(1) file-read-data /private/var/mobile/Containers/Shared/AppGroup/9B524570-1765-4C24-9E0C-15BC3982F0DC/downloadedFonts/Chewy/Chewy-Regular.ttf
error 14:39:07.708914-0800 chronod [u 8D2C83B3-A6CB-432E-A9D4-9BC8F7056B10:m (null)] [<private>(<private>)] Connection to plugin invalidated while in use.
fault 14:39:07.710284-0800 widgetxExtension -[EXSwiftUI_Subsystem beginUsing:withBundle:] unexpectedly called multiple times.
error 14:39:07.803468-0800 chronod Encountered an error reading the view archive for <private>; error: <private>
It seems that it's a permission issue, and the textview can't access the font file it needs when the widget is rendering.
Notes:
1) Font is definitely registered because I can see them in
for fontFamily in UIFont.familyNames {
for fontName in UIFont.fontNames(forFamilyName: fontFamily) {
print(fontName)
								...
in both the Main App target and the Widget Extension target
2) If I make make the font part of the app bundle and add to 'Fonts provided by application' , the are loaded absolutely fine in the Main App and the Widget on simulator and iPhone 7 real device.
3) I do see this error sometimes in the Widget extension target log, don't know if it's related.
widgetxExtension[1385:254599] [User Defaults] Couldn't read values in CFPrefsPlistSource<0x28375b880> (Domain: group.co.appevolve.onewidget, User: kCFPreferencesAnyUser, ByHost: Yes, Container: (null), Contents Need Refresh: Yes): Using kCFPreferencesAnyUser with a container is only allowed for System Containers, detaching from cfprefsd
4) I suspected something to do with app groups, so I tried to copy the font into the Widget Extension container and load from there, but had the same result.
Please help! Thank you.
I am developing an app with a widget available. Until recently the widget was being shown on both an iPhone and an iPad devices. Then I changed some logic in the widget, and now it only displays on the iPhone.
I also use the Xcode simulators in the development, and the widget is available on both iPhone and iPad simulators, with same code.
Summarizing the status for the widget.
Simulators
iPhone (13 Pro Max, iPhone 12) - OK
iPad Pro (12.9" 5th Gen) - OK
Devices
iPhone (11) - OK
iPad (Air 2) - Not available
I can attach the Widget extension to the debugger for the simulators and the iPhone device. However the process in not there when I use the iPad device, so cannot attach the widget code to the debugger. Even if I attach the widget extension by name to the debugger, it is as if it is not running.
I can run the widget extension directly on the iPad device (using an appropriate build scheme) and the details of the info displayed by the widget is the same as for the iPhone. So it is not running the widget on an iPad that is the problem.
It seems like the widget extension is not embedded in the app code when the app is loaded on to an iPad device.
Has anyone experienced anything similar?
Not necessarily with a widget, it could be with another framework extension.
I am trying to upload my app to AppStore using Xcode. The app uses widgets, and after archiving the app, I run the validation. It reports two errors.
Invalid directory. The bundle Payload/……/…WidgetExtension.appex is not contained in a correctly named directory. It should be under "Plugins".
CFBundleIdentifier Collision. There is more than one bundle with the CFBundleIdentifier value '…widget' under the iOS application.
I suspect that the two are linked issues, and that whilst trying to debug the widget in a simulator I may have duplicated any entry somewhere in the settings, but I am not clear where to look, as widget appears in several places, to fix the issue - info.plist, Build settings, General tab, other?
The widget also appears in the 'Products' list along with the app. I am not sure if the widget should be there, and if not how to remove it from the 'Products' list, but still make it available in the app.
Post not yet marked as solved
I'm using musickit on iOS 15 in my APP. I want to detect user change the player's play state when app enters the background.
Post not yet marked as solved
Hi, my App has two kind of widgets. On some iOS 15.4 and 15.4.1 devices, one widget works perfectly fine and another can't load data even in the Widgets Gallery. But for most devices the two widgets work both fine!!! My 15.4.1 iPhones and iPad can't reproduce the problem.
The problematic widget only display a redacted view:
The widget should look like:
Both of the widgets use Core Data to fetch data and shared UserDefaults to get user settings. The problematic widget is a task calendar, so it has some data but not very large.
I've try to collect crash log using TestFlight, but there's no crash logs.
I've implemented placeholder getSnapshot and getTimeline in the widget's IntentTimelineProvider.
I'm not using unwrap optionals in SwiftUI.
It's frustrating. My users love this widget but can't use it. Any idea why this problem happen?
Post not yet marked as solved
Anyone know how to solve this error?
I have no idea why it's happening because I'm not using NSFileHandle or anything like that at all, my widget is super simple: makes an API call, processes it with SwiftyJSON, and displays very simple data.
Thanks in advance
Post not yet marked as solved
much like the notes app, would be it be some way to lock apps on a home screen pages from moving around unintentionally to another home screen?
i’ve had several occasions where my phone screen apps get moved around by having the device in awkward positions or holding it a funny way (i.e getting out of a car in a tight space, while holding your phone).
i’m not sure whether this is or is not a feature yet, but, any insight would be appreciated!
Post not yet marked as solved
I am building an app with the WidgetKit extension (created using File > New > Target > Widget Extension). When I run the app scheme on my Mac, I can see my local app and add the widget in the widget sidebar using "Edit Widgets." However, when I build the app using the iOS scheme on my iPhone, I cannot add the widget to my phone, as it does not appear in the widget selection window. The app works perfectly fine and launches.
I am using an iPhone 13 pro on iOS 15.4.1.
Is this a recent bug? A couple of months ago on a different project, this was working.
Post not yet marked as solved
So I was watching the WWDC video [Add configuration and intelligence to you widget] (https://developer.apple.com/videos/play/wwdc2020/10194) and it seemed like there Xcode automatically generated the IntentHandler.swift file.
But I can't find the IntentHandler.swift file in my Xcode. Of course, I added Widget as target checking add intent configuration, and also added intent parameters too.
What I am doing wrong here? Or is that I Xcode doesn't automatically generate IntentHandler.swift file ?
Thanks
Post not yet marked as solved
I want a widget to use the same custom fonts that the containing app uses. I do not want multiple copies of the font files in the app bundle. I put the font files into a framework shared by the app and the widget extension. I have code to register the fonts from the framework bundle using CTFontManagerRegisterGraphicsFont. That works from the app, but it does not work from the widget extension because it requires UIKit. (At least I think that's why it doesn't seem to work from my widget extension.)
Widgets cannot run UIKit code. Is there a way to programmatically register the fonts in a widget? If not is there a way I can make a UIAppFonts Info.plist entry point to a framework?
Post not yet marked as solved
Our app added a widget extension. but some of devices can't search widget in Widget Gallery.
We found workaround.
Delete app
Reboot device
Install app
Why does this happen? What's a better solution?
Is this iOS bug? If iOS bug, please let know bug report link to us.
Post not yet marked as solved
The widget family obtained via @Environment(\.widgetFamily) is wrong when preview widgets in Xcode 13.3.
The problem can be reproduced by creating a default app project and a widget extension in Xcode 13.3.
Post not yet marked as solved
Hi,
I am working on a widget for my existing Mac app. The problem I am running into is that when I add a Link from the widget to the main app, the method 'openURLs' isn't called at all:
- (void) application: (NSApplication *)application openURLs:(NSArray<NSURL *> *)urls
So I'm not able to direct the app on how to best handle the widget tap.
I'm trying to work around that by trying to detect if a Link was selected. Something like this could work
Link("Test", destination: URL(string: "https://duckduckgo.com")!)
.environment(\.openURL, OpenURLAction { url in
print("---> testing link actioned")
return .systemAction
})
When I add this to the main app, it works fine. But when I add something like this to the widget, the completion handler isn't called (supposedly).
Can someone confirm if this was supposed to work inside a widget? Or is it only something that works from the main app?
I am trying to make a circular progress bar for my widget, I am using this code to do so. On the base app content view, everything comes out how I want it to. But when I try the same view on my widget, Circle().trim seems to not allow a drawing to happen and the circle disappears completely.
I have seen a couple of posts about this on the forums. I am currently running on Xcode 12.3
ZStack{
Color(red: 0, green: 0, blue: 0)
ZStack {
Circle()
.rotation(.degrees(112))
.trim(from:0.0, to: 0.87)
.stroke(style: StrokeStyle(lineWidth: 8.0, lineCap: .round))
.opacity(0.3)
.foregroundColor(Color.gray)
.frame(width: 120, height: 120, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/)
Circle()
.rotation(.degrees(112))
.trim(from:0.0, to: 0.2)
.stroke(style: StrokeStyle(lineWidth: 8.0, lineCap: .round, lineJoin: .round))
.foregroundColor(Color.white)
.frame(width: 120, height: 120, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/)
}
.padding()
}
The app uses Core Data + CloudKit. When the app gets launched WidgetKit file fetches correct entries from Core Data but when I add new entries to Core Data from the main app and call WidgetCenter.shared.reloadTimelines() updated entries don't get fetched and old data is displayed inside the widget.
Main app and Widget target have the same App group, iCloud capability enabled for both targets.
When I call WidgetCenter.shared.reloadTimelines() from the Main app Widget reloads (timer added to Widget view sets to zero) but fetch from Core Data doesn't return newly added entries. Then I restart the app and correct entries get fetched.
Why doesn't it fetch correct entries when WidgetCenter.shared.reloadTimelines() gets called and only works when app is relaunched?
Widget's code:
import WidgetKit
import SwiftUI
import CoreData
struct Provider: TimelineProvider {
		func placeholder(in context: Context) -> HabitsEntry {
				HabitsEntry(date: Date(), habitsCount: 0)
		}
		func getSnapshot(in context: Context, completion: @escaping (HabitsEntry) -> ()) {
				let entry = HabitsEntry(date: Date(), habitsCount: 0)
				completion(entry)
		}
		func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {				
				let managedObjectContext = Storage.viewContext
				let request = NSFetchRequest<NSFetchRequestResult>(entityName: "DailyHabit")
				let predicate = Storage.getCurrentDatePredicate()
				request.predicate = predicate
				var habits = 0
				do { habits = try managedObjectContext.count(for: request)	}
				catch let error as NSError {print ("Could not fetch \(error), \(error.userInfo)")}
				let entry = [HabitsEntry(date: Date(), habitsCount: habits)]
				let timeline = Timeline(entries: entry, policy: .never)
				completion(timeline)
		}
}
struct HabitsEntry: TimelineEntry {
		let date: Date
		var habitsCount: Int
}
struct HabitsHomeWidgetEntryView : View {
		var entry: Provider.Entry
		var body: some View {
			Text(entry.date, style: .timer)
			Text("There are \(entry.habitsCount) daily habits")
		}
}
@main
struct HabitsHomeWidget: Widget {
		let kind: String = "HabitsHomeWidget"
		var body: some WidgetConfiguration {
				StaticConfiguration(kind: kind, provider: Provider()) { entry in
						HabitsHomeWidgetEntryView(entry: entry)
				}
				.configurationDisplayName("My Widget")
				.description("This is an example widget.")
		}
}	
Here is the code for Core Data:
import UIKit
import CoreData
import WidgetKit
class Storage {
	
	static let shared = Storage()
	
	static var persistentContainer: NSPersistentContainer {
		return Storage.shared.persistentContainer
	}
	
	static var viewContext: NSManagedObjectContext {
		return persistentContainer.viewContext
	}
	
	lazy var persistentContainer: NSPersistentContainer = {
		let container: NSPersistentContainer
		
		let containerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: Constants.appGroupName)!
		let storeURL = containerURL.appendingPathComponent("NoRegrets.sqlite")
		let description = NSPersistentStoreDescription(url: storeURL)
		if isICloudContainerAvailable {
			container = NSPersistentCloudKitContainer(name: Constants.persistentContainerName)
		} else {
			container = NSPersistentContainer(name: Constants.persistentContainerName)
		}
		description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
		description.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
		description.shouldMigrateStoreAutomatically = true
		description.shouldInferMappingModelAutomatically = true
		let storeDescription = description
		
		container.persistentStoreDescriptions = [storeDescription]
		
		container.loadPersistentStores(completionHandler: { (storeDescription, error) in
			if let error = error as NSError? {
				return
			}
		})
		
		container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
		container.viewContext.automaticallyMergesChangesFromParent = true
		
		do {
			try container.viewContext.setQueryGenerationFrom(.current)
		} catch {
			print("Failed to pin viewContext to the current generation: \(error)")
		}
		return container
	}()
	
	var isICloudContainerAvailable: Bool {
		FileManager.default.ubiquityIdentityToken != nil
	}
	
	// MARK: - Core Data Saving support
	func saveContext() {
		let context = persistentContainer.viewContext
		if context.hasChanges {
			do {
				try context.save()
				
			} catch {
				let nserror = error as NSError
				print("Saving error: \(nserror)")
			}
		}
	}
}
// MARK: - Helper methods
extension Storage {
	func getCurrentUser() -> User? {
		let fetchRequest = User.createFetchRequest()
		fetchRequest.fetchLimit = 1
		fetchRequest.sortDescriptors = [NSSortDescriptor(key: "objectID", ascending: false)]
		
		let user = try? Storage.viewContext.fetch(fetchRequest).first
		return user
	}
	
	class func getCurrentDatePredicate() -> NSPredicate {
		var calendar = Calendar.current
		calendar.timeZone = NSTimeZone.local
		let dateFrom = calendar.startOfDay(for: Date())
		let dateTo = calendar.date(byAdding: .day, value: 1, to: dateFrom)
		let fromPredicate = NSPredicate(format: "date >= %@", dateFrom as NSDate)
		let toPredicate = NSPredicate(format: "date < %@", dateTo! as NSDate)
		return NSCompoundPredicate(andPredicateWithSubpredicates: [fromPredicate, toPredicate])
	}
}
Post not yet marked as solved
I'm trying to add a widget to my SwiftUI Application. I've made a Widget Extension in my App. Upon running the target in the simulator, this error appears in the console:
ClipperWidgetExtension[7343:594354] Fatal error: PlatformViewRepresentableAdaptor<ListRepresentable<SystemListDataSource<Never>, SelectionManagerBox<Never>>>: file /Library/Caches/com.apple.xbs/Sources/Chrono_Sim/Chrono-63.5/WidgetKit/SPI/WidgetViewArchive.swift, line 254
The line that Xcode brings my attention to is this:
completion(Timeline(entries: [SimpleEntry()], policy: .never))
The whole code for my timeline provider and entry:
struct Provider: TimelineProvider {
public typealias Entry = SimpleEntry
public func snapshot(with context: Context, completion: @escaping (SimpleEntry) -> ()) {
let entry = SimpleEntry()
completion(entry)
}
public func timeline(with context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
completion(Timeline(entries: [SimpleEntry()], policy: .never))
}
}
struct SimpleEntry: TimelineEntry {
var date: Date = Date()
}
Any help would be appreciated :)
Post not yet marked as solved
Hi,
I have added widgets to my iOS app and I would like to make this feature only accessible to "pro" users that have made a non-consumable in-app purchase.
Currently, I am doing the following: I store an "isUnlocked" property in the Keychain after the purchase is made
I read data to be displayed in the widget and here I also query the Keychain and store whether the widget is unlocked
I have no refresh policy, but only change the widget data on a significant time change
a different view is displayed when the app is locked
Some dummy code snippets:
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> Void) {
		let entry = readContents()
		let timeline = Timeline(entries: [entry], policy: .never)
		completion(timeline)
}
struct WidgetEntryView: View {
		let entry: Provider.Entry
@Environment(\.widgetFamily) var family
@ViewBuilder
var body: some View {
switch family {
case .systemSmall:
if !entry.isUnlocked {
LockedWidgetView()
} else if let event = entry.event {
SmallWidgetEventView(event: event)
} else {
NoDataWidgetView()
}
...
func applicationSignificantTimeChange(_ application: UIApplication) {
		if #available(iOS 14.0, *) {
				WidgetCenter.shared.reloadAllTimelines()
		}
...
However, 2 unexpected things happen: the view is refreshed intraday (not only at midnight i.e. at significant time change)
sometimes the LockedWidgetView is displayed.
Especially the latter is problematic, because it gives false information to a user that has already made the in-app purchase.
How can I achieve my goal of only displaying info when the user has made the in-app purchase?
Thanks in advance.
P.S. Although it would not have my preference, I would also find it acceptable if the widget is only shown as option to add once the purchase is made. In other words, I was considering changing the Widget itself:
struct MyWidget: Widget {
private var supportedFamilies: [WidgetFamily] = isUnlocked() ? [.systemSmall, .systemMedium] : []
but I believe I cannot re-initialise the widget from the app when the user makes the in-app purchase, because the only refresh option that I have is
WidgetCenter.shared.reloadAllTimelines()
SwiftUI previews don't seem to work in Xcode 12, beta 5, when using Firebase Analytics. The app builds and runs fine to simulator or device, but fails generating SwiftUI preview for a widget with the following message:
ld: in /Users/../.../Pods/FirebaseAnalytics/Frameworks/FIRAnalyticsConnector.framework/FIRAnalyticsConnector(FIRAnalyticsConnector_e321ed8e3db06efc9803f6c008e67a34.o), building for iOS Simulator, but linking in object file built for iOS, file '/Users/.../.../Pods/FirebaseAnalytics/Frameworks/FIRAnalyticsConnector.framework/FIRAnalyticsConnector' for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
It was working fine in beta 4. Anyone else seen this? Any ideas?
Is there a problem with an app having more than one widget extension?
I previously had two 'Today View' extensions that I'm replacing but after installing the app on device with two widget extensions, I'm only seeing the second one in the widget picker.
I see that there's WidgetBundle but my two widgets have very different datasets so the timeline stuff won't mesh.
Post not yet marked as solved
I have a CoreData database, and I have attempted to migrate it to an app group to share it between my main app and a widget extension. The database seems that seems to be fetched by the app and the widget seem to be completely separate from each other.
My CoreData code (persistence.swift)
import CoreData
struct PersistenceController {
static let shared = PersistenceController()
static var preview: PersistenceController = {
let result = PersistenceController(inMemory: true)
let viewContext = result.container.viewContext
let newUserData = Userdata(context: viewContext)
newUserData.hourly = 15.3
let newSession = Session(context: viewContext)
newSession.id = UUID()
newSession.start = Date()
newSession.end = Date()
do {
try viewContext.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
return result
}()
let container: NSPersistentContainer
init(inMemory: Bool = false) {
container = NSPersistentContainer(name: "FManger")
let storeURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.omaribrahim-uchicago.FManger")!.appendingPathComponent("FManger.sqlite")
var defaultURL: URL?
if let storeDescription = container.persistentStoreDescriptions.first, let url = storeDescription.url {
defaultURL = FileManager.default.fileExists(atPath: url.path) ? url : nil
}
if defaultURL == nil {
container.persistentStoreDescriptions = [NSPersistentStoreDescription(url: storeURL)]
}
container.loadPersistentStores(completionHandler: { [unowned container] (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
if let url = defaultURL, url.absoluteString != storeURL.absoluteString {
let coordinator = container.persistentStoreCoordinator
if let oldStore = coordinator.persistentStore(for: url) {
do {
try coordinator.migratePersistentStore(oldStore, to: storeURL, options: nil, withType: NSSQLiteStoreType)
} catch {
print(error.localizedDescription)
}
// delete old store
let fileCoordinator = NSFileCoordinator(filePresenter: nil)
fileCoordinator.coordinate(writingItemAt: url, options: .forDeleting, error: nil, byAccessor: { url in
do {
try FileManager.default.removeItem(at: url)
} catch {
print(error.localizedDescription)
}
})
}
}
})
}
}
My Widget data constructor and main function
import WidgetKit
import SwiftUI
import Intents
struct Provider: IntentTimelineProvider {
func placeholder(in context: Context) -> SimpleEntry {
SimpleEntry(date: Date(), data: .previewData, error: false, configuration: ConfigurationIntent())
}
func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) {
let entry = SimpleEntry(date: Date(), data: .previewData, error: false, configuration: configuration)
completion(entry)
}
func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
var entries: [SimpleEntry] = []
@FetchRequest(
sortDescriptors:[NSSortDescriptor(key: "start", ascending: false)],
animation: .default)
var sessions: FetchedResults<Session>
if (sessions.count > 0) {
let checkedIn = sessions[0].end != nil
let totalMinutes = totalMinutes(sessions: sessions)
var auxMinutes = 0.0
if (checkedIn) {
auxMinutes = timeBetween(fromDate: sessions[0].start!, toDate: Date())
}
let currentDate = Date()
for i in 0 ..< 5 {
let entryDate = Calendar.current.date(byAdding: .hour, value: i, to: currentDate)!
let cSession = fromMinutes(minutes: (Double(i) * 60) + auxMinutes)
let widgetData = SimpleEntry.PayData(currentSession: cSession,
grossTotal: totalMinutes * 15.3 + cSession,
paycheckDay: Date() + (7 * 24 * 60 * 60),
checkedIn: checkedIn)
let entry = SimpleEntry(date: entryDate,
data: widgetData,
error: false,
configuration: configuration)
entries.append(entry)
}
}
else {
let entry = SimpleEntry(date: Date(),
data: .error,
error: true,
configuration: configuration)
entries.append(entry)
}
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
}
@main
struct Widgets: Widget {
let kind: String = "Widgets"
let persistenceController = PersistenceController.shared
var body: some WidgetConfiguration {
IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: Provider()) { entry in
WidgetsEntryView(entry: entry)
.environment(\.managedObjectContext, persistenceController.container.viewContext)
}
.configurationDisplayName("Pay Tracker")
.description("Check your current shift's earning, total earnings, and the date of your next payment in a glance.")
}
}
I am pretty sure I did something wrong, but what is it?