Codizon https://codizon.com A new horizon for your solutions Sat, 23 Apr 2022 16:21:04 +0000 en hourly 1 https://wordpress.org/?v=5.9.8 /wp-content/uploads/cropped-favico2-32x32.png Codizon https://codizon.com 32 32 SpaceScripting /2022/04/spacescripting/ Tue, 05 Apr 2022 20:50:42 +0000 /?p=2012 Overview

SpaceScripting enables architects to create 3D terrain models for their plots with ease.

]]>
Wedbliss /2021/12/wedbliss/ Wed, 01 Dec 2021 16:43:00 +0000 /?p=1975 Overview

Wedbliss helps couples easily find their dream wedding venue. The platform features an interactive map of expertly curated wedding venues across Poland, in which couples can adjust filters to display venues matching specified criteria.

Developed using a Lean Startup approach, the product has undergone a number of feedback-driven iterations. The following article illustrates our development approach through three stages of the product lifecycle: Discover, Build, and Drive.

The first step was to conduct an in-depth market analysis. This facilitated a refined product vision and allowed us to clearly map a series of user and business outcomes, as detailed in the Discover section.

This real-world research was invaluable, allowing the team to uncover various pain points associated with a couple’s search for their dream wedding venue. Understanding these pain points made designing and architecting our custom Wedbliss solution straightforward, as highlighted in the Build section.

Lastly, we made sure that everything was put in place for smooth and efficient maintenance of the product. In the Drive section, we underscore some of the best practices and standard industry tools used to collect key performance indicators (KPIs) to track product success.

*Note: Early feedback has indicated a considerable market interest in our product. Unfortunately, the wedding industry has been badly hit by the COVID-19 pandemic – product launch will be revisited when revenue projections recover.

Discover

The Discovery phase successfully validated and refined the product’s unique value proposition.

Our team conducted a number of small scale experiments to collect user data (i.e. surveys, interviews, content analysis, etc.). This provided early insights into user desire for a more transparent and efficient service in the early stages of their search for a wedding venue. Specifically, users were interested in shortlisting a small number of venues, based on key features such as capacity, price, location, and reviews.

At this stage, we understood what was important to the user, but had not yet formulated a final product response. We used Figma to visualize our ideas, testing prototype solutions directly with potential customers (our Figma approach is detailed further in the next section) to identify how best to meet user needs. This new feedback was then used to modify product design and the process of gathering feedback would begin again. Wireframes and early-stage designs were iterated rapidly, informed by these direct feedback loops.

This iterative design process is useful at any phase of the product lifecycle, including when a product has already been launched in the market and there is a desire to make improvements.

Key Takeaway

Using a feedback/outcome-driven framework, allowed the team to quickly establish a clear set of priorities for the platform. The process empowered the team to make leaner, faster decisions and in turn, accelerate delivery.

Build

The Build phase defined effective processes for design and code delivery.

When building our designs, Figma is our go-to tool. It is straightforward to use, navigate, and collaborate on. Most importantly, however, it allows for great flexibility in structuring our components.

Let’s rewind here for a moment to explain what a component is.

We believe that modularity is the key to creating a flexible/Agile design system. For a system to be modular, it must have interchangeable parts. These parts are called components. Components can be as simple as an individual button, or as complex as an entire navigation header (consisting of other components like logos, buttons and menu items). Once you make something a component, you can create other instances of it, which are essentially connected copies of that component — so if you update the design of the original component, all instances will reflect that change. For example, when you need to change something, like your brand’s link color or home icon, you can make the change once (in the original main component or style) and watch it update across all your designs.

Whether in Figma or in code, components are at the core of our approach to architecting products.

However, bringing our components to life (i.e., writing code), is an exciting but error-prone process, made especially challenging when collaborating with a larger team. It is, therefore, crucial to align on processes and have a shared understanding of the system design (i.e., the architecture).

Most of our processes were defined by SCRUM – probably the most widely used project management framework for Agile development. As for the architecture, incorporating BLoC and State Machine paradigm into our solution were the two core choices that helped us effectively expand (build new features) and navigate an increasingly complex project.

BLoC stands for Business Logic Components and at its core is centered around the concept of Unidirectional Data Flow. If you’re interested to learn more check out this great article written by one of our developers explaining the concept in more detail. We recommend further reading on BLoC to fully understand its benefits. Overall, it helped us separate business logic from the presentation layer and enabled us to reuse code more efficiently.

