Alphabitz — Project A
The first entry in the Alphabitz series is going to be a simple arithmetic app that performs various unit conversions with a very simple UI. It’s like the calculator app idea, but with a little more real world scenarios that are useful in any project. As with every app in this series, the goal for this tutorial is to create the same app across multiple platforms to be used as a starting point for your own project 🏗.
Note: This tutorial assumes you have some basic understanding of declarative UI syntax and MVVM design patterns. If this is your first trip in app development you may want to check out some intro-level tutorials and articles for Swift, SwiftUI, Kotlin, Jetpack Compose, and Svelte on Ray Wenderlich or Stack Overflow .
All the source code for all projects can be downloaded here: Arithmetick
Research Stage 🔎
I like to break down my research into two distinct parts, the backend/data part and the frontend/ui part. For this stage I’m doing more of a high level approach to see what I need or how I’ll use certain pieces that can be shared across each platform. For this projects I came up with the following research items:
- For the backend logic I wanted to make sure the categories I use were available on all three platforms. Looking at some available frameworks or built in libraries I found the following 6 common categories that can be used: 📏 Length, 🌡 Temperature, ⚖️ Mass, 💧Volume, ☁️Pressure , and ⚡Energy.
- For the frontend I need to setup some base components (Colors, Fonts and Icons) that will give each app a similar look and “feel” while still following best practices for each platform. To keep things simple I’ll use the defaults for each platform (so SF Font/Symbols on iOS, Roboto/Material on Android and Serif/FontAwesome for Web). For the actual UI, I’ll use a simple UI with the main screen showing each option as a card, and the conversion screen just containing an input field and list for converted results.
This type of research helps me see how feasible each project would actually be and what effort would be needed on each platform. The research stage is usually a good time to come up with the app name, and for this one I settled with Arithmetick.
Project Setup 🚧
Before I start working on the designs I’ll setup my project folder both online and offline. I’ll use GitHub for hosting my project, so I create an initial project folder locally, add a simple README file to get started and sync the folder online. I use the following folder structure for these types of projects since I can also sync up Klokki to create time trackers for each folder while working.
-- arithmetick
---- README
---- android
---- design
---- ios
---- web
Design Stage 🎨
I sort of follow the Atomic Design process mixed with the Agile Development practice to iterate through each stage until I get a product that I’m happy with. For the design process I’ll start with the lowest fidelity type of design (wireframe) and then move closer to the hi-fidelity type of design (prototype) until I have enough items to get started on development. I’m currently using Sketch for a majority of my design work.
Lo-Fidelity (Wireframe)
Since I’m a mobile first type of developer my designs also start on the mobile side. I’m also more familiar with the iOS side, so I’ll start with the iOS app wireframe and then use that as a blueprint for the other apps. The wireframe is really simple, just two screens; Category View (Main screen) and Converter View (secondary screen). For the Category View I figured showing the categories as a card like item will give me the ability to use a similar color and icon for each category that will make the apps have a more unified look. For the converter view, again keeping it simple it can be a Text Field for entering values and then a Table showing the different converted values. For web I know this may change a little since a Table with just a single value will have a lot of empty space on larger screens. For now the wireframes are done so I can move on to add a little more details to the designs.
Mid-Fidelity (Mockup)
To keep designs consistent with what’s available on each platform I like to start with the default UI kits for each and then add/tweak the designs where needed. Again, starting with iOS I grab the current iOS Sketch library from here and start with adding the navigation screens, which will give me my Category and Converter view templates all setup. I drop a few card shapes with SF symbol icons to get the idea of the category and modify a table view a little bit to see how the converted results would look.
This also gives me a chance to start associating some colors to each category as well, which I can replicate on each platform to make the apps have a more unified look.
I then jump over to the Android side and grab the current Material Design template here and do the same thing I did with the iOS design. This time there are some differences, like how the Cards are displayed using the Material design guidelines but it’s a fairly quick process to get the same UI layout as the iOS app.
The last wireframe needed is for the Web App, and since I took this mobile first approach I know I want it to have a similar layout with the cards for the category items. This area is all still a little new to me but I know I’ll use TailwindCSS for the colors so I can use this sketch library for colors and icons. I’ll just create a few simple placeholders for both small and large layouts and the mock ups are all done 👏.
Design Hand Off 🤝
Once I have enough “design” items in the mockups that I can use to start coding I’ll wrap up the design process with some little extras that are sometimes overlooked. First is the app icon, if you have enough to create it now then it’s a good idea to generate all the assets for the different applications. I’m not doing anything super fancy for these projects, all of the icons will include a simple icon from the Symbolicons library and the colors from my companies brand. There are all kinds of apps and kits available for generating app icons so there’s no need to list where to go to do this, what I would suggest is make sure you are generating the right types of assets for each platform (I like to use templates from Apply Pixels 😄 )
- iOS uses various sizes inside the Asset Catalog, some tools will generate all the sizes or just a few sizes and you will have to find/match the different sizes (since some are the same as others just with different names)
- Android uses an Adaptive icon so it can be dynamic for the different device types. Most tools will generate the two layers and using the Asset Studio inside Android Studio allows you to assign the layers to the App Icon.
- Web will need a favicon, as well as images needed for mobile browsers. Again most tools will generate all these sizes for you, just make sure to add them in the correct folder when ready.
Development Stage 🏗
Similar to the design stage, I’ll use an Atomic Design mixed with Agile Development to iterate through the development of each app. I’ll also start with the iOS App, again since it’s my stronger area and works as a blueprint for the other apps.
iOS App 📱
Minimum Requirements: Xcode 13 & Swift 5
Setting up the project is fairly simple, no need to go into details here, just make sure to choose Swift
and SwiftUI
as the language and framework options. In the project structure I also uncheck the iPad box, for now this will be an iPhone only type of project. I use the simulator to test frequently, so although not mentioned I would suggest building frequently to validate changes are working on a device and not just the preview windows.
Once the project is opened, one of the first things I like to do is setup my preview window to show both light and dark modes of the app. This can be done with a small tweak to the PreviewProvider
on your SwiftUI view using a ForEach
loop and adding the .preferredColorScheme()
modifier.
SwiftUI makes it easy to code the same way I design, adding a wireframe with the bare minimum and then modify from there. To start off with the category view screen I know I’m going to need a grid so to get started I use a LazyVGrid
with a placeholder Text
view to view the names of each category. The LazyVGrid
uses a GridItem
to setup the size of each column and the ForEach
loop makes setting up repeating elements super easy.
I like to use Enum’s whenever possible, it makes code easier to read and helps with making unit tests easier to write. I can setup my enum class to also provide the images and colors that each category will use. iOS also has a built in library for unit measurements, so I can include those in my enum class to ensure each category has the right units for the conversion screen. I’ll also use the SFSafeSymbols package using the Swift Package Manager since this package also makes adding SF Symbols a little easier 😃.
One caveat here is that the Enum can’t be used in the ForEach
loop on the SwiftUI side, it needs to be Identifiable
which isn’t allowed on Enums. To work around this we just need to setup a class that can be used on the SwiftUI side and takes the enum as the initializer.
The allItems()
function is useful for our SwiftUI view so now all we need to do is a small tweak to the wireframe and we have the Categories all setup with the UI components needed to show them as Cards.
Creating the cards is fairly straightforward, using a VStack
as the base component will make sure everything is stacked vertically. For the top of the card I wanted to show the icon and the count of available options so those items will get wrapped in a HStack
and to make the whole view looks like a card I use a combination of .overlay
and .background
modifiers with a RoundedRectangle
and that gives me everything I need to update the main view again to now use the Card View instead of the Text view.
With this setup all it takes is a single line change on the main view to swap the TextView
with the new CategoryCardView
and that’s it, the main view is pretty much setup. There are few more things that will be needed, for example wrapping the main view in a NavigationView
and adding a NavigationLink
to the Card View to transition to the converter view after being tapped.
Now it’s time to setup the converter view, and this is where all the hard work is going to take place 😅. In order to make the UI responsive in SwiftUI I need to use an ObservableObject
that can use Published
variables to trigger updates when the converted values are updated based on the input value. Using the .sink
handler will also allow me to execute the conversion code when the input field is updated so the user will see converted values as they type without having to hit the enter key. As for the actual conversion logic, the process is handled with a few steps; first get the value using the selected unit, then filter list of conversion options to exclude selected unit, and lastly use the built in .converted(to: )
method in the Dimension class that will create the list of converted values.
Now it’s just a matter of hooking up the Published
variables with the UI on the converter view, setup the input field to accept just numbers and setup a list view that will show the converted results. The list will also allow you to tap on any option and switch to that Unit. To make the converted results list requires adding a .onTapGesture()
modifier and also adding a .contentShape(Rectangle())
makes the entire row respond to the tap.
At this point the app is pretty much done, but before wrapping up the iOS app there is one more step to perform, Unit Tests 👨🔬. For me I like to get the ViewModel to get as much coverage as possible since that’s where most of the logic for the app takes place. Following the same process as before I’ll start with testing the smaller functions and then iterate to make sure all the category items have similar tests. For now I’ll just use the default XCTestCase
for Swift and just some simple XCTAssertEqual
and XCTAssertFalse
statements to get the results I need.
Now the iOS project is complete, before working on building the published version of the app lets dive into the Android version.
Android App 🤖
Minimum Requirements: Android Studio 2021 (Chipmunk) & Kotlin 1.5. At the time of writing most of these features were marked Experimental and may change over time.
Using my iOS app as a blueprint, I already know what I need to get started with the Android side of things as well. To get started choose an Empty Compose Activity(Material 3)
and choose Kotlin with minimum of API 25 (Lollipop). As with the iOS app, I use a simulator to verify and will build often while working through each step.
One of the differences for the Android platform is how the Colors and Themes are used. In order to get the colors I need I use the Material Theme Builder and export using the Jetpack Compose Theme. After dropping this into the app, I can now setup my Dark & Light Preview light previews, which is done by creating different Preview Composable
and setting the useDarkTheme
variable. I like to use the showBackground
and showSystemUi
on the Preview windows as well just to get the full preview on each type.
To provide the same navigation and live data I need to add a few Compose libraries by updating the app gradle file with following dependencies to get started.
kotlin {
// ...
dependencies {
implementation 'androidx.navigation:navigation-compose:2.5.0-alpha01'
implementation "androidx.compose.runtime:runtime-livedata:1.0.5"
}
}
Now since I already know that I need a Category model that will handle all the logic for the main screen, instead of building the wireframe I can setup the actual model to use in the view. With Kotlin I can use a Enum
as well and I can setup the model that uses the enum values for all my main display items.
With the model setup, my wireframe can skip the step of using an array of Strings to test with and I can get my array of Category items to use with a simple Text view for now to show the title. Using the LazyVerticalGrid
is experimental right now, but it’s exactly what I wanted to give the same card view so I’ll use it. Setting it up is actually pretty similar to the grid view on iOS as well.
Next step is to work on the Card view for the Category item. The Material design library comes with a Card
view that works in Jetpack Compose and with a mixture of Columns
and Rows
it’s pretty easy to get a similar layout on the Android side. For the Image resource, I’m using the painterResource
option so it works with the view model and an icon resource id. I’ll come back later to add the navigation logic, just to not crowd this step with a little too much complexity right away.
Now just like the iOS app, I can go back to my Category View and by changing the Text
field to the CardView
will give me my Category screen all ready to go 👏
With that piece done, the next piece to work on is the Converter View. Again, since we have the iOS app to use as a blueprint I can go straight to the view model and setup all the converter logic. In Jetpack Compose the UI is made responsive by using MutableLiveData
inside of the ViewModel and then use .postValue()
on these variables to make the UI respond to those changes. Most of the conversion logic follows the same pattern from iOS with getting all conversion options, set the selected value and then perform the conversion for each option available. The only change I had to make was for the Unit options, seems like the built in ones on Android don’t provide all the same functionality as the iOS library. I found this library measured that works, but it didn’t have all the same units to use so I had to make a few tweaks after adding it.
Hooking up the Converter View to the View Model requires using either a remember
or observeAsState()
API method on the live data variables and like the iOS app just a simple TextField
for input and a LazyColumn
for the result list will give me everything I need. To make the conversions perform while typing, the modifier onValueChange
can be used to call the conversion logic on the TextField.
There’s another piece that needs to be added before the Converter View can be viewed after tapping a category card, the NavigationController
. The Jetpack Compose side uses a NavHost
that takes the different Views that are available and the route that is used to get to that view. Using arguments
in the NavController we can identify the id of the Category Card selected and then pass that selected category to the Converter View. In the MainActivity.kt
first setup the NavHost and then update the main onCreate
to use the updated Navigation Component. From here the navController
needs to be passed down to the Card View, and in the onClick
method add the navigation call with the category id passed in the arguments.
onClick = { navController.navigate(NavigationRoutes.DETAIL.name + "/${item.option.id}") }
From here the Android app is pretty much ready to go. The Category screen should show all the icons and colors for each type and can navigate to the converter view. The last piece is to add a Unit Test, and again on the Android side I’ll use the default testing library. Since I’m using a ViewModel with some MutableLiveData there’s a Rule
that needs to be added to the testing library in order to access the live data values. Once that’s added, writing the tests is fairly straight forward and almost the same as the iOS side again.
Alright, now with the mobile apps done it’s time for the Responsive Web App.
Web App 🕸
Minimum Requirements: Visual Studio Code, Vite, Svelte, TypeScript and TailwindCSS.
The Web App is definitely a more “open” playground to use compared to mobile apps since there is far more variety in the frameworks that can be used. While there are some frameworks that are more widely used than others, I chose a stack that I felt worked best for my style of development coming from a mobile only background. For testing, I like to use Sizzy as a development browser since I can see both small and large screens.
- Vite — A newer build tool that is extremely fast and provides Hot Reloading (Live Updates)
- Svelte — A front end framework for building reactive components
- TypeScript — For the backend logic to add type safety, something I’m used to as a mobile developer
- TailwindCSS — A CSS Framework to make responsive classes easier to write and read.
To get started I’ll use the Vite scaffold option to setup a project using npm init vite@latest
and choose the svelte-typescript
option. Once the project is done, adding TailwindCSS can be done with a simple command npx svelte-add@latest tailwindcss
and now the only item needed is FontAwesome for icons. For FontAwesome I like to use the local option, downloading the assets and adding the CSS link to the index.html
file and now the web app base is ready to go.
Experience from the previous apps helps in letting me know some basic structure that is needed for the web app to work, so I’ll start with the navigation piece. Svelte comes with a svelte-navigator library that can be used for setting up the navigation. I’ll start out just with some simple links and empty pages to make sure it’s all setup correctly.
Running the app should result in two simple pages that can be navigated between using the links on each page. Now like with the other apps, I know I need a model for the Category items, and although TypeScript does have a Enum class, it’s not exactly the same as how the other platforms work so I’ll use a exportable namespace to do all the UI exports like I did in the other apps. I’ll also add the UOM library at this point so I can use the units in my Enum class.
To build the UI, I’m going to use the DaisyUI framework which adds some extra utilities to TailwindCSS and the Notus Svelte template to take some of the work out building an entire UI from scratch. To keep the UI simple, and have a similar feel to the mobile apps I’m going to just have the main page show some simple text and a few card like shapes for each category. To handle the navigation I’ll use the built in svelte-store
to keep track of the selected category, selected unit and current value entered in the input field. First I setup the card layout.
One of the nice parts of using Svelte is the ability to use a #each
loop to create multiple category cards without having to retype all the same classes for each item.
With the Category items done the next piece, like with all the other apps is the Converter View. I’ll start with the ViewModel, since like the other apps I need to setup the logic for performing the conversions and getting all the results formatted for the result table. The logic still follows a very similar pattern, first get the current selected unit and the input value for that amount, get all conversion options for the selected category and then filter out the selected item from the results.
Hooking up the ViewModel to the UI components is the same as the other platforms, an input
field to enter a value and then a list to show the results. Since there isn’t really a default “grid” to use on the web, and since a table would end up with a lot of extra space I’m going to use a card style design instead. For the input view, in order to provide the same functionality as the mobile apps and convert the value as the user enters a value I’ll use the on:input
event to call the conversion logic in the view model. For the converted list (or cards), I’ll use the Svelte store to setup a .subscribe
event on the selected unit value that will let the user swap the input value with any of the conversion options. Here’s just the main pieces for each of the views
This will pretty much wrap up the Web App functionality with the mobile apps, but there are a few more items needed for a web app to be complete. Basically it’s just wrapping all this content with a Navigation Bar and Footer area that contains basic data. After these basic items are added the last piece to add is some Unit Tests for the web app, and for this I’ll use the Vitest framework. The Vitest framework uses a very easy to understand format, using describe
to setup a test class and it
and expect
functions to explain what kind of test is being executed in each it
function.
With the Unit Tests completed we know have 3 apps ready to be published and pushed out to the public.
Deployment Stage 🚀
Following the same pattern as before, I’ll go through the deployment options for each app, starting with the iOS App first.
iOS App Package 📦
Publishing an iOS App requires the App Store, regardless of the type of distribution that you want to use. For this app I’ll use the Ad Hoc distribution option so I can install the app on just my own specific devices. Using the Product menu choose the Archive option to start the build process. In the Archive window choose the Ad Hoc option and follow the instructions to end up with a .ipa
file that can be installed to a device using the Apple Configurator app.
Android App Package 📦
Publishing an Android App is a little bit more open than the iOS side and doesn’t require having to use the Google Play store to build a Ad Hoc version of the app. To get started use the Build menu to go to Generate Signed APK and then choose the App Bundle option. The next screen will ask you to setup a keystore for the app that is needed for future updates to the package so make sure this is safely stored somewhere. Last step is to choose where to save the APK file and which version to use for the build (use the release build) and that’s it for the Android side. The app can be installed on a device manually or uploaded to the Google Play store for public distribution.
Web Package 📦
For the web app, Vite comes with scripts already setup for generating a build and to preview the build as well. Once the build script is finished the final step is to find a host for the web app. There are quite a few options available from free to commercial, depending on the needs of the project and the resources available. For this project, a simple free hosting option can be used with Vercel and using the CLI option all you need to do is run the vercel
command from the project dist
folder and follow the prompts to launch the app. The live preview can be found here: Arithmetick preview
Wrapping it up 🤔
Even with just a high level overview of each app, this was quite a bit to take in if you followed along for all three apps. The nice thing is this project is small enough that it can be taken in any direction now if you want to add more features to this app. Here’s a few ideas of areas that can be expanded on:
- More conversion options — This might require more libraries or different libraries.
- Ability to add favorites — Another feature that adds more navigation options, is creating a favorites screen to easily access conversions that are used often.
- More tests — UI tests can be added, or some automated testing from the GitHub side.
Whatever you do, I just hope this was a good starting point to help visualize some ideas you might have. I’ll continue the series with B , which is currently in pre-design stage. Stay tuned for more 😄.