{"id":31441,"date":"2021-01-26T16:11:38","date_gmt":"2021-01-26T21:11:38","guid":{"rendered":"https:\/\/mjtsai.com\/blog\/?p=31441"},"modified":"2021-01-26T16:11:38","modified_gmt":"2021-01-26T21:11:38","slug":"swiftui-unit-testing","status":"publish","type":"post","link":"https:\/\/mjtsai.com\/blog\/2021\/01\/26\/swiftui-unit-testing\/","title":{"rendered":"SwiftUI Unit Testing"},"content":{"rendered":"<p><a href=\"https:\/\/nalexn.github.io\/swiftui-unit-testing\/\">Alexey Naumov<\/a> (via <a href=\"https:\/\/twitter.com\/steipete\/status\/1353397574993108992\">Peter Steinberger<\/a>):<\/p>\n<blockquote cite=\"https:\/\/nalexn.github.io\/swiftui-unit-testing\/\"><p>So I decided to build <a href=\"https:\/\/github.com\/nalexn\/ViewInspector\">ViewInspector<\/a>, a library that allows for inspecting the SwiftUI view hierarchy at runtime.<\/p><p>[&#8230;]<\/p><p>And as it turned out, there were many pitfalls waiting for me on the way:<\/p><ol><li>All types in reflection are erased to <code>Any<\/code><\/li><li>Computed properties, such as <code>var body: some View<\/code>, are not available in reflection<\/li><li>Generic private structs and function types which are tricky to cast the value to<\/li><li>Initializing a struct which all <code>init<\/code> methods are private<\/li><li>SwiftUI dependency injection through <code>Environment<\/code><\/li><li>Property wrappers, such as <code>@State<\/code>, with an elusive storage for values<\/li><li>Significant variations of the hierarchy after a tiny tweak of the input. For example, <code>Text(\"Hi\")<\/code> vs <code>Text(hiValue)<\/code><\/li><li>Overall obscurity and lack of information about the private structures<\/li><\/ol><p>In this piece, I want to share abnormal use cases and hacky tricks I had to appeal to when building this library using just the standard capabilities of Swift language.<\/p><\/blockquote>","protected":false},"excerpt":{"rendered":"<p>Alexey Naumov (via Peter Steinberger): So I decided to build ViewInspector, a library that allows for inspecting the SwiftUI view hierarchy at runtime.[&#8230;]And as it turned out, there were many pitfalls waiting for me on the way:All types in reflection are erased to AnyComputed properties, such as var body: some View, are not available in [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"apple_news_api_created_at":"2021-01-26T21:11:41Z","apple_news_api_id":"57c35d0f-32f7-4401-bc7c-791b8b208830","apple_news_api_modified_at":"2021-01-26T21:11:42Z","apple_news_api_revision":"AAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/w==","apple_news_api_share_url":"https:\/\/apple.news\/AV8NdDzL3RAG8fHkbiyCIMA","apple_news_coverimage":0,"apple_news_coverimage_caption":"","apple_news_is_hidden":false,"apple_news_is_paid":false,"apple_news_is_preview":false,"apple_news_is_sponsored":false,"apple_news_maturity_rating":"","apple_news_metadata":"\"\"","apple_news_pullquote":"","apple_news_pullquote_position":"","apple_news_slug":"","apple_news_sections":"\"\"","apple_news_suppress_video_url":false,"apple_news_use_image_component":false,"footnotes":""},"categories":[4],"tags":[31,1837,30,1891,74,71,901,943,1812,268],"class_list":["post-31441","post","type-post","status-publish","format-standard","hentry","category-programming-category","tag-ios","tag-ios-14","tag-mac","tag-macos-11-0","tag-opensource","tag-programming","tag-swift-programming-language","tag-swift-runtime","tag-swiftui","tag-testing"],"apple_news_notices":[],"_links":{"self":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/31441","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/comments?post=31441"}],"version-history":[{"count":1,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/31441\/revisions"}],"predecessor-version":[{"id":31442,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/31441\/revisions\/31442"}],"wp:attachment":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/media?parent=31441"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/categories?post=31441"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/tags?post=31441"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}