When I run my Apple Watch app in Xcode I cannot get to the purchase flow. It will always say
Unable to purchase app Sign in with your apple id from apple watch app on your iPhone in a popup. I thought the situation would be different in TestFlight however the same is happening to me as well as my testers in TestFlight, despite the IAP already being approved by App Review.
Now how can I solve this issue, how can I make sure this won't lock users out of my app when I release this version to production?
Post
Replies
Boosts
Views
Activity
Users want to be able to launch my app from their watch face. I'm not interested in any dynamic updates etc., I literally just want to display my app's icon and be done with it.
Using WidgetKit I have written a class that resizes my app's icon to the TimelineProviderContext.displaySize and that works for 50% of the Watch faces. However, some watch faces require the icon to be black and white whilst on other watch faces you can use colors.
If you use a color icon on a watch face that expected a black-and-white (or alpha channel) image, it will just display a gray circle.
How am I supposed to handle the 2 different color schemes in the watch faces? I have checked for TimelineProviderContext.family.description now to see if I can differentiate it based on that however this leads to contradictions as well; it says accessoryCircular for the "Activity Digital" face (which supports color) but also for for "California" face (which does not support color). What other value can one check for in order to know whether the watch face allows a color icon?
I always hated setting up complications in watchOS, they couldn't have done it worse for developers who just want the icon to show up on the Watch face, not even providing a simple sample (I'm happy to get corrected on this) but with WidgetKit it seems impossible to achieve anything at all
I can't get the simplest Widget to work. I simply want to present the app's icon. It does what it's supposed to do in the simulator but on the actual device it only says "Please adopt containerBackground API" at the place where the Widget would have shown otherwise.
I found out that I am supposed to add .containerBackground(Color.clear, for: .widget) for no reason at all to make this work. However, when I do that, I just get a gray circle where the app icon was supposed to show. The png files should be fine because again on the simulator it works so I have no idea what I am doing wrong.
If someone can tell me what I'm doing wrong or if someone can share their Widget class that would be awesome - I literally only want to show my app's icon on the Watch face but I already fail at that, no idea why they made WidgetKit impossible to work with.
import SwiftUI
import WidgetKit
struct IconEntry: TimelineEntry {
let date = Date()
let icon: Image
}
struct IconProvider: TimelineProvider {
func placeholder(in context: Context) -> IconEntry {
return IconEntry(icon: getImageForContext(context, color: false))
}
func getSnapshot(in context: Context, completion: @escaping (IconEntry) -> ()) {
let entry = IconEntry(icon: getImageForContext(context, color: false))
completion(entry)
}
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
let entry = IconEntry(icon: getImageForContext(context, color: false))
let timeline = Timeline(entries: [entry], policy: .never)
completion(timeline)
}
}
struct IconWidget: Widget {
let kind: String = "IconWidget"
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: IconProvider()) { entry in
IconView()
.containerBackground(Color.clear, for: .widget)
}
.supportedFamilies([.accessoryCircular, .accessoryCorner, .accessoryRectangular])
.description(Text("Show the app's icon on a watch face."))
}
}
struct InlineWidget: Widget {
var body: some WidgetConfiguration {
StaticConfiguration(kind: "Inline", provider: IconProvider()) { entry in
Text("App Title")
.containerBackground(Color.clear, for: .widget)
}
.configurationDisplayName("App Title")
.description("Description")
.supportedFamilies([.accessoryInline])
}
}
func getImageForContext(_ context: TimelineProviderContext, color: Bool) -> Image {
var fileNameToUse = "iconColor.png"
if(!color) {
fileNameToUse = "iconAlpha.png"
}
let displaySize = context.displaySize
if(displaySize.width == 0 || displaySize.height == 0) {
return Image(uiImage: .init())
}
let uiImage = UIImage(imageLiteralResourceName: fileNameToUse).resizeImage(width: displaySize.width, height: displaySize.height)
let image = Image(uiImage: uiImage)
return image
}
struct IconView: View {
@Environment(\.widgetRenderingMode) var widgetRenderingMode
var body: some View {
imageFor(widgetRenderingMode)
.resizable()
.onAppear {
print("ICON VIEW \(widgetRenderingMode == .fullColor ? "COLOR" : "ALPHA")")
}
.containerBackground(Color.clear, for: .widget)
}
}
private func imageFor(_ widgetRenderingMode: WidgetRenderingMode) -> Image {
switch widgetRenderingMode {
case .fullColor:
return Image(uiImage: UIImage(imageLiteralResourceName: "iconColor.png"))
default:
return Image(uiImage: UIImage(imageLiteralResourceName: "iconAlpha.png"))
}
}
@main
struct IconBundle: WidgetBundle {
@WidgetBundleBuilder
var body: some Widget {
IconWidget()
InlineWidget()
}
}
extension UIImage {
func resizeImage(width: CGFloat, height: CGFloat) -> UIImage {
let edgeLength = min(width, height)
let newSize = CGSize(width: edgeLength, height: edgeLength)
UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0)
self.draw(in: CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height))
if let resizedImage = UIGraphicsGetImageFromCurrentImageContext() {
UIGraphicsEndImageContext()
return resizedImage
} else {
return UIImage()
}
}
}