Native iOS App Progressing Well

A few weeks ago, I wrote about switching priorities from a web app with MAUI to a native iOS, iPad OS, and macOS app.

Progress is being made. I am currently working on adding meetings to the app and will then be ready for action points to be added.

Getting into SwiftUI after a couple of years with C# has been a good challenge. The principles are not complex, but there is a slight learning curve when working with the frameworks I haven’t used for a while.

To begin with, I am working on functionality rather than focusing on design. When everything works as it should, I will spend time making everything look better.

I’ll provide another update soon.


Native iPhone, iPad, Mac App

I mentioned in the first post here that the intention was to build Notic Meet as a .NET MAUI app, but given my background in iOS app development, I’ve decided to build native from the start and then branch out to the web app.

The backend is in good shape, with just a few things missing, such as caching, but overall, it’s getting there, although the backend won’t be used directly by the native app until the web app is up and running.


Switching to Mongo Atlas

Notic Meet used a Digital Ocean managed database (MongoDB) for a while, but recently we decided to switch to Mongo Atlas. We don’t have any valuable data, so switching was just a matter of changing the connection string and database name in the app settings in the C# project. Other than that, it did take a bit of time to figure out why we couldn’t add new documents, but all went smoothly.

Next on the list, now that the school holidays are almost wrapping up here in England (and recently in the US), is to start getting the first prototype ready. The backend is at a good enough place to move forward and anything that comes up can be adjusted as needed.

Although progress has been slow the last few months, we’re about to pick up speed again.

Let us know in the comments if you have any questions, comments, or ideas.


Building in Public

In recent years, my interest in companies that build in public has piqued. The first time I recall encountering this concept was when Nathan Barry initiated the building of ConvertKit as part of a six-month web app challenge, where his goal was to progress from 0 to $5,000 per month in revenue. Although Nathan didn’t reach that goal within six months, he still launched a successful business, all in public. It is still going today, I use it, and annual revenue has recently passed $30 million.

A few years later, I came across Indie Hackers, a website about individuals building their products publicly. What I find fascinating is that these individuals spend some hours each week striving to build a business. Some find success, some do not, but they all appear to be striving to launch a product while transparently revealing to the world what they are constructing, how it’s being built, and providing updates on their failures and successes.

Building in public interests me for several reasons. One reason is that others share similar interests with me. As I read about what they are doing in blogs and on the IH website, it encourages me to believe that building a small business is a possibility if I put in the work. Along with being motivated, you learn from others’ experiences regarding what strategies succeeded and what didn’t and possibly use this insight to shape your journey.

Indie Hackers describes a four-step process to building in public and shares links to content that can assist you in each of the areas. The four steps are:

  1. Learn what’s possible
  2. Take the leap
  3. Build in public
  4. Grow and thrive

Each of these four areas is detailed to assist you in overcoming the common obstacles you will likely encounter. In the first step, “learn what’s possible” there are blog posts on inspiration, the basics of building a public business and what types of products you can build.

The second step, “take the leap”, has posts linking to finances, building a team, and coming up with an idea. While I often interpret “indie” as an individual, it’s not rare for two founders to collaborate on a project, leveraging each other’s skills to advance. In the case of Notic, I work with a co-founder on the project who greatly contributes to the motivation due to accountability.

The third step, “build in public”, links to articles on building quickly, launching the product, and finding the first users. All of these are deep topics. Here’s a real-life example:

I am currently working on a startup with a six-person team. The three software engineers/developers could only devote a few hours a week to building the product. Creating software, particularly to a high standard, isn’t a swift and straightforward task. We found that the launch date would be months away, if not over a year away. To get started more quickly, we met and discussed what other options we had, and the first idea was to go the no-code route. We quickly settled on Sharetribe, and a few weeks later, after some testing and working out a few business-related ideas, we were able to ship. A fully developed in-house system is still in the plans, but being able to launch with no code was simply a great idea. It might be that the product never gains traction, but at least we could test that sooner rather than later.

This single decision is only part of building in public. Naturally, we need to document and share our decision to opt for a no-code solution rather than launching our product. By sharing our experiences publicly, others may gain insights from our journey and even appreciate our work enough to register themselves.

Building in public is more than moving quickly. As mentioned earlier, several other steps are within that section to help you launch your product and find your first users.

The final step listed is called “begin growing”. This moves on to marketing and engaging with users, bringing them to your product and retaining their interest. Ultimately, we want them to pay for and use the services we build. More importantly, we aim to make their lives easier by using our services. The final aspect of this stage addresses how to handle your success. In an ideal scenario, your business only requires your full attention, and the revenue transitions from being active to passive as you acquire customers and word-of-mouth referrals. This additional freedom might give you what you need, or you might use that as a springboard to launching your next business, perhaps in public.

So where does this leave me? Well, I have previously mentioned another of my startups, which is a tool-sharing service for communities, but this website is Notic, specifically about a product that my co-founder and I are building called Notic Meet. Notic Meet is designed to help note-takers, or those that take minutes in meetings, to keep track of discussions over various meetings. We are excited to be building this project. Although Indie Hackers suggests moving quickly, we’ll take a slower approach in this case, as we are both still mastering C# and .NET to enhance our skills.
Additionally, given the customisation required, it’s challenging to construct this using a no-code solution. I’m good with this taking longer. The plan isn’t necessarily to make a ton of money, rather, it is to build a project and learn how launching a business works.


