2 min read

Open Source Chakra Summer Re-Openning

Open Source Chakra Summer Re-Openning
Photo by Richy Great / Unsplash
#Puredux, #SwiftletModel, #Software Engineering, #iOS App Development

In recent months I revisited my GitHub account and had a lot of fun.

Vapor RestKit

I've updated VeporRestKit for Swift-Concurrency compatibility and released an update. The API has never been so smooth.

I think if I get a chance to use it this year more actively it will get a few more updates.

Puredux

I've made a significant refactoring of the internals that greatly improved Puredux performance and extendibility for future features.

The main thing that I'm planning is a redesign of the current architecture to a multiple-store tree of larger depths.

I also plan to update of side effects and syntax sugar for UIKit and SwiftUI.

Finally, there will be a redesign of internals to Swift-Concurrency and Actors, but it will likely involve breaking changes that I don't want to face at the moment.

Swiftlet Model

/content/images/2024/07/Screenshot-2024-07-24-at-21.17.20.png

I've started a new project and called it SwiftletModel.

It will be an in-memory value-typed lightweight alternative to SwiftData with persistence capabilities.

It will perfectly fit the case when the app data model is rather complicated but the app is still a thin client and you have reasons to avoid CoreData/SwiftData/Realm.

I just imagined what could be the perfect CoreData and decided to implement it.

It's still in the early development stage, but

---SPOILER ALERT---

It's gonna be very sexy.

Here is how models will be defined:

struct Chat: EntityModel, Codable {
    let id: String
    
    @HasMany(\.users, inverse: \.chats)
    var users: [User]?
    
    @HasMany(\.messages, inverse: \.chat)
    var messages: [Message]?
    
    @HasMany(\.admins, inverse: \.adminOf)
    var admins: [User]?
    
    mutating func normalize() {
        $users.normalize()
        $messages.normalize()
        $admins.normalize()
    }
    
    func save(to context: inout Context) throws {
        context.insert(self)
        try save(\.$users, inverse: \.$chats, to: &context)
        try save(\.$messages, inverse: \.$chat, to: &context)
        try save(\.$admins, inverse: \.$adminOf, to: &context)
    }
    
    func delete(from context: inout Context) throws {
        try delete(\.$messages, inverse: \.$chat, from: &context)
        context.remove(Chat.self, id: id)
        detach(\.$users, inverse: \.$chats, in: &context)
        detach(\.$admins, inverse: \.$adminOf, in: &context)
    }
}

And this is how models will be queried:

let user = User
	.query("1", in: context)
	.with(\.$chats) {
		$0.with(\.$messages) {
			$0.with(\.$attachment)  
				.id(\.$author) 
		}
		.with(\.$users)
		.ids(\.$admins)
	}
	.resolve()

Planned Features

Currently I plan these features for the initial release:

  • Strongly typed Relations with compile-time checks
  • Handy model manipulation for data normalization and denormalization
  • Incomplete data handling
  • Fully Codable out-of-the-box
  • Persistence to file in JSON format

The prorope is almost done. Now it requires outlining public API, cleaning up, adding unit tests, fixing bugs, writing docs, etc.

A huge pile of work, lol. Anyway, I'm already happy about it and now I just need some motivation to push it to the first release.