DropBox Chooser and documentsPicker for Swift developers 3.0 - swift

While the Chooser implementations for iOS are present Here. It is however limited to Objective-C. Is it possible to create a chooser in swift manually?
(A dropBox chooser)
I am also unable to sufficiently call the documentspicker functions, where one can pull any document from any app the user may have installed.
Thank you in advance

⭐Solved
From your project's capabilites. First enable both the iCloud serivces and the key Sharing, now import MobileCoreServices in your class. Finally extended the following three classes inside your class.
UIDocumentMenuDelegate,UIDocumentPickerDelegate,UINavigationControllerDelegate
Now implement the following functions..
public func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt url: URL) {
let myURL = url as URL
print("import result : /(myURL)")
}
public func documentMenu(_ documentMenu:UIDocumentMenuViewController, didPickDocumentPicker documentPicker: UIDocumentPickerViewController) {
documentPicker.delegate = self
present(documentPicker, animated: true, completion: nil)
}
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
print("view was cancelled")
dismiss(animated: true, completion: nil)
}
How to call all of this? Add the following bit of code to your clickable function..
func clickFunction(){
let importMenu = UIDocumentMenuViewController(documentTypes: [String(kUTTypePDF)], in: .import)
importMenu.delegate = self
importMenu.modalPresentationStyle = .formSheet
self.present(importMenu, animated: true, completion: nil)
}
Click your button. The following menu will pop up ..
In the case of DropBox. Upon clicking on any item. You will be redirected to your app. And the Url will be printed.
Manipulate the documentTypes to your need. In my app, Users permitted to Pdf only. So, suit yourself.
kUTTypePDF
Also if you feel like customizing your own menu bar. Add the following code and customize your own function inside the handler
importMenu.addOption(withTitle: "Create New Document", image: nil, order: .first, handler: { print("New Doc Requested") })
Enjoy it.

Related

Swift Document Picker, can not reach the file

I am writing a project which involves picking a file and getting the content of the file. However, I think it is not reaching the correct url of the file.
Here is the function where it calls the document picker. It is activated by a button.
#IBAction func selectFile(_ sender: Any) {
let documentPicker = UIDocumentPickerViewController(documentTypes: [kUTTypePlainText as String], in: .import)
documentPicker.delegate = self
documentPicker.allowsMultipleSelection = false
present(documentPicker, animated: true, completion: nil)
}
And here is the extension to UIDocumentPickerViewController
extension ViewController: UIDocumentPickerDelegate {
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
print(urls)
do {
let fileContent = try String(contentsOf: urls[0], encoding: .utf8)
print(fileContent)
} catch {
return
}
}
}
In the console output, fileContent is not being printed out, instead, here is what it prints out.
Failed to associate thumbnails for picked URL
file:///Users/<user>/Library/Developer/CoreSimulator/Devices/480A2D02-810F-435E-BF44-4B2F6FC614A9/data/Containers/Data/Application/BAA825D0-C4D8-4B33-AFB3-3737ADCA0B29/Documents/test2.txt with the Inbox copy
file:///Users/<user>/Library/Developer/CoreSimulator/Devices/480A2D02-810F-435E-BF44-4B2F6FC614A9/data/Containers/Data/Application/BAA825D0-C4D8-4B33-AFB3-3737ADCA0B29/tmp/<project>-Inbox/test2.txt:
Error Domain=QLThumbnailErrorDomain Code=102 "(null)"
UserInfo={NSUnderlyingError=0x600003348060
{Error Domain=GSLibraryErrorDomain Code=3 "Generation not found" UserInfo={NSDescription=Generation not found}}}
There aren't many resources about this online, can someone help look at what I did wrong here?
Actually I made a small change on the UIDocumentPickerViewController initialiser. Adding the updated code below.
#IBAction func selectFile(_ sender: Any) {
let documentPicker = UIDocumentPickerViewController(documentTypes: ["public.text"], in: .import)
documentPicker.delegate = self
documentPicker.allowsMultipleSelection = false
present(documentPicker, animated: true, completion: nil) }
Rest of the code same, only replaced this ["public.text"] instead of [kUTTypePlainText as String].
May be it resolves your problem. Please let me know the feedback.
Credit gose to adrian check this link using-uidocumentpickerviewcontroller-to-import-text-in-swift
I know this is an old question, but if it is of help to anyone, I fixed the console issue by changing the instance type of UIDocumentPickerViewController, entering .open instead of .import
In my case, the behaviour does not change and I can choose a file without losing the expected behaviour.

UIDocumentPickerViewController allows selecting a file the first time the app is run, but not subsequently

