如何使用Swift处理错误(FileManager和其他一般情况下)

时间:2017-01-21 05:25:13

标签: ios objective-c swift swift3 nsfilemanager

注意:我之前发布了一个懒惰的问题,用于将代码转换为Swift 3(删除它)

Apple有一些用于管理文件的示例代码。这是一个老指南,全部是Objective-C。我将代码段转换为Swift 3. 我关心的是错误处理部分。我嵌套了多个do / catch块...只是想知道如果这是做事的最佳方式?

对此here提出了类似的问题/答案。

文档为:Apple File System Programming Guide,在"管理文件和目录"部分。

这是我的代码(转换为Swift 3):

func backupMyApplicationData() {
        // Get the application's main data directory
        let directories = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)

        guard directories.count > 0,
            let appSupportDir = directories.first,
            let bundleID = Bundle.main.bundleIdentifier else {
            return
        }

        // Build a path to ~/Library/Application Support/<bundle_ID>/Data
        // where <bundleID> is the actual bundle ID of the application.
        let appDataDir = appSupportDir.appendingPathComponent(bundleID).appendingPathComponent("Data")

        // Copy the data to ~/Library/Application Support/<bundle_ID>/Data.backup
        let backupDir = appDataDir.appendingPathExtension("backup")

        // Perform the copy asynchronously.
        DispatchQueue.global(qos: .default).async { _ in
            // It's good habit to alloc/init the file manager for move/copy operations,
            // just in case you decide to add a delegate later.
            let fileManager = FileManager()

            do {
                // Just try to copy the directory.
                try fileManager.copyItem(at: appDataDir, to: backupDir)

            } catch CocoaError.fileWriteFileExists {
                // If an error occurs, it's probably because a previous backup directory
                // already exists.  Delete the old directory and try again.
                do {
                    try fileManager.removeItem(at: backupDir)
                } catch let error {
                    // If the operation failed again, abort for real.
                    print("Operation failed again, abort with error: \(error)")
                }

            } catch let error {
                // If the operation failed again, abort for real.
                print("Other error: \(error)")
            }
        }
    }

这是他转换的文档中的Apple代码:

- (void)backupMyApplicationData {
   // Get the application's main data directory
   NSArray* theDirs = [[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory
                                 inDomains:NSUserDomainMask];
   if ([theDirs count] > 0)
   {
      // Build a path to ~/Library/Application Support/<bundle_ID>/Data
      // where <bundleID> is the actual bundle ID of the application.
      NSURL* appSupportDir = (NSURL*)[theDirs objectAtIndex:0];
      NSString* appBundleID = [[NSBundle mainBundle] bundleIdentifier];
      NSURL* appDataDir = [[appSupportDir URLByAppendingPathComponent:appBundleID]
                               URLByAppendingPathComponent:@"Data"];

      // Copy the data to ~/Library/Application Support/<bundle_ID>/Data.backup
      NSURL* backupDir = [appDataDir URLByAppendingPathExtension:@"backup"];

      // Perform the copy asynchronously.
      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
         // It's good habit to alloc/init the file manager for move/copy operations,
         // just in case you decide to add a delegate later.
         NSFileManager* theFM = [[NSFileManager alloc] init];
         NSError* anError;

         // Just try to copy the directory.
         if (![theFM copyItemAtURL:appDataDir toURL:backupDir error:&anError]) {
            // If an error occurs, it's probably because a previous backup directory
            // already exists.  Delete the old directory and try again.
            if ([theFM removeItemAtURL:backupDir error:&anError]) {
               // If the operation failed again, abort for real.
               if (![theFM copyItemAtURL:appDataDir toURL:backupDir error:&anError]) {
                  // Report the error....
               }
            }
         }

      });
   }
}

有什么想法吗?

3 个答案:

答案 0 :(得分:1)

删除现有备份后,您忘记重试复制操作。此外,&#34;抓住错误&#34;可以写成只是&#34; catch&#34;因为错误将自动分配给名为&#34;错误&#34;如果你没有指定捕获模式。以下是包含这些更改的代码:

func backupMyApplicationData() {
    // Get the application's main data directory
    let directories = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)

    guard
        directories.count > 0,
        let appSupportDir = directories.first,
        let bundleID = Bundle.main.bundleIdentifier
    else {
        return
    }

    // Build a path to ~/Library/Application Support/<bundle_ID>/Data
    // where <bundleID> is the actual bundle ID of the application.
    let appDataDir = appSupportDir.appendingPathComponent(bundleID).appendingPathComponent("Data")

    // Copy the data to ~/Library/Application Support/<bundle_ID>/Data.backup
    let backupDir = appDataDir.appendingPathExtension("backup")

    // Perform the copy asynchronously.
    DispatchQueue.global(qos: .default).async { _ in
        // It's good habit to alloc/init the file manager for move/copy operations,
        // just in case you decide to add a delegate later.
        let fileManager = FileManager()

        do {
            // Just try to copy the directory.
            try fileManager.copyItem(at: appDataDir, to: backupDir)                
        } catch CocoaError.fileWriteFileExists {
            // Error occurred because a previous backup directory
            // already exists. Delete the old directory and try again.
            do {
                try fileManager.removeItem(at: backupDir)
            } catch {
                // The delete operation failed, abort.
                print("Deletion of existing backup failed. Abort with error: \(error)")
                return
            }
            do {
                try fileManager.copyItem(at: appDataDir, to: backupDir)
            } catch {
                // The copy operation failed again, abort.
                print("Copy operation failed again. Abort with error: \(error)")
            }                
        } catch {
            // The copy operation failed for some other reason, abort.
            print("Copy operation failed for other reason. Abort with error: \(error)")
        }
    }
}

如果您希望翻译更接近Objective-C原创,只有一个错误输出,请尝试以下操作:

func backupMyApplicationData() {
    // Get the application's main data directory
    let directories = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)

    guard
        directories.count > 0,
        let appSupportDir = directories.first,
        let bundleID = Bundle.main.bundleIdentifier
    else {
        return
    }

    // Build a path to ~/Library/Application Support/<bundle_ID>/Data
    // where <bundleID> is the actual bundle ID of the application.
    let appDataDir = appSupportDir.appendingPathComponent(bundleID).appendingPathComponent("Data")

    // Copy the data to ~/Library/Application Support/<bundle_ID>/Data.backup
    let backupDir = appDataDir.appendingPathExtension("backup")

    // Perform the copy asynchronously.
    DispatchQueue.global(qos: .default).async { _ in
        // It's good habit to alloc/init the file manager for move/copy operations,
        // just in case you decide to add a delegate later.
        let fileManager = FileManager()

        // Just try to copy the directory.
        if (try? fileManager.copyItem(at: appDataDir, to: backupDir)) == nil {
            // If an error occurs, it's probably because a previous backup directory
            // already exists.  Delete the old directory and try again.
            if (try? fileManager.removeItem(at: backupDir)) != nil {
                do {
                    try fileManager.copyItem(at: appDataDir, to: backupDir)
                } catch {
                    // The copy retry failed.
                    print("Failed to backup with error: \(error)")
                }
            }
        }
    }
}

答案 1 :(得分:0)

您对Objective-C原文的翻译不正确,正如另一个答案中所指出的那样。但是,这似乎不是你的问题。看来你的问题是关于嵌套。

要回答它,只需看看你想要模仿的原件:

     NSFileManager* theFM = [[NSFileManager alloc] init];
     NSError* anError;
     if (![theFM copyItemAtURL:appDataDir toURL:backupDir error:&anError]) {
        if ([theFM removeItemAtURL:backupDir error:&anError]) {
           if (![theFM copyItemAtURL:appDataDir toURL:backupDir error:&anError]) {

           }
        }
     }

注意什么? 嵌套。因此,代码结构与原始结构之间的唯一区别是嵌套的。 Objective-C原文创建了if子句的嵌套。您的Swift转换会创建一个do / catch块的嵌套 - 因为它必须这样做,因为例如Objective-C copyItemAtURL返回BOOL,而Swift copyItem(at:)则不返回 - 如果有问题,则抛出。

所以我认为我们可以得出结论,嵌套正是正确的做法。实际上,你的代码有什么问题(原因不是原文的准确翻译)是因为它没有足够的嵌套!

您可以尝试通过替换catch块替换if块中的至少一个来测试这是什么类型的错误,但是您仍然会嵌套,那么为什么要这么麻烦呢?你只会抛弃do / catch构造的所有优雅和清晰度。

答案 2 :(得分:-1)

在Swift 2和3中,有3种方法可以使用可能产生错误的方法。

  1. 如果发生错误,请告诉我。

    do {
        try something()
        try somethingElse()
        print("No error.")
    } catch {
        print("Error:", error)
    }
    
  2. 我不在乎错误。如果发生错误,只需返回nil。

    try? something()
    
  3. 我不相信这会有任何错误。如果发生错误,请崩溃我的应用程序。

    try! something()