Introducing Inneal

While most of my AI related app releases have been dedicated to image generation, (counting two Ealain releases and then Aislingeach), my gateway into generative AI was chatbots. If you look at the chronology of this blog, that’s pretty obvious, since it all began with my post about “uncensored” chatbots, which I’ve recently updated to note this very app I’m talking about now.

So, yeah, it’s weird that it took me nearly a year to finally make my own chatbot app. The reason for this is largely just that open source LLMs took a little time to cook before getting better. They’re still not perfect, or anything, but they’re a lot more fun to play around with and you can essentially get a good experience “out of the box” with any of them. It felt like the time was right to try to make a straight-forward app for character-based role-play chat.

If you don’t care to read the rest, well, here’s some links for you.

It feels moderately awkward to explain this app to people, because the main way LLMs are in the news and overall public consciousness is that they’re writing assistants, or coding assistants, or maybe they’re going to steal customer service jobs. But this app is not that, it’s not meant to be an assistant. It’s meant to allow you to chat to fictional characters, which at face value sounds… immature? Or stupid? I don’t know, but it definitely makes me cringe a little bit.

Additionally, as my original blog post makes clear, depending on the audience, there’s a bit of ick around the entire subject matter because a lot of the reason these open source LLMs exist and are good for this purpose is because people wanted to have sex with chatbots. That’s just how it is. There’s a lot of lonely (or just horny) people out there, across the whole spectrum of genders and sexualities, and a vast pornography industry in existence that proves it. So, of course people want to use this new technology in that way.

So is that an explicit endorsement, that my app is for people to use to have sex with chatbots? Well, that’s the pickle I’m in with the app, when I’m explaining it to people, because it’s hard to convince someone that a grown adult might just enjoy talking to random fictional characters. And that’s mostly how I dogfood the app, just shooting the shit with random character cards, and seeing what funny (or stupid) stuff comes out. This technology can be really entertaining, especially when it pulls a stroke of genius out of its back pocket and surprises you.

So, Inneal is for writing characters and chatting with them, and maybe that is just for fun, like a choose your own adventure story you are actively writing while participating in it; or maybe you do it to stimulate your creative instinct and try to flesh out characters in your own fictional stories; or maybe it’s because you’re lonely and you want to talk to a familiar face, even if they don’t really exist. I’m not going to judge. I’m not the judging kind of person. Even if you want to have sex with them.

This is my second app built entirely with SwiftUI, and my first app using SwiftData for the persistence layer. Ealain, my first SwiftUI app, was pretty simple and didn’t really force me to learn how SwiftUI works properly. Inneal, on the other hand, really forced me to learn quite a bit about SwiftUI and especially how it interacts with SwiftData.

When I’ve used CoreData in the past, I’ve followed advice to abstract it away as best I can and I usually keep all the CoreData related logic compiled together in one class, to try to avoid issues with threading. This results in crashes in Aislingeach to this day with NSFetchedResultsController, but, whatever, they’re pretty rare.

SwiftData doesn’t want you to do this, or at least, if you do, you lose out on a lot of cool stuff. It also doesn’t want you to update or create model objects essentially anywhere but directly in the View code. This feels weird, coming from a background where you feel inclined to try to hide the model away from the views as much as you can. Instead I end up in a situation where the ViewModel is passing data back to the View so that the View can create or update the Model within itself.

I don’t think I am doing this wrong, because it works quite well, and still keeps almost all the backend logic tucked away in the ViewModel, and the View has this strangle-hold on the Model, which sort of makes sense because that is where the model is used and updated anyway.

SwiftUI is a little glitchy, but it’s understandable because I don’t envy any of the behind the scenes work that goes into it. The two notable issues I ran into are: 1) if you setup SwiftData the way Apple intends and then setup CloudKit syncing, your app will crash every time it’s backgrounded. 2) When you use LazyVStack with defaultScrollAnchor, sometimes your internal views just kind of disappear, which is a continued issue I can’t manage to solve 100%. I might end up using one of my free developer technical support tickets to ask about this issue.

The other issue I’m sure is perennial, which is that you will reach a point where the compiler simply dies instead of making any attempt at telling you where the bug in your code is. This means you must be very careful about how many code changes you make before doing a build & run, because otherwise you’ll end up pretty stuck not having any idea what you broke.

All that said, I really enjoyed building this app with SwiftUI and don’t think I will go back to using UIKit unless I really have to for some reason. It really sped up the development process and cut down on a lot of unnecessary boilerplate style code created by strict adherence to the delegate method.

I guess that’s it. Go download the app and chat with some bots!