Swiftpack.co -  rizumita/Sede as Swift Package
Swiftpack.co is a collection of thousands of indexed Swift packages. Search packages.
rizumita/Sede
Sede is a library for SwiftUI to bind a view and a model and to provide routing.
.package(url: "https://github.com/rizumita/Sede.git", from: "v0.4.7")

Sede

Sede is a library for SwiftUI to connect views and models, and to provide routing.

Concept

Sede aimed to compatible with SwiftUI by fully utilizing SwiftUI features. It can be loose coupling SwiftUI's View and Model, and Model can use View's Environment and EnvironmentObject freely. View can reference Model and send messages freely.

Install

You can install Sede by Swift Package Manager.

How to use

Create views

@Seeded provides a model's data and dispatches messages.

@Router provides a routing function.

import Sede

struct PersonInputView: View {
    @Seeded<Model, Msg> var seed
    @Router<AppRoute> var router

    var body: some View {
        NavigationView {
            List {
                VStack {
                    TextField("Name", text: $seed.name)
                    TextField("Profile", text: $seed.profile)
                    Button("Save") { seed(.save) }
                }

                ForEach(seed.people) { person in
                    NavigationLink(destination: router(.displayPerson(person))) { Text(person.name) }
                }
            }
                .navigationTitle("Input Person")
        }
            .navigationViewStyle(StackNavigationViewStyle())
    }
}

extension PersonInputView {
    struct Model: ObservableValue {
        static var published: [PartialKeyPath<PersonInputView.Model>] {
            \Self.people
        }

        var name: String
        var profile: String
        var people: [Person]
    }

    enum Msg {
        case resetFields
        case update
        case save
    }
}

struct PersonDisplayView: View {
    @Seeded<Person, Never> var seed

    var body: some View {
        VStack {
            Text(seed.name)
            Text(seed.profile)
        }
    }
}

Create routing

Routable.locate(route:) method must return a view for Route.

enum AppRoute {
    case inputPerson
    case displayPerson(Person)
}

struct AppRouter: Routable {
    func locate(route: AppRoute) -> some View {
        switch route {
        case .inputPerson:
            PersonInputView().seed(PersonInputSeeder())

        case .displayPerson(let person):
            PersonDisplayView().seed(VariableSeeder(person))
        }
    }
}

Create models

struct Person: Identifiable {
    var id: UUID = UUID()
    var name: String
    var profile: String
}

class PeopleRepository: ObservableObject {
    @Published private(set) var people: [Person] = []

    func add(person: Person) {
        people.append(person)
    }
}

Create seeders

struct PersonInputSeeder: Seedable {
    @Seeding<PersonInputView.Model, PersonInputView.Msg> var seed

    @EnvironmentObject var peopleRepository: PeopleRepository
    var observedObjects: some ObservableObject { peopleRepository }

    func initialize() {
        seed.initialize(PersonInputView.Model(name: "test", profile: "", people: peopleRepository.people))
    }

    func update() {
        seed(.update)
    }

    func receive(msg: PersonInputView.Msg) {
        switch msg {
        case .resetFields:
            seed.name = ""
            seed.profile = ""

        case .update:
            seed.people = peopleRepository.people

        case .save:
            guard !seed.name.isEmpty else { return }
            peopleRepository.add(person: Person(name: seed.name, profile: seed.profile))
            seed {
                Msg.resetFields
                Msg.update
                Msg.print
            }
        }
    }
}

Cmd expresses a command to be run messages.

ex.

Cmd<Msg>.none                                                // Not run any message
Cmd<Msg>.ofMsg(.someMessage)                                 // Run .someMessage
Cmd<Msg>.batch([.ofMsg(.someMessage), .ofMsg(.someMessage)]) // Run .someMessage twice

Task expressed an async task to be run messages.

ex.

Task(publisher).attemptToMsg { _ in .update }   // When the publisher sends value, Msg.update is sent. 

Create app

@main
struct PersonListApp: App {
    var body: some Scene {
        WindowGroup {
            AppRouter().base(.inputPerson)
                .environmentObject(PeopleRepository())  // Set the repository as environment object for seeders 
        }
    }
}

License

MIT

GitHub

link
Stars: 2
Last commit: 6 weeks ago

Ad: Job Offers

iOS Software Engineer @ Perry Street Software
Perry Street Software is Jack’d and SCRUFF. We are two of the world’s largest gay, bi, trans and queer social dating apps on iOS and Android. Our brands reach more than 20 million members worldwide so members can connect, meet and express themselves on a platform that prioritizes privacy and security. We invest heavily into SwiftUI and using Swift Packages to modularize the codebase.

Swiftpack is being maintained by Petr Pavlik | @ptrpavlik | @swiftpackco | API