Skip to content

Commit 78a82e3

Browse files
committed
Merge branch 'combine-main'
2 parents 8652585 + a284f94 commit 78a82e3

File tree

60 files changed

+7332
-20
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+7332
-20
lines changed

.github/workflows/combine.yml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Copyright 2020 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
name: combine
16+
17+
on:
18+
pull_request:
19+
paths:
20+
# Combine sources
21+
- 'FirebaseCombineSwift/**'
22+
23+
# Podspec
24+
- 'FirebaseCombineSwift.podspec'
25+
26+
# This workflow
27+
- '.github/workflows/combine.yml'
28+
29+
# Rebuild on Ruby infrastructure changes.
30+
- 'Gemfile'
31+
32+
schedule:
33+
# Run every day at 11pm (PST) - cron uses UTC times
34+
- cron: '0 7 * * *'
35+
36+
jobs:
37+
xcodebuild:
38+
# Don't run on private repo unless it is a PR.
39+
if: (github.repository == 'Firebase/firebase-ios-sdk' && github.event_name == 'schedule') || github.event_name == 'pull_request'
40+
runs-on: macos-latest
41+
42+
strategy:
43+
matrix:
44+
target: [iOS]
45+
46+
steps:
47+
- uses: actions/checkout@v2
48+
49+
- name: Setup build
50+
run: scripts/install_prereqs.sh CombineSwift ${{ matrix.target }} xcodebuild
51+
52+
- name: Build and test
53+
run: scripts/third_party/travis/retry.sh scripts/build.sh CombineSwift ${{ matrix.target }} xcodebuild
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<Scheme
3+
LastUpgradeVersion = "1240"
4+
version = "1.3">
5+
<BuildAction
6+
parallelizeBuildables = "YES"
7+
buildImplicitDependencies = "YES">
8+
</BuildAction>
9+
<TestAction
10+
buildConfiguration = "Debug"
11+
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
12+
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
13+
shouldUseLaunchSchemeArgsEnv = "YES">
14+
<Testables>
15+
<TestableReference
16+
skipped = "NO">
17+
<BuildableReference
18+
BuildableIdentifier = "primary"
19+
BlueprintIdentifier = "FirestoreTestingSupportTests"
20+
BuildableName = "FirestoreTestingSupportTests"
21+
BlueprintName = "FirestoreTestingSupportTests"
22+
ReferencedContainer = "container:">
23+
</BuildableReference>
24+
</TestableReference>
25+
</Testables>
26+
</TestAction>
27+
<LaunchAction
28+
buildConfiguration = "Debug"
29+
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
30+
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
31+
launchStyle = "0"
32+
useCustomWorkingDirectory = "NO"
33+
ignoresPersistentStateOnLaunch = "NO"
34+
debugDocumentVersioning = "YES"
35+
debugServiceExtension = "internal"
36+
allowLocationSimulation = "YES">
37+
</LaunchAction>
38+
<ProfileAction
39+
buildConfiguration = "Release"
40+
shouldUseLaunchSchemeArgsEnv = "YES"
41+
savedToolIdentifier = ""
42+
useCustomWorkingDirectory = "NO"
43+
debugDocumentVersioning = "YES">
44+
</ProfileAction>
45+
<AnalyzeAction
46+
buildConfiguration = "Debug">
47+
</AnalyzeAction>
48+
<ArchiveAction
49+
buildConfiguration = "Release"
50+
revealArchiveInOrganizer = "YES">
51+
</ArchiveAction>
52+
</Scheme>

FirebaseAuth/Tests/Sample/SwiftApiTests/Credentials.swift

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
1-
/*
2-
* Copyright 2020 Google LLC
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-
*/
1+
// Copyright 2020 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
1614

1715
import Foundation
1816

FirebaseAuthTestingSupport.podspec

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
Pod::Spec.new do |s|
2+
s.name = 'FirebaseAuthTestingSupport'
3+
s.version = '1.0.0'
4+
s.summary = 'Firebase SDKs testing support types and utilities.'
5+
6+
s.description = <<-DESC
7+
Type declarations and utilities needed for unit testing of the code dependent on Firebase SDKs
8+
DESC
9+
10+
s.homepage = 'https://developers.google.com/'
11+
s.license = { :type => 'Apache', :file => 'LICENSE' }
12+
s.authors = 'Google, Inc.'
13+
14+
s.source = {
15+
:git => 'https://github.com/Firebase/firebase-ios-sdk.git',
16+
:tag => 'CocoaPods-' + s.version.to_s
17+
}
18+
19+
ios_deployment_target = '10.0'
20+
osx_deployment_target = '10.12'
21+
tvos_deployment_target = '10.0'
22+
watchos_deployment_target = '6.0'
23+
24+
s.ios.deployment_target = ios_deployment_target
25+
s.osx.deployment_target = osx_deployment_target
26+
s.tvos.deployment_target = tvos_deployment_target
27+
s.watchos.deployment_target = watchos_deployment_target
28+
29+
s.cocoapods_version = '>= 1.4.0'
30+
s.prefix_header_file = false
31+
s.requires_arc = true
32+
33+
base_dir = 'FirebaseTestingSupport/Auth/'
34+
35+
s.source_files = [
36+
base_dir + 'Sources/**/*.{m,mm,h}',
37+
]
38+
39+
s.public_header_files = base_dir + '**/*.h'
40+
41+
s.dependency 'FirebaseAuth', '~> 7.7'
42+
43+
s.pod_target_xcconfig = {
44+
'GCC_C_LANGUAGE_STANDARD' => 'c99',
45+
'OTHER_CFLAGS' => '-fno-autolink',
46+
'HEADER_SEARCH_PATHS' =>
47+
'"${PODS_TARGET_SRCROOT}" '
48+
}
49+
50+
s.test_spec 'unit' do |unit_tests|
51+
unit_tests.scheme = { :code_coverage => true }
52+
unit_tests.platforms = {:ios => ios_deployment_target, :osx => osx_deployment_target, :tvos => tvos_deployment_target}
53+
unit_tests.source_files = [
54+
base_dir + 'Tests/**/*.swift'
55+
]
56+
end
57+
end

