Post

Replies

Boosts

Views

Activity

Reply to Catalyst + Manual Signing?
Using custom entitlements works by specifying separate entitlements files for the CODESIGNENTITLEMENTS key. I use xcconfig files for this: CODE_SIGN_ENTITLEMENTS = $(SRCROOT)/Config/MyApp.entitlements CODE_SIGN_ENTITLEMENTS = $(SRCROOT)/Config/MyApp_Mac.entitlements Of course, you can also do this in the build settings directly by specifying a platform conditional (Any macOS SDK)
Jun ’20
Reply to Crash in CarPlay: [CPInterfaceController clientPushedIllegalTemplateOfClass:]
Hm, the crash mentions the grid template being pushed but maybe it is because you try to set the template as the root template (instead of embedding it in a tab template)? Tab and list templates might be the only allowed root templates. Another problem might be that you exceed the number of allowed templates in the navigation stack (afaik 5 for audio apps), so it just happens to be a grid template that's pushed on a "full" stack.
Mar ’21
Reply to Can we get the CarPlay app development guidelines ?
You can already access the development guidelines here: https://developer.apple.com/carplay/documentation/CarPlay-App-Programming-Guide.pdf After you have been accepted you get an email with that link and tell you to watch the WWDC20 video “Accelerate Your App with CarPlay”. They don't provide any sample code. This was for a CarPlay Audio entitlement so it may be possible that you get sample code if you're building an Automaker App.
Mar ’21
Reply to Crash in CarPlay: [CPInterfaceController clientPushedIllegalTemplateOfClass:]
I have taken a look at CarPlaySupport.framework with Hopper to check when this exception is thrown (calls to clientPushedIllegalTemplateOfClass). It is called from _templateIsValidForCurrentEntitlement which check whether the template is valid at all for the current entitlement (it is) and then there is this aforementioned check if the top view controller is a CPSNowPlayingViewController the template to be pushed has to be a CPListTemplate. Can you check again, that there are no calls to pushTemplate containing anything but list templates?
Mar ’21
Reply to CPListItem Image
We have actually contacted Developer Technical Support for this and while the response wasn't exactly what we needed it pointed us in the right direction to come up with something that works for us. Note that there are still scaling issues when ALL items within a section have no detail text (but that's another issue). Here is what we use: extension CPListItem { convenience init(text: String?, detailText: String?, remoteImageUrl: URL?, placeholder: UIImage?, accessoryImage: UIImage? = nil, accessoryType: CPListItemAccessoryType = .none, imageOperation: RemoteImageOperation? = nil) { self.init(text: text, detailText: detailText, image: placeholder, accessoryImage: accessoryImage, accessoryType: accessoryType) setImageUrl(remoteImageUrl) } func setImageUrl(_ url: URL?) { guard let imageUrl = url else { return } Current.downloadImage(imageUrl) { image in guard let cropped = image?.cpCropSquareImage, let resized = cropped.resized(to: CPListItem.maximumImageSize), let carPlayImage = resized.carPlayImage else { return } DispatchQueue.main.async { [weak self] in self?.setImage(carPlayImage) } } } } extension UIImage { var carPlayImage: UIImage? { guard let traits = Current.interfaceController?.carTraitCollection else { return nil } let imageAsset = UIImageAsset() imageAsset.register(self, with: traits) return imageAsset.image(with: traits) } var cpCropSquareImage: UIImage { /* basic image cropping ... */ } func resized(to newSize: CGSize) -> UIImage? { /* basic image resizing ... */ } } The basic idea is to put the image in a UIImageAsset and take it out again which magically makes things work. Other than that we also crop it to a square and resize it to the maximum allowed image size. Our Current is like a dependency container where download is just a function to access our image cache/downloader and the interfaceController is the one we get when connecting the CarPlay scene (this is important for the native scale of the CarPlay display instead of the connected iOS device's display scale).
Nov ’21
Reply to App Intents Siri Phrases Localization
I couldn't get localization to work at all in the new AppIntents framework (I filed several radars). Almost nothing with parameters works and even some unparameterized phrases just show the localization key. I thought we could still use %@ like in normal localizations and it does work for the dialog in the perform function. i.e. return .result(dialog: "play.news.intent.success \(station.name)") gives me the correct result for the "play.news.intent.success %@" localization key
Aug ’22
Reply to App Intents Siri Phrases Localization
Thanks to @sowenjub I got it working. He wrote a really nice blog post that sums up everything (https://sowenjub.me/writes/localizing-app-shortcuts-with-app-intents/) The gist is that you need 2 localization files: an AppShortcuts.strings for the phrases and Localizable.strings for the rest (it is possible to have something other than that specific name but you have to provide the table name in every place you reference the Strings which is quite annoying) Then everything in code like the phrase "Key: Add a meal (.$content) to (.applicationName)" (implicitly looked up in AppShortcuts.strings) gets referenced via the following in your AppShortcuts.strings (i.e. english): "Key: Add a meal {content} to {applicationName}" = "Add a meal {content} to {applicationName}"; And stuff like app entitiy typeDisplayNames: static var typeDisplayName: LocalizedStringResource = "some.entitiy.type.name" (implicitly looked up in Localizable.strings; if you have an Alternative.strings file use something like .init("some.entitiy.type.name", table: "Alternative", locale: .autoupdatingCurrent, bundle: .main, comment: nil)) gets referenced via the following in your Localizable.strings (i.e. english): "some.entitiy.type.name" = "Some";
Sep ’22
Reply to Gestures in Live Activities
Hi Bastian. I'm sorry but that is special treatment for Apple's Live Activities. You can't have any interaction with the live activity that won't route you to your application. It's also currently not possible to show a marquee label if text is too long for your activity.
Nov ’22
Reply to How can I detect if my app was opened by tapping a Live Activity?
To quote the documentation If you don’t explicitly provide a deep link into your app with widgetURL(_:) or Link, the system launches your app and passes a NSUserActivity object to the scene(_:willContinueUserActivityWithType:) and scene(_:continue:) callbacks. Implement both callbacks and check whether the NSUserActivity object’s activityType is NSUserActivityTypeLiveActivity, and add code to open a screen in your app that fits the context of the active Live Activity. So if you omit widgetURL and Links you should be able to check for the incoming activity type to be NSUserActivityTypeLiveActivity. Maybe this type will also be set when using widgetURL and Links but I haven't tried that yet.
Nov ’22