{"id":16206937,"url":"https://github.com/k-o-d-e-n/realtime","last_synced_at":"2025-03-19T07:31:35.909Z","repository":{"id":62452771,"uuid":"119575722","full_name":"k-o-d-e-n/Realtime","owner":"k-o-d-e-n","description":"Reactive ORM framework that makes the creation of complex database structures is simple. Support Linux.","archived":false,"fork":false,"pushed_at":"2022-10-22T09:49:20.000Z","size":1629,"stargazers_count":10,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-17T04:43:24.718Z","etag":null,"topics":["database","firebase","firebase-database","firebase-realtime-database","firebase-relationship","foundationdb","google","google-api","ios","mobile-development","model","property-wrappers","realtime","realtime-database","swift","swift-combine","swift-framework","transaction","uitableview"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/k-o-d-e-n.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-01-30T18:18:19.000Z","updated_at":"2024-02-22T00:07:45.000Z","dependencies_parsed_at":"2022-11-01T23:46:23.029Z","dependency_job_id":null,"html_url":"https://github.com/k-o-d-e-n/Realtime","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/k-o-d-e-n%2FRealtime","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/k-o-d-e-n%2FRealtime/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/k-o-d-e-n%2FRealtime/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/k-o-d-e-n%2FRealtime/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/k-o-d-e-n","download_url":"https://codeload.github.com/k-o-d-e-n/Realtime/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244379242,"owners_count":20443422,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["database","firebase","firebase-database","firebase-realtime-database","firebase-relationship","foundationdb","google","google-api","ios","mobile-development","model","property-wrappers","realtime","realtime-database","swift","swift-combine","swift-framework","transaction","uitableview"],"created_at":"2024-10-10T10:09:05.120Z","updated_at":"2025-03-19T07:31:35.535Z","avatar_url":"https://github.com/k-o-d-e-n.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Realtime\n\n[![Version](https://img.shields.io/cocoapods/v/Realtime.svg?style=flat)](http://cocoapods.org/pods/Realtime)\n[![License](https://img.shields.io/cocoapods/l/Realtime.svg?style=flat)](http://cocoapods.org/pods/Realtime)\n[![Platform](https://img.shields.io/cocoapods/p/Realtime.svg?style=flat)](http://cocoapods.org/pods/Realtime)\n\nRealtime is ORM framework that makes the creation of complex database structures is simple.\n\n## Features\n\n:point_right:  **Simple scalable model structure**\n\n:point_right: **Files**\n\n:point_right:  **Collections**\n\n:point_right:  **References**\n\n:point_right:  **UI, Form**\n\n### Support Firebase Database\nFirebase Realtime Database is fully supported and uses in production.\nIf you use clean Firebase API, Realtime can help to create app quicker, herewith to apply complex structures to store data, to update UI using reactive behaviors.\nRealtime provides lightweight data traffic, lazy initialization of data, good distribution of data.\n\n### Support FoundationDB\nFoundationDB is supported, but with some limitations, because FDB has no native observing mechanisms.\n\n## Usage\n\n### Initialization\n\nIn `AppDelegate` in `func application(_:didFinishLaunchingWithOptions:)` you must call code below, to configure working environment.\nNow for cache policy is valid values `case .noCache, .persistance` only. Cache in memory is not implemented yet.\n```swift\nfunc application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -\u003e Bool {\n    /// ...\n\n    /// initialize Realtime\n    RealtimeApp.initialize(...)\n\n    ///...\n    return true\n}\n```\n\n### Model\n\nTo create any model data structure you can make by subclassing `Object`.\nYou can define child properties using classes:\n+ `Object` subclasses;\n+ `ReadonlyProperty`, `Property`, `Reference`, `Relation`, `ReadonlyFile`, `File`;\n+ `References`, `Values`, `AssociatedValues`, and so on;\nIf you use lazy properties, you need implement class function `lazyPropertyKeyPath(for:)`. (Please tell me if you know how avoid it, without inheriting NSObject).\nThis function called for each subclass, therefore you don't need call super implementation. \nExample:\n```swift\nclass User: Object {\n    lazy var name: Property\u003cString\u003e = \"name\".property(in: self)\n    lazy var age: Property\u003cInt\u003e = \"age\".property(in: self)\n    lazy var photo: File\u003cUIImage?\u003e = \"photo\".file(in: self, representer: .png)\n    lazy var groups: References\u003cRealtimeGroup\u003e = \"groups\".references(in: self, elements: .groups)\n    lazy var scheduledConversations: Values\u003cConversation\u003e = \"scheduledConversations\".values(in: self)\n    lazy var ownedGroup: Relation\u003cRealtimeGroup?\u003e = \"ownedGroup\".relation(in: self, \"manager\")\n\n    override class func lazyPropertyKeyPath(for label: String) -\u003e AnyKeyPath? {\n        switch label {\n        case \"name\": return \\User.name\n        case \"age\": return \\User.age\n        case \"photo\": return \\User.photo\n        case \"groups\": return \\User.groups\n        case \"ownedGroup\": return \\User.ownedGroup\n        case \"scheduledConversations\": return \\User.scheduledConversations\n        default: return nil\n        }\n    }\n}\n\nlet user = User(in: Node(key: \"user_1\"))\nuser.name \u003c== \"User name\"\nuser.photo \u003c== UIImage(named: \"img\")\n\nlet transaction = user.save(in: .root)\ntransaction.commit(with: { state, err in\n    /// process error\n})\n```\n\n### Properties\n\n***ReadonlyProperty*** - readonly stored property for any value.\n\n***Property*** - stored property for any value.\n\n### References\n\n***Reference*** - stores reference on any database value. Doesn't imply referential integrity. Use it if record won't be removed or else other reason that doesn't need referential integrity.\n\n***Relation*** - stores reference on any database value.\n\n### Files\n\n***ReadonlyFile*** - readonly stored property for file in Firebase Storage.\n\n***File*** - stored property for file in Firebase Storage.\n\nAll properties adopt `@propertyWrapper` feature, but while Swift is unsupported access to `self` in custom lazy properties, this way to define properties generally useless.\n\n### Collections\n```swift\nclass Some: Object {\n    lazy var array: Values\u003cObject\u003e = \"some_array\".values(in: self)\n    lazy var references: References\u003cObject\u003e = \"some_linked_array\".references(in: self, elements: .linkedObjects)\n    lazy var dictionary: AssociatedValues\u003cObject\u003e = \"some_dictionary\".dictionary(in: self, keys: .keyObjects)\n}\n```\nSome mutable operations of collections can require `isSynced` state. To achieve this state use `func runObserving()` function or set property `keepSynced: Bool` to `true`.\n\n***(Distributed)References*** is array that stores objects as references.\nSource elements must locate in the same reference. On insertion of object to this array creates link on side object.\n\n***(Explicit)Values*** is array that stores objects by value in itself location. 'Explicit' prefix is used in collection that stores elements without collection view.\n\n`References`, `Values` mutating:\n```swift\ndo {\n    let transaction = Transaction()\n    ...\n    let element = Element()\n    try array.write(element: element, in: transaction)\n    try otherArray.remove(at: 1, in: trasaction)\n\n    transaction.commit { (err) in\n        // process error\n\n        self.tableView.reloadData()\n    }\n} catch let e {\n    // process error\n}\n```\n\n***(Explicit)AssociatedValues*** is dictionary where keys are references, but values are objects. On save value creates link on side key object.\n\n`AssociatedValues` mutating:\n```swift\ndo {\n    let transaction = Transaction()\n    ...\n    let element = Element()\n    try dictionary.write(element: element, key: key, in: transaction)\n    try otherDictionary.remove(by: key, in: transaction)\n\n    transaction.commit { (err) in\n        // process error\n    }\n} catch let e {\n    // process error\n}\n```\n\n***MapRealtimeCollection*** is immutable collection that gets elements from map function. This is the result of x.lazyMap(_ transform:) method, where x is any RealtimeCollection. \n```swift\nlet userNames = Values\u003cUser\u003e(in: usersNode).lazyMap { user in\n    return user.name\n}\n```\n\n### Operators\n\n+ `\u003c==`  - assignment operator. Can use to assign (or to retrieve) value to (from) any Realtime property.\n+ `====`, `!===` - comparison operators. Can use to compare any Realtime properties where their values conform to `Equatable` protocol.\n+ `??` - infix operator, that performs a nil-coalescing operation, returning the wrapped value of an Realtime property or a default value.\n+ `\u003c-` - prefix operator. Can use to convert instance of `Closure, Assign` types to explicit closure or backward.\n\n### Transactions\n\n***Transaction*** - object that contains all information about write transactions.\nAlmost all data changes perform using this object.\nThe most mutable operations just take transaction as parameter, but to create custom complex operations you can use this methods:\n```swift\n/// adds operation of save RealtimeValue as single value as is\nfunc set\u003cT\u003e(_ value: T, by node: Node) where T: RealtimeValue \u0026 RealtimeValueEvents\n/// adds operation of delete RealtimeValue\nfunc delete\u003cT\u003e(_ value: T) where T: RealtimeValue \u0026 RealtimeValueEvents\n/// adds operation of update RealtimeValue\nfunc update\u003cT\u003e(_ value: T) where T: ChangeableRealtimeValue \u0026 RealtimeValueEvents \u0026 Reverting\n/// method to merge actions of other transaction\nfunc merge(_ other: Transaction)\n```\nFor more details see Example project.\n\n### UI\n\n***SingleSectionTableViewDelegate*** -  provides single section data source for UITableView with auto update.\n***SectionedTableViewDelegate*** -  provides sectioned data source for UITableView with auto update.\n***CollectionViewDelegate*** - provides data source for UICollectionView with auto update.\n```swift\ndelegate.register(UITableViewCell.self) { (item, cell, user, ip) in\n    item.bind(\n        user.name, { cell, name in\n            cell.textLabel?.text = name \n        }, \n        { err in\n            print(err)\n        }\n    )\n}\ndelegate.bind(tableView)\ndelegate.tableDelegate = self\n\n// data\nusers.changes\n    .listening(\n        onValue: { [weak tableView] (e) in\n            guard let tv = tableView else { return }\n            switch e {\n            case .initial: tv.reloadData()\n            case .updated(let deleted, let inserted, let modified, let moved):\n                tv.beginUpdates()\n                tv.insertRows(at: inserted.map({ IndexPath(row: $0, section: 0) }), with: .automatic)\n                tv.deleteRows(at: deleted.map({ IndexPath(row: $0, section: 0) }), with: .automatic)\n                tv.reloadRows(at: modified.map({ IndexPath(row: $0, section: 0) }), with: .automatic)\n                moved.forEach { from, to in\n                    tv.moveRow(at: IndexPath(row: from, section: 0), to: IndexPath(row: to, section: 0))\n                }\n                tv.endUpdates()\n            }\n        },\n        onError: onError\n    )\n    .add(to: listeningCollector)\n```\n\n### Forms\n\nAvailable as separated module with `Combine` support.\n\n```swift\nclass User: Object {\n    var name: Property\u003cString\u003e\n    var age: Property\u003cInt\u003e\n}\n\nclass FormViewController: UIViewController {\n    var form: Form\u003cUser\u003e\n\n    override func viewDidLoad() {\n        super.viewDidLoad()\n        \n        let name = Row\u003cTextCell, Model\u003e.inputRow(\n            \"input\",\n            title: Localized.name,\n            keyboard: .name,\n            placeholder: .inputPlaceholder(Localized.name),\n            onText: { $0.name \u003c== $1 }\n        )\n        name.onUpdate { (args, row) in\n            args.view.textField.text \u003c== args.model.name\n        }\n        let age = Row\u003cTextCell, Model\u003e.inputRow(\n            \"input\",\n            title: Localized.age,\n            keyboard: .numberPad,\n            placeholder: requiredPlaceholder,\n            onText: { $0.age \u003c== $1 }\n        )\n        age.onUpdate { (args, row) in\n            args.view.textField.text \u003c== args.model.age\n        }\n        let button: Row\u003cButtonCell, Model\u003e = Row(reuseIdentifier: \"button\")\n        button.onUpdate { (args, row) in\n            args.view.titleLabel.text = Localized.login\n        }\n        button.onSelect { [unowned self] (_, row) in\n            self.submit()\n        }\n\n        let fieldsSection: StaticSection\u003cModel\u003e = StaticSection(headerTitle: nil, footerTitle: nil)\n        fieldsSection.addRow(name)\n        fieldsSection.addRow(age)\n\n        let buttonSection: StaticSection\u003cModel\u003e = StaticSection(headerTitle: nil, footerTitle: nil)\n        buttonSection.addRow(button)\n\n        form = Form(model: User(), sections: [fieldsSection, buttonSection])\n        form.tableView = tableView\n        form.tableDelegate = self\n    }\n}\n```\n\n### Local listening\n\nTo receive changes on local level use objects that conform this protocol. It has similar RxSwift interface.\n```swift\npublic protocol Listenable {\n    associatedtype OutData\n\n    /// Disposable listening of value\n    func listening(_ assign: Assign\u003cOutData\u003e) -\u003e Disposable\n}\n```\n\n### Debugging\nAdd debug argument 'REALTIME_CRASH_ON_ERROR' passed on launch, to catch internal errors.\n\n### JS\nAlso exists NodeJS module, created for Vue.js application. Source code you can found in `js` folder.\n\n## Limitations\nRealtime objects should not passed between threads.\n\n## Example\n\nTo run the example project, clone the repo, and run `pod install` from the Example directory first.\n\n## Requirements\n\nXcode 9+, Swift 4.1+.\n\n## Installation\n\nSwiftPM\n```swift\n.package(url: \"https://github.com/k-o-d-e-n/realtime.git\", .branch(\"master\"))\n```\n\nRealtime is available through [CocoaPods](http://cocoapods.org). To install\nit, simply add the following line to your Podfile:\n\n```ruby\npod 'Realtime'\n```\n```ruby\npod 'RealtimeForm/Combine', :git =\u003e 'https://github.com/k-o-d-e-n/Realtime.git', :branch =\u003e 'master'\n```\n\n## Author\n\nKoryttsev Denis, koden.u8800@gmail.com  \nTwitter: [@K_o_D_e_N](https://twitter.com/K_o_D_e_N)\n\n## License\n\nRealtime is available under the MIT license. See the LICENSE file for more info.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fk-o-d-e-n%2Frealtime","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fk-o-d-e-n%2Frealtime","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fk-o-d-e-n%2Frealtime/lists"}