I get strange bug when I'm supposed to create my ProfileViewModel
. I'm creating ViewModel as @StateObject
then passing it to other views via .environmentObject()
. Unfortunately when I'm launching simulator I have blank black screen only when this particular ViewModel is created. When I comment the line that creates it, I get the content on the screen.
I'm creating it here:
@StateObject private var profileViewModel = ProfileViewModel()
I'm passing it to other views here:
case .profile:
withAnimation(.linear) {
ProfileView()
.environmentObject(authStateManager)
.environmentObject(tabBarStateManager)
.environmentObject(profileViewModel)
}
My ProfileViewModel
looks like this:
import UIKit
class ProfileViewModel: ObservableObject {
@Published var profile: Profile = Profile.demoProfile
@Published var orders: [Order] = Order.demoOrders
@Published var returns: [Return] = Return.demoReturns
@Published var oldImage = UIImage(named: "blank_profile_image")!
@Published var image = UIImage(named: "blank_profile_image")!
@Published var shouldPresentOrderRateView: Bool = false
@Published var shouldPresentReturnCreationView: Bool = false
var datesForOrdersViewListSections: [String] {
var ordersShortDates: [String] = []
for order in orders {
ordersShortDates.append(Date.getMonthNameAndYearFrom(date: order.orderDate))
}
return ordersShortDates.uniqued().sorted { firstDate, secondDate in
firstDate.suffix(4) > secondDate.suffix(4)
}
}
var datesForReturnsViewListSections: [String] {
var returnsShortDates: [String] = []
for userReturn in returns {
returnsShortDates.append(Date.getMonthNameAndYearFrom(date: userReturn.returnDate))
}
return returnsShortDates.uniqued().sorted { firstDate, secondDate in
firstDate.suffix(4) > secondDate.suffix(4)
}
}
func uploadPhoto() {
if !image.isEqual(oldImage) {
oldImage = image
}
}
func getOrdersFor(date: String) -> [Order] {
return orders.filter {
Date.getMonthNameAndYearFrom(date: $0.orderDate) == date
}
}
func getReturnsFor(date: String) -> [Return] {
return returns.filter {
Date.getMonthNameAndYearFrom(date: $0.returnDate) == date
}
}
func changeDefaultAddress(address: Address) {
removeAddress(address: address)
profile.otherAddresses.append(profile.address)
profile.address = address
}
func removeAddress(address: Address) {
for (index, otherAddress) in profile.otherAddresses.enumerated() {
if otherAddress == address {
profile.otherAddresses.remove(at: index)
break
}
}
}
func editPersonalData(firstName: String = "", lastName: String = "", emailAddress: String = "") {
if !firstName.isEmpty {
profile.firstName = firstName
}
if !lastName.isEmpty {
profile.lastName = lastName
}
if !emailAddress.isEmpty {
profile.email = emailAddress
}
}
func addNewAddress(address: Address, toBeDefault: Bool = false) {
if toBeDefault {
profile.otherAddresses.append(profile.address)
profile.address = address
} else {
profile.otherAddresses.append(address)
}
}
func editCardData(cardNumber: String, validThru: String, cardholderName: String) {
if profile.creditCard != nil {
profile.creditCard!.cardNumber = cardNumber
profile.creditCard!.validThru = validThru
profile.creditCard!.cardholderName = cardholderName
}
}
func addNewCard(card: CreditCard) {
profile.creditCard = card
}
func changeDefaultPaymentMethod(newDefaultPaymentMethod: PaymentMethod) {
profile.defaultPaymentMethod = newDefaultPaymentMethod
}
func addUserRating(productID: String, rating: Int, review: String?) {
profile.addRatingFor(productID: productID, rating: rating, review: review)
}
}
To be honest, I don't know why it occured. Until some moment everything worked fine. I made some changes in logic not connected to ViewModel and I installed KingFisher package
from xcode package manager
. Then I uninstalled it because I no longer needed it. Everything started right before uninstalling it. I cannot link any of my actions that could be causing it, nor have I made any changes to ProfileViewModel
since then.
I have already tried:
- Restarting xcode and my mac
- Cleaning derived data folder
- Discarding (using GIT) any changes I made to code
Xcode console shows no output, no errors, everything builds fine
Actually I have found out now that my app do not want to show anything because of two properties of ProfileViewModel
:
@Published var orders: [Order] = Order.demoOrders
@Published var returns: [Return] = Return.demoReturns
When both these structures are commented out, everything works as expected.
Structures of above mentioned:
Order
import Foundation
struct Order {
var id: String = UUID().uuidString
var orderDate: Date = Date()
var estimatedDeliveryDate: Date
var client: Profile
var shoppingCart: Cart
var shippingMethod: ShippingMethod
var shippingAddress: Address
var paymentMethod: PaymentMethod = .creditCard
var invoice: Bool
var totalCost: Double
var status: OrderStatus = .placed
init(client: Profile, shoppingCart: Cart, shippingMethod: ShippingMethod, shippingAddress: Address, paymentMethod: PaymentMethod = .creditCard, invoice: Bool = false) {
self.client = client
self.shoppingCart = shoppingCart
self.shippingMethod = shippingMethod
self.shippingAddress = shippingAddress
self.paymentMethod = paymentMethod
self.invoice = invoice
self.estimatedDeliveryDate = calculateEstimatedDeliveryDate(orderDate: Date())
self.totalCost = shoppingCart.products.keys.map { $0.price }.reduce(0, )
}
}
extension Order: Equatable, Hashable {
static func == (lhs: Order, rhs: Order) -> Bool {
return lhs.id == rhs.id
}
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
}
extension Order: CustomStringConvertible {
var description: String {
"\(id)\nOrder Date: \(Date.getDayMonthYearFrom(date: orderDate))\nEstimated Delivery Date: \(Date.getDayMonthYearFrom(date: estimatedDeliveryDate))\nShipping Method: \(shippingMethod.rawValue)\nPayment Method: \(paymentMethod.rawValue)\nTotal Cost: \(totalCost)\nStatus: \(status)"
}
}
extension Order {
static let demoOrders: [Order] = [Order(client: Profile.demoProfile,
shoppingCart: Cart.demoCart,
shippingMethod: .pickup,
shippingAddress: Address.demoAddress),
Order(client: Profile.demoProfile,
shoppingCart: Cart.demoCart,
shippingMethod: .parcel,
shippingAddress: Address.demoAddress),
Order(client: Profile.demoProfile,
shoppingCart: Cart.demoCart,
shippingMethod: .parcel,
shippingAddress: Address.demoAddress),
Order(client: Profile.demoProfile,
shoppingCart: Cart.demoCart,
shippingMethod: .parcel,
shippingAddress: Address.demoAddress)]
}
and Return
:
import Foundation
struct Return {
var id: String = UUID().uuidString
var returnDate: Date = Date()
var clientID: String
var orderID: String
var products: [Product]
var returnPrice: Double
var returnMethod: ShippingMethod
var status: ReturnStatus = .reported
var bankAccountNumber: String = ""
var bankAccountOwnerName: String = ""
var bankAccountOwnerStreetAndHouseNumber: String = ""
var bankAccountOwnerPostalCode: String = ""
var bankAccountOwnerCity: String = ""
var bankAccountOwnerCountry: String = ""
}
enum ReturnStatus: String {
case reported = "Reported"
case sent = "Sent"
case delivered = "Delivered"
case moneyReturned = "Money returned"
case closed = "Closed"
}
extension Return: Equatable, Hashable {
static func == (lhs: Return, rhs: Return) -> Bool {
return lhs.id == rhs.id
}
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
}
extension Return: CustomStringConvertible {
var description: String {
"\(id)\nReturn Date: \(Date.getDayMonthYearFrom(date: returnDate))\nClient ID: \(clientID)\nOrder ID: \(orderID)\nReturn Price: \(returnPrice)\nReturn Method: \(returnMethod.rawValue)\nStatus: \(status.rawValue)"
}
}
extension Return {
static let demoReturns: [Return] = [Return(id: UUID().uuidString,
returnDate: Date(),
clientID: Profile.demoProfile.id,
orderID: Order.demoOrders[0].id,
products: Product.demoProducts,
returnPrice: Order.demoOrders[0].totalCost,
returnMethod: Order.demoOrders[0].shippingMethod,
status: .reported),
Return(id: UUID().uuidString,
returnDate: Date(),
clientID: Profile.demoProfile.id,
orderID: Order.demoOrders[1].id,
products: Product.demoProducts,
returnPrice: Order.demoOrders[1].totalCost,
returnMethod: Order.demoOrders[1].shippingMethod,
status: .reported)]
}
I tried removing single properties from those structs like Profile, Cart or Products but problem remained.
CodePudding user response:
It was the strangest bug I have ever seen using Swift. When I discovered the problem is connected with
@Published var orders: [Order] = Order.demoOrders
@Published var returns: [Return] = Return.demoReturns
structures constructed in ProfileViewModel
then I tried to create them from scratch starting from orders
like:
@Published var orders: [Order] = [Order(client: Profile(firstName: "",
lastName: "",
username: "",
birthDate: Date(),
email: "",
address: Address(streetName: "",
streetNumber: "",
apartmentNumber: "",
zipCode: "",
city: "",
country: "")),
shoppingCart: Cart(),
shippingMethod: .courier,
shippingAddress: Address(streetName: "",
streetNumber: "",
apartmentNumber: "",
zipCode: "",
city: "",
country: ""))]
And all of the sudden everything started to work, so I reverted back to:
@Published var orders: [Order] = Order.demoOrders
@Published var returns: [Return] = Return.demoReturns
And there was no black screen anymore! Don't know exactly what might have been causing it.