DEV Community

Cover image for How I used SwiftUI to create Devui, a companion app for DEV authors
Emilio Schepis
Emilio Schepis

Posted on • Edited on

How I used SwiftUI to create Devui, a companion app for DEV authors

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 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.

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.

3. Show me some code!

This app utilizes the brand new 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.

// 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()
Enter fullscreen mode Exit fullscreen mode

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

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

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.

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

SF Symbols are used throughout the various views to ensure consistency and familiarity with the entire Apple ecosystem.

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

The only external dependency is Resolver, an ultralight dependency injection framework.

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

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.

💻 If you'd like to take a look at the source code you can do so here.

🌍 If you'd like to help me translate this into as many languages as possible please open a PR or leave a comment.

Until the next one. 👋🏻

- Emilio

Top comments (3)

Collapse
 
hlc0000 profile image
hlc0000

Hello, I'm an IOS developer. Recently, when using swift to develop applications, I found that there are few caches written by pure swift. So I wrote a cache -- swiftlycache, a lightweight general-purpose IOS cache library using swift 5. If you are using swift for development, if you also need to use cache, maybe you can try swiftlycache, maybe you will like it, If you like, you can also introduce it to your friends. Thank you
github.com/hlc0000/SwiftlyCache

Collapse
 
emilioschepis profile image
Emilio Schepis

Hi, that's very interesting, thanks for linking it! I will definitely check that out. Do you plan on making it available through Swift Package Manager?

Collapse
 
hlc0000 profile image
hlc0000

In future updates, perhaps through Swift Package Manager