Skip to content

Commit 071b4a3

Browse files
committed
1.5.0 collected changes
1 parent 5006055 commit 071b4a3

29 files changed

+965
-142
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ Here's a list of ObjectBox releases, and the Swift versions they were compiled w
7373

7474
| ObjectBox version(s) | Swift version |
7575
|:--------------------:|:-------------:|
76+
| 1.5.0 | 5.3(.2) |
7677
| 1.4.1 | 5.3 |
7778
| 1.3, 1.4.0 | 5.2 |
7879
| 1.2 | 5.1 |
@@ -138,7 +139,7 @@ License
138139
-------
139140
All files in this repository are under the Apache 2 license:
140141

141-
Copyright 2018-2020 ObjectBox Ltd. All rights reserved.
142+
Copyright 2018-2021 ObjectBox Ltd. All rights reserved.
142143

143144
Licensed under the Apache License, Version 2.0 (the "License");
144145
you may not use this file except in compliance with the License.

Source/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ build_swiftlint:
1717
build_sourcery:
1818
@echo "$(BLUE)Building Sourcery...$(NC)"
1919
@echo $(SOURCERY_PATH)
20-
(cd "$(SOURCERY_PATH)"; swift build --configuration release)
20+
(cd "$(SOURCERY_PATH)"; rake build)
2121

2222
archive_framework:
2323
(cd "$(FRAMEWORK_PATH)"; make all)

Source/README.md

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,29 @@ Repository Contents
88
-------------------
99
- `ios-framework/`: The Cocoa Swift framework.
1010
- `docs/swift_output/`: The generated framework documentation.
11-
- `external/`: git submodule and pre-built binary container.
11+
- `external/`: git submodule and/or pre-built binary container.
1212
This contains the ObjectBoxCore static libraries and our code generator.
13-
- `fetch_dependencies.command`: Script for downloading libObjectBoxCore into `externals`.
14-
You must run this script before you can build the framework.
1513
- `docs/`: Documentation and discussion of concepts, ideas, and approaches to bring ObjectBox to Swift.
1614

15+
**Scripts** and how they depend on each other (subject to future simplifications):
16+
17+
- `fetch_dependencies.command`: populates `external/objectbox-static` with libObjectBoxCore.
18+
libObjectBoxCore is a crucial requirement build the Swift framework.
19+
- `create-xcframework.sh`: builds the multi-platform archive containing binaries for multiple platforms and architectures.
20+
21+
Tests
22+
-----
23+
ObjectBox comes with a couple of tests of different categories:
24+
25+
* Unit tests: `ios-framework/CommonTests`, based on XCTestCase
26+
* Integration tests "CodeGen": `ios-framework/CodeGenTests` run via script (for now only via Xcode/xcodebuild);
27+
uses a separate Xcode project and an ObjectBox generator executable to generate actual binding classes.
28+
[README](ios-framework/CodeGenTests/README.md)
29+
* Integration tests "IntegrationTests": `ios-framework/IntegrationTests`, currently not maintained, run via script;
30+
somewhat similar to CodeGen; subject to a general clean up; see also its [README](ios-framework/IntegrationTests/Readme.md)
31+
* External integration test project: https://github.com/objectbox/objectbox-swift-integration-test
32+
runs "real projects" with "full ObjectBox round-trip" on internal CI and CircleCI
33+
1734
Setup
1835
-----
1936
* Install latest Xcode (Swift 5.3+) with command line tools prepared to build from the shell
@@ -98,9 +115,6 @@ You look at and build the framework itself via `ios-framework/ObjectBox.xcodepro
98115

99116
Build notes
100117
-----------
101-
* Build phases; check Xcode project
102-
* "Rename C Header": takes the standard C objectbox.h and augments it with some Swift specifics into ObjectBoxC.h
103-
(TODO: Can we do this differently, e.g. use the standard objectbox.h and then have a second .h for Swift specifics?)
104118
* SwiftLint (macOS build only): calls `swiftlint lint --config .swiftlint-macOS.yml`
105119
* Edit .swiftlint-macOS.yml file to customize (e.g. "id" is OK despite less than 3 chars)
106120

@@ -175,4 +189,3 @@ xcodebuild -derivedDataPath ./DerivedData test -project ObjectBox.xcodeproj -sch
175189
xcodebuild -derivedDataPath ./DerivedData test -project ObjectBox.xcodeproj -scheme ObjectBox-iOS -destination 'platform=iOS Simulator,name=iPhone 11' -only-testing ObjectBoxTests-iOS/StoreTests/test32vs64BitForOs | xcpretty
176190
```
177191

