The move file operation, move a file from one folder or sub folder in the application sandbox, to another folder or subfolder in the application sandbox, and vice versa.

Code presented on this page assume you are using Xcode 7.0 and Swift version 2.0. So if you are using a older or newer version of Swift, it may produce errors. Fix them by using Xcode’s Fix-it tool. Also, I assume you aren’t a newbie to the Swift Programming Language and you know your way around Xcode Integrated Development Editor.

Today’s Objectives

In today’s workshop, you will implement four new features in the NiftyTextFile project.

1. Create a subfolder in the Documents folder

You will create a subfolder called Trash Can in the sandbox’s Documents folder. It will hold text files the app user delete from the Documents folder and the File List’s table view cells.

niftytextfile-part8-10

2. Move a text file to the Trash Can subfolder

When the app user swipe a table view cell and tap the red Delete button, the app will move the swiped cell’s text file out of the Documents folder and put it in the Trash Can subfolder. In addition to this, the app will remove the text text file from the table view’s dataSource, and delete the swiped table view cell. The diagram below shows the move text file operation. The QuickTime movie shows the move text file operation in action.

niftytextfile-part8-0a

3. Move a text file out of the Trash Can subfolder

When the app user select a text file from the Trash Can’s pickerView and tap the Restore button, the app will move the text file from the Trash Can subfolder, and put it back in the Documents folder. The app will refresh the pickerView; so the deleted text file no longer appear in its component. The app will also display a message in the textView. The diagram below shows the move text file operation. The QuickTime movie shows the move text file operation in action.

niftytextfile-part8-0b

4. Delete a text file from the Trash Can subfolder

When the app user select a text from the Trash Can’s pickerView and tap the Delete button, the app will permanently delete it from the Trash Can subfolder. The app will refresh the pickerView; so the deleted text file no longer appear in its component. The app will also display a message in the textView. The diagram below shows the delete file operation. The QuickTime movie shows the delete text file operation in action.

niftytextfile-part8-11

5. Change a button image programmatically

You will add a feature in the NiftyTextFile project that change a trash can button’s image property programmatically. If a trash can subfolder is empty; an empty trash can image should appear on the trash can button; otherwise, a full trash can image should appear on the trash can button. The diagram below shows the image swapping feature you will implement in the NiftyTextFile project. The QuickTime movie shows the image swapping in action.

The Create Subfolder Code

As of now, the Trash Can subfolder doesn’t exists in the NiftyTextFile’s Documents folder. So before you implement the move text file operations and the delete text file operation in the project, you have to enter this function in the TextFileManger class file. It create a subfolder in the Documents folder. The name of the subfolder the function will create is passed as a parameter.

func createSubfolder(subFolderName: String) {
  let subfolderPath = getDocumentsDirectory().stringByAppendingString(subFolderName)
    
  if NSFileManager.defaultManager().fileExistsAtPath(subfolderPath) == false {
    var createSubfolderError: NSError? = nil
        
    do {
      try NSFileManager.defaultManager().createDirectoryAtPath(subfolderPath, withIntermediateDirectories: false, attributes: nil)
    } catch let error as NSError {
      createSubfolderError = error
    }
        
    if createSubfolderError != nil {
      print("Create subfolder error:\n\(createSubfolderError)")
    }
  }
}

Now, here’s the code to create a subfolder called Trash Can, in the sandbox’s Documents folder. Put the code in the AppDelegate.swift file.

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

  var window: UIWindow?
  let File = TextFileManager()

  func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
  File.createSubfolder("Trash Can")    
  return true
}

Code you entered in the AppDelegate.swift file’s didFinishLaunchingWithOptions() will fire only if the Trash Can subfolder doesn’t exists in the sandbox’s Documents folder. If you added say, five text files in the sandbox’s Documents folder. In Finder, the Documents folder and the Trash Can subfolder will look like this:

niftytextfile-part8-1

Modify The Storyboard

Now, since the app user will perform two move file operations and a delete file operation, you’ll have to modify the File List View Controller scene by adding a Button object on the left side of the Search Bar, clear out its Title attribute, and set its Image attribute to display an empty trash can image.

notebookYou’ll have to download an empty and full trash can icon from iconfinder.com and add them in the project’s Assets folder.

When you are done, the File List View Controller scene should look like this:

niftytextfile-part8-2

Next, drag a View Controller from the Object Library and drop it on the right side of the initial scene. Configure the new scene by adding three Buttons objects and set their Title attributes to display: Back, Restore, and Delete. Next, add a Picker View and a Text View on the scene’s canvas. Set the Text View’s Text attribute to display text shown in the image below. Also, remove the check mark from the textView’s “User Interaction Enabled” attribute. Set the Text View’s width to 566 and its height to 260. Next, set the Picker View’s width to 560 and its height to 216. Next, connect the Picker View’s dataSource and delegate outlet in Interface Builder. Now, add missing constraints to the scenes object and connect the Back button to the scene’s Exit object. When prompted, select the unwindToMainView Action Segue.