My app, when run, creates a json document (using a UIDocument subclass) in its document directory. And then, when opening up the UIDocumentPickerViewController to select a file, if the app has written a new file, the behaviour is as expected.
However, if I run the app again (and overwrite the last created file), the delegate method didPickDocumentsAt doesn't get called, unless I browse around for a few seconds.
What am I missing here?
#IBAction func showDocumentPicker() {
let documentPicker = UIDocumentPickerViewController(documentTypes: [kUTTypeJSON as String], in: .import)
documentPicker.allowsMultipleSelection = false
documentPicker.delegate = self
self.present(documentPicker, animated: true, completion: nil)
} //this function is in the initial definition of the class and is connected to a UIBarButton
extension BudgetExportViewController: UIDocumentPickerDelegate {
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
print("selected document: \(urls.first)")
print("555555555555555555555555555555")
//document = BudgetExportDocument(fileURL: urls.first!)
// CFURLStartAccessingSecurityScopedResource(urls.first! as CFURL)
// let documentData = try? Data.init(contentsOf: urls.first!)
// let json = try? JSONDecoder().decode(BudgetExportData.self, from: documentData!)
// budgetThisMonth = json
// print("Budgetthismonth")
// print(budgetThisMonth)
// CFURLStopAccessingSecurityScopedResource(urls.first! as CFURL)
}
}
Apparently, the problem was the fact that I was overwriting the same file so many times. Now, if a file with the same name exists, it won't write over it and the UIDocumentPickerViewController behaviour is as expected.

Run a function N time anywhere in the app in Swift

I trying to make a calling app for my project and I want to add a function that keeps checking if someone if calling. My app uses Firebase where I have a key for each users to check if he made a call or not.
There's two problem I am facing here, the first one is, as I said, that I want my function to keep checking anywhere in the app for an incoming call. The other problem is that i have a viewcontroller that I want to pop up when someone is calling. I have found this code on github but it uses navigationcontroller which I am not using in my app :
extension UIViewController{
func presentViewControllerFromVisibleViewController(viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
if let navigationController = self as? UINavigationController, let topViewController = navigationController.topViewController {
topViewController.presentViewControllerFromVisibleViewController(viewControllerToPresent: viewControllerToPresent, animated: true, completion: completion)
} else if (presentedViewController != nil) {
presentedViewController!.presentViewControllerFromVisibleViewController(viewControllerToPresent: viewControllerToPresent, animated: true, completion: completion)
} else {
present(viewControllerToPresent, animated: true, completion: completion)
}
}
}
For your question on monitoring when incoming calls occur and to be called as a result, see this answer. It's probably what you need (I've never tried it, however). The example shows creating a CXCallObserver and setting your AppDelegate as delegate.
For your second question, I'd first try this answer which leverages the window.rootViewController so you can do this from your AppDelegate. Generally, the root VC is your friend when trying to do UI your AppDelegate. :)
A better answer based on Alex's added comments:
I'd first look at how to set up an observer to your Firebase model so that you can get a callback. If you don't have a way to do that, I'd use KVO on the Firebase model property. But to do exactly as you're requesting, and to do so lazily from AppDelegate (rather than from a singleton), see this code:
// In AppDelegate
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool
{
self.timerToCheckForCalls = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(timerFired), userInfo: nil, repeats: true)
}
func timerFired()
{
let didCall = // TODO: query your Firebase model...
guard didCall == true else
{
return
}
self.displayCallerView()
}
func displayCallerView()
{
// See below link.
}
See this answer for how to present your view controller, even when your app might be showing an action sheet, alert, etc... which I think you'd especially value since you need to display the caller regardless of what your app is doing.
Note while user is scrolling a UITextView, the timer won't fire yet. There may be other situations where the timer could be delayed too. So it really would be best to observe your Firebase model or receive a KVO callback than to use a timer.
If you want to make a function that can be called from anywhere, use a singleton pattern. You can also use that to store your special view controller.
Bear in mind that this code SHOULD NOT considered fully functioning code and will need to be customized by you to suit your needs.
class MyClass {
let shared = MyClass()
var viewController: SpecialViewController?
func checkForCall() {
// do function stuff
}
func getSpecialViewController() {
let storyBoard = UIStoryboard.init(name: "main", bundle: nil)
// keep it so we don't have to instantiate it every time
if viewController == nil {
viewController = storyBoard.instantiateViewController(withIdentifier: "SomeViewController")
}
return viewController
}
}
// Make an extension for UIViewController so that they can all
// use this function
extension UIViewController {
func presentSpecialViewController() {
let vc = MyClass.shared.getSpecialViewController()
present(vc, animated: false, completion: nil)
}
}
Somewhere in your code:
// in some function
MyClass.shared.checkForCall()
Somewhere else in code:
presentSpecialViewController()

Swift 3: fix issue Type "className" does not conform to protocol 'UIDocumentPickerDelegate'

