重新安装应用程序后如何从 iCloud 中的文件中获取数据?

How to get data from a file in iCloud after reinstalling the app?

应用程序使用iCloud存储符合Codable协议的对象NSKeyedArchiver/NSKeyedUnarchiver。 设备之间的同步是可以的,除非在设备上重新安装应用程序(或安装在新设备上)并且包含数据的文件存在 - 在这种情况下 NSKeyedUnarchiver.unarchiveObject(withFile: filePathe) return 无。 当我在新设备上安装应用程序(在同一设备上重新安装)时,如何从 iCloud 的现有文件中获取数据?

class ViewController: UIViewController {

@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var weightLabel: UILabel!
@IBOutlet weak var nameTextField: UITextField!
@IBOutlet weak var weightTextField: UITextField!

var iCloudContainer: URL? {
    return FileManager().url(forUbiquityContainerIdentifier: nil)
}

func getFilePath(container: URL, fileName: String) -> String {
    let filePath = container.appendingPathComponent(fileName).path

    return filePath
}

override func viewDidLoad() {
    super.viewDidLoad()
}

@IBAction func fetchButtonPressed(_ sender: UIButton) {

    let container = self.iCloudContainer
    let filePathe = getFilePath(container: container!, fileName: "Person")

    if let jsonData = NSKeyedUnarchiver.unarchiveObject(withFile: filePathe) as? Data {
        if let person = try? JSONDecoder().decode(Person.self, from: jsonData) {
            nameLabel.text = person.name
            weightLabel.text = String(person.weight)
        } else {
            nameLabel.text = "No data loaded"
            weightLabel.text = "No data loaded"
        }
    } else {
        nameLabel.text = "No data loaded"
        weightLabel.text = "No data loaded"
    }

}

@IBAction func saveButtonPressed(_ sender: UIButton) {
    let container = self.iCloudContainer
    let filePathe = getFilePath(container: container!, fileName: "Person")

    let person = Person(name: nameTextField.text!, weight: Double(weightTextField.text!)!)
    let jsonData = try? JSONEncoder().encode(person)
    NSKeyedArchiver.archiveRootObject(jsonData!, toFile: filePathe)
}

要将数据从 iCloud 容器获取到本地 Ubiquity 容器,您应该使用元数据从 iCloud 查找项目并将其下载到设备。

    lazy var metadataQuery : NSMetadataQuery = {
    let query = NSMetadataQuery()
    query.searchScopes = [NSMetadataQueryUbiquitousDocumentsScope]
    query.predicate = NSPredicate(format: "%K CONTAINS %@", NSMetadataItemFSNameKey, "List")

    NotificationCenter.default.addObserver(self, selector: #selector(didFinishGathering), name: NSNotification.Name.NSMetadataQueryDidUpdate, object: query)
    NotificationCenter.default.addObserver(self, selector: #selector(didFinishGathering), name: NSNotification.Name.NSMetadataQueryDidFinishGathering, object: query)

    return query
}()

override func viewDidLoad() {
    super.viewDidLoad()

    self.metadataQuery.start()

}

    deinit {
        NotificationCenter.default.removeObserver(self)
    }


@objc func didFinishGathering(notification: Notification?) {
    let query = notification?.object as? NSMetadataQuery

    query?.enumerateResults { (item: Any, index: Int, stop: UnsafeMutablePointer<ObjCBool>) in
        let metadataItem = item as! NSMetadataItem

        if isMetadataItemDownloaded(item: metadataItem) == false {

            let url = metadataItem.value(forAttribute: NSMetadataItemURLKey) as! URL

            try? FileManager.default.startDownloadingUbiquitousItem(at: url)
        }
    }

    guard let queryresultsCount = query?.resultCount else { return }
    for index in 0..<queryresultsCount {
        let item = query?.result(at: index) as? NSMetadataItem
        let itemName = item?.value(forAttribute: NSMetadataItemFSNameKey) as! String

        let container = filesCoordinator.iCloudContainer
        let filePath = filesCoordinator.getFilePath(container: container!, fileName: "TaskList")
        let addressPath = filesCoordinator.getFilePath(container: container!, fileName: "CategoryList")

        if itemName == "TaskList" {
            if let jsonData = NSKeyedUnarchiver.unarchiveObject(withFile: filePath) as? Data {
                if let person = try? JSONDecoder().decode(Person.self, from: jsonData) {
                    nameLabel.text = person.name
                    weightLabel.text = String(person.weight)
                } else {
                    nameLabel.text = "NOT decoded"
                    weightLabel.text = "NOT decoded"
                }
            } else {
                nameLabel.text = "NOT unarchived"
                weightLabel.text = "NOT unarchived"
            }
        } else if itemName == "CategoryList" {
            if let jsonData = NSKeyedUnarchiver.unarchiveObject(withFile: addressPath) as? Data {
                if let address = try? JSONDecoder().decode(Address.self, from: jsonData) {
                    streetLabel.text = address.street
                    houseLabel.text = String(address.house)
                } else {
                    streetLabel.text = "NOT decoded"
                    houseLabel.text = "NOT decoded"
                }
            } else {
                streetLabel.text = "NOT unarchived"
                houseLabel.text = "NOT unarchived"
            }
        }
    }
}

func isMetadataItemDownloaded(item : NSMetadataItem) -> Bool {
    if item.value(forAttribute: NSMetadataUbiquitousItemDownloadingStatusKey) as? String == NSMetadataUbiquitousItemDownloadingStatusCurrent {
        return true
    } else {
        return false
    }
}


@IBAction func saveButtonPressed(_ sender: UIButton) {
    let container = filesCoordinator.iCloudContainer
    let personPath = filesCoordinator.getFilePath(container: container!, fileName: "TaskList")
    let addressPath = filesCoordinator.getFilePath(container: container!, fileName: "CategoryList")

    let person = Person(name: nameTextField.text!, weight: Double(weightTextField.text!)!)
    let jsonPersonData = try? JSONEncoder().encode(person)
    NSKeyedArchiver.archiveRootObject(jsonPersonData!, toFile: personPath)

    let address = Address(street: streetTextField.text!, house: Int(houseTextField.text!)!)
    let jsonAddressData = try? JSONEncoder().encode(address)
    NSKeyedArchiver.archiveRootObject(jsonAddressData!, toFile: addressPath)
}

}//结束class