Daltron / NotificationBanner

The easiest way to display highly customizable in app notification banners in iOS
MIT License
4.78k stars 662 forks source link

Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value #307

Closed Surferdude667 closed 2 years ago

Surferdude667 commented 4 years ago

Hi there! This looks awesome, however I can't make it work.

I added this in my viewDidLoad()

Skærmbillede 2020-06-24 kl  16 39 57

But when I run the app it crashes on this line:

Skærmbillede 2020-06-24 kl  16 40 13
abeticlairon commented 4 years ago

Same thing happens to me!

burhanshakir commented 4 years ago

Same here, but only seems to be happening on device, works fine on the simulator

FakeAK commented 4 years ago

Same thing here, it happens randomly for me, sometimes it happens sometimes not, can't reproduce it all the time

Surferdude667 commented 4 years ago

https://github.com/Daltron/NotificationBanner/issues/284#issuecomment-582237682

Look at this. I managed to fix it with this approach :) (However this is an issue)

angi-betancourt commented 4 years ago

I have the same issue but on a different line: Fatal error: Unexpectedly found nil while unwrapping an Optional value: file /Users/isw/Documents/Home/AbarroTICs/frontend-ios/Código/Pods/NotificationBannerSwift/NotificationBanner/Classes/BaseNotificationBanner.swift, line 149 Screen Shot 2020-07-30 at 13 55 58

It's an iphone app ios12+ with pod 'NotificationBannerSwift', '~> 3.0.0' I have this class that centralize notification banner usage Screen Shot 2020-07-30 at 14 01 28 And I have this on an IBAction @ a ViewController Screen Shot 2020-07-30 at 14 02 00

belov1 commented 4 years ago

I have the same thing.

image
jonathanfoster commented 4 years ago

TL;DR: If you're having these strange issues, make sure you're instantiating and showing the banner on the same thread and avoid any shared objects without a locking mechanism.

I can into similar issues (exact same as @Surferdude667 and @belov1) and it turned out to be a concurrency issues for me. I was using NWPathMonitor to monitor network connectivity and a StatusBarNotificationBanner to show status, both were class members.

So the network monitor was being updated on a separate thread meanwhile the banner was updated on the main thread causing lots of strange behavior. Once I instantiated the the banner and called show on the main thread, everything was fine.

Good

class BaseViewController: UIViewController {

    let networkStatusMonitor = NWPathMonitor()

    override func viewDidLoad() {
        super.viewDidLoad()

        self.networkStatusMonitor.pathUpdateHandler = self.onNetworkStatusChange
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        self.networkStatusMonitor.start(queue: DispatchQueue(label: "NetworkStatusMonitor"))
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)

        self.networkStatusMonitor.cancel()
    }

    func onNetworkStatusChange(path: NWPath) {
        DispatchQueue.main.async {
            if path.status != .satisfied {
                // Instantiate banner and show on main thread, avoid self reference in closure
                let networkStatusOfflineBanner = StatusBarNotificationBanner(
                    title: "The Internet connection appears to be offline.",
                    style: .danger)

                networkStatusOfflineBanner.show()
            }
        }
    }

}

Bad

class BaseViewController: UIViewController {

    let networkStatusMonitor = NWPathMonitor()
    let networkStatusOfflineBanner = StatusBarNotificationBanner(
                    title: "The Internet connection appears to be offline.",
                    style: .danger)

    override func viewDidLoad() {
        super.viewDidLoad()

        self.networkStatusMonitor.pathUpdateHandler = self.onNetworkStatusChange
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        self.networkStatusMonitor.start(queue: DispatchQueue(label: "NetworkStatusMonitor"))
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)

        self.networkStatusMonitor.cancel()
    }

    func onNetworkStatusChange(path: NWPath) {
        DispatchQueue.main.async {
            if path.status != .satisfied {
                // Don't access a shared resource from separate threads, strange things will happen
                self.networkStatusOfflineBanner.show()
            } 
        }
    }

}
misteu commented 4 years ago

Hey there, it does crash because the library depends on the view or at least the window being existent. Unfortunately, the library includes a lot of force unwrapping.

For this particular issue, I found a solution. I will open a Pull Request for it soon.

I guess there are more locations where the force unwrapping could make it crash.

Neilfau commented 4 years ago

Also had this issue, the library cannot find the window, I forked the project and made the following changes and it now works:

     private let appWindow: UIWindow? = {
         if #available(iOS 13.0, *) {
-            return UIApplication.shared.connectedScenes
-                .first { $0.activationState == .foregroundActive }
-                .map { $0 as? UIWindowScene }
-                .map { $0?.windows.first } ?? UIApplication.shared.delegate?.window ?? nil
+            return UIApplication.shared.windows.filter { $0.isKeyWindow }.first
         }
findus commented 3 years ago

For me the issue was that the app crashed if a Uialertdialog was being presented the moment the dialog gets displayed. https://github.com/findus/NotificationBanner/commit/aefd75e8c916953f481d39afcdfede1487fae45c This fixes the force unwrap of the view, but i am not sure if this screws up something else in the long run, if the subview does not get added, apart from that some notifications might get not shown.

DominikButz commented 3 years ago

Same problem with UIAlert and in-app-purchase view controller. I tried to use the notification banner to display success or failure of an in-app-purchase - it crashes every time. I use it in a SwiftUI app, so it is scene-based. @Neilfau 's solution (in @Findus fork) worked for me.

moh-abk commented 3 years ago

Same problem with UIAlert and in-app-purchase view controller. I tried to use the notification banner to display success or failure of an in-app-purchase - it crashes every time. I use it in a SwiftUI app, so it is scene-based. @Neilfau 's solution (in @findus fork) worked for me.

any chance you can share a snippet of how you use it with SwiftUI?

DominikButz commented 3 years ago

Hi, usage is not different from UIKit. you can see how this framework basically works in the show function:

public func show(queuePosition: QueuePosition = .back,
                 bannerPosition: BannerPosition = .top,
                 queue: NotificationBannerQueue = NotificationBannerQueue.default,
                 on viewController: UIViewController? = nil) {
    parentViewController = viewController
    bannerQueue = queue
    show(placeOnQueue: true, queuePosition: queuePosition, bannerPosition: bannerPosition)
}

So with SwiftUI you would typically let the UIViewController parameter nil (although you can try passing in a UIHostingController, e.g. if you started your app in iOS 13. if the view controller is nil the internal show function will place the banner into the app window, which should also be available in SwiftUI. The advantage of placing the banner in the app window is that the banner should always be on top - even if for example a SwiftUI sheet is currently showing. I hope this helps

stale[bot] commented 3 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

orchid-bloom commented 3 years ago

SwiftUI iOS 14.5 also has this error Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value: file NotificationBannerSwift/BaseNotificationBanner.swift, line 380

alexookah commented 3 years ago

@Daltron Should we make some PL to fix this?

stale[bot] commented 2 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] commented 2 years ago

This issue has been automatically closed due to inactivity.