178-

Source/create-xcframework.sh

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,26 +7,40 @@ myDir=$( cd "$(dirname "$0")" ; pwd -P )
77
cd "${myDir}/ios-framework"
88
dir_build="${myDir}/build-deploy"
99
mkdir -p "$dir_build"
10+
derived_data_path=$( mktemp -d )
1011

11-
function build_archive() {
12+
function build() {
1213
echo "************* Building archive for $1 $2 (${3:-$1}) *************"
13-
xcodebuild archive -scheme "$1" -destination "$2" -archivePath "$dir_build/${3:-$1}" \
14-
SKIP_INSTALL=NO BUILD_LIBRARIES_FOR_DISTRIBUTION=YES
14+
xcodebuild build \
15+
-scheme "$1" \
16+
-destination "$2" \
17+
-configuration Release \
18+
-derivedDataPath "${derived_data_path}" \
19+
-quiet \
20+
SKIP_INSTALL=NO \
21+
BUILD_LIBRARIES_FOR_DISTRIBUTION=YES
1522
}
1623

17-
build_archive "ObjectBox-macOS" "platform=macOS"
18-
build_archive "ObjectBox-iOS" "generic/platform=iOS"
19-
build_archive "ObjectBox-iOS" "generic/platform=iOS Simulator" "ObjectBox-iOS-Sim"
24+
build "ObjectBox-macOS" "platform=macOS"
25+
build "ObjectBox-iOS" "generic/platform=iOS"
26+
build "ObjectBox-iOS Simulator" "generic/platform=iOS Simulator"
2027

2128
echo "************* Building XCFramework *************"
2229
path_in_xcarchive="Products/Library/Frameworks/ObjectBox.framework"
2330
dir_xcframework="$dir_build/ObjectBox.xcframework"
2431
rm -rf "$dir_xcframework"
25-
xcodebuild -create-xcframework -output "$dir_xcframework" \
26-
-framework "$dir_build/ObjectBox-macOS.xcarchive/$path_in_xcarchive" \
27-
-framework "$dir_build/ObjectBox-iOS.xcarchive/$path_in_xcarchive" \
28-
-framework "$dir_build/ObjectBox-iOS-Sim.xcarchive/$path_in_xcarchive"
2932

30-
zip -r "$dir_xcframework.zip" "$dir_xcframework"
33+
xcodebuild -create-xcframework \
34+
-output "$dir_xcframework" \
35+
-framework "${derived_data_path}/Build/Products/Release/ObjectBox.framework" \
36+
-framework "${derived_data_path}/Build/Products/Release-iphoneos/ObjectBox.framework" \
37+
-framework "${derived_data_path}/Build/Products/Release-iphonesimulator/ObjectBox.framework"
3138

32-
ls -lh "$dir_xcframework.zip"
39+
pushd "$dir_xcframework/.."
40+
zip --symlinks -r -o "$dir_xcframework.zip" "ObjectBox.xcframework"
41+
popd
42+
43+
rm -rf ${derived_data_path}
44+
45+
ls -lh "$dir_xcframework.zip"
46+
xcrun swift package compute-checksum "$dir_xcframework.zip"

Source/fetch_dependencies.command

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,47 @@ if [ "$verify_only" = true ]; then
2727
else
2828

2929
if [ -d "$code_dir" ]; then # Do we have an existing code repo?
30-
pushd "$code_dir" # todo fix this workaround for building into cbuild dir in "our" objectbox-swift dir
31-
echo "Have repository, building."
32-
"$code_dir/scripts/apple-build-static-libs.sh" "$dest_dir" release
30+
pushd "$code_dir" # note: this also "fixed" building into cbuild dir in "our" objectbox-swift dir
31+
build_params="" # must also part of the cache key
32+
commit_id=$(git rev-parse HEAD)
33+
cache_dir="$HOME/Library/Caches/ObjectBox"
34+
mkdir -p "${cache_dir}"
35+
find "${cache_dir}" -name "objectbox-static-*.zip" -type f -mtime +30 # -delete # TODO enable delete once this looks good
36+
cache_key="${commit_id}"
37+
if [ -n "$build_params" ]; then
38+
cache_key="${cache_key}-$(echo "$build_params" | tr -cd '[a-zA-Z0-9]._-')"
39+
fi
40+
cache_zip="${cache_dir}/objectbox-static-${cache_key}.zip"
41+
do_build=true
42+
git_clean=false
43+
git_status=$(git status --porcelain)
44+
# ignore untracked uws submodule (left over when switching from a sync to a non-sync branch)
45+
git_status=${git_status#"?? objectbox/src/main/cpp/external/uws-objectbox/"}
46+
if [ -z "$git_status" ]; then
47+
git_clean=true
48+
if [ -f "${cache_zip}" ]; then
49+
echo "ObjectBox core is clean and cache ZIP found for ${cache_key}. Extracting..."
50+
unzip -o "${cache_zip}" -d "${dest_dir}"
51+
do_build=false
52+
else
53+
echo "ObjectBox core is clean but no cache ZIP found for ${cache_key}. Building..."
54+
fi
55+
else
56+
git status
57+
echo "ObjectBox core is not clean, won't use caching. Building..."
58+
fi
59+
if [ "$do_build" = true ]; then
60+
"$code_dir/scripts/apple-build-static-libs.sh" $build_params "$dest_dir" release
61+
if [ "$git_clean" = true ] ; then # clean before?
62+
git_status=${git_status#"?? objectbox/src/main/cpp/external/uws-objectbox/"}
63+
if [ -z "$git_status" ]; then # still clean
64+
cp "${dest_dir}/objectbox-static.zip" "${cache_zip}"
65+
echo "Cache ZIP created: ${cache_zip}"
66+
else
67+
echo "Git status is not clean anymore; skipped caching the ZIP"
68+
fi
69+
fi
70+
fi
3371
popd
3472
else # Download static public release and unzip into $dest
3573
if [ ! -d "${dest_dir}" ] || [ ! -e "${dest_dir}/libObjectBoxCore-iOS.a" ]; then
@@ -70,7 +108,14 @@ for filename in ./*.a; do
70108
# Match our version/date pattern like "2.6.1-2020-06-09"
71109
obx_version=$(strings "$filename" | grep "[0-9]\.[0-9]\.[0-9]-[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]")
72110
echo " >> Version found: $obx_version"
73-
obx_symbols=$(nm -gj "$filename" | grep -c obx_ || true)
74-
obx_sync_symbols=$(nm -gj "$filename" | grep -c obx_sync_ || true)
75-
echo " >> Symbols found: $obx_symbols obx, $obx_sync_symbols obx_sync"
111+
obx_symbols=$(nm -gj "$filename" | grep -c obx_) || true
112+
obx_sync_symbols=$(nm -gj "$filename" | grep -c obx_sync_) || true
113+
# Also include "external libs" to expose potential build problems
114+
obx_lws_symbols=$(nm -gj "$filename" | grep -c lws_) || true
115+
obx_mbedtls_symbols=$(nm -gj "$filename" | grep -c mbedtls_) || true
116+
echo " >> Symbols found: $obx_symbols obx, $obx_sync_symbols obx_sync, $obx_lws_symbols lws, $obx_mbedtls_symbols mbedtls"
117+
obx_archs=$(lipo -archs "$filename")
118+
echo " >> Architectures: $obx_archs"
119+
sha=($(shasum -a 256 "$filename"))
120+
echo " >> SHA256: $sha"
76121
done

Source/ios-framework/CodeGenTests/RunToolTests.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ date
88

99
if [ -z ${PROJECT_DIR} ]; then
1010
echo "PROJECT_DIR unavailable; please run from Xcode"
11+
12+
# TODO: In the future we could also setup the vars for derived data etc. to make it work; until then exit...
13+
14+
# macOS does not have realpath and readlink does not have -f option, so do this instead:
15+
script_dir=$( cd "$(dirname "$0")" ; pwd -P )
16+
17+
PROJECT_DIR="${script_dir}/../"
18+
1119
exit 1
1220
fi
1321

Source/ios-framework/CodeGenTests/ToolTestProject3.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88

99
import ObjectBox
1010

11-
1211
// objectbox: entity
12+
// objectbox: sync = { "sharedGlobalIds": true }
1313
class BusRoute {
1414
var id: EntityId<BusRoute> = 0
1515

Source/ios-framework/CodeGenTests/expected/entity-info/EntityInfo.generated3.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ extension BusRoute: ObjectBox.EntityInspectable {
2727

2828
fileprivate static func buildEntity(modelBuilder: ObjectBox.ModelBuilder) throws {
2929
let entityBuilder = try modelBuilder.entityBuilder(for: BusRoute.self, id: 1, uid: 5107964062888457216)
30+
try entityBuilder.flags([.syncEnabled, .sharedGlobalIds])
3031
try entityBuilder.addProperty(name: "id", type: EntityId<BusRoute>.entityPropertyType, flags: [.id], id: 1, uid: 7895576389419683840)
3132

3233
try entityBuilder.lastProperty(id: 2, uid: 6687926154759915520)

Source/ios-framework/CodeGenTests/expected/model/model3.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"id": "1:5107964062888457216",
88
"lastPropertyId": "2:6687926154759915520",
99
"name": "BusRoute",
10+
"flags": 6,
1011
"properties": [
1112
{
1213
"flags": 1,

Source/ios-framework/CommonSource/Entities/EntityFlags.swift

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@
1717
// automatically generated by the FlatBuffers compiler, do not modify
1818
// swiftlint:disable all
1919

20-
21-
2220
/// Not really an enum, but binary flags to use across languages
2321
public enum EntityFlags: UInt32 {
2422
public typealias T = UInt32
@@ -29,9 +27,17 @@ public enum EntityFlags: UInt32 {
2927
/// Enable "data synchronization" for this entity type: objects will be synced with other stores over the network.
3028
/// It's possible to have local-only (non-synced) types and synced types in the same store (schema/data model).
3129
case syncEnabled = 2
32-
30+
/// Makes object IDs for a synced types (SYNC_ENABLED is set) global.
31+
/// By default (not using this flag), the 64 bit object IDs have a local scope and are not unique globally.
32+
/// This flag tells ObjectBox to treat object IDs globally and thus no ID mapping (local <-> global) is performed.
33+
/// Often this is used with assignable IDs (ID_SELF_ASSIGNABLE property flag is set) and some special ID scheme.
34+
/// Note: typically you won't do this with automatically assigned IDs, set by the local ObjectBox store.
35+
/// Two devices would likely overwrite each other's object during sync as object IDs are prone to collide.
36+
/// It might be OK if you can somehow ensure that only a single device will create new IDs.
37+
case sharedGlobalIds = 4
38+
3339

34-
public static var max: EntityFlags { return .syncEnabled }
40+
public static var max: EntityFlags { return .sharedGlobalIds }
3541
public static var min: EntityFlags { return .useNoArgConstructor }
3642
}
3743

Source/ios-framework/CommonSource/Errors.swift

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,17 @@ internal func checkLastError(_ error: obx_err) throws {
109109
try throwObxErr(error, message: message)
110110
}
111111

112+
internal func checkLastErrorSuccessFlag(_ error: obx_err) throws -> Bool {
113+
if error == OBX_SUCCESS {
114+
return true
115+
} else if error == OBX_NO_SUCCESS {
116+
return false
117+
} else {
118+
try checkLastError(error) // Should always throw at this point
119+
return false
120+
}
121+
}
122+
112123
/// E.g. prints error
113124
func checkLastErrorNoThrow(_ error: obx_err) {
114125
if error == OBX_SUCCESS { return }
@@ -125,18 +136,23 @@ internal func failFatallyIfError() {
125136
checkFatalError(obx_last_error_code())
126137
}
127138

139+
/// Reserved for "wrong usages" by the user that the compiler cannot detect (try/catch otherwise).
140+
internal func fatalErrorWithStack(_ message: String) -> Never {
141+
for symbol in Thread.callStackSymbols {
142+
// Print the stack trace without the "unexciting" symbols
143+
if !symbol.contains("XCTest") && !symbol.contains("xctest") && !symbol.contains("CoreFoundation")
144+
&& !symbol.contains("checkFatalError") && !symbol.contains("failFatallyIfError") {
145+
print(symbol)
146+
}
147+
}
148+
fatalError(message)
149+
}
150+
128151
/// Reserved for "wrong usages" by the user that the compiler cannot detect (try/catch otherwise).
129152
internal func checkFatalError(_ err: obx_err) {
130153
if err != OBX_SUCCESS {
131-
for symbol in Thread.callStackSymbols {
132-
// Print the stack trace without the "unexciting" symbols
133-
if !symbol.contains("XCTest") && !symbol.contains("xctest") && !symbol.contains("CoreFoundation")
134-
&& !symbol.contains("checkFatalError") && !symbol.contains("failFatallyIfError") {
135-
print(symbol)
136-
}
137-
}
138154
let message = String(utf8String: obx_last_error_message()) ?? "Unknown"
139-
fatalError("\(message) (\(err))")
155+
fatalErrorWithStack("\(message) (\(err))")
140156
}
141157
}
142158

@@ -170,7 +186,7 @@ internal func throwObxErr(_ err: obx_err, message: String = "") throws -> Never
170186
/// This is NOT an error condition, and thus no last error info is set.
171187
case OBX_NOT_FOUND:
172188
throw ObjectBoxError.notFound(message: message)
173-
189+
174190
// General errors
175191
case OBX_ERROR_ILLEGAL_STATE:
176192
if message.hasPrefix("Cannot start a write transaction inside a read only transaction") {
@@ -188,7 +204,7 @@ internal func throwObxErr(_ err: obx_err, message: String = "") throws -> Never
188204
throw ObjectBoxError.general(message: message)
189205
case OBX_ERROR_UNKNOWN:
190206
throw ObjectBoxError.unknown(code: OBX_ERROR_UNKNOWN, message: message)
191-
207+
192208
// Storage errors (often have a secondary error code)
193209
case OBX_ERROR_DB_FULL:
194210
throw ObjectBoxError.dbFull(message: message)
@@ -200,7 +216,7 @@ internal func throwObxErr(_ err: obx_err, message: String = "") throws -> Never
200216
#if os(macOS) // Only macOS needs an App Group to do its mutexes, iOS uses a different mutex.
201217
if message.hasPrefix("Could not open env for DB") { // Error reported from obx_store_open().
202218
throw ObjectBoxError.storageGeneral(message: message + " - did you perhaps forget to set up an "
203-
+ "\"App Group\" Capability in your target settings?")
219+
+ "\"App Group\" Capability in your target settings?")
204220
}
205221
#endif
206222
throw ObjectBoxError.storageGeneral(message: message)
@@ -240,9 +256,10 @@ internal func throwObxErr(_ err: obx_err, message: String = "") throws -> Never
240256

241257
case 2: // testStorageException receives this code for a Store on a nonexistent file path.
242258
throw ObjectBoxError.storageGeneral(message: message.isEmpty ? "Storage error \(err)" : message)
243-
259+
244260
default:
245261
throw ObjectBoxError.unknown(code: err, message: message)
246262
}
247263
}
264+
248265
// swiftlint:enable cyclomatic_complexity function_body_length
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//
2+
// Copyright © 2021 ObjectBox Ltd. All rights reserved.
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
//
16+
17+
internal class Util {
18+
19+
/// Note: binary representation matters for ObjectBox, not signed/unsigned state (e.g. a casted UInt32.max works).
20+
/// - Returns: a new Int32 array or the given array if it already was an Int32 array.
21+
/// - Throws: ObjectBoxError.illegalArgument if a value is outside the 32 bit integer range
22+
internal static func toInt32Array<T>(_ collection: [T]) throws -> [Int32] where T: FixedWidthInteger {
23+
if MemoryLayout<T>.size > 4 { // casting down: check if values fit into 32 bit
24+
return try collection.map {
25+
if $0 < Int32.min || $0 > UInt32.max { // That should work with signed and unsigned Ts
26+
throw ObjectBoxError.illegalArgument(message: "Value outside the 32 bit integer range: \($0)")
27+
}
28+
return Int32(truncatingIfNeeded: $0)
29+
}
30+
} else {
31+
return collection as? [Int32] ?? collection.map { Int32(truncatingIfNeeded: $0) }
32+
}
33+
}
34+
35+
/// Note: binary representation matters for ObjectBox, not signed/unsigned state (e.g. a casted UInt64.max works).
36+
/// - Returns: a new Int64 array or the given array if it already was an Int64 array.
37+
internal static func toInt64Array<T>(_ collection: [T]) -> [Int64] where T: FixedWidthInteger {
38+
return collection as? [Int64] ?? collection.map { Int64(truncatingIfNeeded: $0) }
39+
}
40+
}

0 commit comments

Comments
 (0)