Fighting Bugs

For the last couple of weeks, I spent a number of hours tracking down a bug. This bug was just related to converting the case of the fields in the DTO to be compatible with the controller. I wasn’t doing any conversion, so it defaulted to FirstName, LastName, etc…

What made this particular bug hard to find was that the LoginDto was working just fine, whereas the ProfileDto was failing with a 400 error when the endpoint was called in the controller.

LoginDto worked because Email and Password are just single words; thus, they are compatible with the controller. The LoginDto also had a RememberMe bool, but because a bool defaults to false, even if I passed in RememberMe or not, it would just default to a false because it would ignore RememberMe.

I used Postman to figure it out by passing in the raw data. After realising what happened, I switched all items to snake_case, FirstName to first_name, etc. This resolved the issue, and there were no more complaints that my payload was missing the needed data.

Why I Mention This

I’ll give a more detailed example on my programming blog, but I posted this here to highlight that some things can slow you down. This took several hours to track down, and in the end, it was something straightforward that was overlooked. Time to move forward now! Hopefully, we will get a beta out before the end of 2023.

Database Updates

Backend Services for Notic Meet

Last week I wrote about the database that Notic Meet would use, that being NoSQL document storage using MongoDB.

With V1 of the DB design created, it’s time to work on the initial backend services for Notic Meet. Rather than build all of them before building the front end, we will be building in slices, with the front and back ends being built together for each feature.

Before working on those vertical slices, we are currently working on getting the backend to a point where we can add endpoints easily and getting the server project to a place where new models and endpoints can be easily added.

The app uses .NET and C#, with the project being set up with the Blazor WebAssembly App template with .NET Hosted selected.

This template provided a good enough start and created three projects within the solution. Those being:

  • Client – Which is used for the frontend
  • Server – Home to the API controllers as well as services that those controllers access
  • Shared – Which, in our case, will be used to store some frontend logic as well as connect the frontend to the backend so that we don’t need to deal with HTTP requests in the client project

On the front end, I switched to using MudBlazor, a component library I have used for the past year since moving to use C#.

To demonstrate briefly how the communications work from front end to back end, it begins by setting up dependency injection in program.cs in the client project.

builder.Services.AddTransient<IClientUserService, ClientUserService>();
builder.Services.AddTransient<IClientMeetingService, ClientMeetingService>();

This shows the ClientUserService and ClientMeetingService being injected. The former will be the place for all interactions and logic related to users, and the latter will be for meetings. Several other client services will be added, but these two are sufficient to get the backend to a place where it can be used. AddTransient means that a new instance of each class mentioned will be created each time the service is requested.

To use the client services, they get injected to the .razor pages as follows:

@inject IClientUserService ClientUserService

In the code section of each razor page we can now access endpoints such as:

var user = await ClientUserService.UpdateUser(updateUserDto);

In the implementation of ClientUserService it follows this pattern to reach out to the API endpoint in the server:

public Task<LoginResult> UpdateUser(UserDto data) => Post<LoginResult>("user/updateuser", data);

This makes a POST request to the server calling the user/updateuser endpoint and passing in whatever the UserDto requires.

In the UserController file in the server project we set up a HttpPost:

public async Task<IActionResult> UpdateUser(UpdateUserDto data)
        if (string.IsNullOrEmpty(data.UserId))
            return BadRequest();

        var result = await _userService.UpdateUser(data);
        return Ok(result);

In the controller above, we have defined a HttpPost endpoint called updateuser that accepts the UpdateUserDto model. We then check that it contains a userId; if so, we call the user service updateUser method.

So far, we have called the ClientUserService, which calls an endpoint defined in a controller on the server, which then calls the UserService where all of the logic to update the user will be held.

I won’t carry on at this point with how the data is fetched from MongoDb as we only have a prototype of that at the moment, but a brief explanation is as follows:

The UserService accesses the data context which will be something like this:


If you wanted to update a meeting instead, that would typically go through the ClientMeetingService > Meeting Controller > MeetingService and then to dataContext.Meetings.Update(data);

By working with the data context, we use abstraction to separate the logic of how data is fetched. Currently, the data context connects to a MongoDataRepository class that interacts directly with our MongoDB. Still, given that we have an interface MongoDataRepository uses, we could easily switch to CosmosDataRepository if we decide to use a different provider. We would need to migrate any data once the web app has gone live, but the option is there should we switch. Abstracting the logic behind the data context means that the UserService, MeetingService, and any other service doesn’t need to worry about how it works and doesn’t care where the data comes from.

We are about 70% done with the data context, about 70% with the MongoDataRepository, and zero per cent with Redis caching. For that part, the MongoDataRepository will likely implement an ICaching interface and have some logic written to decide what needs to be cached, how long, when it needs to be flushed/fetched, etc.

Let us know in the comments if you have any questions.