I'm migrating my code from Swift 2 to Swift 3 but my code throws this error: Type "className" does not conform to protocol 'UIDocumentPickerDelegate'. I've migrate many parts of my document as you can see but error stills,
extension className: UIDocumentMenuDelegate {
//Opening the Document Menu
func documentMenu(_ documentMenu: UIDocumentMenuViewController, didPickDocumentPicker documentPicker: UIDocumentPickerViewController) {
documentPicker.delegate = self
print ("documentMenu")
self.present(documentPicker, animated: true, completion: nil)
}
func documentMenuWasCancelled(_ documentMenu: UIDocumentMenuViewController) {
}
}
//Using UIDocumentPickerDelegate, error persists here.
extension className: UIDocumentPickerDelegate {
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt url: URL) {
if(controller.documentPickerMode == UIDocumentPickerMode.import){
print ("success")
}
}
}
class className: BaseViewController{
...Tons of code here...
#IBAction func importKeyButtonPressed(_ sender: UIButton) {
let importMenu = UIDocumentMenuViewController(documentTypes: ["public.data","public.text","public.content"], in: UIDocumentPickerMode.import)
var documentPicker = UIDocumentPickerViewController(documentTypes: ["public.txt"], in: UIDocumentPickerMode.import)
documentPicker.delegate = self
documentPicker.modalPresentationStyle = UIModalPresentationStyle.fullScreen
self.present(documentPicker, animated: true, completion: nil)
}
How can I solve it?, I've use all methods that procotol requires. Thanks
I revised your information on this link that has a similar problem.
Cannot conform to STPAddCardViewControllerDelegate since Xcode 8 GM on Swift 3
The problem is the swift compiler than can´t recognize automatically some information. So in this case:
didPickDocumentAt url: URL
Then i follow the problem to this link:
https://bugs.swift.org/browse/SR-2596
In this link the information is that Swift 3 mistake data types, so i research some more and get to this page. The mistaken type in this case is "URL".
Then the solution is in the same page. I write it bellow:
weak var delegate : UIDocumentPickerDelegate?
#available(iOS 8.0, *)
public func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt url: Foundation.URL ){
//
print("something")
}
To that matter i reproduce the error in my computer, it is caused when diferent imported libraries has the same definition for data types, in this case URL type, which Swift 3 doesnt recognize automatically neither tells correctly the error. So it has to be defined directly.

Share a video and text on Twitter, Instagram and other services using UIActivityViewController

I am trying to share a video and a text on Instagram, Facebook, Twitter and the native services like Mail, Messages, .... I can not figure out how to get both, Instagram and Twitter to show up in the sharing actionsheet:
If i pass in an array of text and a URL as activity items into the controller, just Instagram shows up, but not Twitter.
let url: NSURL = NSURL() // a url that directs to a video
let items: [AnyObject] = ["Check out this video", url]
let shareable = UIActivityViewController(activityItems: items, applicationActivities: nil)
controller.presentViewController(shareable,
animated: true,
completion: nil)
If i create a class that implements the UIActivityItemSource protocol instead and use that as activityItems, just Twitter shows up, but not Instagram:
class VideoActivityItemSource: NSObject, UIActivityItemSource {
private let videoUrl: NSURL
private let shareText = "View the full video here!"
init(url: NSURL) {
self.videoUrl = url
}
func activityViewControllerPlaceholderItem(activityViewController: UIActivityViewController) -> AnyObject {
return ""
}
func activityViewController(activityViewController: UIActivityViewController, itemForActivityType activityType: String) -> AnyObject? {
switch activityType {
case UIActivityTypePostToFacebook:
return self.videoUrl
case UIActivityTypeMail:
return self.videoUrl
default:
return ["text": self.shareText, "url": self.videoUrl]
}
}
func activityViewController(activityViewController: UIActivityViewController, subjectForActivityType activityType: String?) -> String {
return "Hey check this new cool app!!!"
}
func activityViewController(activityViewController: UIActivityViewController, thumbnailImageForActivityType activityType: String?, suggestedSize size: CGSize) -> UIImage? {
return nil
}
}
and then replace the items by this:
items = [VideoActivityItemSource(url: url)]
I have no idea why in this case Twitter won't show up in the action sheet. Does somebody have an idea how to solve this?
I found the answer. The correct way to do this is to use the implementation of the UIActivityItemSource protocol. The reason for Instagram not showing up in the second solution where i am using the VideoActivityItemSource class is that i am returning an empty String in the activityViewControllerPlaceholderItem function.
Although Apple's documentation says that the type of the object returned in this function does not have to match the type that is used by the itemForActivityType function, it actually needs to be processable by the sharing service. In the case of Instagram it needs to be a video or an image, otherwise Instagram does not show up as a sharing option in the actionsheet.
So the solution is to return a UIImage in the activityViewControllerPlaceholderItem function instead of an empty String, then both Twitter and Instagram will show up as sharing options.
func activityViewControllerPlaceholderItem(activityViewController: UIActivityViewController) -> AnyObject {
// this needs to return some random image to make sure Twitter and Instagram show up in the sharing actionsheet
return UIImage(named: "someImage")!
}
Make sure you have the Instagram app on your phone.
`let activityVC = UIActivityViewController(activityItems: yourobjectArray, applicationActivities: nil)
activityVC.setValue("clipSnapshot", forKey: "subject")
if let activityPopOver = activityVC.popoverPresentationController {
activityPopOver.sourceView = self.view
activityPopOver.permittedArrowDirections = self.subviewView.isHidden ? .up : .left
}
self.present(activityVC, animated: true, completion: nil)
}`
When you see the sharing window and still don't see Instagram then goto the end of the list.
Click on "More" and check if instagram and twitter are included