FirebaseCombineSwift.podspec

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
Pod::Spec.new do |s|
2+
s.name = 'FirebaseCombineSwift'
3+
s.version = '7.6.0'
4+
s.summary = 'Swift extensions with Combine support for Firebase'
5+
6+
s.description = <<-DESC
7+
Combine Publishers for Firebase.
8+
DESC
9+
10+
s.homepage = 'https://firebase.google.com'
11+
s.license = { :type => 'Apache', :file => 'LICENSE' }
12+
s.authors = 'Google, Inc.'
13+
14+
s.source = {
15+
:git => 'https://github.com/firebase/firebase-ios-sdk.git',
16+
:tag => 'CocoaPods-' + s.version.to_s
17+
}
18+
19+
s.social_media_url = 'https://twitter.com/Firebase'
20+
21+
s.swift_version = '5.0'
22+
23+
ios_deployment_target = '13.0'
24+
osx_deployment_target = '10.15'
25+
tvos_deployment_target = '13.0'
26+
watchos_deployment_target = '6.0'
27+
28+
s.ios.deployment_target = ios_deployment_target
29+
s.osx.deployment_target = osx_deployment_target
30+
s.tvos.deployment_target = tvos_deployment_target
31+
s.watchos.deployment_target = watchos_deployment_target
32+
33+
s.cocoapods_version = '>= 1.4.0'
34+
s.prefix_header_file = false
35+
36+
source = 'FirebaseCombineSwift/Sources/'
37+
s.exclude_files = [
38+
source + 'Core/**/*.swift',
39+
]
40+
s.source_files = [
41+
source + '**/*.swift',
42+
source + '**/*.m',
43+
]
44+
s.public_header_files = [
45+
source + '**/*.h',
46+
]
47+
48+
s.framework = 'Foundation'
49+
s.ios.framework = 'UIKit'
50+
s.osx.framework = 'AppKit'
51+
s.tvos.framework = 'UIKit'
52+
53+
s.dependency 'FirebaseCore', '~> 7.6'
54+
s.dependency 'FirebaseAuth', '~> 7.6'
55+
s.dependency 'FirebaseFunctions', '~> 7.6'
56+
s.dependency 'FirebaseStorage', '~> 7.6'
57+
s.dependency 'FirebaseStorageSwift', '~> 7.6-beta'
58+
59+
s.pod_target_xcconfig = {
60+
'HEADER_SEARCH_PATHS' => '"${PODS_TARGET_SRCROOT}"',
61+
}
62+
63+
s.test_spec 'unit' do |unit_tests|
64+
unit_tests.scheme = { :code_coverage => true }
65+
unit_tests.platforms = {
66+
:ios => ios_deployment_target,
67+
:osx => osx_deployment_target,
68+
:tvos => tvos_deployment_target
69+
}
70+
unit_tests.source_files = [
71+
'FirebaseCombineSwift/Tests/Unit/**/*.swift',
72+
'FirebaseCombineSwift/Tests/Unit/**/*.h',
73+
'SharedTestUtilities/FIROptionsMock.[mh]',
74+
'SharedTestUtilities/FIRComponentTestUtilities.[mh]',
75+
]
76+
unit_tests.exclude_files = 'FirebaseCombineSwift/Tests/Unit/**/*Template.swift'
77+
unit_tests.requires_app_host = true
78+
unit_tests.pod_target_xcconfig = {
79+
'SWIFT_OBJC_BRIDGING_HEADER' => '$(PODS_TARGET_SRCROOT)/FirebaseCombineSwift/Tests/Unit/FirebaseCombine-unit-Bridging-Header.h'
80+
}
81+
unit_tests.dependency 'OCMock'
82+
unit_tests.dependency 'FirebaseAuthTestingSupport'
83+
unit_tests.dependency 'FirebaseFunctionsTestingSupport'
84+
end
85+
end

