简体   繁体   English

在iOS、SwiftUI的文档目录中保存数据

[英]Saving data in Documents Directory in iOS, SwiftUI

I cannot figure out how to save data (multiple properties) in Documents Directory with SwiftUI. I know two variants, 1st - when you have one array property and that works great, the problem here - I don't know how to add additional properties to it.我不知道如何在 SwiftUI 的文档目录中保存数据(多个属性)。我知道两种变体,第一种 - 当你有一个数组属性并且效果很好时,这里的问题 - 我不知道如何添加其他属性给它。 Or maybe it is normal to create about 3 swift-files like this for each property for one project.或者,为一个项目的每个属性创建大约 3 个这样的 swift-files 是正常的。

1 variant, saving in Documents Directory: 1 个变体,保存在文档目录中:

class Stack: ObservableObject {
    @Published var cards: [Card]
    static let saveKey = "SavedCards"

    init() {
        self.cards = []

        if let data = loadFile() {
            if let decoded = try? JSONDecoder().decode([Card].self, from: data) {
                self.cards = decoded
                return
            }
        }
    }

    func add(card: Card) {
        cards.insert(card, at: 0)
        save()
    }

    func save() {
        if let encoded = try? JSONEncoder().encode(cards) {
            saveFile(data: encoded)
        }
    }

    private func saveFile(data: Data) {
        let url = getDocumentsDirectory().appendingPathComponent(Self.saveKey)

        do {
            try data.write(to: url, options: [.atomicWrite, .completeFileProtection])
        } catch let error {
            print("Could not write data: " + error.localizedDescription)
        }
    }

    func loadFile() -> Data? {
        let url = getDocumentsDirectory().appendingPathComponent(Self.saveKey)

        if let data = try? Data(contentsOf: url) {
            return data
        }

        return nil
    }

    private func getDocumentsDirectory() -> URL {
        let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        return paths[0]
    }
}

The 2nd variant is to use CodingKeys for each property.第二种变体是为每个属性使用 CodingKeys。 But I cannot figure out how exactly I can add methods here and use it in other views, to save data from pressing a button for example.但是我无法弄清楚如何在此处添加方法并在其他视图中使用它,例如通过按下按钮来保存数据。 It seems like I have to encode and decode in every view over and over again for each change of data.似乎我必须在每个视图中为每次数据更改一遍又一遍地编码和解码。 It just seems wrong.这似乎是错误的。

2 variant with Coding Keys 2 种带编码键的变体

class Profile: Codable, ObservableObject {
    enum CodingKeys: CodingKey {
        case categories, playedCards, playedRounds
    }

    @Published var categories: [Category] = [Category.red, .green, .blue, .yellow, .pink, .gray]
    @Published var playedCards = 0
    @Published var playedRounds = 0

    init() { }

    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)

        categories = try container.decode([Category].self, forKey: .categories)
        playedCards = try container.decode(Int.self, forKey: .playedCards)
        playedRounds = try container.decode(Int.self, forKey: .playedRounds)
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)

        try container.encode(categories, forKey: .categories)
        try container.encode(playedCards, forKey: .playedCards)
        try container.encode(playedRounds, forKey: .playedRounds)
    }
}

So my question is how to use 1 variant with multiple variables.所以我的问题是如何将 1 个变体与多个变量一起使用。 Or if I should use the 2nd variant, how can I "nicely" encode and decode variables from other views.或者如果我应该使用第二个变体,我怎样才能“很好地”编码和解码来自其他视图的变量。

For now, my solution for 2nd variant is to write standard decode() / encode() methods in ContentView and use it in onDismiss: of Sheet Views to save data from different screens without repetitions.目前,我对第二个变体的解决方案是在 ContentView 中编写标准的 decode() / encode() 方法,并在onDismiss: of Sheet Views 中使用它来保存来自不同屏幕的数据而不重复。

func loadData() {
    let filename = getDocumentsDiretory().appendingPathComponent("SavedData")

    do {
        let data = try Data(contentsOf: filename)
        profile = try JSONDecoder().decode(Profile.self, from: data)
    } catch {
        print("Unable to load saved profile data")
    }
}

func saveData() {
    do {
        let filename = getDocumentsDiretory().appendingPathComponent("SavedData")
        let data = try JSONEncoder().encode(self.profile)
        try data.write(to: filename, options: [.atomic, .completeFileProtection])
    } catch {
        print("Unable to save profile data")
    }
}

func getDocumentsDiretory() -> URL {
    let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
    return paths[0]
}

.sheet(isPresented: $showingSheet, onDismiss: saveData) {
    ProfileView()
 }
 .onAppear(perform: loadData)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM