{"id":23718819,"url":"https://github.com/underthestars-zhy/magicdata","last_synced_at":"2025-09-03T20:32:46.303Z","repository":{"id":38049955,"uuid":"499806750","full_name":"underthestars-zhy/MagicData","owner":"underthestars-zhy","description":"A easy and powerful datebase for Swift. Based on SQLite. Faster than other DB.","archived":false,"fork":false,"pushed_at":"2022-07-28T13:21:02.000Z","size":123,"stargazers_count":21,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-04-20T14:20:47.716Z","etag":null,"topics":["database","ios-swift","sqlite","swift"],"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/underthestars-zhy.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":"2022-06-04T11:29:41.000Z","updated_at":"2024-02-03T01:34:02.000Z","dependencies_parsed_at":"2022-07-11T04:00:25.480Z","dependency_job_id":null,"html_url":"https://github.com/underthestars-zhy/MagicData","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/underthestars-zhy%2FMagicData","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/underthestars-zhy%2FMagicData/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/underthestars-zhy%2FMagicData/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/underthestars-zhy%2FMagicData/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/underthestars-zhy","download_url":"https://codeload.github.com/underthestars-zhy/MagicData/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":231916684,"owners_count":18445443,"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","ios-swift","sqlite","swift"],"created_at":"2024-12-30T21:32:06.580Z","updated_at":"2024-12-30T21:32:07.354Z","avatar_url":"https://github.com/underthestars-zhy.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# MagicData\n\nA replacement for CoreData, or Realm. It is very easy to use and is a light version.\n\n## Support\n- macOS 12 or later\n- iOS 13 or later\n- Linux with some settings of [SQLite](https://github.com/stephencelis/SQLite.swift/blob/master/Documentation/Linux.md) \n\n## Guides\n\n### MagicData\n\nWe use **MagicData** to manage all the magic objects, which means **MagicData** can add, update, or delete the objects. All the **MagicDatas** are in the same actor thread. In this way, we keep the thread safe.\n\nHere are some ways to create **MagicData**.\n```swift\nlet magic = try await MagicData() // This will create a database at the app's document path\nlet magic = try await MagicData(path: URL(fileURLWithPath: \"\").path) // This will create a database at your custom path\nlet magic = try await MagicData(type: .temporary) // This will create a auto-delete database\nlet magic = try await MagicData(type: .memory) // This will create a database in the memory\n```\n\n## MagicObject\n\n**MagicObject** is like the meaning of the table in SQLite, but it is more powerful. **MagicObject** should be a struct. But if you want to sync the non-magical value in the instance, you can choose the `class`.\n\n```swift\nstruct TestModel: MagicObject {\n    @PrimaryMagicValue var id: String\n\n    @MagicValue var name: String\n    @MagicValue var age: Int\n\n    @OptionMagicValue var school: Data?\n    @OptionMagicValue var petName: String?\n    @OptionMagicValue var hight: Double?\n\n    var customString: String {\n        \"My ID: \\(id), name: \\(name)\"\n    }\n\n    init() {}\n\n    init(name: String) {\n        self.name = name\n    }\n}\n```\n\nAll **MagicObjects** require the line init().\nIf you want to use the primary value to query the data or update the data, you need to set the `@PrimaryMagicValue`. Each **PrimaryMagicValue** has a distinct default value.\n**MagicValue** has the ability to save all Magical values, which is not an option. just for the `init() {}` line. As you see, you can create an object without setting the value, but if you access the value, a crash will happen.\n**OptionMagicValue** like the **MagicValue**, but it can store the option value. It has a defualt value `nil`.\n\nAlthough `TestModel` is a sturct but if you copy it, and change it, the value will change in the original instance too.\n\n```swift\nlet test = TestModel(name: \"hi\")\nlet test2 = test\ntest2.name = \"hello\"\nprint(test.name) // Hello\n```\n\nAnd also, if you just want to change the value of `MagicValue`, you don't need to set the `struct` as `var`.\n\nIf you gain a new instance from the database and change a value that you have had with the same primary value, the data will not sync between them.\n\n```swift\nlet test = TestModel(name: \"hi\")\nlet id = test.id\nmagic.update(test)\nlet test2 = magic.object(of: TestModel.self).where { $0.id == id }\ntest2.name = \"hello\"\n\nprint(test.name) // \"hi\"\n```\n\n### Codable\n\n`MagicObject` conforms to the `Codable`. However, you can never decode a `MagicObject`. `MagicObject` will be encoded to an int value that represents the object's zIndex. \n\n## Magical\n\n**Magical**s are kinds of values that can be stored in the database.\n\nNow we support theses:\n\n* `String` will be stored as `Text` in the database.\n* `UUID` will be stored as `Text` in the database.\n* `Int` will be stored as `Int` in the database.\n* `Double` will be stored as `Real` in the database.\n* `Float` will be stored as `Real` in the database.\n* `Data` will be stored as `Blob` in the database.\n* `Codable` will be stored as `Blob` in the database.\n* `Arrary` will be stored as `Blob` in the database.\n* `Dictionary` will be stored as `Blob` in the database.\n* `Set` will be stored as `Blob` in the database.\n\n### Points of Codable\n\nFirst of all, we cannot store `Codable`, but it can be stored as `MagicalCodable`. `MagicalCodable` is a variant of `Codable`.\n\n```swift\nstruct Job: MagicalCodable {\n    let title: String\n    let salary: Int\n}\n\n@OptionMagicValue var job: Job?\n```\n\n### Ponints of Arrary \u0026 Dictionary\n\nWe only support the `Arrary` or `Dictionary` which conforms to the `Codable`.\n\n### Primary\n\nSome value can be used in the `@PrimaryMagicValue`:\n\n- **String** has a defualt **UUID String** value.\n- **UUID** has a default **UUID** value.\n- **Int** has auto increase ability.\n\n## MagicAsset\n\n```swift\nstruct TestModel: MagicObject {\n    @PrimaryMagicValue var uuid: UUID\n\n    @MagicValue var asset: MagicAsset\u003cString\u003e\n\n    init() {\n        asset = .init()\n    }\n}\n```\n\n`MagicAsset` conforms to `Magical` too. But it only store a path in the database. It is used to save large files, the files will be saved in the local file system instead of database. The `MagicAsset`'s element need to conform the `MagicAssetConvert`. Here are the list:\n\n* String\n* Data\n* MagicalCodable\n* Array where Element: Codable\n* Set where Element: Codable\n* Dictionay where Key: Codable, Value: Codable\n\n## AsyncMagical\n\n`AsyncMagical` allows you don't get the value during the query, so you can get it later. It is very helpful when you save `MagicObject` or `MagicAsset`. Because it can delay the time you get the object, and speed up the time of querying.\n\n### Support List\n\n* MagicAsset\n* Array where Element: MagicObject\n* MagicalSet\n* MagicObject\n* Dictionay where Key: Codable, Value: MagicObject\n\n### How to use\n\n```swift\nstruct TestModel: MagicObject {\n    @PrimaryMagicValue var uuid: UUID\n\n    @MagicValue var asset: AsyncMagical\u003cMagicAsset\u003cString\u003e\u003e\n\n    init() {}\n\n    init(_ object: Sub) {\n        asset = .init(value: object)\n    }\n}\n```\n\n* Get Value: `try await instanceCopy.asset.get()`\n* Set Value: `instance.asset.set(.init(value: \"Hello\"))`\n\n### AsyncStream\n\n#### Support List\n\n* Array where Element: MagicObject\n* MagicalSet\n* Dictionay where Key: Codable, Value: MagicObject\n\n#### How to use\n\n```swift\nfor try await item in try instanceCopy.array.createAsyncStream() {\n    res.append(item)\n}\n\n```\n\n#### Random Value\n\n#### Support List\n\n* Array where Element: MagicObject\n* MagicalSet\n* Dictionay where Key: Codable, Value: MagicObject\n\n#### How to use\n\n```swift\ntry await instance.array.randomValue()?.uuid\ninstance.array.randomValue(in: 1..\u003c2)?.int // Onlt support when Index == Int\n```\n\n## Add/Update\n\n```swift\ntry await magic.update(object)\n```\nIf object already exits in the database or the object has a copy in the set, the set will not add the object, but will **update the object**.\n\n## Remove\n\n### Remove one object\n\n```swift\ntry await magic.remove(test1)\n```\n\n### Remove All\n\n```swift\ntry await magic.removeAll(of: TestModel.self)\n```\n\n## Query All\n\n```swift\ntry await magic.object(of: TestModel.self)\n```\n\nThis will give back all the values.\n\n## Query by primary value\n\n```swift\ntry await magic.object(of: TestModel.self, primary: AnyPrimaryValue)\n```\n\nThis will throw an error if the primary value isn't in the database.\n\n## Know whethere the object exits\n\n```swift\ntry await magic.has(of: TestModel.self, primary: instance1.uuid)\n```\n\n**Requirement**: MagicalObject has a primary value\n\n## Relationship\n\n**MagicData** support relationship like core data.\n\n### One to Many\n\n```swift\nstruct TestModel: MagicObject {\n    @PrimaryMagicValue var uuid: UUID\n\n    @MagicValue var sub: Sub\n\n    init() {}\n\n    init(_ sub: Sub) {\n        self.sub = sub\n    }\n}\n\nstruct Sub: MagicObject {\n    @PrimaryMagicValue var uuid: UUID\n\n    @MagicValue var text: String\n\n    init() {}\n\n    init(_ text: String) {\n        self.text = text\n    }\n}\n```\n\nYou could use `@OptionMagicValue` as well. This kind of relationship is a little different from coredata's. because it isn't a lazy value. That means, it will fetch the `sub` in the database, when you fetch the `TestModel`.\n\n### Many to Many\n\n#### Set\n\n```swift\nstruct TestModel: MagicObject {\n    @PrimaryMagicValue var uuid: UUID\n\n    @MagicValue var set: MagicalSet\u003cSub\u003e\n\n    init() {\n        set = .init([])\n    }\n}\n\nstruct Sub: MagicObject {\n    @MagicValue var text: String\n\n    init() {}\n\n    init(_ text: String) {\n        self.text = text\n    }\n}\n```\n\n`MagicalSet` just like a default set, but it only can store `MagicObject`.\n\n**Although `MagicalSet` is a `Collection`, but I strongly suggest you that you shouldn't use it.**\n\n#### Arrary\n\n**It only supports single arrary, which means it doesn't support something like `[[MagicObject]]`**\n\n```swift\nstruct TestModel: MagicObject {\n    @PrimaryMagicValue var uuid: UUID\n\n    @MagicValue var arrary: [Sub]\n\n    init() {\n        arrary = .init([])\n    }\n}\n\nstruct Sub: MagicObject {\n    @MagicValue var text: String\n\n    init() {}\n\n    init(_ text: String) {\n        self.text = text\n    }\n}\n```\n\n#### Dictionary\n\n**It only supports single dictionaries, which means it doesn't support something like `[Int: [String: MagicObject]]`**\n\n```swift\nstruct TestModel: MagicObject {\n    @PrimaryMagicValue var uuid: UUID\n\n    @MagicValue var dict: [String: Sub]\n\n    init() {\n        dict = [:]\n    }\n}\n\nstruct Sub: MagicObject {\n    @MagicValue var text: String\n\n    init() {}\n\n    init(_ text: String) {\n        self.text = text\n    }\n}\n```\n\n### Reverse\n\n```swift\nstruct TestModel: MagicObject {\n    @PrimaryMagicValue var uuid: UUID\n\n    @MagicValue var set: MagicalSet\u003cSub\u003e\n\n    init() {\n        set = .init([])\n    }\n}\n\nstruct Sub: MagicObject {\n    @MagicValue var text: String\n    @ReverseMagicValue(\\TestModel.$set) var father: AsyncReverseMagicSet\u003cTestModel\u003e\n\n    init() {}\n\n    init(_ text: String) {\n        self.text = text\n    }\n}\n```\n\nYou cannot set the value of `@ReverseMagicValue`. And the `AsyncReverseMagicSet` is an `AsyncSequence`.\n\n### Points of MagicalSet\n\n`MagicalSet` is not a normal set. It can only promise you that it will save a set in the database and get a set from the database. But in the runtime, it can be a non-set value. \n\n### Insert\n\n```swift\ninstance.set.insert(sub1)\n```\n\nIf the object has been saved, and the set doesn't contain the value that has the same `zIndex`, the value will be insterted.\nIf the object hasn't been insterted, the value will always be insterted.\n\n### Remove\n\n```swift\ninstanceCopy1.set.remove(sub1)\n```\n\nOnly remove the saved object that has the same `zIndex`.\n\n```swift\ninstanceCopy1.set.removeAll(where perform: (Element) -\u003e Bool)\n```\n\nYou can decide which object will be removed.\n\n### Fetch\n\nEvery time you fetch the object, we will remove the item that has the same `zIndex`. But we can not promise it in the runtime.\n\n## What is ZIndex\n\nZIndex is `MagicalData`'s own primary key. It will automatically be added to your table. We use this key to judge whether the two objects are equal, or whether the object is in the database.\nZIndex is `nil` when the object hasn't saved.\u003cbr\u003e\nYou cannot get the ZIndex through the `MagicalData`, but maybe we will make it public in the future.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funderthestars-zhy%2Fmagicdata","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Funderthestars-zhy%2Fmagicdata","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funderthestars-zhy%2Fmagicdata/lists"}