Skip to content

Commit

Permalink
[Core] Fix iOS 12 spec lint failure from incomplete `FirebaseCoreInte…
Browse files Browse the repository at this point in the history
…rnal` implementation (#13952)
  • Loading branch information
ncooke3 authored Oct 22, 2024
1 parent 4546bd2 commit 1333530
Show file tree
Hide file tree
Showing 10 changed files with 88 additions and 67 deletions.
4 changes: 2 additions & 2 deletions Firebase.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'Firebase'
s.version = '11.4.1'
s.version = '11.4.2'
s.summary = 'Firebase'

s.description = <<-DESC
Expand Down Expand Up @@ -43,7 +43,7 @@ Simplify your app development, grow your user base, and monetize more effectivel
end

s.subspec 'CoreOnly' do |ss|
ss.dependency 'FirebaseCore', '11.4.1'
ss.dependency 'FirebaseCore', '11.4.2'
ss.source_files = 'CoreOnly/Sources/Firebase.h'
ss.preserve_paths = 'CoreOnly/Sources/module.modulemap'
if ENV['FIREBASE_POD_REPO_FOR_DEV_POD'] then
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,11 @@ class AuthBackendRPCImplementationTests: RPCBaseTests {

#if COCOAPODS || SWIFT_PACKAGE
private class FakeHeartbeatLogger: NSObject, FIRHeartbeatLoggerProtocol {
func headerValue() -> String? {
// `asyncHeaderValue` should be used instead.
fatalError("FakeHeartbeatLogger headerValue should not be used in tests.")
}

func asyncHeaderValue() async -> String? {
let payload = flushHeartbeatsIntoPayload()
guard !payload.isEmpty else {
Expand Down
4 changes: 2 additions & 2 deletions FirebaseCore.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'FirebaseCore'
s.version = '11.4.1'
s.version = '11.4.2'
s.summary = 'Firebase Core'

s.description = <<-DESC
Expand Down Expand Up @@ -53,7 +53,7 @@ Firebase Core includes FIRApp and FIROptions which provide central configuration
# Remember to also update version in `cmake/external/GoogleUtilities.cmake`
s.dependency 'GoogleUtilities/Environment', '~> 8.0'
s.dependency 'GoogleUtilities/Logger', '~> 8.0'
s.dependency 'FirebaseCoreInternal', '~> 11.4'
s.dependency 'FirebaseCoreInternal', '>= 11.4.2', '< 12.0'

s.pod_target_xcconfig = {
'GCC_C_LANGUAGE_STANDARD' => 'c99',
Expand Down
4 changes: 4 additions & 0 deletions FirebaseCore/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Firebase 11.4.2
- [fixed] CocoaPods only release to fix iOS 12 build failure resulting from
incomplete implementation in the FirebaseCoreInternal CocoaPod.

# Firebase 11.4.1
- [fixed] CocoaPods only release to revert breaking change in
`FirebaseCoreExtension` SDK. (#13942)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,34 +126,30 @@ public final class HeartbeatController {
}
}

@available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 6.0, *)
public func flushAsync() async -> HeartbeatsPayload {
return await withCheckedContinuation { continuation in
let resetTransform = { (heartbeatsBundle: HeartbeatsBundle?) -> HeartbeatsBundle? in
guard let oldHeartbeatsBundle = heartbeatsBundle else {
return nil // Storage was empty.
}
// The new value that's stored will use the old's cache to prevent the
// logging of duplicates after flushing.
return HeartbeatsBundle(
capacity: self.heartbeatsStorageCapacity,
cache: oldHeartbeatsBundle.lastAddedHeartbeatDates
)
public func flushAsync(completionHandler: @escaping (HeartbeatsPayload) -> Void) {
let resetTransform = { (heartbeatsBundle: HeartbeatsBundle?) -> HeartbeatsBundle? in
guard let oldHeartbeatsBundle = heartbeatsBundle else {
return nil // Storage was empty.
}
// The new value that's stored will use the old's cache to prevent the
// logging of duplicates after flushing.
return HeartbeatsBundle(
capacity: self.heartbeatsStorageCapacity,
cache: oldHeartbeatsBundle.lastAddedHeartbeatDates
)
}

// Asynchronously gets and returns the stored heartbeats, resetting storage
// using the given transform.
storage.getAndSetAsync(using: resetTransform) { result in
switch result {
case let .success(heartbeatsBundle):
// If no heartbeats bundle was stored, return an empty payload.
continuation
.resume(returning: heartbeatsBundle?.makeHeartbeatsPayload() ?? HeartbeatsPayload
.emptyPayload)
case .failure:
// If the operation throws, assume no heartbeat(s) were retrieved or set.
continuation.resume(returning: HeartbeatsPayload.emptyPayload)
}
// Asynchronously gets and returns the stored heartbeats, resetting storage
// using the given transform.
storage.getAndSetAsync(using: resetTransform) { result in
switch result {
case let .success(heartbeatsBundle):
// If no heartbeats bundle was stored, return an empty payload.
completionHandler(heartbeatsBundle?.makeHeartbeatsPayload() ?? HeartbeatsPayload
.emptyPayload)
case .failure:
// If the operation throws, assume no heartbeat(s) were retrieved or set.
completionHandler(HeartbeatsPayload.emptyPayload)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,12 @@ public class _ObjC_HeartbeatController: NSObject {
///
/// - Note: This API is thread-safe.
/// - Returns: A heartbeats payload for the flushed heartbeat(s).
@available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 6.0, *)
public func flushAsync() async -> _ObjC_HeartbeatsPayload {
let heartbeatsPayload = await heartbeatController.flushAsync()
return _ObjC_HeartbeatsPayload(heartbeatsPayload)
public func flushAsync(completionHandler: @escaping (_ObjC_HeartbeatsPayload) -> Void) {
// TODO: When minimum version moves to iOS 13.0, restore the async version
// removed in #13952.
heartbeatController.flushAsync { heartbeatsPayload in
completionHandler(_ObjC_HeartbeatsPayload(heartbeatsPayload))
}
}

/// Synchronously flushes the heartbeat for today.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,29 +52,36 @@ class HeartbeatLoggingIntegrationTests: XCTestCase {
)
}

@available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 6.0, *)
func testLogAndFlushAsync() async throws {
func testLogAndFlushAsync() throws {
// Given
let heartbeatController = HeartbeatController(id: #function)
let expectedDate = HeartbeatsPayload.dateFormatter.string(from: Date())
let expectation = self.expectation(description: #function)
// When
heartbeatController.log("dummy_agent")
let payload = await heartbeatController.flushAsync()
// Then
try HeartbeatLoggingTestUtils.assertEqualPayloadStrings(
payload.headerValue(),
"""
{
"version": 2,
"heartbeats": [
heartbeatController.flushAsync { payload in
// Then
do {
try HeartbeatLoggingTestUtils.assertEqualPayloadStrings(
payload.headerValue(),
"""
{
"agent": "dummy_agent",
"dates": ["\(expectedDate)"]
"version": 2,
"heartbeats": [
{
"agent": "dummy_agent",
"dates": ["\(expectedDate)"]
}
]
}
]
"""
)
expectation.fulfill()
} catch {
XCTFail("Unexpected error: \(error)")
}
"""
)
}
waitForExpectations(timeout: 1.0)
}

/// This test may flake if it is executed during the transition from one day to the next.
Expand Down
38 changes: 22 additions & 16 deletions FirebaseCore/Internal/Tests/Unit/HeartbeatControllerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,35 +58,41 @@ class HeartbeatControllerTests: XCTestCase {
assertHeartbeatControllerFlushesEmptyPayload(controller)
}

@available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 6.0, *)
func testLogAndFlushAsync() async throws {
func testLogAndFlushAsync() throws {
// Given
let controller = HeartbeatController(
storage: HeartbeatStorageFake(),
dateProvider: { self.date }
)
let expectation = expectation(description: #function)

assertHeartbeatControllerFlushesEmptyPayload(controller)

// When
controller.log("dummy_agent")
let heartbeatPayload = await controller.flushAsync()

// Then
try HeartbeatLoggingTestUtils.assertEqualPayloadStrings(
heartbeatPayload.headerValue(),
"""
{
"version": 2,
"heartbeats": [
controller.flushAsync { heartbeatPayload in
// Then
do {
try HeartbeatLoggingTestUtils.assertEqualPayloadStrings(
heartbeatPayload.headerValue(),
"""
{
"agent": "dummy_agent",
"dates": ["2021-11-01"]
"version": 2,
"heartbeats": [
{
"agent": "dummy_agent",
"dates": ["2021-11-01"]
}
]
}
]
"""
)
expectation.fulfill()
} catch {
XCTFail("Unexpected error: \(error)")
}
"""
)
}
waitForExpectations(timeout: 1.0)

assertHeartbeatControllerFlushesEmptyPayload(controller)
}
Expand Down
3 changes: 2 additions & 1 deletion FirebaseCore/Sources/FIRHeartbeatLogger.m
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ - (FIRHeartbeatsPayload *)flushHeartbeatsIntoPayload {
}

- (void)flushHeartbeatsIntoPayloadWithCompletionHandler:
(void (^)(FIRHeartbeatsPayload *))completionHandler {
(void (^)(FIRHeartbeatsPayload *))completionHandler
API_AVAILABLE(ios(13.0), macosx(10.15), macCatalyst(13.0), tvos(13.0), watchos(6.0)) {
[_heartbeatController flushAsyncWithCompletionHandler:^(FIRHeartbeatsPayload *payload) {
completionHandler(payload);
}];
Expand Down
2 changes: 1 addition & 1 deletion FirebaseCoreInternal.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'FirebaseCoreInternal'
s.version = '11.4.0'
s.version = '11.4.2'
s.summary = 'APIs for internal FirebaseCore usage.'

s.description = <<-DESC
Expand Down

0 comments on commit 1333530

Please sign in to comment.