In our previous project we had to implement logic for downloading various types of files and previewing them in a new controller. First thing that came to mind was a controller with UIWebView as subView or even better – WKWebView. StackOverflow gave the same answer to this question. That was a solid solution, we could open most of the files and it showed us how powerful UIWebView was. However, we came across QuickLook framework. This framework is used for taking a “quick look” at different type of files. Primary class of this framework is QLPreviewController.

QLPreviewController can display these types of documents:

– iWork documents

– Microsoft Office documents (Office ‘97 and newer)

 

Implementation

Implementation is very easy and straightforward and in this tutorial we will create a simple app with documents saved in a bundle.

Firstly, create a new project, delete in projects StoryBoard default controller and default ViewController class. Add UITableViewController to StoryBoard for easier and faster creation of file list. Create subclass of UITableViewController (name it however you want, in this example it will be called DocumentListTableViewController) and set it to be the owner of UITableViewController we just added to StoryBoard.

In DocumentListTableViewController add these three properties.

 let fileTypes = ["pdf", "zip", "jpg", "png", "csv", "xls", "txt"]
var selectedUrls:[URL] = []
var filePaths = [String]() {
didSet {
self.tableView.reloadData()
}
}

filetypes will be used to identify types we will preview, filePaths will be an array of file paths for tableView dataSource and selectedUrls will be an array of urls for QLPreviewController dataSource.

Drag and drop files to your project with extensions that you want and change array fileTypes to have extensions of files that are added to the bundle.
In this example we added several files (testTxt.txt, jpgExample.jpg, pngExample.png, Archive.zip… ).
In the same class add fetchData method that simulates getting data from the server, in this case it will get file urls from bundle which are set in fileTypes array.

 private func fetchData() {
guard let fetchedUrls = try? FileManager.default.contentsOfDirectory(atPath: Bundle.main.bundlePath) else {
return
}
self.filePaths = fetchedUrls.filter{ self.fileTypes.contains($0.pathExtension)  }
}
To make filtering filePaths of files in bundle easier we added an extension for String.
extension String {
var ns: NSString {
return self as NSString
}
var pathExtension: String {
return ns.pathExtension 
}
}

We will set tableViewNumberOfSection method to return value 2. The first section will represent all documents and other cells will be a representation of every single file path in filePaths array. In tableViewNumberOfRows dataSource method, set condition to return one row in the first section and for else case return count of filePaths array. In cellForRow method in first section, we will set a custom name for the cell to know that the cell is showing all files. In the second section, cell label will be representing name of the single file that will be open. For example:

let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
if indexPath.section == 0 {
cell.textLabel?.text = "Multiple items"
} else {
cell.textLabel?.text = self.filePaths[indexPath.row]
}
return cell

Everything is set now for the list, you need to call fetchData method on viewDidLoad to fill the array with data. You can run your project and the app should look something like this.


One more thing before we dive into QLPreviewController. We will add logic to didSelectRow tableView delegate method. As I mentioned earlier, there are more ways to do this but it’s important to get URL objects that are instantiated with filePaths array.

var filerUrls:[URL] = []
var selectedDocumentPaths = self.filePaths
if indexPath.section == 1 {
selectedDocumentPaths = [selectedDocumentPaths[indexPath.row]]
}
filerUrls = selectedDocumentPaths.map { urlString in
let documentUrlComponents = urlString.components(separatedBy: ".")
let filePath = Bundle.main.path(forResource: documentUrlComponents[0], ofType: documentUrlComponents[1])
return URL(fileURLWithPath: filePath!)
}
self.selectedUrls = filerUrls

QLPreviewController

QLPreviewController has dataSource which must be set for the controller. Also, it has a delegate protocol that is optional and with his methods you will have more control over controller transitions and animations.

Now we have come to the main part of this tutorial. At the top of DocumentListTableViewController add import QuickLook to gain access to QuickLook framework classes and methods and extend the controller by adding QLPreviewControllerDataSource protocol.

extension DocumentListTableViewController: QLPreviewControllerDataSource {

In extension add these two dataSource methods.

func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
return self.selectedUrls.count
}
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
return selectedUrls[index] as QLPreviewItem
}

Names of these methods speak for themselves, numberOfPreviewItems returns number of items that will be presented in QLPreviewController and previewController returns url of a file.

Last thing to do is to initialize QLPreviewController and set DocumentListTableViewController to be its delegate. This will be set at the bottom of didSelectRow method after self.selectedUrls = fileUrls line.
Names of these methods speak for themselves, numberOfPreviewItems returns number of items that will be presented in QLPreviewController and previewController returns url of a file.

 let controller = QLPreviewController()
controller.dataSource = self
self.navigationController?.pushViewController(controller, animated: true)

And that’s it!
You can run the project and by selecting the first cell (“Multiple items”) QLPreviewController will open the first item from the list. In the right bottom corner of tabBar there will be an icon that opens a list of all items that are in dataSource array.

If you select another cells it will just open single item without the possibility of opening QLPreviewsController list (however, you can return back to your custom tableView list).

 

Conclusion

Hope this was helpful, it’s a very simple framework and in combination with UIDocumentMenuViewController it can be very easy to handle file previewing in your app.
Source code of this tutorial is on this link http://git.plavatvornica.com/aatanac/QuickLookExample.git