Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for SwiftUI? #252

Open
contreras2004 opened this issue Aug 5, 2022 · 14 comments · May be fixed by #283
Open

Support for SwiftUI? #252

contreras2004 opened this issue Aug 5, 2022 · 14 comments · May be fixed by #283

Comments

@contreras2004
Copy link

I have attempted to use Nimble_snapshots with swiftUI Views using a UIHostingController but it just gives me a white snapshot of the window. Ant ideas on how can I make this work or why it does not work?

@ashfurrow
Copy link
Owner

Sorry, I haven’t worked much with SwiftUI and I’m not sure. We use the underlying snapshot test case library, maybe check their GitHub repo for similar issues?

@robinbonin
Copy link
Contributor

I have attempted to use Nimble_snapshots with swiftUI Views using a UIHostingController but it just gives me a white snapshot of the window. Ant ideas on how can I make this work or why it does not work?

Hi! Got it to work with this setup:

import SwiftUI

@available(iOS 14, *)
extension View {
    func view(sizeThatFitsWidth width: CGFloat? = nil, colorScheme: ColorScheme? = nil) -> UIView {
        let vc = UIHostingController(rootView: self.environment(\.colorScheme, colorScheme ?? .light))
        vc._disableSafeArea = true
        vc.view.backgroundColor = .clear
        
        let calculatedSize = width.map { vc.view.sizeThatFits(CGSize(width: $0, height: CGFloat.greatestFiniteMagnitude)) } ?? UIScreen.main.bounds.size
        
        let window = UIWindow(frame: CGRect(origin: .zero, size: calculatedSize))
        window.rootViewController = vc
        window.makeKeyAndVisible()

        return vc.view
    }
}

And then:

it("should render as expected for colorscheme light") {
                    expect(sut.view(sizeThatFitsWidth: 400)).to(haveValidSnapshot())
                }
                
                it("should render as expected for colorscheme dark") {
                    expect(sut.view(sizeThatFitsWidth: 400, colorScheme: .dark)).to(haveValidSnapshot())
                }

@contreras2004
Copy link
Author

It works! Thanks!

@ashfurrow
Copy link
Owner

@robinbonin Thanks for letting us know!

@Ripcord715
Copy link

Hello!

I'm experiencing the same issue, and the steps above didn't seem to resolve it. This only seems to happen when I'm testing against iOS 16, not 15.5. When I'm trying to record a new snapshot it's just blank white. Here's the code I'm using:

expect(subject.view(sizeThatFitsWidth: 390)).to(recordDynamicSizeSnapshot( named: named, identifier: nil, sizes: size, isDeviceAgnostic: true, usesDrawRect: true, resizeMode: ResizeMode.frame ))

Any help would be greatly appreciated! I did try looking at the snapshot test case library GitHub page, and didn't find anything unfortunately.

If there's any additional info that would be helpful, please let me know!

Thank you very much!

@contreras2004
Copy link
Author

contreras2004 commented Oct 20, 2022

Hey @Ripcord715

Based on @robinbonin's answer I created this extension. At least it is working for me using Xcode 14.0.1 on iOS 16.

public extension View {
    func view(width: CGFloat? = nil, height: CGFloat? = nil, colorScheme: ColorScheme? = nil) -> UIView {
        let viewController = UIHostingController(rootView: self.environment(\.colorScheme, colorScheme ?? .light))
        viewController._disableSafeArea = true

        let calculatedSize = width.map {
            viewController.view.sizeThatFits(
                CGSize(width: $0, height: height ?? CGFloat.greatestFiniteMagnitude))
        } ?? UIScreen.main.bounds.size

        let window = UIWindow(frame: CGRect(origin: .zero, size: calculatedSize))
        window.rootViewController = viewController
        window.makeKeyAndVisible()
        window.backgroundColor = colorScheme == .light ? .white : .black
        return viewController.view
    }
}

And you can use it with any SwiftUI view like this:

let yourView = YourSwiftUIView()
expect(yourView.view()) == recordSnapshot()

@Ripcord715
Copy link

Thank you very much for the reply @contreras2004 ! I gave it a shot, and unfortunately I'm still getting a white snapshot.

@contreras2004
Copy link
Author

hmmm, could you post a bit pf your view's code? Maybe I could try rendering it myself

@Ripcord715
Copy link

@contreras2004 - Sorry I wasn't able to get back to you sooner. I asked our iOS developer to take a look at what was going on, and it sounds like our view is loading just after the snapshot is being taken when we use iOS 16. We were able to get a workaround that works for now by preloading the data, but we would rather figure out if it was possible to delay the snapshot being taken until after the view has finished loading naturally. Is there a way to do that in Nimble Snapshots?

Thanks again for your help!

@contreras2004
Copy link
Author

contreras2004 commented Nov 2, 2022

@Ripcord715 Maybe you could try using the .toEventually(recordSnapshot()) and .toEventually(haveValidSnapshot()) matchers. Also when dealing with service calls you must mock the services, you shouldn't use the same provider as in a real app. Use a mock json with the required response from the server that returns the data you need to test your screen. Just to be clear, this has nothing to do with Nimble-Snapshots package.

Hope it helps 🙂

@Ripcord715
Copy link

@contreras2004 - Thank you very much, I'll give that a go!

@contreras2004
Copy link
Author

@contreras2004 - Sorry I wasn't able to get back to you sooner. I asked our iOS developer to take a look at what was going on, and it sounds like our view is loading just after the snapshot is being taken when we use iOS 16. We were able to get a workaround that works for now by preloading the data, but we would rather figure out if it was possible to delay the snapshot being taken until after the view has finished loading naturally. Is there a way to do that in Nimble Snapshots?

Thanks again for your help!

@Ripcord715 were you able to resolve this? Now i'm having the same issue. I notice that this is because the function that loads the data is in the .onAppear { } of the swiftUI view.

@Ripcord715
Copy link

Ripcord715 commented Nov 28, 2022

@contreras2004 - Apologies for not getting back sooner, I wanted my senior dev to take a look at my work before replying. Yes, the View extension along with the .toEventually did work.
expect(subject.view(width: 390, height: 925)) .toEventually( recordDynamicSizeSnapshot(), timeout: .seconds(waitTime), pollInterval: .milliseconds(waitTime) )

expect(testSubject).toEventually(haveValidDynamicSizeSnapshot(), timeout: .seconds(waitTime), pollInterval: .milliseconds(waitTime))

@avipami
Copy link

avipami commented May 28, 2024

I had the same problems and with the extension and toEventually worked for me! thank god you guys exist !
Now its only the fetching of images i need to get fixed

@ashfurrow ashfurrow linked a pull request Sep 11, 2024 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants