Skip to content

Commit d09c2d0

Browse files
ObjectBox Swift database 4.0.1
1 parent e8f0fba commit d09c2d0

26 files changed

+1801
-32
lines changed

CHANGELOG.md

+13
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,19 @@ Notable changes to the ObjectBox Swift library.
44

55
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).
66

7+
## 4.0.1 - 2024-10-16
8+
9+
- Built with Xcode 15.0.1 and Swift 5.9.
10+
- Make closing the Store more robust. In addition to transactions, it also waits for ongoing queries. This is just an
11+
additional safety net. Your apps should still make sure to finish all Store operations, like queries, before closing it.
12+
- Generator: no longer print a `Mapping not found` warning when an entity class uses `ToMany`.
13+
- Some minor vector search performance improvements.
14+
- Update to [ObjectBox C 4.0.2](https://github.com/objectbox/objectbox-c/releases/tag/v4.0.2).
15+
16+
### Sync
17+
18+
- **Fix a serious regression, please update as soon as possible.**
19+
720
## 4.0.0 - 2024-07-22
821

922
**ObjectBox now supports [Vector Search](https://docs.objectbox.io/ann-vector-search)** to enable efficient similarity searches.

Package.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ let package = Package(
1616
targets: [
1717
.binaryTarget(
1818
name: "ObjectBox",
19-
url: "https://github.com/objectbox/objectbox-swift/releases/download/v4.0.0/ObjectBox-xcframework-4.0.0.zip",
20-
checksum: "2bed7b8b87dd46dda64fa1f5e7a0b1df87908a783690fda09c6dc943a01a32e5"
19+
url: "https://github.com/objectbox/objectbox-swift/releases/download/v4.0.1/ObjectBox-xcframework-4.0.1.zip",
20+
checksum: "02a0f686ced7488a9afe312e3b1a7cac634848709bd04cd904a3a5fa26ec8d31"
2121
)
2222
]
2323
)

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ Here's a list of ObjectBox releases, and the Swift versions they were compiled w
144144

145145
| ObjectBox version(s) | Swift version |
146146
|:--------------------:|:-------------:|
147+
| 4.0.1 | 5.9 |
147148
| 4.0.0 | 5.9 |
148149
| 2.0.0 | 5.9 |
149150
| 1.9.2 | 5.9 |

Source/fetch_dependencies.command

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ set -e
1010

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

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

1919
# Params supported by apple-build-static-libs.sh
2020
build_params=""

Source/ios-framework/CodeGenTests/README.md

+4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ This project is used to test code generation in ObjectBox's Sourcery-descended c
66
It will run the codegen over a given target in the project, then build and run the product. You can do
77
whatever testing you need to do in the actual Swift code.
88

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

1115
These tests require a copy of ObjectBox Swift Code Generator in a known location. To get that all set up, do the following:

Source/ios-framework/CodeGenTests/RunToolTests.sh

+2
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,8 @@ fail_codegen_target_num () {
193193

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

196+
# Setting --debug-parsetree for the generator also makes it generate non-random UIDs,
197+
# see objectbox-swift-generator/Sourcery/main.swift runCLI().
196198
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"
197199
$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
198200

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
// Generated using the ObjectBox Swift Generator — https://objectbox.io
2+
// DO NOT EDIT
3+
4+
// swiftlint:disable all
5+
import ObjectBox
6+
import Foundation
7+
8+
// MARK: - Entity metadata
9+
10+
11+
extension Author: ObjectBox.__EntityRelatable {
12+
internal typealias EntityType = Author
13+
14+
internal var _id: EntityId<Author> {
15+
return EntityId<Author>(self.id.value)
16+
}
17+
}
18+
19+
extension Author: ObjectBox.EntityInspectable {
20+
internal typealias EntityBindingType = AuthorBinding
21+
22+
/// Generated metadata used by ObjectBox to persist the entity.
23+
internal static var entityInfo = ObjectBox.EntityInfo(name: "Author", id: 1)
24+
25+
internal static var entityBinding = EntityBindingType()
26+
27+
fileprivate static func buildEntity(modelBuilder: ObjectBox.ModelBuilder) throws {
28+
let entityBuilder = try modelBuilder.entityBuilder(for: Author.self, id: 1, uid: 16640)
29+
try entityBuilder.addProperty(name: "id", type: PropertyType.long, flags: [.id], id: 1, uid: 14592)
30+
try entityBuilder.addProperty(name: "name", type: PropertyType.string, id: 2, uid: 15616)
31+
32+
try entityBuilder.lastProperty(id: 2, uid: 15616)
33+
}
34+
}
35+
36+
extension Author {
37+
/// Generated entity property information.
38+
///
39+
/// You may want to use this in queries to specify fetch conditions, for example:
40+
///
41+
/// box.query { Author.id == myId }
42+
internal static var id: Property<Author, EntityId<Author>, EntityId<Author>> { return Property<Author, EntityId<Author>, EntityId<Author>>(propertyId: 1, isPrimaryKey: true) }
43+
/// Generated entity property information.
44+
///
45+
/// You may want to use this in queries to specify fetch conditions, for example:
46+
///
47+
/// box.query { Author.name.startsWith("X") }
48+
internal static var name: Property<Author, String, Void> { return Property<Author, String, Void>(propertyId: 2, isPrimaryKey: false) }
49+
50+
fileprivate func __setId(identifier: ObjectBox.Id) {
51+
self.id = EntityId(identifier)
52+
}
53+
}
54+
55+
extension ObjectBox.Property where E == Author {
56+
/// Generated entity property information.
57+
///
58+
/// You may want to use this in queries to specify fetch conditions, for example:
59+
///
60+
/// box.query { .id == myId }
61+
62+
internal static var id: Property<Author, EntityId<Author>, EntityId<Author>> { return Property<Author, EntityId<Author>, EntityId<Author>>(propertyId: 1, isPrimaryKey: true) }
63+
64+
/// Generated entity property information.
65+
///
66+
/// You may want to use this in queries to specify fetch conditions, for example:
67+
///
68+
/// box.query { .name.startsWith("X") }
69+
70+
internal static var name: Property<Author, String, Void> { return Property<Author, String, Void>(propertyId: 2, isPrimaryKey: false) }
71+
72+
}
73+
74+
75+
/// Generated service type to handle persisting and reading entity data. Exposed through `Author.EntityBindingType`.
76+
internal class AuthorBinding: ObjectBox.EntityBinding {
77+
internal typealias EntityType = Author
78+
internal typealias IdType = EntityId<Author>
79+
80+
internal required init() {}
81+
82+
internal func generatorBindingVersion() -> Int { 1 }
83+
84+
internal func setEntityIdUnlessStruct(of entity: EntityType, to entityId: ObjectBox.Id) {
85+
entity.__setId(identifier: entityId)
86+
}
87+
88+
internal func entityId(of entity: EntityType) -> ObjectBox.Id {
89+
return entity.id.value
90+
}
91+
92+
internal func collect(fromEntity entity: EntityType, id: ObjectBox.Id,
93+
propertyCollector: ObjectBox.FlatBufferBuilder, store: ObjectBox.Store) throws {
94+
let propertyOffset_name = propertyCollector.prepare(string: entity.name)
95+
96+
propertyCollector.collect(id, at: 2 + 2 * 1)
97+
propertyCollector.collect(dataOffset: propertyOffset_name, at: 2 + 2 * 2)
98+
}
99+
100+
internal func createEntity(entityReader: ObjectBox.FlatBufferReader, store: ObjectBox.Store) -> EntityType {
101+
let entity = Author()
102+
103+
entity.id = entityReader.read(at: 2 + 2 * 1)
104+
entity.name = entityReader.read(at: 2 + 2 * 2)
105+
106+
return entity
107+
}
108+
}
109+
110+
111+
112+
extension Book: ObjectBox.__EntityRelatable {
113+
internal typealias EntityType = Book
114+
115+
internal var _id: EntityId<Book> {
116+
return EntityId<Book>(self.id.value)
117+
}
118+
}
119+
120+
extension Book: ObjectBox.EntityInspectable {
121+
internal typealias EntityBindingType = BookBinding
122+
123+
/// Generated metadata used by ObjectBox to persist the entity.
124+
internal static var entityInfo = ObjectBox.EntityInfo(name: "Book", id: 2)
125+
126+
internal static var entityBinding = EntityBindingType()
127+
128+
fileprivate static func buildEntity(modelBuilder: ObjectBox.ModelBuilder) throws {
129+
let entityBuilder = try modelBuilder.entityBuilder(for: Book.self, id: 2, uid: 20736)
130+
try entityBuilder.addProperty(name: "id", type: PropertyType.long, flags: [.id], id: 1, uid: 17664)
131+
try entityBuilder.addProperty(name: "name", type: PropertyType.string, id: 2, uid: 18688)
132+
try entityBuilder.addToManyRelation(id: 1, uid: 19712,
133+
targetId: 1, targetUid: 16640)
134+
135+
try entityBuilder.lastProperty(id: 2, uid: 18688)
136+
}
137+
}
138+
139+
extension Book {
140+
/// Generated entity property information.
141+
///
142+
/// You may want to use this in queries to specify fetch conditions, for example:
143+
///
144+
/// box.query { Book.id == myId }
145+
internal static var id: Property<Book, EntityId<Book>, EntityId<Book>> { return Property<Book, EntityId<Book>, EntityId<Book>>(propertyId: 1, isPrimaryKey: true) }
146+
/// Generated entity property information.
147+
///
148+
/// You may want to use this in queries to specify fetch conditions, for example:
149+
///
150+
/// box.query { Book.name.startsWith("X") }
151+
internal static var name: Property<Book, String, Void> { return Property<Book, String, Void>(propertyId: 2, isPrimaryKey: false) }
152+
/// Use `Book.authors` to refer to this ToMany relation property in queries,
153+
/// like when using `QueryBuilder.and(property:, conditions:)`.
154+
155+
internal static var authors: ToManyProperty<Author> { return ToManyProperty(.relationId(1)) }
156+
157+
158+
fileprivate func __setId(identifier: ObjectBox.Id) {
159+
self.id = EntityId(identifier)
160+
}
161+
}
162+
163+
extension ObjectBox.Property where E == Book {
164+
/// Generated entity property information.
165+
///
166+
/// You may want to use this in queries to specify fetch conditions, for example:
167+
///
168+
/// box.query { .id == myId }
169+
170+
internal static var id: Property<Book, EntityId<Book>, EntityId<Book>> { return Property<Book, EntityId<Book>, EntityId<Book>>(propertyId: 1, isPrimaryKey: true) }
171+
172+
/// Generated entity property information.
173+
///
174+
/// You may want to use this in queries to specify fetch conditions, for example:
175+
///
176+
/// box.query { .name.startsWith("X") }
177+
178+
internal static var name: Property<Book, String, Void> { return Property<Book, String, Void>(propertyId: 2, isPrimaryKey: false) }
179+
180+
/// Use `.authors` to refer to this ToMany relation property in queries, like when using
181+
/// `QueryBuilder.and(property:, conditions:)`.
182+
183+
internal static var authors: ToManyProperty<Author> { return ToManyProperty(.relationId(1)) }
184+
185+
}
186+
187+
188+
/// Generated service type to handle persisting and reading entity data. Exposed through `Book.EntityBindingType`.
189+
internal class BookBinding: ObjectBox.EntityBinding {
190+
internal typealias EntityType = Book
191+
internal typealias IdType = EntityId<Book>
192+
193+
internal required init() {}
194+
195+
internal func generatorBindingVersion() -> Int { 1 }
196+
197+
internal func setEntityIdUnlessStruct(of entity: EntityType, to entityId: ObjectBox.Id) {
198+
entity.__setId(identifier: entityId)
199+
}
200+
201+
internal func entityId(of entity: EntityType) -> ObjectBox.Id {
202+
return entity.id.value
203+
}
204+
205+
internal func collect(fromEntity entity: EntityType, id: ObjectBox.Id,
206+
propertyCollector: ObjectBox.FlatBufferBuilder, store: ObjectBox.Store) throws {
207+
let propertyOffset_name = propertyCollector.prepare(string: entity.name)
208+
209+
propertyCollector.collect(id, at: 2 + 2 * 1)
210+
propertyCollector.collect(dataOffset: propertyOffset_name, at: 2 + 2 * 2)
211+
}
212+
213+
internal func postPut(fromEntity entity: EntityType, id: ObjectBox.Id, store: ObjectBox.Store) throws {
214+
if entityId(of: entity) == 0 { // New object was put? Attach relations now that we have an ID.
215+
let authors = ToMany<Author>.relation(
216+
sourceId: EntityId<Book>(id.value),
217+
targetBox: store.box(for: ToMany<Author>.ReferencedType.self),
218+
relationId: 1)
219+
if !entity.authors.isEmpty {
220+
authors.replace(entity.authors)
221+
}
222+
entity.authors = authors
223+
try entity.authors.applyToDb()
224+
}
225+
}
226+
internal func createEntity(entityReader: ObjectBox.FlatBufferReader, store: ObjectBox.Store) -> EntityType {
227+
let entity = Book()
228+
229+
entity.id = entityReader.read(at: 2 + 2 * 1)
230+
entity.name = entityReader.read(at: 2 + 2 * 2)
231+
232+
entity.authors = ToMany<Author>.relation(
233+
sourceId: EntityId<Book>(entity.id.value),
234+
targetBox: store.box(for: ToMany<Author>.ReferencedType.self),
235+
relationId: 1)
236+
return entity
237+
}
238+
}
239+
240+
241+
/// Helper function that allows calling Enum(rawValue: value) with a nil value, which will return nil.
242+
fileprivate func optConstruct<T: RawRepresentable>(_ type: T.Type, rawValue: T.RawValue?) -> T? {
243+
guard let rawValue = rawValue else { return nil }
244+
return T(rawValue: rawValue)
245+
}
246+
247+
// MARK: - Store setup
248+
249+
fileprivate func cModel() throws -> OpaquePointer {
250+
let modelBuilder = try ObjectBox.ModelBuilder()
251+
try Author.buildEntity(modelBuilder: modelBuilder)
252+
try Book.buildEntity(modelBuilder: modelBuilder)
253+
modelBuilder.lastEntity(id: 2, uid: 20736)
254+
modelBuilder.lastRelation(id: 1, uid: 19712)
255+
return modelBuilder.finish()
256+
}
257+
258+
extension ObjectBox.Store {
259+
/// A store with a fully configured model. Created by the code generator with your model's metadata in place.
260+
///
261+
/// # In-memory database
262+
/// To use a file-less in-memory database, instead of a directory path pass `memory:`
263+
/// together with an identifier string:
264+
/// ```swift
265+
/// let inMemoryStore = try Store(directoryPath: "memory:test-db")
266+
/// ```
267+
///
268+
/// - Parameters:
269+
/// - directoryPath: The directory path in which ObjectBox places its database files for this store,
270+
/// or to use an in-memory database `memory:<identifier>`.
271+
/// - maxDbSizeInKByte: Limit of on-disk space for the database files. Default is `1024 * 1024` (1 GiB).
272+
/// - fileMode: UNIX-style bit mask used for the database files; default is `0o644`.
273+
/// Note: directories become searchable if the "read" or "write" permission is set (e.g. 0640 becomes 0750).
274+
/// - maxReaders: The maximum number of readers.
275+
/// "Readers" are a finite resource for which we need to define a maximum number upfront.
276+
/// The default value is enough for most apps and usually you can ignore it completely.
277+
/// However, if you get the maxReadersExceeded error, you should verify your
278+
/// threading. For each thread, ObjectBox uses multiple readers. Their number (per thread) depends
279+
/// on number of types, relations, and usage patterns. Thus, if you are working with many threads
280+
/// (e.g. in a server-like scenario), it can make sense to increase the maximum number of readers.
281+
/// Note: The internal default is currently around 120. So when hitting this limit, try values around 200-500.
282+
/// - readOnly: Opens the database in read-only mode, i.e. not allowing write transactions.
283+
///
284+
/// - important: This initializer is created by the code generator. If you only see the internal `init(model:...)`
285+
/// initializer, trigger code generation by building your project.
286+
internal convenience init(directoryPath: String, maxDbSizeInKByte: UInt64 = 1024 * 1024,
287+
fileMode: UInt32 = 0o644, maxReaders: UInt32 = 0, readOnly: Bool = false) throws {
288+
try self.init(
289+
model: try cModel(),
290+
directory: directoryPath,
291+
maxDbSizeInKByte: maxDbSizeInKByte,
292+
fileMode: fileMode,
293+
maxReaders: maxReaders,
294+
readOnly: readOnly)
295+
}
296+
}
297+
298+
// swiftlint:enable all

0 commit comments

Comments
 (0)