State Machine is a computational model that consists of states and transition functions. A transition function reads an input and determines the next state. A program cannot be in two states at any given time. This makes the app’s behavior predictable and easily testable. All we have to do is come up with a finite number of states, their possible transitions, and code them up. One of our developers created a library that allows us to express states and the corresponding transitions for a given BLoC in Dart language https://pub.dev/packages/state_graph_bloc.

The tech stack can be summarized as a multiplatform Dart application, designed for code sharing between Angular Dart used for web development and Flutter used for mobile apps.

The industry is moving towards multi-platform development tools (i.e., one technology to build apps for mobile, web, desktop, and embedded devices) with Flutter at the front of such initiatives. The move is motivated by the need to cut the enormous costs involved in the development and maintenance of multiple tech stacks.

The only reason we used Angular Dart instead of Flutter on the Web is that the latter was still in its early stages of beta testing at the time of building Wedbliss. In our case, migrating Angular Dart to Flutter on the Web will be easy, since we maintain one codebase and most of the code is already shared between web and mobile (as both are written in Dart programming language).

Lastly, it is important to note our approach to designing the backend. We used Ktor and MongoDB. We chose Ktor due to its low setup costs (i.e., lightweight framework with a quick and easy setup). Another advantage was that Ktor is built with Kotlin (a much more concise language when compared to Java), and we took full advantage of Kotlin features such as lambda syntax and DSLs. As for MongoDB, there is a never-ending debate on whether you should choose SQL or NoSQL databases. The truth is, there is no silver bullet and it all depends on your use case. For us, the focus was on flexibility. We chose NoSQL MongoDB due to its schemaless nature that allowed for rapid transformations as our data needs changed. Rather than fitting an application to meet schema requirements, we allowed for experiments to define our optimal database structure.

Key Takeaway

Our component-based approach to designing and building our product allowed for quick and easy propagations of changes – saving us a lot of time when going through the numerous iterations of Wedbliss.

Drive

Coming soon.

]]>
Unidirectional Data Flow for Newbies /2019/05/unidirectional-flow-for-beginners/ Sat, 11 May 2019 12:50:58 +0000 /?p=373 Unidirectional Data Flow is a buzz phrase that thrilled development conferences all around the world. There are plenty of frameworks that now use this concept such as State Machines, Redux, Flux, Bloc, Mobx, among others. These are widely used on all frontend platforms (mobile, web, desktop applications, etc.) regardless of programming language or framework.

Key Concepts

First, what does “unidirectional” mean? In simple words, it means that data only has one way to be transferred to other parts of an application (i.e., a single input leads to a single output). Have you ever encountered issues like concurrency modification exceptions or race conditions? These can all be tackled with a unidirectional flow approach since only one function and one thread can make changes at a time.

Events (or “actions“) describe mouse clicks, network call completions, input changes, etc. All are added to the FIFO queue.

A worker thread takes the last state and last event in the queue, and returns a new state.

The transition between states is often called a “reducer”, a “reduce function”, or a “transition”.

The transition should be a blocking function and quick. If it cannot be synchronous, consider running a task in the background and emit a new event when the task starts, finishes, or fails. This type of background task is often called a “side effect” or “middleware”.

An interface (or a “view”) not only adds events to the queue, but also observes the current state and updates itself accordingly. This approach works in a cycle, as shown in the figure below.

Further details will be provided in the following sections.

State

In general, a state is a structure (class or enumeration) that holds the current status of an application or UI (i.e., components visibility, input values, etc.). To avoid ambiguous modifications, the state should always be immutable – no function or object should be able to modify the values of its fields. Below is an example of immutability.

Mutable classes can be modified at any time:

data class MyMutableClass(
  var field: String // variable
)

val instance = MyMutableClass("hello")
instance.field = "world"
print(instance.field) // outputs "world"

However, modifications to immutable classes are forbidden by the compiler or result in a runtime exception:

data class MyImmutableClass(
  val field: String // constant
)

val instance = MyImmutableClass("hello")
instance.field = "world" // error!

Note: You should also ensure that collection fields are immutable.

So how then do you reflect the change? The answer is simple: create a new object or make a copy of the existing one. Ideally, check if your programming language supports a copy operator.

A “new state” can be created with a copy of the existing one with some of its fields changed.

data class State(val text: String)

val instance = State("hello")
val newInstance = instance.copy(text = "world")
print(newInstance) // outputs "State(text=world)"