niftytextfile-part8-3

You have to add a Cocoa Touch class in the project and configure the options window to look like the image shown below. In the Identity inspector, connect the new scene to the TrashCanViewController class.

niftytextfile-part8-4

Now, create segue that connect the File List View Controller scene’s trash can button to the Trash Can View Controller scene. When prompted, select show from the Action Segue menu.

niftytextfile-part8-12

Next, set the segue’s Identifier attribute to showTrashcanView. Next, use the Assistant editor to create an IBOutlet for the Trash Can scene’s textView and pickerView, an IBAction for the Restore and Delete button. Next, configure the TrashCanViewController class file to look like this:

niftytextfile-part8-6

When you are done modifying the storyboard and the TrashCanViewController class, the storyboard should look like this:

niftytextfile-part8-5

Build and Run The App

Now is a good time to build and run the app on your real device or the Simulator. The File List View should look like the first image shown below. When you tap/click the trash can button, the app will display the Trash Can View. The second image below shows what it should look like.

niftytextfile-part8-7 niftytextfile-part8-8

Populate the PickerView’s dataSource

What you have to do now is read the Trash Can subfolder and dump the results in the pickerView’s dataSource; which is an array called fileList. Start by entering this function in the TextFileManager class.

// This function fetch only text files from the Trash Can folder and return them
func readTrashCanDir() > [String] {
  var fetchedResults = [String]()
  let trashcanDir = getDocumentsDirectory().stringByAppendingString("/Trash Can")
    
  do {
    let directoryItems: Array = try NSFileManager.defaultManager().contentsOfDirectoryAtPath(trashcanDir)
        
    for item in directoryItems as [String] {
      if item.hasSuffix("txt") {
        fetchedResults.append(item)
      }
    }
        
  } catch let readError as NSError {
    print("Fetch files from the Trash Can directory failed for this reason:\n\(readError)")
  }
    
  return fetchedResults
}

Next, enter this statement in the TrashCanViewController.swift file’s viewDidLoad() function.

fileList = File.readTrashCanDir()

Recap

So far, you configured the NiftyTextFile project by creating a subfolder called Trash Can, in the Documents folder. Next, you configured the storyboard by adding a trash can button on the File List View. You added a new scene on the storyboard and you added a class called TrashCanViewController in the project. You then hooked up the class to the Trash Can View Controller scene. Finally, you added code in the TrashCanViewController class to do the following:

  • Read the Trash Can subfolder and dump the results in the pickerView’s dataSource; which is an array called fileList.
  • Populate the pickerView’s component rows with text file names that are in the fileList array.
  • Display the selected text file’s name in the textView.

Since the Trash Can subfolder is currently empty, nothing is shown in the pickerView’s component rows.

The Move and Delete Text File Code

Finally, you are ready to implement two move text file operations and a Delete text file operation in the NiftyTextFile project. The first move text file operation will happen when the app user tap the red Delete button on the File List’s table view cell. The second move text file operation will occur when the app user tap the Restore button that’s on the Trash Can view. The delete text file operation will happen when the app user tap the Delete button on the Trash Can view.

niftytextfile-part8-13

With that said, here’s the functions to implement both move text file operations and the delete text file operation. Put them in the TextFileManager class.

// When called, this function move a text file from the Documents folder, to the Trash Can subfolder
func moveFileToTrashCan(fileToMove: String) {
  let sourceFilePath = getDocumentsDirectory().stringByAppendingString("/"+fileToMove)
  let trashcanPath = getDocumentsDirectory().stringByAppendingString("/Trash Can/" + fileToMove)
  let fileManager = NSFileManager.defaultManager()
    
  do {
    try fileManager.copyItemAtPath(sourceFilePath, toPath: trashcanPath)
  } catch let fileCopyError as NSError {
        
    print("FILE COPY ERROR:\n\(fileCopyError)")
    return // Don't execute code after this block
  }
    
  do {
    try fileManager.removeItemAtPath(sourceFilePath)
  } catch let fileDeleteError as NSError {
    print("File DELETE ERROR:\n\(fileDeleteError)")
  }
}

