SwiftUI Tricks: Part 2

Continue for a series of articles about tips and tricks for SwiftUI.

Photo by Amza Andrei on Unsplash

It is hard to imagine application without buttons. Sometimes it is not obvious. any clickable link i.e. text or image or combination of both may be realised on top of button functionality power. In FitRadar user is interacting with application by pressing and clicking and pushing many UI elements expanding details about trainer, trainer’s rating, statistics and other information. Same for sport event which has many attributes and user can instantly get access to any of it.

Here is technical problem to solve: quite often you don’t want your UI element look like button (change color on press, etc). The most advised approach online would be to use custom button style PlainButtonStyle.

Button {
// do some logic
} label: {
// some fancy UI
}
.buttonStyle(PlainButtonStyle())

However, it’s still keep it’s visual effect as described in documentation

/// A button style that doesn't style or decorate its content while idle, but
/// may apply a visual effect to indicate the pressed, focused, or enabled state
/// of the button.

Here is trick to remove any visual or behavioural indication of a button. Let’s declare a custom style

struct NoneButtonStyle: ButtonStyle {
func makeBody(configuration: Self.Configuration) -> some View {
configuration.label
}
}

Now use where you want to keep functionality of a button yet keep it simple

Button {
// do some logic
} label: {
// some fancy UI
}
.buttonStyle(NoneButtonStyle())

That’s all for today. Join the waiting list at https://www.fitradar.me to be the first one to know when app released.

P.S. Text by Dmitrijs Beloborodvs

Making sure data is valid and business rules are kept

fitness
workout
gym
sports
personal trainer
venture capital
startup

Our Fitradar back-end solution is created based on Clean Architecture as described in this article and explained in this conference talk. One part of the back-end solution is incoming request validation, and in order to implement it as a part of the MediatR pipeline we are using FluentValidation library that replaces built-in ASP.NET Core validation system. At the beginning of the project it seemed that all we have to do is just to create a custom validator derived from AbstractValidator and perform the validation of an Application Model in a single class. And that is how we started to implement the validation. And since the library has quite enhanced capabilities that allow implementing complex validation rules for a while we didn’t notice any problems. Until the moment when we realized that not all validation rules are simple incoming Data Model validation. Some of the rules expressed our business logic rules and restrictions. And so we started to think about splitting the rules across the layers and following are multi step validation solution we came up with:

  • Application level validation. At this level incoming JSON data model fields validation is performed. The field types are validated automatically by ASP.NET core built-in Model binding but the other validation rules are executed by FluentValidation including:
    • required field validation
    • text field length validation
    • numeric field range validation
  • Domain level validation. At the domain level we apply Domain Driven Design approach and therefore instead of FluentValidation we decided to use Specification pattern. We liked the idea that we can move the validation logic outside the Domain Entity to the separate Validator. By doing that we were able to bind validation logic to incoming commands instead of Domain Entity. For example we have an entity SportEvent and several commands that call methods inside that entity like Update Sport Event and Cancel Sport Event. Now because the different commands have different business requirements for the same Entity property it would be hard to implement them with FluentValidation. For example property BookedSeats in case of Update Sport Event command should be 0, but in case of Cancel Sport Event command it is allowed that someone has already booked a particular Sport Event. And bellow is how the Cancel Sport Event validator looks like:
public sealed class EventCancellationValidator : EntityValidatorBase
{
    public EventCancellationValidator()
    {
        AddValidation("CancellationTimeValidation",            
                new ValidationRule<SportEventInstance>(
                new Specification<SportEventInstance>(sportEvent =>
                    sportEvent.StartTime > DateTime.UtcNow),
                ValidationErrorCodes.CANCELLATION_TOO_LATE, "It is not possible to cancel the 
        Sport Event after it has started", "SportEventStartTime"));
    }
}
  • Database level validation. For accessing database we are using Entity Framework Core and to make sure the database models are in tune with database constraints we are using built in property attributes and classes that implement IEntityTypeConfiguration interface that allows to define database constraints like Primary Key, Foreign Key and Unique Key.

P.S. Please follow our Facebook page for more!

SwiftUI Tricks: Part 1

Photo by Clint Patterson on Unsplash

This is the first of mini articles in a series of SwiftUI tips and tricks.

One of the common tasks in mane applications is to determine screen width and layout items accordingly. FitRadar is not an exception. Many UI elements should be adjusted to screen size. There are two problems, tho: “screen” and “width”.

Screen

Most often developer’s think of a “view on screen” when saying “screen”. Physical screen size is equal to view controller size and vice versa. Yet we know that today (as per middle of 2021) we have iPad which allow run two apps side by side, or even one app in two “panels”. Know knows what else Apple introduce in future. We expect our fitness app to run on all possible devices: phones, tablets, watches, etc. So, we can’t rely on physical screen.

Width

Since there is no yet (as per middle of 2021) devices with changing screen, most often developers think of a width of screen as a constant value. However, changing screen zoom (General -> Display & Brightness ->Display Zoom) actually change it. The other and most common scenario: rotation.

So, knowing these two problems we cannot rely anymore on something simple as

UIScreen.main.bounds.size.width

GeometryReader

GeometryReader is second most advised way to get current view geometry. It does it best, but practise show that item places inside it acts differently on iOS 13 and iOS 14. And we actually want to avoid place our UI elements inside it.

For the trick we use PreferenceKey. Just add following code somewhere in project

struct SizePreferenceKey: PreferenceKey {   typealias Value = CGSize   static var defaultValue: Value = .zero   static func reduce(value: inout Value, nextValue: () -> Value) {    value = nextValue()   }}

Next, define some variable to keep current screen width

@State private var screenWidth: CGFloat = 0

Place geometry reader somewhere on the view which size you need to track. But other UI elements can now be placed outside GeometryReader

VStack {    GeometryReader { proxy in        Spacer()         .preference(key: SizePreferenceKey.self, value: proxy.size)    }    .frame(height: 1)    .onPreferenceChange(SizePreferenceKey.self) { preferences in        self.screenWidth = preferences.width    }    Rectangle()        .fill(Color.red)        .frame(width: screenWidth / 2, height: screenWidth / 3)}

This solution works equally on iOS 13 and 14. Enjoy!

P.S. Please visit our website: https://www.fitradar.me/ and join the mailing list! Fitness is closer than you think.

P.S.S. Text by https://dmi3j.medium.com/

Performance or Readability

sports
venture capital
startup

In our Fitradar backend solution we are heavily using a database in order to implement the necessary use cases. Almost every call to our backend API includes calls to the database. In order to make it easier to work with database from C# code we are using Entity Framework Core. The library allows us quickly and easy save C# objects in database and fetch persisted C# objects from the database. In many use cases the EF Core is used just for these basic operations – saving and fetching but the business logic is isolated in other classes. Nevertheless for some use cases we discovered that it is more straightforward to execute the business logic directly in the database in the form of stored procedures or user functions. And in this article I wanted to share some of the experience we had while were working on complex database operations and the solution we arrived at.

One of the main reasons why we started to consider moving some parts from the C# code to the database was the user experience. We noticed that some operations on the mobile application are taking considerable amounts of time and might degrade overall user experience and we started to look how to improve the performance of those slow operations. Some of the reasons we discovered that were causing the long response time were multiple calls to the database and unoptimized database operations. Striving for the clean code in our backend solution we tried to avoid stored procedures, user functions and triggers. Introducing SQL code that can’t be mapped to the C# code within a single use case would mean introducing another codebase. So far EF core allowed us to abstract from SQL code and we very happy to have all the logic in C# code. Besides one more language and codebase there were following issues that were holding us back from using SQL directly:

  • Adding SQL code in DbContext in the form of strings would take away the Intellisense for SQL and that would make us deal not only with logic errors but with syntactic errors as well in the runtime.
  • Refactoring with Visual Studio built-in tools would become more complex
  • In case we needed to debug our method that uses stored procedure or fires SQL trigger we would have to debug C# and SQL code separately
  • Working with two codebases readability of single use case is degrading – in order to understand the full logic we have to switch between C# and SQL
  • There are different logging systems for ASP.NET Core and SQL and therefore it is harder to keep unified log format.
  • More complex deploy process

But at the same time for complex persistence and database query operations we would have:

  • Significant gain in performance
  • Terse code, since SQL is specifically designed to work with relational databases and many operations can be expressed in much shorter code compared with C# and LINQ without losing the readability.

The main question we had to answer in order to understand whether we need to move some of the logic to stored procedures, triggers and user functions was: “Is the gain in performance worth it?” For some use cases we could see the improvements even with the test data and for the other use cases we predicted that changes we are planning to introduce will start to impact the performance once certain amount of data will be stored in a database. And so we decided to move some of our logic from C# to SQL code, but before that we really wanted to mitigate some of the above-mentioned problems we discovered during analysis. And to do that we decided to create separate SQL Server Database Project instead of keeping the SQL code in EF Core DbContext class and by doing that we solved the problem with structuring the code, Intellisense and SQL code maintenance.

P.S. Visit our website: https://www.fitradar.me/ and join the waiting list. We launch very soon!