5 reasons I still feel SwiftUI is premature

Enebin
5 min readAug 23, 2022

--

Photo by Will Porada on Unsplash

While developing most of projects with UIKit, I happened to have a chance to develop a project with SwiftUI these days.

Many features of SwiftUI are really fascinating compared to the UIKit’s. Nevertheless, I’m pretty sure that SwiftUI is really premature to be used yet, at least in iOS 14.

There are 5 reasons why I felt so.

1. Still dependent to UIKit

Although SwiftUI has gradually improved to be independent, I’m feeling it’s still inseparable with UIKit. Here’re some examples:

Unable to customize navigation bar

One of the remarkable features of SwiftUI is NavigationView which frees us from executing method like instantiateViewController or navigationController.pushViewController. For that, however, we should give up customizing our navigation bar.

NavigationView doesn’t offer any modifier changing its appearance. There are only ones like navigationBarBackButtonHidden, navigationBarTitleDisplayMode that just change the way the content is displayed.

How can we change its appearance like background color or text color then? There’s no way to do it but to bring UIKit back.

UINavigationBar.appearance().backgroundColor = UIColor.red
UINavigationBar.appearance().titleTextAttributes = [.foregroundColor: UIColor.white]

You should add above codes before the view is rendered. It’s usually written in init or onAppear of your view struct.

It seems that navigation view is completely overhauled in iOS 16. Deprecating NavigationView, Apple came up with a new component, NavigationStack. You can use .toolbarBackground to customize your navigation bar now.

Tab bar, even worse

It’s even worse with tab bar.

One of my tasks was making tab bar’s corner rounded and changing background color to specific color. To do this, I had to use UIKit’s feature again:

UITabBarController.tabBar.barTintColor = UIColor(Color.tabBarBackground)            UITabBarController.tabBar.isTranslucent = true                        UITabBarController.tabBar.layer.masksToBounds = true            UITabBarController.tabBar.layer.cornerRadius = 14            UITabBarController.tabBar.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]

Funny thing is, it’s not working at all. Well, to be honest, it occasionally worked. That doesn’t mean okay though.

Fortunately, I could solve this with a brilliant library called Introspect. It worked for almost 90% of problems related to UIKit.

With the Introspect, code becomes:

TextEditor is meaningless

TextEditor is the most ironic component in SwiftUI I guess, because it’s also unable to customize its appearance with vanilla SwiftUI.

Even missing in iOS 13, it’s the only component that supports multiline text editing. In fact, its defect has nothing to do with the functionality. It works pretty fine.

The problem is, background color never changes from the original color, white.

To deal with it, you should remove the TextEditor’s background with UITextView.appearance() to reveal expected background color.

Alternatively, another solution could be to use an UIViewRepresentable to create your own text field that wraps the UITextView.

Now you can change background color in iOS 16 using scrollContentBackground(.hiddne) with background

(reference: https://stackoverflow.com/a/62848618/11768262)

2. Limited use of scroll view

As you know, SwiftUI is designed to be used without delegates which is very common in UIKit.

In SwiftUI, thus, it’s just impossible to get detailed information of scroll such as contentOffset or velocity.

If you want to make advanced functionality like paging or snap scroll, you have to measure them with something like GeometryReader or gesture, which is extremely painful compared to just adopting UIScrollViewDelegate.

Here’s an example of how it can be made.(StackOverflow)

3. Navigation link’s destination is preloaded

As you can see from the gif, destination of the NavigationLink is preloaded before it actually appears.

It may not be a big deal, but you’d agree that errors always happen in trivial things like this. Let’s say you request API calls in the initializer of destination view. It will create unintended expense while you’re not aware of it.

There is, fortunately, a very simple solution to make the view load lazily.

struct NavigationLazyView<Content: View>: View {
let build: () -> Content
init(_ build: @autoclosure @escaping () -> Content) {
self.build = build
}
var body: Content {
build()
}
}

You can use it like:

NavigationLink(destination: NavigationLazyView(DetailView(data: DataModel))) { 
Text("Item")
}

(reference: https://stackoverflow.com/a/61234030/11768262)

4. Incomplete ForEach

ForEach is also useful for passing @Binding to one’s child views. Here’s a common usage of it with array bindings.

ForEach(data: $viewModel.posts, id:\.id) { post in  ... }

So far there doesn’t seem to be any problems but the tricky part arises when you’re trying to use index with it.

When to use index? For example, to implement a feature like infinite scroll using LazyVstack, you might want to pass the index of each element to its onAppear.

For the non-binding data, we can easily do this with .enumerated like this example.

How can I do that with binding then? We can try the same thing as we did with non-binding data.

ForEach(data: Array($viewModel.posts.enumerated()), id:\.element.id) { post in  ... }

Well, then you will get an error saying that

Conformance of 'Binding<Value>' to 'Sequence' is only available in iOS 15.0.

Solution?

In fact, you can avoid this error writing some extra codes for ForEach. Check this out: Bindable SwiftUI list elements | Swift by Sundell

5. Too many SDKs don’t support SwiftUI yet

It’s been almost 4 years since SwiftUI is introduced but there’re still a lot of SDKs not supporting it.

One of the most famous SDKs, Lottie also doesn’t support SwiftUI.

It means that to use this library, you should wrap it with UIViewControllerRepresentable and design your own parameters yourself. If you expect ‘ready to use’ library you had in UIKit, it would be such a bummer.

Even other huge SDKs like Google Firebase or Facebook don’t support SwiftUI in their developer’s guide yet. In this case, you’ll have to find another way other than using AppDelegate to instantiate the SDK because AppDelegate doesn’t exist in SwiftUI project since iOS 14.

Conclusion

These are the reasons why I still think SwiftUI is premature. Obviously, it has saved me a lot of time in UI implementations and newer trends like ObservableObjects have made it possible to write more sophisticated code.

Yet, many features natural in UIKit are not natural in SwiftUI and whenever you’re facing each of them it consumes much more time than you expected.

Which one to choose will be up to you. Thanks for reading!

--

--