Skip to content

Instantly share code, notes, and snippets.

@shaps80
Last active December 3, 2022 15:36
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shaps80/8ee53f7e3f07e3cf44f2331775edff98 to your computer and use it in GitHub Desktop.
Save shaps80/8ee53f7e3f07e3cf44f2331775edff98 to your computer and use it in GitHub Desktop.
A complete SwiftUI UIActivityViewController implementation.

Moved

This is now available in a dedicated package: ActivityView

Alternatively my SwiftUI Backports now includes a more complete implementation of ShareLink that's also more performant.

@shaps80
Copy link
Author

shaps80 commented Nov 27, 2020

Note how the ActivityView is added as a background rather than via a modifier. This is intentional to get around some interoperability issues between UIKit and SwiftUI.

Also, popovers on iPad will 'just work' and even nested ActivityView's work without issue 👍

@chbeer
Copy link

chbeer commented May 27, 2021

Thank you for this code. Nice way to present a UIActivityViewController... why it is that hard in SwiftUI is beyond me 🤷‍♂️...

I extended it to have a binding for the activityItems... here are my changes:

@Binding var activityItems: [Any]

init(isPresented: Binding<Bool>, items: Binding<[Any]>,
         activities: [UIActivity]? = nil,
func updateUIViewController(_ uiViewController: ActivityViewControllerWrapper, context: Context) {
        uiViewController.isPresented = $isPresented
        uiViewController.activityItems = $activityItems
final class ActivityViewControllerWrapper: UIViewController {

    var activityItems: Binding<[Any]>

let controller = UIActivityViewController(activityItems: activityItems.wrappedValue,

@shaps80
Copy link
Author

shaps80 commented Jun 3, 2021

Why did you need a binding @chbeer? A binding only makes sense if the controller for example can modify the items itself, which then needs to be reflected in the UI. But given this isn't possible with a share sheet directly I don't see the point of adding a binding here – unless I'm missing something?

@chbeer
Copy link

chbeer commented Jun 3, 2021

Maybe I‘m holding it wrong, but in my case the item got created in the controller, then given to the share sheet which is then shown. It‘s a generated URL in my case

@chbeer
Copy link

chbeer commented Jun 3, 2021

Ah, with controller you mean the controller that opens the share sheet 🤦‍♂️ You‘re right! No binding necessary (my old UIKit knowledge shows through)

@shaps80
Copy link
Author

shaps80 commented Jun 3, 2021

👍

@skywalkerlw
Copy link

This is great.
I noticed there's a short freezing behavior(1 or 2 second)s before showing activity view

@JetForMe
Copy link

JetForMe commented Jul 3, 2021

This is great.
I noticed there's a short freezing behavior(1 or 2 second)s before showing activity view

I think that's typical of UIActivityViewController. It certainly takes its time with me, regardless of app.

@shaps80
Copy link
Author

shaps80 commented Jul 5, 2021

@JetForMe / @skywalkerlw – that’s true but it can be considerably worse for larger assets for example. There are APIs for improving the performance here, checkout the docs on LinkPresentation and that should help you! 👍🏻

@lahariganti
Copy link

lahariganti commented Jul 10, 2021

@shaps80

  • Having bit of a trouble conforming to UIActivityItemSource and sending over metadata with this setup, any pointers? Most probably due to the way I have set things up
  • 5:52 (providing custom meta data) of https://developer.apple.com/videos/play/wwdc2019/262/ doesn't work (don't know what's missing in the setup)
var body: some View {
  GeometryReader { proxy in
    VStack() {
    }
    .onTapGesture {
      self.isPresented = true
      // err, may be I could fetch metadata here :/
    }
    .background(Group {
      switch self.type {
      case .link:
        ActivityView(isPresented: self.$isPresented, metadata: self.$currentMetadata or not required for custom meta data, items: [self or whatever items I want to send over for custom meta data])
      case .snapshot(let image, _):
         ActivityView(isPresented: self.$isPresented, metadata: self.$currentMetadata or not required for custom meta data, items: [self or whatever items I want to send over for custom meta data])
      }
    })
  }
}

@JetForMe
Copy link

JetForMe commented Aug 4, 2021

I'm finding I don't know the activityItems when the button is tapped. I have to make a network call to Firebase (ugh) to generate a link, and only then present the activity controller. At first I decided to make the activityItems an @autoclosure, and then pass them in as @State properties, but I don't want setting those to re-create my view. I’m not sure what the best thing to do here is. I don't really want to start generating the link until after the user taps the share button in the first place.

@shaps80
Copy link
Author

shaps80 commented Sep 7, 2021

@chbeer @skywalkerlw @lahariganti @JetForMe

Just to let you know I'm now officially supporting this (and many other) SwiftUI views via a dedicated organisation, SwiftUI+.

If you can create any Issues on the new repo then I can respond and track them appropriately. I will respond much more quickly and can help you more effectively.

Also note, the API has some improvements and more improvements are on the way.

In terms of performance Activity controller's do take a second to start and even longer in DEBUG builds, so if you run on device, then stop the debugger. Re-run the app from the icon on your device and re-test that way (as a user would) and I think you'll find the speed of the presentation is far better.

That being said, there are other things you may can do to improve, namely implementing the LinkPresentation APIs if you can, since those load asynchronously (alongside the presentation itself). But please create Issues on the new repo to continue this conversation if needed, as I will no longer be checking them here.

Thanks.

Shaps

@JetForMe
Copy link

JetForMe commented Sep 8, 2021

That's great, thanks!

@shaps80
Copy link
Author

shaps80 commented Sep 9, 2021

👍

@shaps80
Copy link
Author

shaps80 commented Oct 3, 2022

My new library, SwiftUI Backports now includes a more complete implementation of ShareLink that's also more performant.

This will be available in the next release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment