skip to Main Content

I wrote a working function for the application, but the error came out "The nil value was unexpectedly found when an optional value was implicitly deployed" limit Limit label.the text I can’t fix.

Properties:

@IBOutlet weak var limitLabel: UILabel!

Function:

func leftLabels(){ 
        let limit = self.realm.objects(Limit.self)
        guard limit.isEmpty == false else {return} 
        
        limitLabel.text = limit[0].limitSum //Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value 
        
        let calendar = Calendar.current 
        
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy/MM/dd HH:mm"
        
        let firstDay = limit[0].limitDate as Date
        let lastDay = limit[0].limitLastDate as Date
        
        let firstComponent = calendar.dateComponents([.year, .month, .day], from: firstDay) 
        let lastComponent = calendar.dateComponents([.year, .month, .day], from: lastDay) 
        
        let startDate = formatter.date(from: "(firstComponent.year!)/(firstComponent.month!)/(firstComponent.day!) 00:00") 

        let endDate = formatter.date(from: "(lastComponent.year!)/(lastComponent.month!)/(lastComponent.day!) 23:59")
        
        let filterLimit: Int = realm.objects(SpendingDB.self).filter("self.date >= %@ && self.date <= %@", startDate ?? "", endDate ?? "").sum(ofProperty: "cost")
        
        ForThePeriod.text = "(filterLimit)" 
        
        let a = Int(limitLabel.text!)!
        let b = Int(ForThePeriod.text!)!
        let c = a - b 
        
        availableForSpending.text = "(c)" 

I will be glad if you tell me the correct code

enter image description here

2

Answers


  1. As from comments if appears that your view is not yet loaded and some of your views are still nil. Your app crashes because in line limitLabel.text = limit[0].limitSum the limitLabel is nil. It would crash regardless of Realm even by calling limitLabel.text = "Hello world!"

    You can always guard data that you need to avoid changes in your code. Simply add

    guard let limitLabel = limitLabel else { return nil } 
    guard let ForThePeriod = ForThePeriod else { return nil }
    

    and so on.

    I tried to clean up your code a bit. It is hard to understand what exactly are you trying to achieve but something like the following may seem a bit more appropriate:

    func leftLabels() {
        // Elements needed for method to execute.
        guard let limitLabel = limitLabel else { return }
        guard let forThePeriodLabel = forThePeriodLabel else { return }
        guard let availableForSpendingLabel = availableForSpendingLabel else { return }
        
        // Items that will be reused throughout the method later on
        let limits: [Limit]
        let firstLimit: Limit
        let dates: (start: Date?, end: Date?)
        let filterLimit: Int
        
        limits = self.realm.objects(Limit.self)
        guard limits.isEmpty == false else { return }
        firstLimit = limits[0]
        
        // limitLabel
        limitLabel.text = firstLimit.limitSum
        
        // Date components
        dates = {
            let calendar = Calendar.current
            let formatter = DateFormatter()
            formatter.dateFormat = "yyyy/MM/dd HH:mm"
            
            let firstDay = firstLimit.limitDate as Date
            let lastDay = firstLimit.limitLastDate as Date
            
            let firstComponent = calendar.dateComponents([.year, .month, .day], from: firstDay)
            let lastComponent = calendar.dateComponents([.year, .month, .day], from: lastDay)
            
            let startDate = formatter.date(from: "(firstComponent.year!)/(firstComponent.month!)/(firstComponent.day!) 00:00")
            let endDate = formatter.date(from: "(lastComponent.year!)/(lastComponent.month!)/(lastComponent.day!) 23:59")
            
            return (startDate, endDate)
        }()
        
        // forThePeriodLabel
        filterLimit = realm.objects(SpendingDB.self).filter("self.date >= %@ && self.date <= %@", startDate ?? "", endDate ?? "").sum(ofProperty: "cost")
        forThePeriodLabel.text = String(filterLimit)
        
        // availableForSpendingLabel
        availableForSpendingLabel.text = {
            guard let a = Int(firstLimit.limitSum) else { return "" }
            let b = filterLimit
            let c = a - b
            return String(c)
        }()
    }
    

    Note some practices which help you better to structure and solve your code.

    • Guard dangerous data at first
    • Create a list of reusable items for your method (there should be as fewer as possible, in most cases none). Note how these can be later assigned to. And if you try using it before assigning to it, you will be warned by your compiler.
    • Wrap as much code into closed sections such as availableForSpendingLabel.text = { ... code here ... }()
    • Use tuples such as let dates: (start: Date?, end: Date?)
    • Don’t be afraid of using long names such as availableForSpendingLabel

    I would even further try and break this down into multiple methods. But I am not sure what this method does and assume that you have posted only part of it…

    ========== EDIT: Adding alternate approach ==========

    From comments this is a financial application so probably at least dealing with Decimal numbers would make sense. Also introducing approach with adding a new structure which resolves data internally. A formatter is also used to format the number. And some other improvements:

    struct Limit {
        let amount: Decimal
        let startDate: Date
        let endDate: Date
    }
    
    struct Spending {
        let cost: Decimal
        let date: Date
    }
    
    struct LimitReport {
        let limitAmount: Decimal
        let spendingSum: Decimal
        let balance: Decimal
        
        init(limit: Limit) {
            let limitAmount: Decimal = limit.amount
            let spendingSum: Decimal = {
                let calendar = Calendar.autoupdatingCurrent // Is this OK or should it be some UTC or something?
                func beginningOfDate(_ date: Date) -> Date {
                    let components = calendar.dateComponents([.day, .month, .year], from: date)
                    return calendar.date(from: components)!
                }
                let startDate = beginningOfDate(limit.startDate)
                let endDate = calendar.date(byAdding: .day, value: 1, to: startDate)
                
                let spendings: [Spending] = realm.objects(Spending.self).filter { $0.date >= startDate && $0.date < endDate }
                return spendings.reduce(0, { $0 + $1.cost })
            }()
            let balance = limitAmount - spendingSum
            
            self.limitAmount = limitAmount
            self.spendingSum = spendingSum
            self.balance = balance
        }
        
    }
    
    func leftLabels() {
        // Elements needed for method to execute.
        guard let limitLabel = limitLabel else { return }
        guard let forThePeriodLabel = forThePeriodLabel else { return }
        guard let availableForSpendingLabel = availableForSpendingLabel else { return }
        
        guard let limit = self.realm.objects(Limit.self).first else { return }
        
        let formatter = NumberFormatter()
        formatter.numberStyle = .currency
        formatter.currencySymbol = "$"
        
        let report = LimitReport(limit: limit)
        
        limitLabel.text = formatter.string(from: report.limitAmount)
        forThePeriodLabel.text = formatter.string(from: report.spendingSum)
        availableForSpendingLabel.text = formatter.string(from: report.balance)
    }
    
    Login or Signup to reply.
  2. Matic provided a good, comprehensive answer to your question (voted). I thought I’d provide an answer narrowly focused on your crash and a "short and sweet" way to fix it:

    The line in question could crash 2 different ways:

    limitLabel.text = limit[0].limitSum //Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value 
    

    Your limitLabel IBOutlet is declared as an "implicitly unwrapped Optional" (Note the ! after the type, UILabel:

    @IBOutlet weak var limitLabel: UILabel!
    

    An implicitly unwrapped Optional is an Optional where, essentially, the compiler adds a hidden "!" force-unwrap every time you try to reference that object.

    That means that

    limitLabel.text = //something
    

    Is compiled as

    limitLabel!.text = //something
    

    and if limitLabel is nil, you crash.

    If you call your leftLabels() function before your view has been loaded, or if that outlet is never connected, you will crash.

    You can fix that by adding an optional unwrap to the statement:

    limitLabel?.text = //something
    

    (That construct is known as "optional chaining".)

    Given that the crash message you’re getting mentions "implicitly unwrapping an Optional value" it’s likely that that is what is crashing in your case. However, you should fix the other issue as well.

    The second way you can crash is in your array indexing.

        limitLabel.text = limit[0].limitSum  
    

    When you fetch an object from an array by index, your app will crash if the array does not contain an item at that index. The expression limit[0] will crash if the limit array is empty.

    The array type has a computed property first that will return an optional if the array is empty.
    You should change that to limit.first?.limitSum.

    Change the whole line to be:

        limitLabel?.text = limit.first()?.limitSum
    

    And it won’t crash any more.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search