【发布时间】:2021-07-15 04:07:10
【问题描述】:
尽管我了解子集合和 SwiftUI 背后的理论,但我很难将其应用于代码。这个想法是创建一个供应商,该供应商拥有品牌,而品牌又拥有型号、颜色、尺寸等。我正在创建一个患者/库存管理应用程序,我复制了患者代码(我从 @ 那里得到了很多帮助peterfriese)到库存之一,只要我需要更改几行以使子集合能够自行创建,但我完全感到困惑。我在将代码应用到视图中时遇到的困难与对 viewModel 一样。出于某种原因,我也遇到了为我的供应商保存编辑的问题,它只是添加了一个条目而不是编辑它。这是我的代码。
供应商列表视图
import SwiftUI
struct SupplierListView: View {
@ObservedObject private var viewModel = SupplierViewModel()
@State private var presentAddNewSupplierScreen = false
private var addButton: some View {
Button(action: { self.presentAddNewSupplierScreen.toggle() }) {
Image(systemName: "plus")
}
}
private func supplierRowView(supplier: SupplierModel) -> some View {
NavigationLink(destination: SupplierDetailView(supplier: supplier)) {
VStack(alignment: .leading) {
Text(supplier.supplier ?? "")
.font(.headline)
}
}
}
var body: some View {
NavigationView {
List {
ForEach (viewModel.suppliers) { supplier in
supplierRowView(supplier: supplier)
}
.onDelete() { indexSet in
viewModel.removeSuppliers(atOffsets: indexSet)
}
}
.navigationBarTitle("Suppliers")
.navigationBarItems(trailing: addButton)
.onAppear() {
print("SupplierListView appears. Subscribing to data updates.")
self.viewModel.subscribe()
}
.onDisappear() {
// By unsubscribing from the view model, we prevent updates coming in from
// Firestore to be reflected in the UI. Since we do want to receive updates
// when the user is on any of the child screens, we keep the subscription active!
//
// print("BooksListView disappears. Unsubscribing from data updates.")
// self.viewModel.unsubscribe()
}
.sheet(isPresented: self.$presentAddNewSupplierScreen) {
SupplierEditView()
}
}
}
}
struct SupplierListView_Previews: PreviewProvider {
static var previews: some View {
SupplierListView()
}
}
供应商编辑视图
import SwiftUI
enum ModeSupplier {
case new
case edit
}
enum ActionSupplier {
case delete
case done
case cancel
}
extension Optional where Wrapped == String {
var _boundSupplier: String? {
get {
return self
}
set {
self = newValue
}
}
public var boundSupplier: String {
get {
return _boundSupplier ?? ""
}
set {
_boundSupplier = newValue.isEmpty ? nil : newValue
}
}
}
struct SupplierEditView: View {
@Environment(\.presentationMode) var presentationMode
@State var presentActionSheet = false
@ObservedObject var viewModel = SupplierDetailViewModel()
var mode: Mode = .new
var completionHandler: ((Result<Action, Error>) -> Void)?
var cancelButton: some View {
Button(action: { self.handleCancelTapped() }) {
Text("Cancel")
}
}
var saveButton: some View {
Button(action: { self.handleDoneTapped() }) {
Text(mode == .new ? "Done" : "Save")
}
.disabled(!viewModel.modified)
}
var body: some View {
NavigationView {
Form {
Section(header: Text("Supplier Name")) {
TextField("Supplier Name", text: $viewModel.supplier.supplier.bound)
}
if mode == .edit {
Section {
Button("Delete Supplier") { self.presentActionSheet.toggle() }
.foregroundColor(.red)
}
}
}
.navigationBarTitle("New Supplier", displayMode: .inline)
.navigationBarTitleDisplayMode(mode == .new ? .inline : .large)
.navigationBarItems(
leading: cancelButton,
trailing: saveButton
)
.actionSheet(isPresented: $presentActionSheet) {
ActionSheet(title: Text("Are you sure?"),
buttons: [
.destructive(Text("Delete Supplier"),
action: { self.handleDeleteTapped() }),
.cancel()
])
}
}
}
func handleCancelTapped() {
dismiss()
}
func handleDoneTapped(){
viewModel.saveSupplier()
dismiss()
}
func handleDeleteTapped() {
viewModel.handleDeleteTapped()
self.dismiss()
self.completionHandler?(.success(.delete))
}
func dismiss(){
presentationMode.wrappedValue.dismiss()
}
}
struct SupplierEditView_Previews: PreviewProvider {
static var previews: some View {
let supplier = SupplierModel(id: "", supplier: "")
let supplierViewModel = SupplierDetailViewModel(supplier: supplier)
return SupplierEditView(viewModel: supplierViewModel, mode: .edit)
}
}
供应商详情视图
import SwiftUI
struct SupplierDetailView: View {
@Environment(\.presentationMode) var presentationMode
@State var presentEditSupplierSheet = false
var supplier: SupplierModel
private func editButton(action: @escaping () -> Void) -> some View {
Button(action: { action() }) {
Text("Edit")
}
}
var body: some View {
Form {
Section(header: Text("Supplier Name")) {
Text(supplier.supplier ?? "")
}
}
.navigationBarTitle(supplier.supplier ?? "")
.navigationBarItems(trailing: editButton {
self.presentEditSupplierSheet.toggle()
})
.onAppear() {
print("SupplierDetailView.onAppear() for \(self.supplier.supplier)")
}
.onDisappear() {
print("SupplierDetailView.onDisappear()")
}
.sheet(isPresented: self.$presentEditSupplierSheet) {
SupplierEditView(viewModel: SupplierDetailViewModel(supplier: supplier), mode: .edit) { result in
if case .success(let action) = result, action == .delete {
self.presentationMode.wrappedValue.dismiss()
}
}
}
}
}
struct SupplierDetailView_Previews: PreviewProvider {
static var previews: some View {
let supplier = SupplierModel(id: "", supplier: "")
return
NavigationView{
SupplierDetailView(supplier: supplier)
}
}
}
供应商模型
import Foundation
import FirebaseFirestoreSwift
struct SupplierModel: Identifiable, Codable {
@DocumentID var id : String? = UUID().uuidString
var supplier: String?
}
供应商模型视图
import Foundation
import FirebaseFirestore
import FirebaseFirestoreSwift
class SupplierViewModel: ObservableObject {
@Published var suppliers = [SupplierModel]()
private var db = Firestore.firestore()
private var listenerRegistration: ListenerRegistration?
deinit {
unsubscribe()
}
func unsubscribe() {
if listenerRegistration != nil {
listenerRegistration?.remove()
listenerRegistration = nil
}
}
func subscribe() {
if listenerRegistration == nil {
listenerRegistration = db.collection("suppliers").addSnapshotListener { (querySnapshot, error) in
guard let documents = querySnapshot?.documents else {
print("No suppliers")
return
}
self.suppliers = documents.compactMap { queryDocumentSnapshot -> SupplierModel? in
return try? queryDocumentSnapshot.data(as: SupplierModel.self)
}
}
}
}
func removeSuppliers(atOffsets indexSet: IndexSet) {
let suppliers = indexSet.lazy.map { self.suppliers[$0] }
suppliers.forEach { supplier in
if let documentId = supplier.id {
db.collection("suppliers").document(documentId).delete { error in
if let error = error {
print("Unable to remove document: \(error.localizedDescription)")
}
}
}
}
}
}
SupplierDetailModelView
import Foundation
import Firebase
import Combine
class SupplierDetailViewModel: ObservableObject {
@Published var supplier: SupplierModel
@Published var modified = false
private var db = Firestore.firestore()
private var cancellables = Set<AnyCancellable>()
init(supplier: SupplierModel = SupplierModel(id: "", supplier: "")) {
self.supplier = supplier
self.$supplier
.dropFirst()
.sink { [weak self] supplier in
self?.modified = true
}
.store(in: &cancellables)
}
func addSupplier(supplier: SupplierModel){
do {
let _ = try db.collection("suppliers").addDocument(from: supplier)
}
catch {
print(error)
}
}
private func updateSupplier(_ supplier: SupplierModel) {
if let documentId = supplier.id {
do {
try db.collection("suppliers").document(documentId).setData(from: supplier)
}
catch {
print(error)
}
}
}
private func updateOrAddSupplier() {
if let _ = supplier.id {
self.updateSupplier(self.supplier)
}
else {
addSupplier(supplier: supplier)
}
}
private func removeSupplier() {
if let documentId = supplier.id {
db.collection("supplier").document(documentId).delete { error in
if let error = error {
print(error.localizedDescription)
}
}
}
}
func handleDoneTapped() {
self.updateOrAddSupplier()
}
func handleDeleteTapped() {
self.removeSupplier()
}
func saveSupplier(){
addSupplier(supplier: supplier)
}
}
【问题讨论】:
-
我不清楚,看完这个,具体问题是什么。
-
@jnpdx 很抱歉不够清晰。如何使用 firestore 和 swiftUI 在我的供应商上创建品牌子集?
-
有很多代码供我们解析。请花点时间查看How to create a Minimal, Complete, and Verifiable example。就子集合而言,你为什么需要一个子集合——为什么不只是一个集合?供应商有品牌,但许多供应商都有品牌,那么为什么不让品牌成为顶级系列呢?模型听起来也很高级。雪佛兰品牌有汽车模型(文件)。但是这些模型的颜色和大小听起来像是每个模型的属性。你能缩短并澄清问题吗?
-
@Jay 我想创建一个子集合,以便能够创建一个供应商,该供应商的品牌具有颜色、尺寸,而这些品牌又与发票编号相关联。因此,通过阅读 firestore 中的子系列,我发现为供应商创建一个系列,然后为具有型号、颜色、尺寸等的品牌创建一个子系列是最好的方法。你是说收集足以涵盖所有这些吗?如果是这样,您是否有可以帮助我解决此问题的文档?对于我的问题不够明确,我深表歉意。
标签: swift firebase google-cloud-firestore swiftui