Thursday, October 11, 2018

Using UIDocumentPickerViewController in Xamarin forms as a dependency service

Leave a Comment

I'm using Xamarin forms and writing a dependency service for the following objectives :

  1. Open iOS files app. (UIDocumentPickerViewController )

  2. Select any kind of a document.

  3. Copy that document into my application Documents directory. (For app access)

  4. Show that document into my application by storing its path into my SQLite DB.

What I am trying to do here is call the Files app from my application on an Entry click and the click event seems to be working well my dependency service calls perfectly but now when I try to use the UIDocumentPickerViewController I am unable to get View controller context in my dependency service to call the PresentViewController method. Now I know about the xamarin forms context but I don't know if it will work here and I don't even know if it would be a smart idea to use it as it has already been marked as obsolete and since I am not from the iOS background, I don't know what would be the right solution for it.

My code is as follows :

public class DocumentPickerRenderer : IDocumentPicker {     public object PickFile()     {         var docPicker = new UIDocumentPickerViewController(new string[] { UTType.Data, UTType.Content }, UIDocumentPickerMode.Import);         docPicker.WasCancelled += (sender, wasCancelledArgs) =>         {         };         docPicker.DidPickDocumentAtUrls += (object sender, UIDocumentPickedAtUrlsEventArgs e) =>         {             Console.WriteLine("url = {0}", e.Urls[0].AbsoluteString);             //bool success = await MoveFileToApp(didPickDocArgs.Url);             var success = true;             string filename = e.Urls[0].LastPathComponent;             string msg = success ? string.Format("Successfully imported file '{0}'", filename) : string.Format("Failed to import file '{0}'", filename);             var alertController = UIAlertController.Create("import", msg, UIAlertControllerStyle.Alert);             var okButton = UIAlertAction.Create("OK", UIAlertActionStyle.Default, (obj) =>             {                 alertController.DismissViewController(true, null);             });             alertController.AddAction(okButton);             PresentViewController(alertController, true, null);         };         PresentViewController(docPicker, true, null);     } } 

My questions:

  1. Is my methodology correct for picking files?

  2. what will be the object that I will be getting as a callback from a file selection and how will I get the callback?

  3. Is there any other way or something available for xamarin forms, some guide or something that allows me to pick documents from my native file systems and gives a brief on how to handle it in both ios and android?

3 Answers

Answers 1

Hello Guys, You can use following code for picking any type of documents to mention in code using iOS Devices-


use follwing interface:

public interface IMedia {  Task<string> OpenDocument();  }    public Task<string> OpenDocument()     {          var task = new TaskCompletionSource<string>();         try         {             OpenDoc(GetController(), (obj) =>             {                 if (obj == null)                 {                     task.SetResult(null);                     return;                 }                 var aa = obj.AbsoluteUrl;                 task.SetResult(aa.Path);             });         }         catch (Exception ex)         {             task.SetException(ex);         }         return task.Task;     }      static Action<NSUrl> _callbackDoc;      public static void OpenDoc(UIViewController parent, Action<NSUrl> callback)     {         _callbackDoc = callback;         var version = UIDevice.CurrentDevice.SystemVersion;         int verNum = 0;         Int32.TryParse(version.Substring(0, 2), out verNum);          var allowedUTIs = new string[]         {         UTType.UTF8PlainText,         UTType.PlainText,         UTType.RTF,         UTType.PNG,         UTType.Text,         UTType.PDF,         UTType.Image,         UTType.Spreadsheet,         "com.microsoft.word.doc",         "org.openxmlformats.wordprocessingml.document",         "com.microsoft.powerpoint.ppt",         "org.openxmlformats.spreadsheetml.sheet",         "org.openxmlformats.presentationml.presentation",         "com.microsoft.excel.xls",          };          // Display the picker         var pickerMenu = new UIDocumentMenuViewController(allowedUTIs, UIDocumentPickerMode.Import);         pickerMenu.DidPickDocumentPicker += (sender, args) =>         {             if (verNum < 11)             {                 args.DocumentPicker.DidPickDocument += (sndr, pArgs) =>                 {                     UIApplication.SharedApplication.OpenUrl(pArgs.Url);                     pArgs.Url.StopAccessingSecurityScopedResource();                      var cb = _callbackDoc;                     _callbackDoc = null;                     pickerMenu.DismissModalViewController(true);                     cb(pArgs.Url.AbsoluteUrl);                 };             }             else             {                 args.DocumentPicker.DidPickDocumentAtUrls += (sndr, pArgs) =>                 {                     UIApplication.SharedApplication.OpenUrl(pArgs.Urls[0]);                     pArgs.Urls[0].StopAccessingSecurityScopedResource();                      var cb = _callbackDoc;                     _callbackDoc = null;                     pickerMenu.DismissModalViewController(true);                     cb(pArgs.Urls[0].AbsoluteUrl);                 };             }             // Display the document picker             parent.PresentViewController(args.DocumentPicker, true, null);         };          pickerMenu.ModalPresentationStyle = UIModalPresentationStyle.Popover;         parent.PresentViewController(pickerMenu, true, null);         UIPopoverPresentationController presentationPopover = pickerMenu.PopoverPresentationController;         if (presentationPopover != null)         {             presentationPopover.SourceView = parent.View;             presentationPopover.PermittedArrowDirections = UIPopoverArrowDirection.Down;         }     } 

Now you need to call using following code:

var filePath = await DependencyService.Get<IMedia>().OpenDocument(); 

For pick document in Android, you can use following code

  public class IntentHelper  {    public const int DocPicker = 101;   static Action<string> _callback;   public static async void ActivityResult(int requestCode, Result resultCode, Intent data) {  if (requestCode == RequestCodes.DocPicker)     {         if (data.Data == null)         {             _callback(null);         }         else         {             var destFilePath = FilePath.GetPath(CurrentActivity, data.Data);             _callback(destFilePath);         }     } }   public static Activity CurrentActivity {     get     {         return (Xamarin.Forms.Forms.Context as MainActivity);     } }   public static void OpenDocPicker(Action<string> callback) {     _callback = callback;     var intent = new Intent(Intent.ActionOpenDocument);     intent.AddCategory(Intent.CategoryOpenable);     intent.SetType("*/*");     CurrentActivity.StartActivityForResult(intent, RequestCodes.DocPicker); } } 

Answers 2

For pick document in Android, you can use following code:

public class IntentHelper {     public const int DocPicker = 101;     static Action<string> _callback;      public static async void ActivityResult(int requestCode, Result resultCode, Intent data)     {          if (requestCode == RequestCodes.DocPicker)         {             if (data.Data == null)             {                 _callback(null);             }             else             {                 var destFilePath = FilePath.GetPath(CurrentActivity, data.Data);                 _callback(destFilePath);             }         }     }      public static Activity CurrentActivity     {         get         {             return (Xamarin.Forms.Forms.Context as MainActivity);         }     }       public static void OpenDocPicker(Action<string> callback)     {         _callback = callback;         var intent = new Intent(Intent.ActionOpenDocument);         intent.AddCategory(Intent.CategoryOpenable);         intent.SetType("*/*");         CurrentActivity.StartActivityForResult(intent, RequestCodes.DocPicker);     } } 

Use below code to access the helper class: public class Media:

IMedia {     public Task<string> OpenDocument() {          var task = new TaskCompletionSource<string>();          try {                         IntentHelper.OpenDocPicker((path) => { task.SetResult(path); });          } catch (Exception ex) {               task.SetResult(null);          }          return task.Task;      }  } 

Answers 3

Since I was looking for UIDocumentPickerViewController and not UIDocumentMenuViewController the other answer was not what I was looking for :

So this is how I ended up doing it:

Calling the document picker:

   var docPicker = new UIDocumentPickerViewController(new string[]             { UTType.Data, UTType.Content }, UIDocumentPickerMode.Import);             docPicker.WasCancelled += DocPicker_WasCancelled;             docPicker.DidPickDocumentAtUrls += DocPicker_DidPickDocumentAtUrls;             docPicker.DidPickDocument += DocPicker_DidPickDocument;             var _currentViewController = GetCurrentUIController();             if (_currentViewController != null)                 _currentViewController.PresentViewController(docPicker, true, null); 

Where GetCurrentUIController is the function to get the current UI controller something like this :

 public UIViewController GetCurrentUIController()     {         UIViewController viewController;         var window = UIApplication.SharedApplication.KeyWindow;         if (window == null)         {             return null;         }          if (window.RootViewController.PresentedViewController == null)         {             window = UIApplication.SharedApplication.Windows                      .First(i => i.RootViewController != null &&                                  i.RootViewController.GetType().FullName                                  .Contains(typeof(Xamarin.Forms.Platform.iOS.Platform).FullName));         }          viewController = window.RootViewController;          while (viewController.PresentedViewController != null)         {             viewController = viewController.PresentedViewController;         }          return viewController;     } 

For below iOS 11 i added the DidPickDocument event:

 private void DocPicker_DidPickDocument(object sender, UIDocumentPickedEventArgs e)     {         try         {             NSUrl filePath = e.Url.AbsoluteUrl;             //This is the url for your document and you can use it as you please.         }         catch (Exception ex)         {          }       } 

For above iOS 11 you use the DidPickDocumentUrls since multipick is supported there :

  private void DocPicker_DidPickDocumentAtUrls(object sender, UIDocumentPickedAtUrlsEventArgs e)     {         try         {           List<NSUrl> filePath = e.Urls.ToList().Select(y => y.AbsoluteUrl).ToList();         //returns the list of images selected         }         catch (Exception ex)         {             AppLogger.LogException(ex);         }     } 
If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment