Post

Replies

Boosts

Views

Activity

Reply to P2P NFC BTC
There's another possibility: On the sending device, use CardSession to emulate an NFC TAG. On the receiving device, rely on the background NFC reader to forward the NFC data to your app (either with your own scheme, or with an Associated Domain). This works with iPhones and Androids. Only problem is the UI: On the sending device calling CardSession Apple enforces the bottom sheet "Hold near reader" - but in this case the sending device IS the "reader" or "terminal" offering the service, and the other person's phone is the one which should be held next to it.
3w
Reply to How to limit SwiftUI PasteButton for custom URLs?
The copying is always done by Mail.app or Messages.app or 3rd party messengers. Since the PasteButton closure is called after the user tapped on Paste, which of course is only possible if the PasteButton is enabled, we cannot disable it. So the user taps the PasteButton, and then we can examine the pasteBoard, and show an annoying alert that there is no talerURI... The user experience would be way better if we already knew that the pasteBoard contains a talerURI, and only then enabled the PasteButton. Feature request: add an optional filter for URL schemes, so the app can not only ask if there is some kind of URL in the pasteBoard, but also which kind. Some apps might only want "https:" URLs, others want only "mailto:", and we want only "taler:".
Topic: UI Frameworks SubTopic: SwiftUI
Sep ’26
Reply to How to stay narrow on an iPhone Duo?
Your UIKit link doesn't make sense for a SwiftUI app. Of course we could cope with the whole screen - showing the user our small bit of content and mostly empty space. But in our opinion it would be way better if we only used half of the Duo, and thus motivate the user to launch another app alongside. Since our app needs to run on iOS 15+, we will keep using Xcode 26 as long as possible for building and uploading.
Topic: SwiftUI SubTopic:
iPhone Duo SwiftUI Q&A
Sep ’26
Reply to Dynamic layouts with multi-line Text views
I tried your idea in my sample, but it doesn't work for me: The problem is, that I need to check whether the two-line text fits horizontally side-by-side to other views, and if not use a completely different layout where the same text has the whole width and the other views are below. See this sample: struct AmountRowView: View { let title: String let amount: String = "$ 10.00" // could vary, doesn't matter for the problem var body: some View { let titleView = Text(title) .multilineTextAlignment(.leading) let amountView = Text(amount) .border(.gray) let shortLayout = HStack(alignment: .center) { titleView.border(.green) Spacer(minLength: 2) amountView } // green = short let mediumLayout = HStack(alignment: .lastTextBaseline) { ZStack { // Measurer: reports the full unwrapped height at the proposed width. // Drives the fit check; never visible. Text(title) .fixedSize(horizontal: false, vertical: true) .hidden() .accessibilityHidden(true) // Renderer: the actual 2-line layout. Text(title) .lineLimit(2, reservesSpace: true) } .border(.red) // .lineLimit(2, reservesSpace: true) // .fixedSize(horizontal: false, vertical: true) Spacer(minLength: 2) amountView } // red = medium let longLayout = VStack(alignment: .leading) { titleView.border(.blue) HStack { Spacer() amountView } } // blue = long ViewThatFits(in: .horizontal) { shortLayout mediumLayout longLayout // <=== comment / uncomment this line, then change dynamic fontSize } } } struct ContentView: View { var body: some View { List { Section { // should fit in greenV when font <= AX2 AmountRowView(title: "Short title:") // should fit in redV from XXlarge to AX1 AmountRowView(title: "Medium title, more text:") // needs blueV with fontsize >= XXlarge, smaller font fits in redV AmountRowView(title: "Long title with way more text to show here:") } header: { Text("ViewThatFits") } } } } When you comment out longLayout (line 49), you'll see that mediumLayout fits for the middle row, but is not taken from ViewThatFits if longLayout is the 3rd option.
Topic: SwiftUI SubTopic:
SwiftUI Q&A
Jun ’26
Reply to Animate layout change
Adapting sample code for a "Flow Layout", I built this Layout, to be able to animate changes: (see code block below) struct MyLayout: Layout { func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize { guard !subviews.isEmpty else { return .zero } // ask subviews for their ideal size let sizes = subviews.map { $0.sizeThatFits(.unspecified) } var totalHeight: CGFloat = 0 var totalWidth: CGFloat = 0 var lineWidth: CGFloat = 0 var lineHeight: CGFloat = 0 for size in sizes { if lineWidth > 0 { if let propWidth = proposal.width { print("sizeThatFits: proposal.width: \(propWidth)") if lineWidth + horzSpacing + size.width > propWidth { totalHeight += lineHeight + vertSpacing // next line + spacing lineWidth = size.width lineHeight = size.height } else { lineWidth += size.width + horzSpacing // next item + spacing lineHeight = max(lineHeight, size.height) } } else { lineWidth += size.width + horzSpacing // next item + spacing lineHeight = max(lineHeight, size.height) } } else { // first subview, just place it lineWidth = size.width lineHeight = size.height } totalWidth = max(totalWidth, lineWidth) } totalHeight += lineHeight // first line, zero if there is no first return .init(width: totalWidth, height: totalHeight) } func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) { let sizes = subviews.map { $0.sizeThatFits(.unspecified) } var lineX = bounds.minX var lineY = bounds.minY var lineHeight: CGFloat = 0 for index in subviews.indices { let size = sizes[index] if lineX > bounds.minX { if let propWidth = proposal.width { print("placeSubviews: proposal.width: \(propWidth)") if lineX + size.width > propWidth { lineY += lineHeight + vertSpacing lineHeight = 0 lineX = bounds.minX } } } subviews[index].place(at: .init(x: lineX, y: lineY), anchor: .zero, proposal: ProposedViewSize(size) ) lineHeight = max(lineHeight, size.height) lineX += size.width + horzSpacing } } } But this is never called with the space available in proposal, thus it cannot calculate whether the circles would fit adjacent to the squares, or whether they should be moved to the next row. I wonder how the "Flow Layout" should work at all... Then I found this explanation about sizeThatFits: /// the parent view can call this method more than once with different proposals: /// .zero for the layout’s minimum size /// .infinity for the layout’s maximum size /// .unspecified for the layout’s ideal size which absolutely makes no sense IMHO. I need the exact (horizontal) space available as input to be able to calculate my layout, and place the subviews... Of course, I could return oneLine both as ideal and maximum size, and twoLines as minimum size, but it really depends on how much space there is which layout should be chosen, and whether I need to put twoLines into a (horizontal) scrollView or not. I remember having the same problem 15 years ago when implementing heightForTableViewCell: You tell me how much space I have horizontally, then I can return how much space I need vertically to place my content. Was tricky then, seems still not easy now :-( The sample code Apple provides for layout: "Composing custom layouts with SwiftUI" is completely useless. sizeThatFits should measure the box around the circle layout where the Avatars are placed, but it always returns the same size since that doesn't change at all, only the subview placement changes based on the user input. So, any tips how I could solve my animation problem?
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Mar ’25
Reply to UserDefaults.didChangeNotification not firing
OK, got it running: extension UserDefaults { private static let myBoolKey = "myBool" @objc dynamic var myBool: Bool { get { bool(forKey: UserDefaults.myBoolKey) } set { set(newValue, forKey: UserDefaults.myBoolKey) } } } (note that the value of the key "myBool" MUST BE identical to the name of the dynamic myBool, otherwise it doesn't work) and then call it in the init function of the (singleton) class: self.myBoolObservation = UserDefaults.standard.observe(\.myBool, options: [.new, .old,.prior,.initial]) { [weak self](_, _) in self?.myBool = UserDefaults.standard.myBool } However, this works only once on iOS 18, or rather only while the app keeps running. When you kill the app and launch it again, the closure is never called again. On iOS 17 I can quit and relaunch my app, and this code continues to work fine... Thus, still the problem of the thread starter (aolguin) -and mine too- is unsolved: since iOS 18 we don't get notifications in the app when the user changes app settings in Settings->Apps->myApp.
Topic: App & System Services SubTopic: General Tags:
Mar ’25
Reply to UserDefaults.didChangeNotification not firing
Hi Quinn, sadly this doesn't work for my SwiftUI iOS app. I added the startObserving() routine to my controller class, and called it from init, but it neither triggers the print(change) closure when I switch to iOS Settings->Apps->myApp and toggle the Toggle, nor when I return back to my app... Is this because the UserDefaults property is a string and my Settings.bundle Root.plist defines a PSToggleSwitchSpecifier? How can I observe a Toggle (instead of a string)?
Topic: App & System Services SubTopic: General Tags:
Mar ’25
Reply to Share arbitrary struct from HostApp to Extension and get arbitrary result back
Third, I played around with the NSExtensionActivationRule. While I can easily hide my extension (and thus the ContainingApp) from being shown in the ShareSheet, I cannot prevent Freeform and Threema in the application row, and “New Quick Note”, "Save to Files”, and “Airdrop” being shown when the HostApp opens the ShareSheet: How can the HostApp limit the possible share targets to just my ActionExtension and nothing else?
Topic: App & System Services SubTopic: General Tags:
Aug ’24
Reply to Share arbitrary struct from HostApp to Extension and get arbitrary result back
Second, SwiftUIs ShareLink is able to use the transferRepresentation of my ShareInput and ShareResult structs. I can call itemProvider.loadTransferable to extract my data. However, when using an ItemProvider for NSItemProviderWriting, NSItemProviderReading for the UIActivityItemsConfiguration, the transferred item is not a ShareInput or ShareResult struct, but the ItemProvider as wrapper around my struct. Probably because the transferred item must inherit from NSObject. Thus I cannot use UTType.shareInput.identifier, but must export and import the wrapper UTType.inputItemSource.identifier instead. While this works, it is ugly! Is there a possibility to transfer a struct with a UIActivityItemsConfiguration, or do I need to convert my ShareInput and ShareResult structs into classes, and then add NSItemProviderWriting, NSItemProviderReading with an extension? How does ShareLink do this? It transfers the struct (which is no descendant of NSObject) and not a wrapper...
Topic: App & System Services SubTopic: General Tags:
Aug ’24
Reply to Share arbitrary struct from HostApp to Extension and get arbitrary result back
Hi dts, using your sample code, I was able to transfer my ShareInput struct from the Host-App to my extension and the ShareResult struct back to the Host-App. See the sample project at github. If I defined a SharingExtension, then returnedItems was nil. I only got something back in returnedItems when I defined an ActionExtension - even though it had the exact same code... But there are still some problems: Your sample used NSItemProviderWriting, NSItemProviderReading for the ItemSource. How can I add a preview image and text (so users know what they’re sharing)? With ShareLink, I can specify a SharePreview: let previewTitle = "Choose the ContainingApp" let image = UIImage(systemName: "arrowshape.right.fill") let preview = SharePreview(previewTitle, image: Image(uiImage: image!)) ShareLink("ShareLink", item: shareInput, preview: preview) and get this: but since ShareLink cannot receive data sent back from the extension, I need to use a UIActivityViewController with completionWithItemsHandler: let itemSource = InputItemSource(shareInput: shareInput, previewTitle: previewTitle, previewImage: image!) let configuration = UIActivityItemsConfiguration(objects: [itemSource]) ActivityViewController(configuration: configuration) .presentationDetents([.medium]) As you can see, there’s no preview image and text: I tried to extend the ItemSource class to the UIActivityItemSource protocol: extension InputItemSource: UIActivityItemSource { func activityViewController(_ activityViewController: UIActivityViewController, thumbnailImageForActivityType activityType: UIActivity.ActivityType?, suggestedSize size: CGSize ) -> UIImage? { previewImage.image } func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any { previewTitle } and even added func activityViewControllerLinkMetadata(_ activityViewController: UIActivityViewController) -> LPLinkMetadata? { let metaData = LPLinkMetadata() metaData.title = previewTitle metaData.imageProvider = NSItemProvider(object: previewImage.image) return metaData } but this didn’t work - the share view still doesn’t show a preview. How can I add a preview image and text (so users know what they’re sharing)?
Topic: App & System Services SubTopic: General Tags:
Aug ’24
Reply to Share arbitrary struct from HostApp to Extension and get arbitrary result back
And finally, how do I transfer the ShareResult back to the HostApp? ShareLink has no return path, or does it? And this again will need an encoding conversion in the ShareExtension and a decoding conversion in the HostApp. How do I do that? As far as I'm aware, you can't. You could use alternative data sharing methods such as a shared App groups container. No, I can't. As I wrote before, the host app(s) will be build by a 3rd party. I definitely need to transfer the data back with the sharing call. If ShareLink cannot do that, then I need to fall back to UIActivityViewController with completionHandler - or do you have another suggestion?
Topic: App & System Services SubTopic: General Tags:
Jul ’24
Reply to Share arbitrary struct from HostApp to Extension and get arbitrary result back
Hi Emmanuel, thanks for your answers. In the host target, define "com.fesh.shareInput" as an exported type identifier But this is NOT defined by the host (there can and will be dozens of different host apps using my ShareExtension in the future). The user may have multiple of those 3rd party apps, and they could have a different idea what the data looks like. The real source of truth is only my ShareExtension. So why should I (or rather the 3rd party developers that want to use my extension - I am just building the sample for them) define that struct as exported type? Is this really necessary? In my experience it makes no difference for the ShareSheet whether the host defines it as export or import...
Topic: App & System Services SubTopic: General Tags:
Jul ’24
Reply to P2P NFC BTC
There's another possibility: On the sending device, use CardSession to emulate an NFC TAG. On the receiving device, rely on the background NFC reader to forward the NFC data to your app (either with your own scheme, or with an Associated Domain). This works with iPhones and Androids. Only problem is the UI: On the sending device calling CardSession Apple enforces the bottom sheet "Hold near reader" - but in this case the sending device IS the "reader" or "terminal" offering the service, and the other person's phone is the one which should be held next to it.
Replies
Boosts
Views
Activity
3w
Reply to How to limit SwiftUI PasteButton for custom URLs?
The copying is always done by Mail.app or Messages.app or 3rd party messengers. Since the PasteButton closure is called after the user tapped on Paste, which of course is only possible if the PasteButton is enabled, we cannot disable it. So the user taps the PasteButton, and then we can examine the pasteBoard, and show an annoying alert that there is no talerURI... The user experience would be way better if we already knew that the pasteBoard contains a talerURI, and only then enabled the PasteButton. Feature request: add an optional filter for URL schemes, so the app can not only ask if there is some kind of URL in the pasteBoard, but also which kind. Some apps might only want "https:" URLs, others want only "mailto:", and we want only "taler:".
Topic: UI Frameworks SubTopic: SwiftUI
Replies
Boosts
Views
Activity
Sep ’26
Reply to How to stay narrow on an iPhone Duo?
Your UIKit link doesn't make sense for a SwiftUI app. Of course we could cope with the whole screen - showing the user our small bit of content and mostly empty space. But in our opinion it would be way better if we only used half of the Duo, and thus motivate the user to launch another app alongside. Since our app needs to run on iOS 15+, we will keep using Xcode 26 as long as possible for building and uploading.
Topic: SwiftUI SubTopic:
iPhone Duo SwiftUI Q&A
Replies
Boosts
Views
Activity
Sep ’26
Reply to Dynamic layouts with multi-line Text views
I tried your idea in my sample, but it doesn't work for me: The problem is, that I need to check whether the two-line text fits horizontally side-by-side to other views, and if not use a completely different layout where the same text has the whole width and the other views are below. See this sample: struct AmountRowView: View { let title: String let amount: String = "$ 10.00" // could vary, doesn't matter for the problem var body: some View { let titleView = Text(title) .multilineTextAlignment(.leading) let amountView = Text(amount) .border(.gray) let shortLayout = HStack(alignment: .center) { titleView.border(.green) Spacer(minLength: 2) amountView } // green = short let mediumLayout = HStack(alignment: .lastTextBaseline) { ZStack { // Measurer: reports the full unwrapped height at the proposed width. // Drives the fit check; never visible. Text(title) .fixedSize(horizontal: false, vertical: true) .hidden() .accessibilityHidden(true) // Renderer: the actual 2-line layout. Text(title) .lineLimit(2, reservesSpace: true) } .border(.red) // .lineLimit(2, reservesSpace: true) // .fixedSize(horizontal: false, vertical: true) Spacer(minLength: 2) amountView } // red = medium let longLayout = VStack(alignment: .leading) { titleView.border(.blue) HStack { Spacer() amountView } } // blue = long ViewThatFits(in: .horizontal) { shortLayout mediumLayout longLayout // <=== comment / uncomment this line, then change dynamic fontSize } } } struct ContentView: View { var body: some View { List { Section { // should fit in greenV when font <= AX2 AmountRowView(title: "Short title:") // should fit in redV from XXlarge to AX1 AmountRowView(title: "Medium title, more text:") // needs blueV with fontsize >= XXlarge, smaller font fits in redV AmountRowView(title: "Long title with way more text to show here:") } header: { Text("ViewThatFits") } } } } When you comment out longLayout (line 49), you'll see that mediumLayout fits for the middle row, but is not taken from ViewThatFits if longLayout is the 3rd option.
Topic: SwiftUI SubTopic:
SwiftUI Q&A
Replies
Boosts
Views
Activity
Jun ’26
Reply to Animate layout change
Adapting sample code for a "Flow Layout", I built this Layout, to be able to animate changes: (see code block below) struct MyLayout: Layout { func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize { guard !subviews.isEmpty else { return .zero } // ask subviews for their ideal size let sizes = subviews.map { $0.sizeThatFits(.unspecified) } var totalHeight: CGFloat = 0 var totalWidth: CGFloat = 0 var lineWidth: CGFloat = 0 var lineHeight: CGFloat = 0 for size in sizes { if lineWidth > 0 { if let propWidth = proposal.width { print("sizeThatFits: proposal.width: \(propWidth)") if lineWidth + horzSpacing + size.width > propWidth { totalHeight += lineHeight + vertSpacing // next line + spacing lineWidth = size.width lineHeight = size.height } else { lineWidth += size.width + horzSpacing // next item + spacing lineHeight = max(lineHeight, size.height) } } else { lineWidth += size.width + horzSpacing // next item + spacing lineHeight = max(lineHeight, size.height) } } else { // first subview, just place it lineWidth = size.width lineHeight = size.height } totalWidth = max(totalWidth, lineWidth) } totalHeight += lineHeight // first line, zero if there is no first return .init(width: totalWidth, height: totalHeight) } func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) { let sizes = subviews.map { $0.sizeThatFits(.unspecified) } var lineX = bounds.minX var lineY = bounds.minY var lineHeight: CGFloat = 0 for index in subviews.indices { let size = sizes[index] if lineX > bounds.minX { if let propWidth = proposal.width { print("placeSubviews: proposal.width: \(propWidth)") if lineX + size.width > propWidth { lineY += lineHeight + vertSpacing lineHeight = 0 lineX = bounds.minX } } } subviews[index].place(at: .init(x: lineX, y: lineY), anchor: .zero, proposal: ProposedViewSize(size) ) lineHeight = max(lineHeight, size.height) lineX += size.width + horzSpacing } } } But this is never called with the space available in proposal, thus it cannot calculate whether the circles would fit adjacent to the squares, or whether they should be moved to the next row. I wonder how the "Flow Layout" should work at all... Then I found this explanation about sizeThatFits: /// the parent view can call this method more than once with different proposals: /// .zero for the layout’s minimum size /// .infinity for the layout’s maximum size /// .unspecified for the layout’s ideal size which absolutely makes no sense IMHO. I need the exact (horizontal) space available as input to be able to calculate my layout, and place the subviews... Of course, I could return oneLine both as ideal and maximum size, and twoLines as minimum size, but it really depends on how much space there is which layout should be chosen, and whether I need to put twoLines into a (horizontal) scrollView or not. I remember having the same problem 15 years ago when implementing heightForTableViewCell: You tell me how much space I have horizontally, then I can return how much space I need vertically to place my content. Was tricky then, seems still not easy now :-( The sample code Apple provides for layout: "Composing custom layouts with SwiftUI" is completely useless. sizeThatFits should measure the box around the circle layout where the Avatars are placed, but it always returns the same size since that doesn't change at all, only the subview placement changes based on the user input. So, any tips how I could solve my animation problem?
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
Boosts
Views
Activity
Mar ’25
Reply to UserDefaults.didChangeNotification not firing
OK, got it running: extension UserDefaults { private static let myBoolKey = "myBool" @objc dynamic var myBool: Bool { get { bool(forKey: UserDefaults.myBoolKey) } set { set(newValue, forKey: UserDefaults.myBoolKey) } } } (note that the value of the key "myBool" MUST BE identical to the name of the dynamic myBool, otherwise it doesn't work) and then call it in the init function of the (singleton) class: self.myBoolObservation = UserDefaults.standard.observe(\.myBool, options: [.new, .old,.prior,.initial]) { [weak self](_, _) in self?.myBool = UserDefaults.standard.myBool } However, this works only once on iOS 18, or rather only while the app keeps running. When you kill the app and launch it again, the closure is never called again. On iOS 17 I can quit and relaunch my app, and this code continues to work fine... Thus, still the problem of the thread starter (aolguin) -and mine too- is unsolved: since iOS 18 we don't get notifications in the app when the user changes app settings in Settings->Apps->myApp.
Topic: App & System Services SubTopic: General Tags:
Replies
Boosts
Views
Activity
Mar ’25
Reply to UserDefaults.didChangeNotification not firing
Hi Quinn, sadly this doesn't work for my SwiftUI iOS app. I added the startObserving() routine to my controller class, and called it from init, but it neither triggers the print(change) closure when I switch to iOS Settings->Apps->myApp and toggle the Toggle, nor when I return back to my app... Is this because the UserDefaults property is a string and my Settings.bundle Root.plist defines a PSToggleSwitchSpecifier? How can I observe a Toggle (instead of a string)?
Topic: App & System Services SubTopic: General Tags:
Replies
Boosts
Views
Activity
Mar ’25
Reply to Share arbitrary struct from HostApp to Extension and get arbitrary result back
Third, I played around with the NSExtensionActivationRule. While I can easily hide my extension (and thus the ContainingApp) from being shown in the ShareSheet, I cannot prevent Freeform and Threema in the application row, and “New Quick Note”, "Save to Files”, and “Airdrop” being shown when the HostApp opens the ShareSheet: How can the HostApp limit the possible share targets to just my ActionExtension and nothing else?
Topic: App & System Services SubTopic: General Tags:
Replies
Boosts
Views
Activity
Aug ’24
Reply to Share arbitrary struct from HostApp to Extension and get arbitrary result back
Second, SwiftUIs ShareLink is able to use the transferRepresentation of my ShareInput and ShareResult structs. I can call itemProvider.loadTransferable to extract my data. However, when using an ItemProvider for NSItemProviderWriting, NSItemProviderReading for the UIActivityItemsConfiguration, the transferred item is not a ShareInput or ShareResult struct, but the ItemProvider as wrapper around my struct. Probably because the transferred item must inherit from NSObject. Thus I cannot use UTType.shareInput.identifier, but must export and import the wrapper UTType.inputItemSource.identifier instead. While this works, it is ugly! Is there a possibility to transfer a struct with a UIActivityItemsConfiguration, or do I need to convert my ShareInput and ShareResult structs into classes, and then add NSItemProviderWriting, NSItemProviderReading with an extension? How does ShareLink do this? It transfers the struct (which is no descendant of NSObject) and not a wrapper...
Topic: App & System Services SubTopic: General Tags:
Replies
Boosts
Views
Activity
Aug ’24
Reply to Share arbitrary struct from HostApp to Extension and get arbitrary result back
Hi dts, using your sample code, I was able to transfer my ShareInput struct from the Host-App to my extension and the ShareResult struct back to the Host-App. See the sample project at github. If I defined a SharingExtension, then returnedItems was nil. I only got something back in returnedItems when I defined an ActionExtension - even though it had the exact same code... But there are still some problems: Your sample used NSItemProviderWriting, NSItemProviderReading for the ItemSource. How can I add a preview image and text (so users know what they’re sharing)? With ShareLink, I can specify a SharePreview: let previewTitle = "Choose the ContainingApp" let image = UIImage(systemName: "arrowshape.right.fill") let preview = SharePreview(previewTitle, image: Image(uiImage: image!)) ShareLink("ShareLink", item: shareInput, preview: preview) and get this: but since ShareLink cannot receive data sent back from the extension, I need to use a UIActivityViewController with completionWithItemsHandler: let itemSource = InputItemSource(shareInput: shareInput, previewTitle: previewTitle, previewImage: image!) let configuration = UIActivityItemsConfiguration(objects: [itemSource]) ActivityViewController(configuration: configuration) .presentationDetents([.medium]) As you can see, there’s no preview image and text: I tried to extend the ItemSource class to the UIActivityItemSource protocol: extension InputItemSource: UIActivityItemSource { func activityViewController(_ activityViewController: UIActivityViewController, thumbnailImageForActivityType activityType: UIActivity.ActivityType?, suggestedSize size: CGSize ) -> UIImage? { previewImage.image } func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any { previewTitle } and even added func activityViewControllerLinkMetadata(_ activityViewController: UIActivityViewController) -> LPLinkMetadata? { let metaData = LPLinkMetadata() metaData.title = previewTitle metaData.imageProvider = NSItemProvider(object: previewImage.image) return metaData } but this didn’t work - the share view still doesn’t show a preview. How can I add a preview image and text (so users know what they’re sharing)?
Topic: App & System Services SubTopic: General Tags:
Replies
Boosts
Views
Activity
Aug ’24
Reply to Share arbitrary struct from HostApp to Extension and get arbitrary result back
And finally, how do I transfer the ShareResult back to the HostApp? ShareLink has no return path, or does it? And this again will need an encoding conversion in the ShareExtension and a decoding conversion in the HostApp. How do I do that? As far as I'm aware, you can't. You could use alternative data sharing methods such as a shared App groups container. No, I can't. As I wrote before, the host app(s) will be build by a 3rd party. I definitely need to transfer the data back with the sharing call. If ShareLink cannot do that, then I need to fall back to UIActivityViewController with completionHandler - or do you have another suggestion?
Topic: App & System Services SubTopic: General Tags:
Replies
Boosts
Views
Activity
Jul ’24
Reply to Share arbitrary struct from HostApp to Extension and get arbitrary result back
Hi Emmanuel, thanks for your answers. In the host target, define "com.fesh.shareInput" as an exported type identifier But this is NOT defined by the host (there can and will be dozens of different host apps using my ShareExtension in the future). The user may have multiple of those 3rd party apps, and they could have a different idea what the data looks like. The real source of truth is only my ShareExtension. So why should I (or rather the 3rd party developers that want to use my extension - I am just building the sample for them) define that struct as exported type? Is this really necessary? In my experience it makes no difference for the ShareSheet whether the host defines it as export or import...
Topic: App & System Services SubTopic: General Tags:
Replies
Boosts
Views
Activity
Jul ’24