Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question: using Media.Plugin with Fabulous #15

Open
gaelian opened this issue Feb 12, 2021 · 2 comments
Open

Question: using Media.Plugin with Fabulous #15

gaelian opened this issue Feb 12, 2021 · 2 comments

Comments

@gaelian
Copy link

gaelian commented Feb 12, 2021

Question / Discussion

I'm attempting to use Media.Plugin with Fabulous. Unfortunately, using the Xamarin.Essentials Media Picker would not be ideal as it doesn't yet support everything I need to do. Right now I'm just trying to figure out what compiles and works, I'm not at all sure this is the best way to do it, or if what I'm trying to do is actually possible. Currently I'm trying to get Media.Plugin to open up the photo gallery for picking photos on iOS, via the CrossMedia.Current.PickPhotosAsync method. Current problem is the photo gallery never appears.

In my page, I have a button that dispatches a SelectFromGallery message. This message is matched in update:

    let update msg model =
        match msg with
        | SelectFromGallery -> { model with IsBusy = true }, 
                               Cmd.ofMsg (processSelectFromGallery model |> Async.StartImmediateAsTask).Result, ExternalMsg.NoOp

processSelectFromGallery is called and looks like this:

    let processSelectFromGallery model =
        async {
            try
                match CrossMedia.Current.IsPickPhotoSupported with
                | false -> return SelectFromGalleryError
                | true ->
                    let mediaOptions = new PickMediaOptions (RotateImage = true)
                    let multiPickerOptions = new MultiPickerOptions (MaximumImagesCount = model.MaximumImagesCount)
                    let! currentPhotos = 
                            CrossMedia.Current.PickPhotosAsync (mediaOptions, multiPickerOptions) |> Async.AwaitTask

                    return SelectFromGallerySuccess currentPhotos
            with
            | _ -> return SelectFromGalleryError
        }

I have left out Async.SwitchToThreadPool() as it seems things should be happening on the UI thread otherwise an exception is thrown out from Media.Plugin code. I gathered from this issue that using Async.RunSynchronously silently "refuses" to run the code on the UI thread, hence why I've gone with Async.StartImmediateAsTask, as I was hoping to return the collection of photos with the SelectFromGallerySuccess message. I don't actually get to returning SelectFromGallerySuccess.

CrossMedia.Current.PickPhotosAsync is called and following the code execution inside Media.Plugin I can see Media.Plugin is constructing a UIViewController that looks like it is meant to show the photo gallery picker. But this UIViewController never displays. My hunch is that this is all happening orthogonal to the update loop and so it's not registering. CrossMedia.Current.PickPhotosAsync is meant to return a System.Collections.Generic.List<MediaFile> so the UIViewController construction and display all happens inside Media.Plugin code.

So my questions would be: is my hunch correct? Or am I going about this entirely wrong? Is using MediaPlugin or the Xamarin Essentials MediaPicker (which I assume works in a similar way to James Montemagno's MedaPlugin in so far as constructing its own view controller) with Fabulous actually possible? And if not, what would be the recommended alternate way forward for dealing with picking media from the photo gallery?

@TimLariviere
Copy link
Member

@gaelian Your code is good, except you need to use Cmd.ofAsyncMsg instead of Cmd.ofMsg.

let update msg model =
    match msg with
    | SelectFromGallery ->
        let model = { model with IsBusy = true }
        let cmd = Cmd.ofAsyncMsg (processSelectFromGallery model)
        model, cmd, ExternalMsg.NoOp

Fabulous will execute your async function as soon as update returns.
When the async call returns, Fabulous will dispatch the message returned.

But when you execute the async call yourself with Async.StartImmediateAsTask, there is chance to cause a deadlock as both Fabulous and Media.Plugin require the UI thread to run.

(Also, try to never ever use .Result in any code, even more so in UI Code. It's almost a guaranteed deadlock)

You can see this specific PickPhotosAsync in action in the FabulousContacts sample: https://github.com/TimLariviere/FabulousContacts/blob/e623e9a69c3c5912c5f82e443235751256f5cd6a/FabulousContacts/EditPage.fs#L198-L200

@gaelian
Copy link
Author

gaelian commented Feb 12, 2021

Thanks Tim, I'll give it another go with that guidance. I was looking for examples of MediaPlugin use across the Fabulous repo but I didn't look in FabulousContacts.

(Also, try to never ever use .Result in any code, even more so in UI Code. It's almost a guaranteed deadlock)

Oh yes, trust me, I'm trying! I've read James Montemagno's blog post on that. But was just trying to get something working for now. :P

@TimLariviere TimLariviere transferred this issue from fabulous-dev/Fabulous Jan 15, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants