How I used SwiftUI to create Devui, a companion app for DEV authors
Tags: swift, ios, opensource, meta
Published: 2020-04-16 16:36:40

## 1. Introduction

Hi, my name is Emilio. I'm a software developer living in Milan, Italy.

For a few months now I have been thinking about starting to write about software development, new technologies and my personal journey as a developer.

Last year I completed the [100 Days of SwiftUI](https://github.com/emilioschepis/100-days-of-swiftui) journey, and I've been thinking about using it to create an app that I could share with the world ever since.

My goal was to create a simple app that would use all the latest technologies in the iOS development world, the best practices I learned in the past few months, and that could be a good "platform citizen".

Also, I wanted it to be [completely open source](https://github.com/emilioschepis/devui-app).

## 2. The app

Meet **Devui**, a companion app for DEV authors.

Devui shows you the available statistics for all your posts: page views, positive reactions and number of comments.

I've been developing it in my free time over the past week, and now I think I've reached a nice MVP that features pagination, caching, system logging, dark mode support, first-class accessibility and more.

Do you write on DEV? Would you like to try Devui to keep an eye on your articles?

📲 **You can join the public TestFlight beta [here](https://testflight.apple.com/join/ZThoD9Q3)**.

## 3. Show me some code!

This app utilizes the brand new [Combine](https://developer.apple.com/documentation/combine) framework to handle every aspect of the application flow with a reactive approach.

All network requests expose publishers that can be observed, manipulated and then used to define the state of the application at any given time.

```swift
// DEVService.swift
//
return session.dataTaskPublisher(for: request)
    .tryMap({ result -> Data in
        // ...
    })
    .decode(type: Profile.self, decoder: decoder)
    .mapError({ (error: Error) -> NetworkError in
        // ...
    })
    .handleEvents(receiveOutput: { profile in
        self.profileCache.insert(profile, forKey: cacheKey)
    })
    .receive(on: DispatchQueue.main)
    .eraseToAnyPublisher()
```

This, combined with the declarative nature of the SwiftUI framework, allows the entire program to always be in a well-defined and deterministic state.

```swift
// ArticlesListViewModel.swift
//
devService.getArticles()
    .replaceError(with: [])
    .assign(to: \.articles, on: self)
    .store(in: &cancellables)
```
```swift
// ArticlesListView.swift
//
List(viewModel.articles) { article in
    ArticlesListItemView(article)
        .padding(.bottom)
        .onAppear { self.onArticleAppear(article) }
}
```

The UI utilizes system-defined colors to adapt not only to iOS 13's dark mode, but also to the numerous accessibility settings that can be enabled by users.

```swift
// ArticlesListItemView.swift
//
var tagsList: some View {
    ScrollView(.horizontal, showsIndicators: false) {
        // ...
        .font(.footnote)
        .foregroundColor(Color(UIColor.secondaryLabel))
        // ...
    }
}
```

[SF Symbols](https://developer.apple.com/design/human-interface-guidelines/sf-symbols/overview/) are used throughout the various views to ensure consistency and familiarity with the entire Apple ecosystem.

```swift
// ArticlesListItemInteractionsView.swift
//
HStack(spacing: 4) {
    Image(systemName: "bubble.right.fill")
    Text(viewModel.commentsCount)
        .bold()
}
```

The only external dependency is [Resolver](https://github.com/hmlongco/Resolver), an ultralight dependency injection framework.

```swift
// AppDelegate+Injection.swift
//
extension Resolver {
    public static func registerServices() {
        register { AuthService() }.scope(application)
        register { DEVService() }.scope(application)
        register { ImagesService() }.scope(application)
    }
}
```

The combination of all these features allow for an incredibly small bundle size (less than 800KB as of 1.0.0) and fast performances for all devices.

## 4. Conclusion

Thank you for reading my first ever DEV article. 

I hope you enjoyed a glimpse into this small project. I'm looking forward to reading your comments.

📲 **If you'd like to join the public TestFlight beta you can do so [here](https://testflight.apple.com/join/ZThoD9Q3)**.

💻 **If you'd like to take a look at the source code you can do so [here](https://github.com/emilioschepis/devui-app)**.

🌍 **If you'd like to help me translate this into as many languages as possible please [open a PR](https://github.com/emilioschepis/devui-app/pulls) or leave a comment.**

Until the next one. 👋🏻

\- **Emilio**