Skip to content

Commit c2aaec8

Browse files
authored
[MOBILE-10980] 0.6.0 Release (#159)
1 parent d1ae581 commit c2aaec8

File tree

11 files changed

+119
-49
lines changed

11 files changed

+119
-49
lines changed

CHANGELOG.md

+19-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313
1414
## [Unreleased]
1515

16+
## [0.6.0]
17+
18+
### Added
19+
20+
- Added new `startRecording` option, `resumePreviousSession`, which resumes the previous session on
21+
start if the session has not yet expired.
22+
- Added new signature `stopRecording(deleteUser: Bool)` which deletes the current user state.
23+
Subsequent calls to `startRecording` will have a new user and session as a result.
24+
- Added several internal interfaces to support an upcoming integration.
25+
26+
## [0.5.3]
27+
28+
### Changed
29+
30+
- Improved trace logging for failed Sqlite queries.
31+
1632
## [0.5.2]
1733

1834
### Added
@@ -164,7 +180,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
164180
- Support for manual capture within WKWebView.
165181
- Support for platforms targeting Swift: macOS, watchOS, iOS, iPadOS, tvOS.
166182

167-
[Unreleased]: https://github.com/heap/heap-swift-core-sdk/compare/0.5.2...main
183+
[Unreleased]: https://github.com/heap/heap-swift-core-sdk/compare/0.6.0...main
184+
[0.6.0]: https://github.com/heap/heap-swift-core-sdk/compare/0.5.3...0.6.0
185+
[0.5.3]: https://github.com/heap/heap-swift-core-sdk/compare/0.5.2...0.5.3
168186
[0.5.2]: https://github.com/heap/heap-swift-core-sdk/compare/0.5.1...0.5.2
169187
[0.5.1]: https://github.com/heap/heap-swift-core-sdk/compare/0.5.0...0.5.1
170188
[0.5.0]: https://github.com/heap/heap-swift-core-sdk/compare/0.4.0...0.5.0

Development/Package.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ let package = Package(
3434
]),
3535
.binaryTarget(
3636
name: "HeapSwiftCoreInterfaces",
37-
url: "https://cdn.heapanalytics.com/ios/heap-swift-core-interfaces-0.6.0-alpha.3.zip", // END HeapSwiftCoreInterfaces URL
38-
checksum: "8a6477840ec8871f5dca44c707d087e9e9a1b0c121f80dcb4cb04f2a5eaa625d" // END HeapSwiftCoreInterfaces checksum
37+
url: "https://cdn.heapanalytics.com/ios/heap-swift-core-interfaces-0.6.0.zip", // END HeapSwiftCoreInterfaces URL
38+
checksum: "e92bf74d2525cbc9503ed9f4ef3369e09e3b216c6f6b23a4de8a4614ed1bc840" // END HeapSwiftCoreInterfaces checksum
3939
),
4040
.target(
4141
name: "HeapSwiftCoreTestSupport",

Development/Sources/HeapSwiftCore/Implementations/SqliteDataStore/SqliteConnection.swift

+47-30
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,32 @@
11
import SQLite3
22
import Foundation
33

4-
struct SqliteError: Error, Equatable, Hashable {
4+
struct SqliteError: Error, Equatable, Hashable, CustomStringConvertible {
55
let code: Int32
6+
let message: String
7+
let file: String
8+
let line: UInt
9+
10+
var description: String {
11+
"Sqlite command at \(file):\(line) failed with error \(code) and the following message: \(message)"
12+
}
613
}
714

815
fileprivate extension Int32 {
916

1017
/// Throws an error if a value is not a successful Sqlite return code.
11-
func assertSuccess() throws {
18+
func assertSuccess(message: @autoclosure () -> String, file: String, line: UInt) throws {
1219
switch self {
1320
case SQLITE_OK, SQLITE_ROW, SQLITE_DONE:
1421
return
1522
default:
16-
throw SqliteError(code: self)
23+
throw SqliteError(code: self, message: message(), file: file, line: line)
1724
}
1825
}
26+
27+
func assertSuccess(message: @autoclosure () -> String, in statement: SqliteConnection.Statement) throws {
28+
try assertSuccess(message: "\(message()) in \(statement.query)", file: statement.file, line: statement.line)
29+
}
1930
}
2031

2132
/// A lightweight wrapper around Sqlite that bridges common types.
@@ -30,9 +41,9 @@ final class SqliteConnection {
3041
databaseUrl = url
3142
}
3243

33-
func connect() throws {
44+
func connect(file: String = #fileID, line: UInt = #line) throws {
3445
guard ppDb == nil else { return }
35-
try sqlite3_open(databaseUrl.path, &ppDb).assertSuccess()
46+
try sqlite3_open(databaseUrl.path, &ppDb).assertSuccess(message: "Failed to open database", file: file, line: line)
3647
}
3748

3849
/// Creates
@@ -41,12 +52,12 @@ final class SqliteConnection {
4152
/// - parameters: A list of indexed parameters to apply, of known convertable types.
4253
/// - rowCallback: A callback to execute after each row is read (if any). This is used for extracting row data.
4354
/// - Throws: A `SqliteError` if an error is encountered on any step of the process.
44-
func perform(query: String, parameters: [Sqlite3Parameter] = [], rowCallback: (_ row: Row) throws -> Void = { _ in }) throws {
55+
func perform(query: String, parameters: [Sqlite3Parameter] = [], file: String = #fileID, line: UInt = #line, rowCallback: (_ row: Row) throws -> Void = { _ in }) throws {
4556
guard let ppDb = ppDb else {
46-
throw SqliteError(code: SQLITE_ERROR)
57+
throw SqliteError(code: SQLITE_ERROR, message: "Database pointer is nil", file: file, line: line)
4758
}
4859

49-
let statement = try Statement(query: query, db: ppDb)
60+
let statement = try Statement(query: query, db: ppDb, file: file, line: line)
5061
try statement.bindIndexedParameters(parameters)
5162
defer { statement.finalize() }
5263
try statement.stepUntilDone(rowCallback)
@@ -64,34 +75,40 @@ final class SqliteConnection {
6475

6576
/// A wrapper around a prepared query.
6677
final class Statement {
67-
private var pointer: OpaquePointer?
78+
private(set) var pointer: OpaquePointer?
79+
let query: String
80+
let file: String
81+
let line: UInt
6882

69-
fileprivate init(pointer: OpaquePointer?) {
83+
fileprivate init(pointer: OpaquePointer?, query: String, file: String = #fileID, line: UInt = #line) {
7084
self.pointer = pointer
85+
self.query = query
86+
self.file = file
87+
self.line = line
7188
}
7289

73-
fileprivate convenience init(query: String, db: OpaquePointer) throws {
90+
fileprivate convenience init(query: String, db: OpaquePointer, file: String = #fileID, line: UInt = #line) throws {
7491
var pointer: OpaquePointer?
75-
try sqlite3_prepare_v2(db, query, -1, &pointer, nil).assertSuccess()
76-
self.init(pointer: pointer)
92+
try sqlite3_prepare_v2(db, query, -1, &pointer, nil).assertSuccess(message: "Failed to prepare query: \(query)", file: file, line: line)
93+
self.init(pointer: pointer, query: query)
7794
}
7895

7996
/// Binds parameters to the query.
8097
func bindIndexedParameters(_ parameters: [Sqlite3Parameter]) throws {
8198
for (parameter, index) in zip(parameters, 1...) {
82-
try parameter.bind(at: index, statementPointer: pointer)
99+
try parameter.bind(at: index, statement: self)
83100
}
84101
}
85102

86103
/// Performs a step of the query.
87104
/// - Returns: True if the execution returned a row.
88105
private func step() throws -> Bool {
89106
guard let pointer = pointer else {
90-
throw SqliteError(code: SQLITE_ERROR)
107+
throw SqliteError(code: SQLITE_ERROR, message: "Statement pointer is nil for query: \(query)", file: file, line: line)
91108
}
92109

93110
let result = sqlite3_step(pointer)
94-
try result.assertSuccess()
111+
try result.assertSuccess(message: "Step failed for query: \(query)", file: file, line: line)
95112
if result == SQLITE_DONE {
96113
finalize()
97114
}
@@ -168,48 +185,48 @@ private let SQLITE_TRANSIENT = unsafeBitCast(-1, to: sqlite3_destructor_type.sel
168185

169186
/// A protocol for binding Swift data types to Sqlite parameters.
170187
protocol Sqlite3Parameter {
171-
func bind(at index: Int, statementPointer: OpaquePointer?) throws
188+
func bind(at index: Int, statement: SqliteConnection.Statement) throws
172189
}
173190

174191
extension Int: Sqlite3Parameter {
175-
func bind(at index: Int, statementPointer: OpaquePointer?) throws {
176-
try sqlite3_bind_int64(statementPointer, Int32(index), Int64(self)).assertSuccess()
192+
func bind(at index: Int, statement: SqliteConnection.Statement) throws {
193+
try sqlite3_bind_int64(statement.pointer, Int32(index), Int64(self)).assertSuccess(message: "Failed to bind integer \"\(self)\" at index \(index)", in: statement)
177194
}
178195
}
179196

180197
extension Bool: Sqlite3Parameter {
181-
func bind(at index: Int, statementPointer: OpaquePointer?) throws {
182-
try (self ? 1 : 0).bind(at: index, statementPointer: statementPointer)
198+
func bind(at index: Int, statement: SqliteConnection.Statement) throws {
199+
try (self ? 1 : 0).bind(at: index, statement: statement)
183200
}
184201
}
185202

186203
extension String: Sqlite3Parameter {
187-
func bind(at index: Int, statementPointer: OpaquePointer?) throws {
188-
try sqlite3_bind_text(statementPointer, Int32(index), self, -1, SQLITE_TRANSIENT).assertSuccess()
204+
func bind(at index: Int, statement: SqliteConnection.Statement) throws {
205+
try sqlite3_bind_text(statement.pointer, Int32(index), self, -1, SQLITE_TRANSIENT).assertSuccess(message: "Failed to bind text \"\(self)\" at index \(index)", in: statement)
189206
}
190207
}
191208

192209
extension Data: Sqlite3Parameter {
193-
func bind(at index: Int, statementPointer: OpaquePointer?) throws {
210+
func bind(at index: Int, statement: SqliteConnection.Statement) throws {
194211
try self.withUnsafeBytes { (pointer: UnsafeRawBufferPointer) in
195-
try sqlite3_bind_blob(statementPointer, Int32(index), pointer.baseAddress, Int32(self.count), SQLITE_TRANSIENT).assertSuccess()
212+
try sqlite3_bind_blob(statement.pointer, Int32(index), pointer.baseAddress, Int32(self.count), SQLITE_TRANSIENT).assertSuccess(message: "Failed to bind data of length \(count) at index \(index)", in: statement)
196213
}
197214
}
198215
}
199216

200217
/// This rounds to the nearest second, which is close enough for us.
201218
extension Date: Sqlite3Parameter {
202-
func bind(at index: Int, statementPointer: OpaquePointer?) throws {
203-
try Int(timeIntervalSinceReferenceDate).bind(at: index, statementPointer: statementPointer)
219+
func bind(at index: Int, statement: SqliteConnection.Statement) throws {
220+
try Int(timeIntervalSinceReferenceDate).bind(at: index, statement: statement)
204221
}
205222
}
206223

207224
extension Optional: Sqlite3Parameter where Wrapped: Sqlite3Parameter {
208-
func bind(at index: Int, statementPointer: OpaquePointer?) throws {
225+
func bind(at index: Int, statement: SqliteConnection.Statement) throws {
209226
if let value = self {
210-
try value.bind(at: index, statementPointer: statementPointer)
227+
try value.bind(at: index, statement: statement)
211228
} else {
212-
try sqlite3_bind_null(statementPointer, Int32(index)).assertSuccess()
229+
try sqlite3_bind_null(statement.pointer, Int32(index)).assertSuccess(message: "Failed to bind null at index \(index)", in: statement)
213230
}
214231
}
215232
}

Development/Sources/HeapSwiftCore/Implementations/SqliteDataStore/SqliteDataStore.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ import Foundation
22
import HeapSwiftCoreInterfaces
33

44
extension Operation {
5-
static func forSqlite(connection: SqliteConnection, block: @escaping (_ connection: SqliteConnection) throws -> Void) -> Operation {
5+
static func forSqlite(connection: SqliteConnection, file: String = #fileID, line: UInt = #line, block: @escaping (_ connection: SqliteConnection) throws -> Void) -> Operation {
66
BlockOperation {
77
do {
88
try block(connection)
99
} catch {
10-
HeapLogger.shared.trace("Error occurred executing query: \(error)")
10+
HeapLogger.shared.trace("Error occurred executing query: \(error)", file: file, line: line)
1111
}
1212
}
1313
}
@@ -18,9 +18,9 @@ class SqliteDataStore: DataStoreProtocol {
1818
private let connection: SqliteConnection
1919
internal let dataStoreSettings: DataStoreSettings
2020

21-
func performOnSqliteQueue(waitUntilFinished: Bool = false, block: @escaping (_ connection: SqliteConnection) throws -> Void) {
21+
func performOnSqliteQueue(waitUntilFinished: Bool = false, file: String = #fileID, line: UInt = #line, block: @escaping (_ connection: SqliteConnection) throws -> Void) {
2222
OperationQueue.dataStore.addOperations([
23-
.forSqlite(connection: connection, block: block),
23+
.forSqlite(connection: connection, file: file, line: line, block: block),
2424
], waitUntilFinished: waitUntilFinished)
2525
}
2626

Development/Sources/HeapSwiftCore/Version.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ struct Version {
55
static let major = 0
66

77
/// Minor version.
8-
static let minor = 5
8+
static let minor = 6
99

1010
/// Revision number.
11-
static let revision = 2
11+
static let revision = 0
1212

1313
/// Optional pre-release version
1414
static let prerelease: String? = nil

Development/Sources/HeapSwiftCoreTestSupport/Implementations/CountingContentsquareIntegration.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ import HeapSwiftCoreInterfaces
22

33
class CountingContentsquareIntegration: _ContentsquareIntegration {
44

5-
var sessionTimeoutDuration: TimeInterval
5+
var sessionTimeoutDuration: TimeInterval?
66
var contentsquareMethods: _ContentsquareMethods? = nil
77

88
var pageviews: [Pageview] = []
99

10-
init(sessionTimeoutDuration: TimeInterval) {
10+
init(sessionTimeoutDuration: TimeInterval?) {
1111
self.sessionTimeoutDuration = sessionTimeoutDuration
1212
}
1313

Development/Tests/HeapSwiftCoreTests/EventConsumer/StateManager/SessionExtensionSpec.swift

+22-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ final class SessionExtensionSpec: HeapSpec {
8383
expect(result.current?.contentsquareSessionProperties).to(equal(.createdByContentsquareScreenView))
8484
}
8585

86-
context("with _ContentsquareIntegration") {
86+
context("with _ContentsquareIntegration providing a value") {
8787

8888
var integration: CountingContentsquareIntegration!
8989

@@ -124,6 +124,27 @@ final class SessionExtensionSpec: HeapSpec {
124124
expect(result.current?.sessionExpirationDate).to(beCloseTo(expectedExpirationDate, within: 1))
125125
}
126126
}
127+
128+
context("with _ContentsquareIntegration not providing a value") {
129+
130+
var integration: CountingContentsquareIntegration!
131+
132+
beforeEach {
133+
integration = CountingContentsquareIntegration(sessionTimeoutDuration: nil)
134+
manager.contentsquareIntegration = integration
135+
}
136+
137+
it("extends the session using the Heap timeout") {
138+
integration.sessionTimeoutDuration = 5
139+
let timestamp = sessionTimestamp.addingTimeInterval(60)
140+
let expectedExpirationDate = timestamp.addingTimeInterval(300)
141+
142+
let result = manager.createSessionIfExpired(extendIfNotExpired: true, properties: .init(), at: timestamp)
143+
144+
expect(result.current?.sessionInfo.id).to(equal(originalSessionId))
145+
expect(result.current?.sessionExpirationDate).to(beCloseTo(expectedExpirationDate, within: 1))
146+
}
147+
}
127148
}
128149

129150
describe("extendSessionAndSetLastPageview") {

Development/Tests/HeapSwiftCoreTests/EventConsumer/TransformPipeline/TransformPipelineSpec.swift

+3-3
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ final class TransformPipelineSpec: HeapSpec {
7272
}
7373

7474
it("transforms the data") {
75-
transformerA.applyToAll(sessionReplay: "SR", contentsquareProperties: .init(cspid: "PID", cspvid: "PVID", cssn: "SN", csts: "TS", csuu: "UU"))
75+
transformerA.applyToAll(sessionReplay: "SR", contentsquareProperties: .init(cspid: "PID", csuu: "UU", cssn: "SN", cspvid: "PVID", csts: "TS"))
7676

7777
pipeline.add(transformerA)
7878
dataStore.createNewUserIfNeeded(environmentId: "11", userId: "123", identity: nil, creationDate: Date())
@@ -111,7 +111,7 @@ final class TransformPipelineSpec: HeapSpec {
111111
}
112112

113113
it("transforms the data") {
114-
transformerA.applyToAll(sessionReplay: "SR", contentsquareProperties: .init(cspid: "PID", cspvid: "PVID", cssn: "SN", csts: "TS", csuu: "UU"))
114+
transformerA.applyToAll(sessionReplay: "SR", contentsquareProperties: .init(cspid: "PID", csuu: "UU", cssn: "SN", cspvid: "PVID", csts: "TS"))
115115
pipeline.add(transformerA)
116116

117117
pipeline.insertPendingMessage(message)
@@ -129,7 +129,7 @@ final class TransformPipelineSpec: HeapSpec {
129129
}
130130

131131
it("works with an already completed processor") {
132-
transformerA.applyToAll(sessionReplay: "SR", contentsquareProperties: .init(cspid: "PID", cspvid: "PVID", cssn: "SN", csts: "TS", csuu: "UU"))
132+
transformerA.applyToAll(sessionReplay: "SR", contentsquareProperties: .init(cspid: "PID", csuu: "UU", cssn: "SN", cspvid: "PVID", csts: "TS"))
133133
pipeline.add(transformerA)
134134

135135
let processor = pipeline.processor(for: message)

Development/Tests/HeapSwiftCoreTests/SqliteConnection/SqliteConnectionSpec.swift

+14
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,20 @@ Create Table TestTable (
6565

6666
}
6767

68+
it("throws with meaningful data") {
69+
let query = "Insert Into TestTable (FakeColumn) Values (1);"
70+
do {
71+
try connection.perform(query: query)
72+
XCTFail("The above code should have failed")
73+
} catch let error as SqliteError {
74+
expect(error.code).to(equal(1))
75+
expect(error.message).to(contain(query))
76+
expect("\(error)").to(contain("HeapSwiftCoreTests/SqliteConnectionSpec.swift:71"))
77+
expect("\(error)").to(contain("error 1"))
78+
expect("\(error)").to(contain(query))
79+
}
80+
}
81+
6882
it("can edit and query tables") {
6983

7084
// Fun fact! Sqlite only executes the first statement in a query.

HeapSwiftCore.podspec

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = 'HeapSwiftCore'
3-
s.version = '0.5.2'
3+
s.version = '0.6.0'
44
s.license = { :type => 'MIT' }
55
s.summary = 'The core Heap library used for apps on Apple platforms.'
66
s.homepage = 'https://heap.io'
@@ -18,7 +18,7 @@ Pod::Spec.new do |s|
1818
s.source_files = 'Development/Sources/HeapSwiftCore/**/*.swift'
1919

2020
s.dependency 'SwiftProtobuf', '~> 1.6'
21-
s.dependency 'HeapSwiftCoreInterfaces', '0.6.0-alpha.3'
21+
s.dependency 'HeapSwiftCoreInterfaces', '0.6.0'
2222

2323
s.swift_versions = ['5.0']
2424
end

Package.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ let package = Package(
3232
path: "Development/Sources/HeapSwiftCore"),
3333
.binaryTarget(
3434
name: "HeapSwiftCoreInterfaces",
35-
url: "https://cdn.heapanalytics.com/ios/heap-swift-core-interfaces-0.6.0-alpha.3.zip", // END HeapSwiftCoreInterfaces URL
36-
checksum: "8a6477840ec8871f5dca44c707d087e9e9a1b0c121f80dcb4cb04f2a5eaa625d" // END HeapSwiftCoreInterfaces checksum
35+
url: "https://cdn.heapanalytics.com/ios/heap-swift-core-interfaces-0.6.0.zip", // END HeapSwiftCoreInterfaces URL
36+
checksum: "e92bf74d2525cbc9503ed9f4ef3369e09e3b216c6f6b23a4de8a4614ed1bc840" // END HeapSwiftCoreInterfaces checksum
3737
)
3838
]
3939
)

0 commit comments

Comments
 (0)