将数组保存到 CoreData Swift
Saving array to CoreData Swift
我想用Core Data
保存这种arrays
:
let crypto1 = Cryptos(name: "Bitcoin", code: "bitcoin", symbol: "BTC", placeholder: "BTC Amount", amount: "0.0")
let crypto2 = Cryptos(name: "Bitcoin Cash", code: "bitcoinCash", symbol: "BCH", placeholder: "BCH Amount", amount: "0.0")
这可能吗?
我知道我可以创建一个数组来保存...
let name = "Bitcoin"
let code = "bitcoin"
let symbol = "BTC"
let placeholder = "BTC Amount"
let amount = "0.0"
let cryptos = CryptoArray(context: PersistenceService.context)
cryptos.name = name
cryptos.code = code
cryptos.symbol = symbol
cryptos.placeholder = placeholder
cryptos.amount = amount
crypto.append(cryptos)
PersistenceService.saveContext()
...但是当用户创建理论上无限数量的数组时,这似乎很不方便。
我保存数据、加载数据、编辑数据和删除数据的最佳方式是什么?
CoreData不能直接保存数组,但是可以创建一个函数来保存数组的每个对象。使用 CoreStore 整个过程非常简单:
let dataStack: DataStack = {
let dataStack = DataStack(xcodeModelName: "ModelName")
do {
try dataStack.addStorageAndWait()
} catch let error {
print("Cannot set up database storage: \(error)")
}
return dataStack
}()
func addCrypto(name: String, code: String, symbol: String, placeholder: String, amount: Double) {
dataStack.perform(asynchronous: { transaction in
let crypto = transaction.create(Into<Crypto>())
crypto.name = name
crypto.code = code
crypto.symbol = symbol
crypto.placeholder = placeholder
crypto.amount = amount
}, completion: { _ in })
}
您可以显示 UITableViewController
内的对象。 CoreStore 能够在添加、删除或更新数据库对象时自动更新 table:
class CryptoTableViewController: UITableViewController {
let monitor = dataStack.monitorList(From<Crypto>(), OrderBy(.ascending("name")), Tweak({ fetchRequest in
fetchRequest.fetchBatchSize = 20
}))
override func viewDidLoad() {
super.viewDidLoad()
// Register self as observer to monitor
self.monitor.addObserver(self)
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.monitor.numberOfObjects()
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CryptoTableViewCell", for: indexPath) as! CryptoTableViewCell
let crypto = self.monitor[(indexPath as NSIndexPath).row]
cell.update(crypto)
return cell
}
}
// MARK: - ListObjectObserver
extension CryptoTableViewController : ListObjectObserver {
// MARK: ListObserver
func listMonitorWillChange(_ monitor: ListMonitor<Crypto>) {
self.tableView.beginUpdates()
}
func listMonitorDidChange(_ monitor: ListMonitor<Crypto>) {
self.tableView.endUpdates()
}
func listMonitorWillRefetch(_ monitor: ListMonitor<Crypto>) {
}
func listMonitorDidRefetch(_ monitor: ListMonitor<Crypto>) {
self.tableView.reloadData()
}
// MARK: ListObjectObserver
func listMonitor(_ monitor: ListMonitor<Crypto>, didInsertObject object: Switch, toIndexPath indexPath: IndexPath) {
self.tableView.insertRows(at: [indexPath], with: .automatic)
}
func listMonitor(_ monitor: ListMonitor<Crypto>, didDeleteObject object: Switch, fromIndexPath indexPath: IndexPath) {
self.tableView.deleteRows(at: [indexPath], with: .automatic)
}
func listMonitor(_ monitor: ListMonitor<Crypto>, didUpdateObject object: Crypto, atIndexPath indexPath: IndexPath) {
if let cell = self.tableView.cellForRow(at: indexPath) as? CryptoTableViewCell {
cell.update(object)
}
}
func listMonitor(_ monitor: ListMonitor<Crypto>, didMoveObject object: Switch, fromIndexPath: IndexPath, toIndexPath: IndexPath) {
self.tableView.deleteRows(at: [fromIndexPath], with: .automatic)
self.tableView.insertRows(at: [toIndexPath], with: .automatic)
}
}
假设您有一个 CryptoTableViewCell
函数 update
注册到 CryptoTableViewController
。
这是一个教程问题,而不是一个直接的答案。我建议你给点时间阅读一下 CoreData
。话虽如此,你的问题听起来很笼统,"Saving array to CoreData in Swift",所以我想一步一步解释一个简单的实现也没什么坏处:
第 1 步:创建模型文件 (.xcdatamodeld)
在Xcode、file - new - file - iOS
中选择Data Model
第 2 步:添加实体
Select Xcode 中的文件,找到并单击 Add Entity
,为您的实体命名(CryptosMO
继续),单击 Add Attribute
并添加您要存储的字段。 (在这种情况下,name, code, symbol...
都是 String
类型)。为了方便起见,我会忽略除 name
以外的所有内容。
步骤 3 生成这些实体的对象表示 (NSManagedObject)
在Xcode、Editor - Create NSManagedObject subclass
中并按照步骤进行。
第 4 步让我们创建此子的克隆class
NSManagedObject 不是 thread-safe 所以让我们创建一个可以安全传递的结构:
struct Cryptos {
var reference: NSManagedObjectID! // ID on the other-hand is thread safe.
var name: String // and the rest of your properties
}
第 5 步:CoreDataStore
让我们创建一个商店,让我们可以访问 NSManagedObjectContext
s:
class Store {
private init() {}
private static let shared: Store = Store()
lazy var container: NSPersistentContainer = {
// The name of your .xcdatamodeld file.
guard let url = Bundle().url(forResource: "ModelFile", withExtension: "momd") else {
fatalError("Create the .xcdatamodeld file with the correct name !!!")
// If you're setting up this container in a different bundle than the app,
// Use Bundle(for: Store.self) assuming `CoreDataStore` is in that bundle.
}
let container = NSPersistentContainer(name: "ModelFile")
container.loadPersistentStores { _, _ in }
container.viewContext.automaticallyMergesChangesFromParent = true
return container
}()
// MARK: APIs
/// For main queue use only, simple rule is don't access it from any queue other than main!!!
static var viewContext: NSManagedObjectContext { return shared.container.viewContext }
/// Context for use in background.
static var newContext: NSManagedObjectContext { return shared.container.newBackgroundContext() }
}
Store
使用您的 .xcdatamodeld
文件设置持久容器。
第 6 步:获取这些实体的数据源
Core Data 附带 NSFetchedResultsController
以从允许大量配置的上下文中获取实体,这里是使用此控制器支持数据源的简单实现。
class CryptosDataSource {
let controller: NSFetchedResultsController<NSFetchRequestResult>
let request: NSFetchRequest<NSFetchRequestResult> = CryptosMO.fetchRequest()
let defaultSort: NSSortDescriptor = NSSortDescriptor(key: #keyPath(CryptosMO.name), ascending: false)
init(context: NSManagedObjectContext, sortDescriptors: [NSSortDescriptor] = []) {
var sort: [NSSortDescriptor] = sortDescriptors
if sort.isEmpty { sort = [defaultSort] }
request.sortDescriptors = sort
controller = NSFetchedResultsController(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
}
// MARK: DataSource APIs
func fetch(completion: ((Result) -> ())?) {
do {
try controller.performFetch()
completion?(.success)
} catch let error {
completion?(.fail(error))
}
}
var count: Int { return controller.fetchedObjects?.count ?? 0 }
func anyCryptos(at indexPath: IndexPath) -> Cryptos {
let c: CryptosMO = controller.object(at: indexPath) as! CryptosMO
return Cryptos(reference: c.objectID, name: c.name)
}
}
我们需要从这个 class 的一个实例中得到的是对象的数量,count
和给定 indexPath 中的项目。请注意,数据源 returns 结构 Cryptos
而不是 NSManagedObject
.
的实例
第 7 步:用于添加、编辑和删除的 API
让我们将此 api 添加为 NSManagedObjectContext
的扩展:
但在此之前,这些操作可能会成功或失败,所以让我们创建一个枚举来反映这一点:
enum Result {
case success, fail(Error)
}
API:
extension NSManagedObjectContext {
// MARK: Load data
var dataSource: CryptosDataSource { return CryptosDataSource(context: self) }
// MARK: Data manupulation
func add(cryptos: Cryptos, completion: ((Result) -> ())?) {
perform {
let entity: CryptosMO = CryptosMO(context: self)
entity.name = cryptos.name
self.save(completion: completion)
}
}
func edit(cryptos: Cryptos, completion: ((Result) -> ())?) {
guard cryptos.reference != nil else {
print("No reference")
return
}
perform {
let entity: CryptosMO? = self.object(with: cryptos.reference) as? CryptosMO
entity?.name = cryptos.name
self.save(completion: completion)
}
}
func delete(cryptos: Cryptos, completion: ((Result) -> ())?) {
guard cryptos.reference != nil else {
print("No reference")
return
}
perform {
let entity: CryptosMO? = self.object(with: cryptos.reference) as? CryptosMO
self.delete(entity!)
self.save(completion: completion)
}
}
func save(completion: ((Result) -> ())?) {
do {
try self.save()
completion?(.success)
} catch let error {
self.rollback()
completion?(.fail(error))
}
}
}
第 8 步:最后一步,用例
要获取主队列中存储的数据,请使用Store.viewContext.dataSource
。
要添加、编辑或删除项目,请决定是否要使用 viewContext
在主队列中执行,或者使用 newContext
或从任何任意队列(甚至是主队列)或由使用 Store.container.performInBackground...
存储容器,这将公开一个上下文。
例如添加一个密码:
let cryptos: Cryptos = Cryptos(reference: nil, name: "SomeName")
Store.viewContext.add(cryptos: cryptos) { result in
switch result {
case .fail(let error): print("Error: ", error)
case .success: print("Saved successfully")
}
}
使用加密数据源的简单UITableViewController
:
class ViewController: UITableViewController {
let dataSource: CryptosDataSource = Store.viewContext.dataSource
// MARK: UITableViewDataSource
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataSource.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
return tableView.dequeueReusableCell(withIdentifier: "YourCellId", for: indexPath)
}
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
let cryptos: Cryptos = dataSource.anyCryptos(at: indexPath)
// TODO: Configure your cell with cryptos values.
}
}
我想用Core Data
保存这种arrays
:
let crypto1 = Cryptos(name: "Bitcoin", code: "bitcoin", symbol: "BTC", placeholder: "BTC Amount", amount: "0.0")
let crypto2 = Cryptos(name: "Bitcoin Cash", code: "bitcoinCash", symbol: "BCH", placeholder: "BCH Amount", amount: "0.0")
这可能吗?
我知道我可以创建一个数组来保存...
let name = "Bitcoin"
let code = "bitcoin"
let symbol = "BTC"
let placeholder = "BTC Amount"
let amount = "0.0"
let cryptos = CryptoArray(context: PersistenceService.context)
cryptos.name = name
cryptos.code = code
cryptos.symbol = symbol
cryptos.placeholder = placeholder
cryptos.amount = amount
crypto.append(cryptos)
PersistenceService.saveContext()
...但是当用户创建理论上无限数量的数组时,这似乎很不方便。
我保存数据、加载数据、编辑数据和删除数据的最佳方式是什么?
CoreData不能直接保存数组,但是可以创建一个函数来保存数组的每个对象。使用 CoreStore 整个过程非常简单:
let dataStack: DataStack = {
let dataStack = DataStack(xcodeModelName: "ModelName")
do {
try dataStack.addStorageAndWait()
} catch let error {
print("Cannot set up database storage: \(error)")
}
return dataStack
}()
func addCrypto(name: String, code: String, symbol: String, placeholder: String, amount: Double) {
dataStack.perform(asynchronous: { transaction in
let crypto = transaction.create(Into<Crypto>())
crypto.name = name
crypto.code = code
crypto.symbol = symbol
crypto.placeholder = placeholder
crypto.amount = amount
}, completion: { _ in })
}
您可以显示 UITableViewController
内的对象。 CoreStore 能够在添加、删除或更新数据库对象时自动更新 table:
class CryptoTableViewController: UITableViewController {
let monitor = dataStack.monitorList(From<Crypto>(), OrderBy(.ascending("name")), Tweak({ fetchRequest in
fetchRequest.fetchBatchSize = 20
}))
override func viewDidLoad() {
super.viewDidLoad()
// Register self as observer to monitor
self.monitor.addObserver(self)
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.monitor.numberOfObjects()
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CryptoTableViewCell", for: indexPath) as! CryptoTableViewCell
let crypto = self.monitor[(indexPath as NSIndexPath).row]
cell.update(crypto)
return cell
}
}
// MARK: - ListObjectObserver
extension CryptoTableViewController : ListObjectObserver {
// MARK: ListObserver
func listMonitorWillChange(_ monitor: ListMonitor<Crypto>) {
self.tableView.beginUpdates()
}
func listMonitorDidChange(_ monitor: ListMonitor<Crypto>) {
self.tableView.endUpdates()
}
func listMonitorWillRefetch(_ monitor: ListMonitor<Crypto>) {
}
func listMonitorDidRefetch(_ monitor: ListMonitor<Crypto>) {
self.tableView.reloadData()
}
// MARK: ListObjectObserver
func listMonitor(_ monitor: ListMonitor<Crypto>, didInsertObject object: Switch, toIndexPath indexPath: IndexPath) {
self.tableView.insertRows(at: [indexPath], with: .automatic)
}
func listMonitor(_ monitor: ListMonitor<Crypto>, didDeleteObject object: Switch, fromIndexPath indexPath: IndexPath) {
self.tableView.deleteRows(at: [indexPath], with: .automatic)
}
func listMonitor(_ monitor: ListMonitor<Crypto>, didUpdateObject object: Crypto, atIndexPath indexPath: IndexPath) {
if let cell = self.tableView.cellForRow(at: indexPath) as? CryptoTableViewCell {
cell.update(object)
}
}
func listMonitor(_ monitor: ListMonitor<Crypto>, didMoveObject object: Switch, fromIndexPath: IndexPath, toIndexPath: IndexPath) {
self.tableView.deleteRows(at: [fromIndexPath], with: .automatic)
self.tableView.insertRows(at: [toIndexPath], with: .automatic)
}
}
假设您有一个 CryptoTableViewCell
函数 update
注册到 CryptoTableViewController
。
这是一个教程问题,而不是一个直接的答案。我建议你给点时间阅读一下 CoreData
。话虽如此,你的问题听起来很笼统,"Saving array to CoreData in Swift",所以我想一步一步解释一个简单的实现也没什么坏处:
第 1 步:创建模型文件 (.xcdatamodeld)
在Xcode、file - new - file - iOS
中选择Data Model
第 2 步:添加实体
Select Xcode 中的文件,找到并单击 Add Entity
,为您的实体命名(CryptosMO
继续),单击 Add Attribute
并添加您要存储的字段。 (在这种情况下,name, code, symbol...
都是 String
类型)。为了方便起见,我会忽略除 name
以外的所有内容。
步骤 3 生成这些实体的对象表示 (NSManagedObject)
在Xcode、Editor - Create NSManagedObject subclass
中并按照步骤进行。
第 4 步让我们创建此子的克隆class
NSManagedObject 不是 thread-safe 所以让我们创建一个可以安全传递的结构:
struct Cryptos {
var reference: NSManagedObjectID! // ID on the other-hand is thread safe.
var name: String // and the rest of your properties
}
第 5 步:CoreDataStore
让我们创建一个商店,让我们可以访问 NSManagedObjectContext
s:
class Store {
private init() {}
private static let shared: Store = Store()
lazy var container: NSPersistentContainer = {
// The name of your .xcdatamodeld file.
guard let url = Bundle().url(forResource: "ModelFile", withExtension: "momd") else {
fatalError("Create the .xcdatamodeld file with the correct name !!!")
// If you're setting up this container in a different bundle than the app,
// Use Bundle(for: Store.self) assuming `CoreDataStore` is in that bundle.
}
let container = NSPersistentContainer(name: "ModelFile")
container.loadPersistentStores { _, _ in }
container.viewContext.automaticallyMergesChangesFromParent = true
return container
}()
// MARK: APIs
/// For main queue use only, simple rule is don't access it from any queue other than main!!!
static var viewContext: NSManagedObjectContext { return shared.container.viewContext }
/// Context for use in background.
static var newContext: NSManagedObjectContext { return shared.container.newBackgroundContext() }
}
Store
使用您的 .xcdatamodeld
文件设置持久容器。
第 6 步:获取这些实体的数据源
Core Data 附带 NSFetchedResultsController
以从允许大量配置的上下文中获取实体,这里是使用此控制器支持数据源的简单实现。
class CryptosDataSource {
let controller: NSFetchedResultsController<NSFetchRequestResult>
let request: NSFetchRequest<NSFetchRequestResult> = CryptosMO.fetchRequest()
let defaultSort: NSSortDescriptor = NSSortDescriptor(key: #keyPath(CryptosMO.name), ascending: false)
init(context: NSManagedObjectContext, sortDescriptors: [NSSortDescriptor] = []) {
var sort: [NSSortDescriptor] = sortDescriptors
if sort.isEmpty { sort = [defaultSort] }
request.sortDescriptors = sort
controller = NSFetchedResultsController(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
}
// MARK: DataSource APIs
func fetch(completion: ((Result) -> ())?) {
do {
try controller.performFetch()
completion?(.success)
} catch let error {
completion?(.fail(error))
}
}
var count: Int { return controller.fetchedObjects?.count ?? 0 }
func anyCryptos(at indexPath: IndexPath) -> Cryptos {
let c: CryptosMO = controller.object(at: indexPath) as! CryptosMO
return Cryptos(reference: c.objectID, name: c.name)
}
}
我们需要从这个 class 的一个实例中得到的是对象的数量,count
和给定 indexPath 中的项目。请注意,数据源 returns 结构 Cryptos
而不是 NSManagedObject
.
第 7 步:用于添加、编辑和删除的 API
让我们将此 api 添加为 NSManagedObjectContext
的扩展:
但在此之前,这些操作可能会成功或失败,所以让我们创建一个枚举来反映这一点:
enum Result {
case success, fail(Error)
}
API:
extension NSManagedObjectContext {
// MARK: Load data
var dataSource: CryptosDataSource { return CryptosDataSource(context: self) }
// MARK: Data manupulation
func add(cryptos: Cryptos, completion: ((Result) -> ())?) {
perform {
let entity: CryptosMO = CryptosMO(context: self)
entity.name = cryptos.name
self.save(completion: completion)
}
}
func edit(cryptos: Cryptos, completion: ((Result) -> ())?) {
guard cryptos.reference != nil else {
print("No reference")
return
}
perform {
let entity: CryptosMO? = self.object(with: cryptos.reference) as? CryptosMO
entity?.name = cryptos.name
self.save(completion: completion)
}
}
func delete(cryptos: Cryptos, completion: ((Result) -> ())?) {
guard cryptos.reference != nil else {
print("No reference")
return
}
perform {
let entity: CryptosMO? = self.object(with: cryptos.reference) as? CryptosMO
self.delete(entity!)
self.save(completion: completion)
}
}
func save(completion: ((Result) -> ())?) {
do {
try self.save()
completion?(.success)
} catch let error {
self.rollback()
completion?(.fail(error))
}
}
}
第 8 步:最后一步,用例
要获取主队列中存储的数据,请使用Store.viewContext.dataSource
。
要添加、编辑或删除项目,请决定是否要使用 viewContext
在主队列中执行,或者使用 newContext
或从任何任意队列(甚至是主队列)或由使用 Store.container.performInBackground...
存储容器,这将公开一个上下文。
例如添加一个密码:
let cryptos: Cryptos = Cryptos(reference: nil, name: "SomeName")
Store.viewContext.add(cryptos: cryptos) { result in
switch result {
case .fail(let error): print("Error: ", error)
case .success: print("Saved successfully")
}
}
使用加密数据源的简单UITableViewController
:
class ViewController: UITableViewController {
let dataSource: CryptosDataSource = Store.viewContext.dataSource
// MARK: UITableViewDataSource
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataSource.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
return tableView.dequeueReusableCell(withIdentifier: "YourCellId", for: indexPath)
}
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
let cryptos: Cryptos = dataSource.anyCryptos(at: indexPath)
// TODO: Configure your cell with cryptos values.
}
}