Skip to content

Commit

Permalink
ObjectBox Swift database 4.0.1
Browse files Browse the repository at this point in the history
  • Loading branch information
greenrobot-team committed Oct 22, 2024
1 parent e8f0fba commit d09c2d0
Show file tree
Hide file tree
Showing 26 changed files with 1,801 additions and 32 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,19 @@ Notable changes to the ObjectBox Swift library.

For more insights into what changed in the ObjectBox C++ core, [check the ObjectBox C changelog](https://github.com/objectbox/objectbox-c/blob/main/CHANGELOG.md).

## 4.0.1 - 2024-10-16

- Built with Xcode 15.0.1 and Swift 5.9.
- Make closing the Store more robust. In addition to transactions, it also waits for ongoing queries. This is just an
additional safety net. Your apps should still make sure to finish all Store operations, like queries, before closing it.
- Generator: no longer print a `Mapping not found` warning when an entity class uses `ToMany`.
- Some minor vector search performance improvements.
- Update to [ObjectBox C 4.0.2](https://github.com/objectbox/objectbox-c/releases/tag/v4.0.2).

### Sync

- **Fix a serious regression, please update as soon as possible.**

## 4.0.0 - 2024-07-22

**ObjectBox now supports [Vector Search](https://docs.objectbox.io/ann-vector-search)** to enable efficient similarity searches.
Expand Down
4 changes: 2 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ let package = Package(
targets: [
.binaryTarget(
name: "ObjectBox",
url: "https://github.com/objectbox/objectbox-swift/releases/download/v4.0.0/ObjectBox-xcframework-4.0.0.zip",
checksum: "2bed7b8b87dd46dda64fa1f5e7a0b1df87908a783690fda09c6dc943a01a32e5"
url: "https://github.com/objectbox/objectbox-swift/releases/download/v4.0.1/ObjectBox-xcframework-4.0.1.zip",
checksum: "02a0f686ced7488a9afe312e3b1a7cac634848709bd04cd904a3a5fa26ec8d31"
)
]
)
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ Here's a list of ObjectBox releases, and the Swift versions they were compiled w

| ObjectBox version(s) | Swift version |
|:--------------------:|:-------------:|
| 4.0.1 | 5.9 |
| 4.0.0 | 5.9 |
| 2.0.0 | 5.9 |
| 1.9.2 | 5.9 |
Expand Down
4 changes: 2 additions & 2 deletions Source/fetch_dependencies.command
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ set -e

# objectbox-swift release version on GitHub:
# https://github.com/objectbox/objectbox-swift/releases/download/v${version}
version=4.0.0
version=4.0.1

# C library version attached to the GitHub release:
# ObjectBoxCore-static-${c_version}.zip
c_version=4.0.1
c_version=4.0.2

# Params supported by apple-build-static-libs.sh
build_params=""
Expand Down
4 changes: 4 additions & 0 deletions Source/ios-framework/CodeGenTests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ This project is used to test code generation in ObjectBox's Sourcery-descended c
It will run the codegen over a given target in the project, then build and run the product. You can do
whatever testing you need to do in the actual Swift code.

**Important note**: by passing the `--debug-parsetree` option to the generator in `RunToolTests.sh`
it generates **non-random, stable UIDs**. See `runCLI()` of `objectbox-swift-generator/Sourcery/main.swift`.
These are unlike (notably shorter) UIDs than are generated for a user project.

## Running these tests

These tests require a copy of ObjectBox Swift Code Generator in a known location. To get that all set up, do the following:
Expand Down
2 changes: 2 additions & 0 deletions Source/ios-framework/CodeGenTests/RunToolTests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ fail_codegen_target_num () {

echo "// Ensure there's no leftover code from previous tests." > "$ENTITY_INFO_FILE_ACTUAL"

# Setting --debug-parsetree for the generator also makes it generate non-random UIDs,
# see objectbox-swift-generator/Sourcery/main.swift runCLI().
echo "$SOURCERY --xcode-project \"$TESTPROJECT\" --xcode-target \"ToolTestProject${2}\" --model-json \"$MODEL_FILE_ACTUAL\" --debug-parsetree \"$DUMP_FILE_ACTUAL\" --output \"${ENTITY_INFO_FILE_ACTUAL}\" --disableCache"
$SOURCERY --xcode-project "$TESTPROJECT" --xcode-target "ToolTestProject${2}" --model-json "$MODEL_FILE_ACTUAL" --debug-parsetree "$DUMP_FILE_ACTUAL" --output "${ENTITY_INFO_FILE_ACTUAL}" --disableCache > "$TESTMESSAGESFILE" 2>&1

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,298 @@
// Generated using the ObjectBox Swift Generator — https://objectbox.io
// DO NOT EDIT

// swiftlint:disable all
import ObjectBox
import Foundation

// MARK: - Entity metadata


extension Author: ObjectBox.__EntityRelatable {
internal typealias EntityType = Author

internal var _id: EntityId<Author> {
return EntityId<Author>(self.id.value)
}
}

extension Author: ObjectBox.EntityInspectable {
internal typealias EntityBindingType = AuthorBinding

/// Generated metadata used by ObjectBox to persist the entity.
internal static var entityInfo = ObjectBox.EntityInfo(name: "Author", id: 1)

internal static var entityBinding = EntityBindingType()

fileprivate static func buildEntity(modelBuilder: ObjectBox.ModelBuilder) throws {
let entityBuilder = try modelBuilder.entityBuilder(for: Author.self, id: 1, uid: 16640)
try entityBuilder.addProperty(name: "id", type: PropertyType.long, flags: [.id], id: 1, uid: 14592)
try entityBuilder.addProperty(name: "name", type: PropertyType.string, id: 2, uid: 15616)

try entityBuilder.lastProperty(id: 2, uid: 15616)
}
}

extension Author {
/// Generated entity property information.
///
/// You may want to use this in queries to specify fetch conditions, for example:
///
/// box.query { Author.id == myId }
internal static var id: Property<Author, EntityId<Author>, EntityId<Author>> { return Property<Author, EntityId<Author>, EntityId<Author>>(propertyId: 1, isPrimaryKey: true) }
/// Generated entity property information.
///
/// You may want to use this in queries to specify fetch conditions, for example:
///
/// box.query { Author.name.startsWith("X") }
internal static var name: Property<Author, String, Void> { return Property<Author, String, Void>(propertyId: 2, isPrimaryKey: false) }

fileprivate func __setId(identifier: ObjectBox.Id) {
self.id = EntityId(identifier)
}
}

extension ObjectBox.Property where E == Author {
/// Generated entity property information.
///
/// You may want to use this in queries to specify fetch conditions, for example:
///
/// box.query { .id == myId }

internal static var id: Property<Author, EntityId<Author>, EntityId<Author>> { return Property<Author, EntityId<Author>, EntityId<Author>>(propertyId: 1, isPrimaryKey: true) }

/// Generated entity property information.
///
/// You may want to use this in queries to specify fetch conditions, for example:
///
/// box.query { .name.startsWith("X") }

internal static var name: Property<Author, String, Void> { return Property<Author, String, Void>(propertyId: 2, isPrimaryKey: false) }

}


/// Generated service type to handle persisting and reading entity data. Exposed through `Author.EntityBindingType`.
internal class AuthorBinding: ObjectBox.EntityBinding {
internal typealias EntityType = Author
internal typealias IdType = EntityId<Author>

internal required init() {}

internal func generatorBindingVersion() -> Int { 1 }

internal func setEntityIdUnlessStruct(of entity: EntityType, to entityId: ObjectBox.Id) {
entity.__setId(identifier: entityId)
}

internal func entityId(of entity: EntityType) -> ObjectBox.Id {
return entity.id.value
}

internal func collect(fromEntity entity: EntityType, id: ObjectBox.Id,
propertyCollector: ObjectBox.FlatBufferBuilder, store: ObjectBox.Store) throws {
let propertyOffset_name = propertyCollector.prepare(string: entity.name)

propertyCollector.collect(id, at: 2 + 2 * 1)
propertyCollector.collect(dataOffset: propertyOffset_name, at: 2 + 2 * 2)
}

internal func createEntity(entityReader: ObjectBox.FlatBufferReader, store: ObjectBox.Store) -> EntityType {
let entity = Author()

entity.id = entityReader.read(at: 2 + 2 * 1)
entity.name = entityReader.read(at: 2 + 2 * 2)

return entity
}
}



extension Book: ObjectBox.__EntityRelatable {
internal typealias EntityType = Book

internal var _id: EntityId<Book> {
return EntityId<Book>(self.id.value)
}
}

extension Book: ObjectBox.EntityInspectable {
internal typealias EntityBindingType = BookBinding

/// Generated metadata used by ObjectBox to persist the entity.
internal static var entityInfo = ObjectBox.EntityInfo(name: "Book", id: 2)

internal static var entityBinding = EntityBindingType()

fileprivate static func buildEntity(modelBuilder: ObjectBox.ModelBuilder) throws {
let entityBuilder = try modelBuilder.entityBuilder(for: Book.self, id: 2, uid: 20736)
try entityBuilder.addProperty(name: "id", type: PropertyType.long, flags: [.id], id: 1, uid: 17664)
try entityBuilder.addProperty(name: "name", type: PropertyType.string, id: 2, uid: 18688)
try entityBuilder.addToManyRelation(id: 1, uid: 19712,
targetId: 1, targetUid: 16640)

try entityBuilder.lastProperty(id: 2, uid: 18688)
}
}

extension Book {
/// Generated entity property information.
///
/// You may want to use this in queries to specify fetch conditions, for example:
///
/// box.query { Book.id == myId }
internal static var id: Property<Book, EntityId<Book>, EntityId<Book>> { return Property<Book, EntityId<Book>, EntityId<Book>>(propertyId: 1, isPrimaryKey: true) }
/// Generated entity property information.
///
/// You may want to use this in queries to specify fetch conditions, for example:
///
/// box.query { Book.name.startsWith("X") }
internal static var name: Property<Book, String, Void> { return Property<Book, String, Void>(propertyId: 2, isPrimaryKey: false) }
/// Use `Book.authors` to refer to this ToMany relation property in queries,
/// like when using `QueryBuilder.and(property:, conditions:)`.

internal static var authors: ToManyProperty<Author> { return ToManyProperty(.relationId(1)) }


fileprivate func __setId(identifier: ObjectBox.Id) {
self.id = EntityId(identifier)
}
}

extension ObjectBox.Property where E == Book {
/// Generated entity property information.
///
/// You may want to use this in queries to specify fetch conditions, for example:
///
/// box.query { .id == myId }

internal static var id: Property<Book, EntityId<Book>, EntityId<Book>> { return Property<Book, EntityId<Book>, EntityId<Book>>(propertyId: 1, isPrimaryKey: true) }

/// Generated entity property information.
///
/// You may want to use this in queries to specify fetch conditions, for example:
///
/// box.query { .name.startsWith("X") }

internal static var name: Property<Book, String, Void> { return Property<Book, String, Void>(propertyId: 2, isPrimaryKey: false) }

/// Use `.authors` to refer to this ToMany relation property in queries, like when using
/// `QueryBuilder.and(property:, conditions:)`.

internal static var authors: ToManyProperty<Author> { return ToManyProperty(.relationId(1)) }

}


/// Generated service type to handle persisting and reading entity data. Exposed through `Book.EntityBindingType`.
internal class BookBinding: ObjectBox.EntityBinding {
internal typealias EntityType = Book
internal typealias IdType = EntityId<Book>

internal required init() {}

internal func generatorBindingVersion() -> Int { 1 }

internal func setEntityIdUnlessStruct(of entity: EntityType, to entityId: ObjectBox.Id) {
entity.__setId(identifier: entityId)
}

internal func entityId(of entity: EntityType) -> ObjectBox.Id {
return entity.id.value
}

internal func collect(fromEntity entity: EntityType, id: ObjectBox.Id,
propertyCollector: ObjectBox.FlatBufferBuilder, store: ObjectBox.Store) throws {
let propertyOffset_name = propertyCollector.prepare(string: entity.name)

propertyCollector.collect(id, at: 2 + 2 * 1)
propertyCollector.collect(dataOffset: propertyOffset_name, at: 2 + 2 * 2)
}

internal func postPut(fromEntity entity: EntityType, id: ObjectBox.Id, store: ObjectBox.Store) throws {
if entityId(of: entity) == 0 { // New object was put? Attach relations now that we have an ID.
let authors = ToMany<Author>.relation(
sourceId: EntityId<Book>(id.value),
targetBox: store.box(for: ToMany<Author>.ReferencedType.self),
relationId: 1)
if !entity.authors.isEmpty {
authors.replace(entity.authors)
}
entity.authors = authors
try entity.authors.applyToDb()
}
}
internal func createEntity(entityReader: ObjectBox.FlatBufferReader, store: ObjectBox.Store) -> EntityType {
let entity = Book()

entity.id = entityReader.read(at: 2 + 2 * 1)
entity.name = entityReader.read(at: 2 + 2 * 2)

entity.authors = ToMany<Author>.relation(
sourceId: EntityId<Book>(entity.id.value),
targetBox: store.box(for: ToMany<Author>.ReferencedType.self),
relationId: 1)
return entity
}
}


/// Helper function that allows calling Enum(rawValue: value) with a nil value, which will return nil.
fileprivate func optConstruct<T: RawRepresentable>(_ type: T.Type, rawValue: T.RawValue?) -> T? {
guard let rawValue = rawValue else { return nil }
return T(rawValue: rawValue)
}

// MARK: - Store setup

fileprivate func cModel() throws -> OpaquePointer {
let modelBuilder = try ObjectBox.ModelBuilder()
try Author.buildEntity(modelBuilder: modelBuilder)
try Book.buildEntity(modelBuilder: modelBuilder)
modelBuilder.lastEntity(id: 2, uid: 20736)
modelBuilder.lastRelation(id: 1, uid: 19712)
return modelBuilder.finish()
}

extension ObjectBox.Store {
/// A store with a fully configured model. Created by the code generator with your model's metadata in place.
///
/// # In-memory database
/// To use a file-less in-memory database, instead of a directory path pass `memory:`
/// together with an identifier string:
/// ```swift
/// let inMemoryStore = try Store(directoryPath: "memory:test-db")
/// ```
///
/// - Parameters:
/// - directoryPath: The directory path in which ObjectBox places its database files for this store,
/// or to use an in-memory database `memory:<identifier>`.
/// - maxDbSizeInKByte: Limit of on-disk space for the database files. Default is `1024 * 1024` (1 GiB).
/// - fileMode: UNIX-style bit mask used for the database files; default is `0o644`.
/// Note: directories become searchable if the "read" or "write" permission is set (e.g. 0640 becomes 0750).
/// - maxReaders: The maximum number of readers.
/// "Readers" are a finite resource for which we need to define a maximum number upfront.
/// The default value is enough for most apps and usually you can ignore it completely.
/// However, if you get the maxReadersExceeded error, you should verify your
/// threading. For each thread, ObjectBox uses multiple readers. Their number (per thread) depends
/// on number of types, relations, and usage patterns. Thus, if you are working with many threads
/// (e.g. in a server-like scenario), it can make sense to increase the maximum number of readers.
/// Note: The internal default is currently around 120. So when hitting this limit, try values around 200-500.
/// - readOnly: Opens the database in read-only mode, i.e. not allowing write transactions.
///
/// - important: This initializer is created by the code generator. If you only see the internal `init(model:...)`
/// initializer, trigger code generation by building your project.
internal convenience init(directoryPath: String, maxDbSizeInKByte: UInt64 = 1024 * 1024,
fileMode: UInt32 = 0o644, maxReaders: UInt32 = 0, readOnly: Bool = false) throws {
try self.init(
model: try cModel(),
directory: directoryPath,
maxDbSizeInKByte: maxDbSizeInKByte,
fileMode: fileMode,
maxReaders: maxReaders,
readOnly: readOnly)
}
}

// swiftlint:enable all
Loading

0 comments on commit d09c2d0

Please sign in to comment.