// When called, this function move a text file from Trash Can subfolder, to the Documents folder
func moveFileToDocumentsDir(fileToMove: String) -> Bool {
  var fileRestored = false
  let sourceFile = getDocumentsDirectory().stringByAppendingString("/Trash Can/"+fileToMove)
  let destinationDirectory = getDocumentsDirectory().stringByAppendingString("/"+fileToMove)
  let fileManager = NSFileManager.defaultManager()
    
  do {
    try fileManager.copyItemAtPath(sourceFile, toPath: destinationDirectory)
  } catch let fileCopyError as NSError {
    print("FILE COPY ERROR:\n\(fileCopyError)")
  }
    
  do {
    try fileManager.removeItemAtPath(sourceFile)
    fileRestored = true
  } catch let fileDeleteError as NSError {
    print("File DELETE ERROR:\n\(fileDeleteError)")
  }
    
  return fileRestored
}

// When called, this function delete a text file from the Trash can subfolder
func deleteFileFromTrashCan(fileToDelete: String) -> Bool {
  var fileDeleted = false
  let sourceFile = getDocumentsDirectory().stringByAppendingString("/Trash Can/"+fileToDelete)
  let fileManager = NSFileManager.defaultManager()
    
  do {
    try fileManager.removeItemAtPath(sourceFile)
    fileDeleted = true
  } catch let fileDeleteError as NSError {
    print("File DELETE ERROR:\n\(fileDeleteError)")
  }
    
  return fileDeleted
}

Now, here’s the code to enter in the FileListViewController.swift file’s commitEditingStyle() function. As you already know, code in that function is fired when the user tap the red Delete button in a swiped table view cell.

reminderTo see output the code produce, watch the first QuickTime movie presented above.

if editingStyle == .Delete {
  let file = fileList[indexPath.row]
  File.moveFileToTrashCan(file)
  fileList.removeAtIndex(indexPath.row)
  tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
}

Here’s the code to put in the TrashCanViewController.swift file’s restoreButtonPressed() function, the deleteButtonPressed() function, and output they will produce.
reminderTo see output the code produce, watch the second and third QuickTime movie presented above.

@IBAction func restoreButtonPressed(sender: UIButton) {
  // Move the selected text File from the Trash Can subfolder to the Documents folder
  if selectedFile.isEmpty == true || textView.text == "File restored" {
    textView.textColor = UIColor.redColor()
    textView.text = "Pick a file first"
    return
  }
    
  let fileRestored = File.moveFileToDocumentsDir(selectedFile)
    
  if fileRestored {
    fileList = File.readTrashCanDir()
    pickerView.reloadAllComponents()
    textView.textColor = UIColor.blackColor()
    textView.text = "File restored"
  }
}

@IBAction func deleteButtonPressed(sender: UIButton) {
  // Permanently remove the selected text file from the Trash Can subfolder and the pickerView
  if selectedFile.isEmpty == true || textView.text == "File deleted" {
    textView.textColor = UIColor.redColor()
    textView.text = "Pick a file first"
    return
  }
    
  let fileDeleted = File.deleteFileFromTrashCan(selectedFile)
    
  if fileDeleted {
    fileList = File.readTrashCanDir()
    pickerView.reloadAllComponents()
    textView.textColor = UIColor.blackColor()
    textView.text = "File deleted"
  }
}

The Button Image Swaping Code

See that empty trash can image on the File List’s button. Let’s change its image property programmatically, based on the number of files there are in the Trash Can subfolder. If the subfolder is empty; an empty trash can image should appear on the trash can button; otherwise, a full trash can image should appear on the trash can button. To implement this image swapping feature in the FileListViewController class; you have to enter code in two functions.

Start by putting this code, below existing code in the FileListViewController.swift file’s viewDidAppear() function.

// Check to see if there are files in the Trash Can folder
if File.readTrashCanDir().count > 0 {
  // There are, so switch the image on the trashcanButton
  trashcanButton.setImage(UIImage(named:"trashcan-full"), forState: .Normal)
} else {
  trashcanButton.setImage(UIImage(named: "trashcan-empty"), forState: .Normal)
}

Next, put this code, below existing code in the FileListViewController.swift file’s commitEditingStyle() function.

// Check to see if there are files in the Trash Can subfolder
if File.readTrashCanDir().count > 0 {
  // There are, so change the trashcanBbutton's image
  trashcanButton.setImage(UIImage(named: "trashcan-full"), forState: .Normal)
}

reminderTo see output above code produce, watch the fourth QuickTime movie presented above.

That’s it! You’ve reached the end of the Text File Workshop. You learned how to do the following:

sign-post-thend

  • Write a text file
  • Read a directory
  • Read a text file
  • Update a text file
  • Delete a text file
  • Find a text file
  • Create a subfolder
  • Move a text file to a subfolder
  • Move a text file from a subfolder
  • Delete a text file from a subfolder
  • Change a button image programmatically

Until next time, happy coding. 🙂

Tags:

No responses yet

Leave a Reply

UIDocument Demystified: A Step-by-step Guide on Local and iCloud Document Storage

 

Archives