FirebaseCombineSwift/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Unreleased
2+
3+
- [feature] Added Combine support for Cloud Functions for Firebase
4+
- [feature] WIP: Added Combine support for Firebase Auth
5+
- [feature] WIP: Added Combine support for Firebase Storage

FirebaseCombineSwift/DECISIONS.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Decisions
2+
3+
This file documents some of the decisions we made when developing Combine support for Firebase.
4+
5+
# Module structure
6+
7+
## Discussion
8+
The general idea is to keep all Combine-related code in a separate module (`FirebaseCombineSwift`, to match the naming scheme used for `FirebaseFirestoreSwift` and `FirebaseStorageSwift`).
9+
10+
By using the `#if canImport(moduleName)` directive, we can make sure to only enable the publishers for a module that developers have imported into a build target.
11+
12+
13+
# Implementing Publishers
14+
15+
## Custom Publishers vs. wrapping in Futures / using PassthroughSubject
16+
17+
Instead of implementing [custom publishers](https://thoughtbot.com/blog/lets-build-a-custom-publisher-in-combine), which [Apple discourages developers from doing](https://developer.apple.com/documentation/combine/publisher), we make use of [`PassthroughSubject`](https://developer.apple.com/documentation/combine/passthroughsubject) (for publishers that emit a stream of events), and [`Future`](https://developer.apple.com/documentation/combine/future) for one-shot calls that produce a single value.
18+
19+
## Using capture lists
20+
21+
After discussing internally, we came to the conclusion that the outer closure in the following piece of code is non-escaping, hence there is no benefit to weakly capture `self`. As the inner closure does't refer to `self`, the reference does not outlive the current call stack.
22+
23+
It is thus safe to not use `[weak self]` in this instance.
24+
25+
```swift
26+
extension Auth {
27+
public func createUser(withEmail email: String,
28+
password: String) -> Future<AuthDataResult, Error> {
29+
Future<AuthDataResult, Error> { /* [weak self] <-- not required */ promise in
30+
self?.createUser(withEmail: email, password: password) { authDataResult, error in
31+
if let error = error {
32+
promise(.failure(error))
33+
} else if let authDataResult = authDataResult {
34+
promise(.success(authDataResult))
35+
}
36+
}
37+
}
38+
}
39+
}
40+
```
41+
42+
# Method naming
43+
44+
## Discussion
45+
* Methods that might send a **stream of events** over time will receive a `Publisher` suffix, in line with Apple's own APIs. Any `add` prefix will be removed. This helps to clarify that the user is not _adding_ something that they will have to remove later on ([as is required](https://firebase.google.com/docs/auth/ios/start#listen_for_authentication_state) in most of Firebase's existing APIs). Instead, the result of the publisher needs to be handled just like any other publisher (i.e. be kept in a set of `Cancellable`s).
46+
47+
Examples:
48+
* `addStateDidChangeListener` -> `authStateDidChangePublisher`
49+
* `addSnapshotListener` -> `snapshotPublisher`
50+
51+
* Methods that **return a result once** will not receive a suffix. This effectively means that these methods are overloads to their existing counterparts that take a closure. To silence any `Result of call to xzy is unused` warnings, these methods need to be prefixed with `@discardableresult`. This shouldn't be a problem, since the Future that is created inside those functions is called immediately and will be disposed of by the runtime upon returning from the inner closure.
52+
53+
Examples:
54+
* `signIn` -> `signIn`
55+
* `createUser` -> `createUser`
56+
57+
## Options considered
58+
Using the same method and parameter names for one-shot asynchronous methods results in both methods to be shown in close proximity when invoking code completion
59+
60+
![image](https://user-images.githubusercontent.com/232107/99672274-76f05680-2a73-11eb-880a-3563f293de7d.png)
61+
62+
To achieve the same for methods that return a stream of events, we'd have to name those `addXzyListener`. This would be in contrast to Apple's naming scheme (e.g. `dataTask(with:completionHandler)` -> `dataTaskPublisher(for:)`

FirebaseCombineSwift/DEVELOPING.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Developing
2+
3+
This is a quick overview to help you get started contributing to Firebase Combine.
4+
5+
## Prerequisites
6+
7+
* Xcode 12.x (or later)
8+
* CocoaPods 1.10.x (or later)
9+
* [CocoaPods Generate](https://github.com/square/cocoapods-generate)
10+
11+
## Setting up your development environment
12+
13+
* Check out firebase-ios-sdk
14+
* Install utilities
15+
16+
```bash
17+
$ ./scripts/setup_check.sh
18+
$ ./scripts/setup_bundler.sh
19+
```
20+
21+
## Generating the development project
22+
23+
For developing _Firebase Combine_, you'll need a development project that imports the relevant pods.
24+
25+
Run the following command to generate and open the development project:
26+
27+
```bash
28+
$ pod gen FirebaseCombineSwift.podspec --local-sources=./ --auto-open --platforms=ios
29+
```
30+
31+
## Checking in code
32+
33+
Before checking in your code, make sure to check your code against the coding styleguide by running the following command:
34+
35+
```bash
36+
$ ./scripts/check.sh --allow-dirty
37+
```

0 commit comments

Comments
 (0)