Normally, a state class contains a number of fields:

data class State(
  val id: String,
  val isLoading: Boolean,
  val data: String?
)

Alternatively, you can use an interface:

interface State

class Loading(val id: String) : State
data class Ready(val id: String, val text: String) : State

val instance = Loading(id = "abc")
val newInstance = Ready(id = instance.id, text = "hello world")
print(newInstance) // outputs "Ready(id=abc, text=world)"

Your UI can then observe the current state and update itself to reflect the change:

state.observe { currentState ->
  loadingView.isVisible = currentState is Loading
}

Event / Action

An event is a class, which describes a user action or an asynchronous task’s result. Each event is added to an event queue and processed sequentially.

Here is an example of how event definitions in an application could look:

interface Event

class OnResetClicked: Event
data class OnApiResult(val result: String): Event
data class OnApiError(val error: Throwable): Event

Reduce Function / Reducer

A reduce function defines exactly two parameters (previous state and current event) and should immediately return a new state.

Here is an example:

data class State(val isLoading: Boolean, val data: String?)
data class OnApiReady(val data: String)

fun reduce(val previousState: State, val event: OnApiReady): State {
  return previousState.copy(isLoading = false, data = event.data)
}

Middleware / Side Effect

Sometimes, there is a need to run an asynchronous task like an API call or writing to a file, etc. Such a task should be processed in a background thread, potentially adding new events to the queue as a result of its execution.

Here is an example:

data class State(val isLoading: Boolean, val data: String?)

interface Event
data class OnStart(val id: String): Event
data class OnCompleted(val data: String): Event

fun middleware(val previousState: State, val event: OnStart) {
  Thread().run {
    val objectId = previousState.id
    // make an api call for object with objectId identifier
    // notify about the result
    addEvent(OnCompleted("result for $objectId"))
  }
}

Types of States

There are two types of states: Global and Scoped.

The global state usually reflects the state of the application, while the scoped one reflects the state of a single view fragment such as a Controller, ViewModel, Presenter, or Bloc.

Tips

  • Reducers are supposed to be pure functions,
  • Reducers should not perform heavy operations on the same thread; and,
  • State classes should be kept as small as possible.

Advantages

  • Predictable
    • Forget about atomic fields
    • Forget about the race conditions
    • Forget about concurrent modification exceptions
    • Forget about wrong thread execution
  • Ease of debugging
  • Enhanced testability

Disadvantages

  • Boilerplate
  • Complexity
  • Object copying – memory and CPU overhead

Sample

Check out our Android Game Of Life sample project at GitLab.

]]>
How to reproduce Dagger functions in Kotlin? /2018/04/how-to-reproduce-dagger-functions-or-develop-your-own-di-in-kotlin/ Fri, 27 Apr 2018 16:35:00 +0000 /?p=317 As an Android developer, you must have heard of Dagger. Moreover, you’ve probably faced some difficulties with setting it up in Kotlin projects. What are the benefits of this library? Do we still need those Dagger’s annotation processors and code generators? Being inspired by Antonio Leiva great article on how to use Dagger 2 on Android With Kotlin and Elye’s Dagger 2 for Dummies in Kotlin, as well as relying on my own experience, I’ll try to convince you that actually, we don’t need them at all sometimes. (Well, Dagger is a pretty powerful tool).

In this article, I’ll show you how basic Dagger features can be easily replaced.

Setup

The concept of skipping Dagger in Kotlin deserves its own name. Double “g” for the similarity with “Dagger” and “K” as in “Kotlin”. I called it Kagger, and surprisingly, in Danish kagger means cakes 🍰. It’s not a library, so assuming you’ve already defined Kotlin dependencies, you have nothing to configure at all. Our solution will be pure-kotlin and therefore a piece of cake 🙂

Components and modules

Just like in Dagger, we are using “components” and “modules” terms. The main difference is we split them to their interfaces and implementations. The following comparison should be self-explanatory.

Dagger

In Dagger you would probably define module like:

@Module class AxModule(private val application: Application) {
    @Provides 
    @Singleton 
    fun provideSomeSingleton() = application
        .getSystemService(Context.POWER_SERVICE) as PowerManager
    
    @Provides 
    fun provideSomeBean() = application
        .getSystemService(Context.LOCATION_SERVICE) as LocationManager
}

@Singleton annotation is used to mark single-instance beans, so function provideSomeSingleton() will be called only once. The second one, provideSomeBean() is not annotated as a singleton and will be invoked every time it’s being injected. Let’s imagine that we have another few modules, such as AxModule, BxModule and CxModule.

When it comes to a component, you have to define a list of contained modules and specify which classes will be able to use that component:

@Singleton
@Component(modules = arrayOf(AxModule::class, BxModule::class, CxModule::class))
interface MyComponent {
    fun inject(dummyActivity: DummyActivity)
}

Then you can build your component:

class MyApplication : Application() {
 
    val myComponent: MyComponent by lazy {
        DaggerMyComponent
                .builder()
                .axModule(AxModule(this))
                .bxModule(BxModule())               
                .cxModule(CxModule())
                .build()
    }
 
}

And finally you are able to inject your dependencies:

class DummyActivity : AppCompatActivity() {

   @Inject lateinit var powerManager: PowerManager // singleton
   @Inject lateinit var locationManager: LocationManager // non-singleton

   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContentView(R.layout.activity_dummy)
       
       // there may be another ways to access myComponent
       (application as MyApplication).myComponent.inject(this)
   }
  
}

PowerManager and LocationManager will be accessible after OnCreate call.

Note: In the newest version of Dagger, it’s not necessary to access components by casting application property. I used it to make the example clear.

Kagger / Pure Kotlin

How to achieve the same only with Kotlin?

Firstly, define module interface:

interface AxModule {
   val someSingleton: PowerManager
   fun someBean(): LocationManager
}

Please notice I’ve replaced singleton declaration with a field. That’s the way we emphasize the difference between singletons and non-singletons to avoid misleadings in the future. It’s up to you if you follow this pattern or not. When the definition is done, let’s make an module implementation:

class AxModuleImpl(private val application: Application) : AxModule {
   override val someSingleton by lazy {
       application.getSystemService(Context.POWER_SERVICE) as PowerManager
   }

   override fun someBean() = application
       .getSystemService(Context.LOCATION_SERVICE) as LocationManager
}

What is more, for singleton field, lazy property was used. It’s worth to know it’s thread-safe and we can be sure that object will be instantiated only once.

Let’s move on to component interface:

interface MyComponent: AxModule, BxModule, CxModule

It may seem familiar to Dagger component declaration. Again, apart from its name, we are declaring a list of modules that component should contain. It’s not necessary to specify which classes will be able to “inject” those objects.

For similarity with above Dagger example, let’s implement a component as an object expression:

class MyApplication : Application() {

   val myComponent: MyComponent by lazy {
       object : MyComponent,
               AxModule by AxModuleImpl(this),
               BxModule by BxModuleImpl(),
               CxModule by CxModuleImpl() {}
   }
  
}

Here we are using the true power of Kotlin — delegation. MyComponentinterface extends AxModuleBxModule and CxModule. Thanks to the implementation by delegation we are keeping those modules apart and we don’t have to implement all methods and fields in one place. Each module can contain its own dependencies, as AxModule which uses applicationcontext.

You may create separate class for component implementation as well:

class MyComponentImpl(application: Application): MyComponent,
       AxModule by AxModuleImpl(application),
       BxModule by BxModuleImpl(),
       CxModule by CxModuleImpl()

So MyApplication class simplifies to:

class MyApplication : Application() {
   val myComponent: MyComponent by lazy { MyComponentImpl(this) }
}

If you do so, the access to items looks like this:

class DummyActivity : AppCompatActivity() {
   private val component by lazy {
       // there might be a better way of accessing myComponent
       (application as MyApplication).myComponent
   }
   private val someSingleton by lazy { component.someSingleton }
   private val someBean by lazy { component.someBean() }
  
   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContentView(R.layout.activity_dummy)
       // nothing to declare in onCreate
   }

}

As you can see above, we are “injecting” items without annotation or lateinit keyword. Vals remains vals. In case you want to parameterize non-singleton objects, just add a parameter to a module function:

interface AxModule {
   val someSingleton: PowerManager
   fun someBeanWithParams(param: String): LocationManager
}

For testing purposes, you can create a mocked version of MyComponent and replace only necessary modules, methods or fields.

To help you with better understanding of this matter, I’ve put a compilable project to GitHub repository.

Why does it matter?

With the mechanism described above, we were able to remove simple Dagger dependencies in projects created for Indoorway. What’s more, Kagger may be used outside of Android projects. I’m looking forward for your opinions and thoughts on this.

Featured in Android Weekly #307. Originally posted on Medium.

]]>