[DynamicLinks] Remove library and corresponding infra (#14978)

This commit is contained in:
Nick Cooke authored and GitHub committed 2025-06-20 11:53:29 -04:00
1 parent 6f603aa162
commit 9f311f2376
104 files changed
+16 -10530

No files matched your search

@@ -56,7 +56,6 @@ body:
- Crashlytics
- Database
- Data Connect
- DynamicLinks
- Firestore
- Functions
- In-App Messaging
+1 -1
View File
@@ -24,7 +24,7 @@ jobs:
strategy:
matrix:
# These need to be on a single line or else the formatting won't validate.
pod: ["FirebaseAppDistribution", "FirebaseDynamicLinks", "FirebaseInAppMessaging", "FirebasePerformance"]
pod: ["FirebaseAppDistribution", "FirebaseInAppMessaging", "FirebasePerformance"]
steps:
- uses: actions/checkout@v4
- uses: mikehardy/buildcache-action@c87cea0ccd718971d6cc39e672c4f26815b6c126
-61
View File
@@ -1,61 +0,0 @@
name: dynamiclinks
permissions:
contents: read
on:
workflow_dispatch:
pull_request:
paths:
- 'FirebaseDynamicLinks**'
- '.github/workflows/dynamiclinks.yml'
- 'Interop/Analytics/Public/*.h'
- '.github/workflows/common.yml'
- '.github/workflows/common_cocoapods.yml'
- 'Gemfile*'
schedule:
# Run every day at 1am (PST) - cron uses UTC times
- cron: '0 9 * * *'
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: true
jobs:
spm:
strategy:
matrix:
target: [FirebaseAppCheckUnit, FirebaseAppCheckUnitSwift]
uses: ./.github/workflows/common.yml
with:
target: FirebaseDynamicLinks
buildonly_platforms: iOS
platforms: iOS
pod_lib_lint:
uses: ./.github/workflows/common_cocoapods.yml
with:
product: FirebaseDynamicLinks
platforms: iOS # Dynamic Links only supports iOS.
allow_warnings: true
dynamiclinks-cron-only:
# Don't run on private repo.
if: github.event_name == 'schedule' && github.repository == 'Firebase/firebase-ios-sdk'
runs-on: macos-15
strategy:
matrix:
flags: [
'--use-static-frameworks'
]
needs: pod_lib_lint
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@354a1ad156761f5ee2b7b13fa8e09943a5e8d252 # v1
- name: Setup Bundler
run: scripts/setup_bundler.sh
- name: Xcode
run: sudo xcode-select -s /Applications/Xcode_16.2.app/Contents/Developer
- name: PodLibLint Storage Cron
run: scripts/third_party/travis/retry.sh scripts/pod_lib_lint.rb FirebaseDynamicLinks.podspec --platforms=ios ${{ matrix.flags }} --allow-warnings
+1 -25
View File
@@ -33,7 +33,6 @@ jobs:
auth_run_job: ${{ steps.check_files.outputs.auth_run_job }}
crashlytics_run_job: ${{ steps.check_files.outputs.crashlytics_run_job }}
database_run_job: ${{ steps.check_files.outputs.database_run_job }}
dynamiclinks_run_job: ${{ steps.check_files.outputs.dynamiclinks_run_job }}
firestore_run_job: ${{ steps.check_files.outputs.firestore_run_job }}
functions_run_job: ${{ steps.check_files.outputs.functions_run_job }}
inappmessaging_run_job: ${{ steps.check_files.outputs.inappmessaging_run_job }}
@@ -126,29 +125,6 @@ jobs:
name: codecoverage
path: /Users/runner/*.xcresult
pod-lib-lint-dynamiclinks:
needs: check
# Don't run on private repo unless it is a PR.
if: always() && github.repository == 'Firebase/firebase-ios-sdk' && (needs.check.outputs.dynamiclinks_run_job == 'true'|| github.event.pull_request.merged)
runs-on: macos-14
strategy:
matrix:
target: [iOS]
steps:
- uses: actions/checkout@v4
- uses: mikehardy/buildcache-action@c87cea0ccd718971d6cc39e672c4f26815b6c126
with:
cache_key: ${{ matrix.os }}
- uses: ruby/setup-ruby@354a1ad156761f5ee2b7b13fa8e09943a5e8d252 # v1
- name: Setup Bundler
run: scripts/setup_bundler.sh
- name: Build and test
run: ./scripts/health_metrics/pod_test_code_coverage_report.sh --sdk=FirebaseDynamicLinks --platform=${{ matrix.target }}
- uses: actions/upload-artifact@v4
with:
name: codecoverage
path: /Users/runner/*.xcresult
pod-lib-lint-firestore:
needs: check
# Don't run on private repo unless it is a PR.
@@ -317,7 +293,7 @@ jobs:
path: /Users/runner/*.xcresult
create_report:
needs: [check, pod-lib-lint-abtesting, pod-lib-lint-auth, pod-lib-lint-database, pod-lib-lint-dynamiclinks, pod-lib-lint-firestore, pod-lib-lint-functions, pod-lib-lint-inappmessaging, pod-lib-lint-messaging, pod-lib-lint-performance, pod-lib-lint-remoteconfig, pod-lib-lint-storage]
needs: [check, pod-lib-lint-abtesting, pod-lib-lint-auth, pod-lib-lint-database, pod-lib-lint-firestore, pod-lib-lint-functions, pod-lib-lint-inappmessaging, pod-lib-lint-messaging, pod-lib-lint-performance, pod-lib-lint-remoteconfig, pod-lib-lint-storage]
if: always()
runs-on: macos-14
steps:
-2
View File
@@ -196,7 +196,6 @@ jobs:
- name: Setup Swift Quickstart
run: SAMPLE="$SDK" TARGET="${SDK}Example" NON_FIREBASE_SDKS="FBSDKLoginKit FBSDKCoreKit FBSDKCoreKit_Basics FBAEMKit" scripts/setup_quickstart_framework.sh \
"${HOME}"/ios_frameworks/Firebase/NonFirebaseSDKs/* \
"${HOME}"/ios_frameworks/Firebase/FirebaseDynamicLinks/* \
"${HOME}"/ios_frameworks/Firebase/GoogleSignIn/* \
"${HOME}"/ios_frameworks/Firebase/FirebaseAuth/* \
"${HOME}"/ios_frameworks/Firebase/FirebaseAnalytics/*
@@ -508,7 +507,6 @@ jobs:
- uses: actions/checkout@v4
- name: Setup quickstart
run: SAMPLE="$SDK" TARGET="${SDK}Example" scripts/setup_quickstart_framework.sh \
"${HOME}"/ios_frameworks/Firebase/FirebaseDynamicLinks/* \
"${HOME}"/ios_frameworks/Firebase/FirebaseInAppMessaging/* \
"${HOME}"/ios_frameworks/Firebase/FirebaseAnalytics/*
- name: Xcode
-1
View File
@@ -39,7 +39,6 @@ binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseAppDistributionBi
binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseAuthBinary.json"
binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseCrashlyticsBinary.json"
binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseDatabaseBinary.json"
binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseDynamicLinksBinary.json"
binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseFirestoreBinary.json"
binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseFunctionsBinary.json"
binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseGoogleSignInBinary.json"
-1
View File
@@ -10,7 +10,6 @@ FirebaseAuthInterop
FirebaseCore
FirebaseCoreExtension
FirebaseCoreInternal
FirebaseDynamicLinks
FirebaseFunctions
FirebaseInAppMessaging
FirebaseInstallations
-4
View File
@@ -49,10 +49,6 @@
#import <FirebaseDatabase/FirebaseDatabase.h>
#endif
#if __has_include(<FirebaseDynamicLinks/FirebaseDynamicLinks.h>)
#import <FirebaseDynamicLinks/FirebaseDynamicLinks.h>
#endif
#if __has_include(<FirebaseFirestore/FirebaseFirestore.h>)
#import <FirebaseFirestore/FirebaseFirestore.h>
#endif
@@ -25,7 +25,6 @@ class AuthExists: Auth {}
// Uncomment next line if ABTesting gets added to Firebase.h.
// class ABTestingExists : LifecycleEvents {}
class DatabaseExists: Database {}
class DynamicLinksExists: DynamicLinks {}
class FirestoreExists: Firestore {}
class FunctionsExists: Functions {}
class InAppMessagingExists: InAppMessaging {}
-1
View File
@@ -16,7 +16,6 @@ target 'FirebasePodTest' do
pod 'FirebaseCore', :path => '../../../'
pod 'FirebaseCrashlytics', :path => '../../../'
pod 'FirebaseDatabase', :path => '../../../'
pod 'FirebaseDynamicLinks', :path => '../../../'
pod 'FirebaseFirestore', :path => '../../../'
pod 'FirebaseFirestoreInternal', :path => '../../../'
pod 'FirebaseFunctions', :path => '../../../'
-5
View File
@@ -55,7 +55,6 @@ def labelsForModifiedFiles()
labels.push("api: core") if @has_core_changes
labels.push("api: crashlytics") if @has_crashlytics_changes
labels.push("api: database") if @has_database_changes
labels.push("api: dynamiclinks") if @has_dynamiclinks_changes
labels.push("api: firebaseai") if @has_firebaseai_changes
labels.push("api: firestore") if @has_firestore_changes
labels.push("api: functions") if @has_functions_changes
@@ -93,7 +92,6 @@ has_license_changes = didModify(["LICENSE"])
"Core",
"Crashlytics",
"Database",
"DynamicLinks",
"FirebaseAI",
"Firestore",
"Functions",
@@ -132,8 +130,6 @@ has_license_changes = didModify(["LICENSE"])
@has_crashlytics_api_changes = hasChangesIn("Crashlytics/Crashlytics/Public/")
@has_database_changes = hasChangesIn("FirebaseDatabase")
@has_database_api_changes = hasChangesIn("FirebaseDatabase/Sources/Public/")
@has_dynamiclinks_changes = hasChangesIn("FirebaseDynamicLinks")
@has_dynamiclinks_api_changes = hasChangesIn("FirebaseDynamicLinks/Sources/Public/")
@has_firebaseai_changes = hasChangesIn([
"FirebaseAI",
"FirebaseVertexAI"
@@ -168,7 +164,6 @@ has_license_changes = didModify(["LICENSE"])
@has_core_api_changes ||
@has_crashlytics_api_changes ||
@has_database_api_changes ||
@has_dynamiclinks_api_changes ||
@has_firestore_api_changes ||
@has_functions_api_changes ||
@has_inappmessaging_api_changes ||
-6
View File
@@ -138,12 +138,6 @@ Simplify your app development, grow your user base, and monetize more effectivel
ss.watchos.deployment_target = '7.0'
end
s.subspec 'DynamicLinks' do |ss|
ss.dependency 'Firebase/CoreOnly'
ss.ios.dependency 'FirebaseDynamicLinks', '~> 11.15.0'
ss.ios.deployment_target = '13.0'
end
s.subspec 'Firestore' do |ss|
ss.dependency 'Firebase/CoreOnly'
ss.dependency 'FirebaseFirestore', '~> 11.15.0'
+2
View File
@@ -1,4 +1,6 @@
# Unreleased
- [removed] **Breaking change**: FirebaseDynamicLinks has been removed. See
https://firebase.google.com/support/dynamic-links-faq for more info.
- [removed] **Breaking change**: Removed the `Options.deepLinkURLScheme`
property. This API was exclusively used by the Dynamic Links SDK, which
has been removed.
-60
View File
@@ -1,60 +0,0 @@
Pod::Spec.new do |s|
s.name = 'FirebaseDynamicLinks'
s.version = '11.15.0'
s.summary = 'Firebase Dynamic Links'
s.description = <<-DESC
Firebase Dynamic Links are deep links that enhance user experience and increase engagement by retaining context post-install, across platforms.
DESC
s.homepage = 'https://firebase.google.com'
s.license = { :type => 'Apache-2.0', :file => 'LICENSE' }
s.authors = 'Google, Inc.'
s.source = {
:git => 'https://github.com/firebase/firebase-ios-sdk.git',
:tag => 'CocoaPods-' + s.version.to_s
}
s.social_media_url = 'https://twitter.com/Firebase'
s.ios.deployment_target = '13.0'
s.swift_version = '5.9'
# See https://firebase.google.com/support/dynamic-links-faq
s.deprecated = true
s.cocoapods_version = '>= 1.12.0'
s.prefix_header_file = false
s.source_files = [
'FirebaseDynamicLinks/Sources/**/*.[mh]',
'Interop/Analytics/Public/*.h',
'FirebaseCore/Extension/*.h',
]
s.public_header_files = 'FirebaseDynamicLinks/Sources/Public/FirebaseDynamicLinks/*.h'
s.resource_bundles = {
"#{s.module_name}_Privacy" => 'FirebaseDynamicLinks/Sources/Resources/PrivacyInfo.xcprivacy'
}
s.frameworks = 'QuartzCore'
s.weak_framework = 'WebKit'
s.dependency 'FirebaseCore', '~> 11.15.0'
s.pod_target_xcconfig = {
'GCC_PREPROCESSOR_DEFINITIONS' => 'FIRDynamicLinks3P GIN_SCION_LOGGING',
'HEADER_SEARCH_PATHS' => '"${PODS_TARGET_SRCROOT}"'
}
s.test_spec 'unit' do |unit_tests|
unit_tests.scheme = { :code_coverage => true }
unit_tests.source_files = [
'FirebaseDynamicLinks/Tests/Unit/*.[mh]',
]
unit_tests.requires_app_host = true
unit_tests.resources = 'FirebaseDynamicLinks/Tests/Unit/GoogleService-Info.plist',
# Supply plist for custom domain testing.
'FirebaseDynamicLinks/Tests/Unit/DL-Info.plist'
unit_tests.dependency 'OCMock'
unit_tests.dependency 'GoogleUtilities/MethodSwizzler', '~> 8.1'
unit_tests.dependency 'GoogleUtilities/SwizzlerTestHelpers', '~> 8.1'
end
end
+4
View File
@@ -1,3 +1,7 @@
# 12.0.0
- [removed] **Breaking change**: FirebaseDynamicLinks has been removed. See
https://firebase.google.com/support/dynamic-links-faq for more info.
# 11.8.0
- [deprecated] The `FirebaseDynamicLinks` CocoaPod is deprecated. For information about timelines and alternatives, see the [Dynamic Links deprecation FAQ](https://firebase.google.com/support/dynamic-links-faq).
-40
View File
@@ -1,40 +0,0 @@
# Firebase Dynamic Links SDK for iOS
> [!IMPORTANT]
> Firebase Dynamic Links is **deprecated** and should not be used in new projects. The service will shut down on August 25, 2025.
>
> Please see our [Dynamic Links Deprecation FAQ documentation](https://firebase.google.com/support/dynamic-links-faq) for more guidance.
Firebase Dynamic Links are universal deep links that persist across app installs.
For more info, see the [Firebase website](https://firebase.google.com/products/dynamic-links).
Please visit [our developer site](https://firebase.google.com/docs/dynamic-links/) for integration
instructions, documentations, support information, and terms of service.
## Managing the Pasteboard
Firebase Dynamic Links 4.2.0 and higher use a plist property
`FirebaseDeepLinkPasteboardRetrievalEnabled` that a developer can set to enable/disable the use of
iOS pasteboard by the SDK.
FDL SDK uses the pasteboard for deep-linking post app install (to enable deferred deep-linking,
where the link is copied on the
[app preview page](https://firebase.google.com/docs/dynamic-links/link-previews#app_preview_pages))
and app install attribution; otherwise, FDL does not use the pasteboard for anything else.
Disabling pasteboard access affects the app in the following ways:
* Deferred deep-linking will not work as reliably. At best, your app receives
[weak matches](https://firebase.google.com/docs/reference/unity/namespace/firebase/dynamic-links#linkmatchstrength)
for deep-links.
* App install attribution stats will be less accurate (potentially undercounting app installs).
Enabling pasteboard access affects the app in the following ways:
* On iOS 14, will show a system alert notifying that your app accessed the content in the
pasteboard. This should happen one-time after installation of the app.
* Deferred deep-linking will work as designed. At best, your app receives a
[perfect match](https://firebase.google.com/docs/reference/unity/namespace/firebase/dynamic-links#linkmatchstrength)
for deep-links.
* SDK will be able to more reliably attribute installation stats for links.
For more information, check out the
[iOS documentation](https://firebase.google.com/docs/dynamic-links/ios/receive).
@@ -1,67 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "FirebaseDynamicLinks/Sources/Public/FirebaseDynamicLinks/FDLURLComponents.h"
/**
* Label exceptions from FDL.
*/
FOUNDATION_EXPORT NSString *_Nonnull const kFirebaseDurableDeepLinkErrorDomain;
NS_ASSUME_NONNULL_BEGIN
/// Each of the parameter classes used in FIRDynamicLinkURLComponents needs to be able to
/// provide a dictionary representation of itself to be codified into URL query parameters. This
/// protocol defines that behavior.
@protocol FDLDictionaryRepresenting <NSObject>
@required
@property(nonatomic, readonly) NSDictionary<NSString *, NSString *> *dictionaryRepresentation;
@end
@interface FIRDynamicLinkGoogleAnalyticsParameters () <FDLDictionaryRepresenting>
@end
@interface FIRDynamicLinkIOSParameters () <FDLDictionaryRepresenting>
@end
@interface FIRDynamicLinkItunesConnectAnalyticsParameters () <FDLDictionaryRepresenting>
@end
@interface FIRDynamicLinkAndroidParameters () <FDLDictionaryRepresenting>
@end
@interface FIRDynamicLinkSocialMetaTagParameters () <FDLDictionaryRepresenting>
@end
@interface FIRDynamicLinkNavigationInfoParameters () <FDLDictionaryRepresenting>
@end
@interface FIRDynamicLinkOtherPlatformParameters () <FDLDictionaryRepresenting>
@end
@interface FIRDynamicLinkComponents ()
/// Creates and returns a request based on the url and options. Exposed for testing.
+ (NSURLRequest *)shorteningRequestForLongURL:(NSURL *)url
options:(nullable FIRDynamicLinkComponentsOptions *)options;
/// Sends an HTTP request using NSURLSession. Exposed for testing.
+ (void)sendHTTPRequest:(NSURLRequest *)request
completion:(void (^)(NSData *_Nullable data, NSError *_Nullable error))completion;
@end
NS_ASSUME_NONNULL_END
@@ -1,700 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <TargetConditionals.h>
#if TARGET_OS_IOS
#import <Foundation/Foundation.h>
#import "FirebaseDynamicLinks/Sources/FDLURLComponents/FDLURLComponents+Private.h"
#import "FirebaseDynamicLinks/Sources/FDLURLComponents/FIRDynamicLinkComponentsKeyProvider.h"
#import "FirebaseDynamicLinks/Sources/Public/FirebaseDynamicLinks/FDLURLComponents.h"
#import "FirebaseDynamicLinks/Sources/Logging/FDLLogging.h"
#import "FirebaseDynamicLinks/Sources/Utilities/FDLUtilities.h"
// Label exceptions from FDL.
NSString *const kFirebaseDurableDeepLinkErrorDomain = @"com.firebase.durabledeeplink";
/// The exact behavior of dict[key] = value is unclear when value is nil. This function safely adds
/// the key-value pair to the dictionary, even when value is nil.
/// This function will treat empty string in the same way as nil.
NS_INLINE void FDLSafelyAddKeyValuePairToDictionary(NSString *key,
NSString *stringValue,
NSMutableDictionary *dictionary) {
if (stringValue != nil && stringValue.length > 0) {
dictionary[key] = stringValue;
} else {
[dictionary removeObjectForKey:key];
}
}
@implementation FIRDynamicLinkGoogleAnalyticsParameters {
NSMutableDictionary<NSString *, NSString *> *_dictionary;
}
static NSString *const kFDLUTMSourceKey = @"utm_source";
static NSString *const kFDLUTMMediumKey = @"utm_medium";
static NSString *const kFDLUTMCampaignKey = @"utm_campaign";
static NSString *const kFDLUTMTermKey = @"utm_term";
static NSString *const kFDLUTMContentKey = @"utm_content";
+ (instancetype)parameters {
return [[self alloc] init];
}
+ (instancetype)parametersWithSource:(NSString *)source
medium:(NSString *)medium
campaign:(NSString *)campaign {
return [[self alloc] initWithSource:source medium:medium campaign:campaign];
}
- (instancetype)init {
self = [super init];
if (self) {
_dictionary = [NSMutableDictionary dictionary];
}
return self;
}
- (instancetype)initWithSource:(NSString *)source
medium:(NSString *)medium
campaign:(NSString *)campaign {
self = [self init];
if (self) {
FDLSafelyAddKeyValuePairToDictionary(kFDLUTMSourceKey, [source copy], _dictionary);
FDLSafelyAddKeyValuePairToDictionary(kFDLUTMMediumKey, [medium copy], _dictionary);
FDLSafelyAddKeyValuePairToDictionary(kFDLUTMCampaignKey, [campaign copy], _dictionary);
}
return self;
}
- (void)setSource:(NSString *)source {
FDLSafelyAddKeyValuePairToDictionary(kFDLUTMSourceKey, [source copy], _dictionary);
}
- (NSString *)source {
return _dictionary[kFDLUTMSourceKey];
}
- (void)setMedium:(NSString *)medium {
FDLSafelyAddKeyValuePairToDictionary(kFDLUTMMediumKey, [medium copy], _dictionary);
}
- (NSString *)medium {
return _dictionary[kFDLUTMMediumKey];
}
- (void)setCampaign:(NSString *)campaign {
FDLSafelyAddKeyValuePairToDictionary(kFDLUTMCampaignKey, [campaign copy], _dictionary);
}
- (NSString *)campaign {
return _dictionary[kFDLUTMCampaignKey];
}
- (void)setTerm:(NSString *)term {
FDLSafelyAddKeyValuePairToDictionary(kFDLUTMTermKey, [term copy], _dictionary);
}
- (NSString *)term {
return _dictionary[kFDLUTMTermKey];
}
- (void)setContent:(NSString *)content {
FDLSafelyAddKeyValuePairToDictionary(kFDLUTMContentKey, [content copy], _dictionary);
}
- (NSString *)content {
return _dictionary[kFDLUTMContentKey];
}
- (NSDictionary<NSString *, NSString *> *)dictionaryRepresentation {
return [_dictionary copy];
}
@end
@implementation FIRDynamicLinkIOSParameters {
NSMutableDictionary<NSString *, NSString *> *_dictionary;
}
static NSString *const kFDLIOSBundleIdentifierKey = @"ibi";
static NSString *const kFDLIOSAppStoreIdentifierKey = @"isi";
static NSString *const kFDLIOSFallbackURLKey = @"ifl";
static NSString *const kFDLIOSCustomURLSchemeKey = @"ius";
static NSString *const kFDLIOSMinimumVersionKey = @"imv";
static NSString *const kFDLIOSIPadBundleIdentifierKey = @"ipbi";
static NSString *const kFDLIOSIPadFallbackURLKey = @"ipfl";
+ (instancetype)parametersWithBundleID:(NSString *)bundleID {
return [[self alloc] initWithBundleID:bundleID];
}
- (instancetype)initWithBundleID:(NSString *)bundleID {
self = [super init];
if (self) {
_dictionary = [NSMutableDictionary dictionary];
FDLSafelyAddKeyValuePairToDictionary(kFDLIOSBundleIdentifierKey, [bundleID copy], _dictionary);
}
return self;
}
- (NSString *)bundleID {
return _dictionary[kFDLIOSBundleIdentifierKey];
}
- (void)setAppStoreID:(NSString *)appStoreID {
FDLSafelyAddKeyValuePairToDictionary(kFDLIOSAppStoreIdentifierKey, [appStoreID copy],
_dictionary);
}
- (NSString *)appStoreID {
return _dictionary[kFDLIOSAppStoreIdentifierKey];
}
- (void)setFallbackURL:(NSURL *)fallbackURL {
FDLSafelyAddKeyValuePairToDictionary(kFDLIOSFallbackURLKey, fallbackURL.absoluteString,
_dictionary);
}
- (NSURL *)fallbackURL {
NSString *fallbackURLString = _dictionary[kFDLIOSFallbackURLKey];
return fallbackURLString != nil ? [NSURL URLWithString:fallbackURLString] : nil;
}
- (void)setCustomScheme:(NSString *)customScheme {
FDLSafelyAddKeyValuePairToDictionary(kFDLIOSCustomURLSchemeKey, [customScheme copy], _dictionary);
}
- (NSString *)customScheme {
return _dictionary[kFDLIOSCustomURLSchemeKey];
}
- (void)setMinimumAppVersion:(NSString *)minimumAppVersion {
FDLSafelyAddKeyValuePairToDictionary(kFDLIOSMinimumVersionKey, [minimumAppVersion copy],
_dictionary);
}
- (NSString *)minimumAppVersion {
return _dictionary[kFDLIOSMinimumVersionKey];
}
- (void)setIPadBundleID:(NSString *)iPadBundleID {
FDLSafelyAddKeyValuePairToDictionary(kFDLIOSIPadBundleIdentifierKey, [iPadBundleID copy],
_dictionary);
}
- (NSString *)iPadBundleID {
return _dictionary[kFDLIOSIPadBundleIdentifierKey];
}
- (void)setIPadFallbackURL:(NSURL *)iPadFallbackURL {
FDLSafelyAddKeyValuePairToDictionary(kFDLIOSIPadFallbackURLKey, iPadFallbackURL.absoluteString,
_dictionary);
}
- (NSURL *)iPadFallbackURL {
NSString *fallbackURLString = _dictionary[kFDLIOSIPadFallbackURLKey];
return fallbackURLString != nil ? [NSURL URLWithString:fallbackURLString] : nil;
}
- (NSDictionary<NSString *, NSString *> *)dictionaryRepresentation {
return [_dictionary copy];
}
@end
@implementation FIRDynamicLinkItunesConnectAnalyticsParameters {
NSMutableDictionary<NSString *, NSString *> *_dictionary;
}
static NSString *const kFDLITunesConnectAffiliateTokeyKey = @"at";
static NSString *const kFDLITunesConnectCampaignTokenKey = @"ct";
static NSString *const kFDLITunesConnectProviderTokenKey = @"pt";
+ (instancetype)parameters {
return [[self alloc] init];
}
- (instancetype)init {
self = [super init];
if (self) {
_dictionary = [NSMutableDictionary dictionary];
}
return self;
}
- (void)setAffiliateToken:(NSString *)affiliateToken {
FDLSafelyAddKeyValuePairToDictionary(kFDLITunesConnectAffiliateTokeyKey, [affiliateToken copy],
_dictionary);
}
- (NSString *)affiliateToken {
return _dictionary[kFDLITunesConnectAffiliateTokeyKey];
}
- (void)setCampaignToken:(NSString *)campaignToken {
FDLSafelyAddKeyValuePairToDictionary(kFDLITunesConnectCampaignTokenKey, [campaignToken copy],
_dictionary);
}
- (NSString *)campaignToken {
return _dictionary[kFDLITunesConnectCampaignTokenKey];
}
- (void)setProviderToken:(NSString *)providerToken {
FDLSafelyAddKeyValuePairToDictionary(kFDLITunesConnectProviderTokenKey, [providerToken copy],
_dictionary);
}
- (NSString *)providerToken {
return _dictionary[kFDLITunesConnectProviderTokenKey];
}
- (NSDictionary<NSString *, NSString *> *)dictionaryRepresentation {
return [_dictionary copy];
}
@end
@implementation FIRDynamicLinkAndroidParameters {
NSMutableDictionary<NSString *, NSString *> *_dictionary;
}
static NSString *const kFDLAndroidMinimumVersionKey = @"amv";
static NSString *const kFDLAndroidFallbackURLKey = @"afl";
static NSString *const kFDLAndroidPackageNameKey = @"apn";
+ (instancetype)parametersWithPackageName:(NSString *)packageName {
return [[self alloc] initWithPackageName:packageName];
}
- (instancetype)initWithPackageName:(NSString *)packageName {
self = [super init];
if (self) {
_dictionary = [NSMutableDictionary dictionary];
FDLSafelyAddKeyValuePairToDictionary(kFDLAndroidPackageNameKey, packageName, _dictionary);
}
return self;
}
- (NSString *)packageName {
return _dictionary[kFDLAndroidPackageNameKey];
}
- (void)setMinimumVersion:(NSInteger)minimumVersion {
_dictionary[kFDLAndroidMinimumVersionKey] = @(minimumVersion).stringValue;
}
- (NSInteger)minimumVersion {
return _dictionary[kFDLAndroidMinimumVersionKey].integerValue;
}
- (void)setFallbackURL:(NSURL *)fallbackURL {
FDLSafelyAddKeyValuePairToDictionary(kFDLAndroidFallbackURLKey, fallbackURL.absoluteString,
_dictionary);
}
- (NSURL *)fallbackURL {
NSString *fallbackURLString = _dictionary[kFDLAndroidFallbackURLKey];
return fallbackURLString != nil ? [NSURL URLWithString:fallbackURLString] : nil;
}
- (NSDictionary<NSString *, NSString *> *)dictionaryRepresentation {
return [_dictionary copy];
}
@end
@implementation FIRDynamicLinkSocialMetaTagParameters {
NSMutableDictionary<NSString *, NSString *> *_dictionary;
}
static NSString *const kFDLSocialTitleKey = @"st";
static NSString *const kFDLSocialDescriptionKey = @"sd";
static NSString *const kFDLSocialImageURLKey = @"si";
+ (instancetype)parameters {
return [[self alloc] init];
}
- (instancetype)init {
self = [super init];
if (self) {
_dictionary = [NSMutableDictionary dictionary];
}
return self;
}
- (void)setTitle:(NSString *)title {
FDLSafelyAddKeyValuePairToDictionary(kFDLSocialTitleKey, [title copy], _dictionary);
}
- (NSString *)title {
return _dictionary[kFDLSocialTitleKey];
}
- (void)setDescriptionText:(NSString *)descriptionText {
FDLSafelyAddKeyValuePairToDictionary(kFDLSocialDescriptionKey, [descriptionText copy],
_dictionary);
}
- (NSString *)descriptionText {
return _dictionary[kFDLSocialDescriptionKey];
}
- (void)setImageURL:(NSURL *)imageURL {
FDLSafelyAddKeyValuePairToDictionary(kFDLSocialImageURLKey, imageURL.absoluteString, _dictionary);
}
- (NSURL *)imageURL {
NSString *imageURLString = _dictionary[kFDLSocialImageURLKey];
return imageURLString != nil ? [NSURL URLWithString:imageURLString] : nil;
}
- (NSDictionary<NSString *, NSString *> *)dictionaryRepresentation {
return [_dictionary copy];
}
@end
@implementation FIRDynamicLinkNavigationInfoParameters {
NSMutableDictionary<NSString *, NSString *> *_dictionary;
}
static NSString *const kFDLNavigationInfoForceRedirectKey = @"efr";
+ (instancetype)parameters {
return [[self alloc] init];
}
- (instancetype)init {
self = [super init];
if (self) {
_dictionary = [NSMutableDictionary dictionary];
}
return self;
}
- (BOOL)isForcedRedirectEnabled {
return [_dictionary[kFDLNavigationInfoForceRedirectKey] boolValue];
}
- (void)setForcedRedirectEnabled:(BOOL)forcedRedirectEnabled {
FDLSafelyAddKeyValuePairToDictionary(kFDLNavigationInfoForceRedirectKey,
forcedRedirectEnabled ? @"1" : @"0", _dictionary);
}
- (NSDictionary<NSString *, NSString *> *)dictionaryRepresentation {
return [_dictionary copy];
}
@end
@implementation FIRDynamicLinkOtherPlatformParameters {
NSMutableDictionary<NSString *, NSString *> *_dictionary;
}
static NSString *const kFDLOtherPlatformParametersFallbackURLKey = @"ofl";
+ (instancetype)parameters {
return [[self alloc] init];
}
- (instancetype)init {
self = [super init];
if (self) {
_dictionary = [NSMutableDictionary dictionary];
}
return self;
}
- (NSURL *)fallbackUrl {
NSString *fallbackURLString = _dictionary[kFDLOtherPlatformParametersFallbackURLKey];
return fallbackURLString != nil ? [NSURL URLWithString:fallbackURLString] : nil;
}
- (void)setFallbackUrl:(NSURL *)fallbackUrl {
FDLSafelyAddKeyValuePairToDictionary(kFDLOtherPlatformParametersFallbackURLKey,
fallbackUrl.absoluteString, _dictionary);
}
- (NSDictionary<NSString *, NSString *> *)dictionaryRepresentation {
return [_dictionary copy];
}
@end
@implementation FIRDynamicLinkComponentsOptions
+ (instancetype)options {
return [[self alloc] init];
}
// This is implemented to silence the 'not implemented' warning.
- (instancetype)init {
return [super init];
}
@end
@implementation FIRDynamicLinkComponents
#pragma mark Deprecated Initializers.
+ (instancetype)componentsWithLink:(NSURL *)link domain:(NSString *)domain {
return [[self alloc] initWithLink:link domain:domain];
}
- (instancetype)initWithLink:(NSURL *)link domain:(NSString *)domain {
NSURL *domainURL = [NSURL URLWithString:domain];
if (domainURL.scheme) {
FDLLog(FDLLogLevelWarning, FDLLogIdentifierSetupWarnHTTPSScheme,
@"You have supplied a domain with a scheme. Please enter a domain name without the "
@"scheme.");
}
NSString *domainURIPrefix = [NSString stringWithFormat:@"https://%@", domain];
self = [super init];
if (self) {
_link = link;
_domain = domainURIPrefix;
}
return self;
}
#pragma mark Initializers.
+ (instancetype)componentsWithLink:(NSURL *)link domainURIPrefix:(NSString *)domainURIPrefix {
return [[self alloc] initWithLink:link domainURIPrefix:domainURIPrefix];
}
- (instancetype)initWithLink:(NSURL *)link domainURIPrefix:(NSString *)domainURIPrefix {
self = [super init];
if (self) {
_link = link;
/// Must be a URL that conforms to RFC 2396.
NSURL *domainURIPrefixURL = [NSURL URLWithString:domainURIPrefix];
if (!domainURIPrefixURL) {
FDLLog(FDLLogLevelError, FDLLogIdentifierSetupInvalidDomainURIPrefix,
@"Invalid domainURIPrefix. Please input a valid URL.");
return nil;
}
if (![[domainURIPrefixURL.scheme lowercaseString] isEqualToString:@"https"]) {
FDLLog(FDLLogLevelError, FDLLogIdentifierSetupInvalidDomainURIPrefixScheme,
@"Invalid domainURIPrefix scheme. Scheme needs to be https");
return nil;
}
_domain = [domainURIPrefix copy];
}
return self;
}
+ (void)shortenURL:(NSURL *)url
options:(FIRDynamicLinkComponentsOptions *)options
completion:(FIRDynamicLinkShortenerCompletion)completion {
if (![FIRDynamicLinkComponentsKeyProvider APIKey]) {
NSError *error = [NSError
errorWithDomain:kFirebaseDurableDeepLinkErrorDomain
code:0
userInfo:@{
NSLocalizedFailureReasonErrorKey : NSLocalizedString(
@"API key is missing.", @"Error reason message when API key is missing"),
}];
completion(nil, nil, error);
return;
}
NSURLRequest *request = [self shorteningRequestForLongURL:url options:options];
if (!request) {
NSError *error = [NSError errorWithDomain:kFirebaseDurableDeepLinkErrorDomain
code:0
userInfo:nil];
completion(nil, nil, error);
return;
}
[self sendHTTPRequest:request
completion:^(NSData *_Nullable data, NSError *_Nullable error) {
NSURL *shortURL;
NSArray *warnings;
if (data != nil && error == nil) {
NSError *deserializationError;
id JSONObject = [NSJSONSerialization JSONObjectWithData:data
options:0
error:&deserializationError];
if ([JSONObject isKindOfClass:[NSDictionary class]]) {
if ([JSONObject[@"shortLink"] isKindOfClass:[NSString class]]) {
shortURL = [NSURL URLWithString:JSONObject[@"shortLink"]];
} else {
if ([JSONObject[@"error"] isKindOfClass:[NSDictionary class]]) {
NSMutableDictionary *errorUserInfo = [[NSMutableDictionary alloc] init];
NSDictionary *errorDictionary = JSONObject[@"error"];
if ([errorDictionary[@"message"] isKindOfClass:[NSString class]]) {
errorUserInfo[NSLocalizedFailureReasonErrorKey] =
errorDictionary[@"message"];
}
if ([errorDictionary[@"status"] isKindOfClass:[NSString class]]) {
errorUserInfo[@"remoteStatus"] = errorDictionary[@"status"];
}
if (errorDictionary[@"code"] &&
[errorDictionary[@"code"] isKindOfClass:[NSNumber class]]) {
errorUserInfo[@"remoteErrorCode"] = errorDictionary[@"code"];
}
error = [NSError errorWithDomain:kFirebaseDurableDeepLinkErrorDomain
code:0
userInfo:errorUserInfo];
}
}
if ([JSONObject[@"warning"] isKindOfClass:[NSArray class]]) {
NSArray *warningsServer = JSONObject[@"warning"];
NSMutableArray *warningsTmp =
[NSMutableArray arrayWithCapacity:[warningsServer count]];
for (NSDictionary *warningServer in warningsServer) {
if ([warningServer[@"warningMessage"] isKindOfClass:[NSString class]]) {
[warningsTmp addObject:warningServer[@"warningMessage"]];
}
}
if ([warningsTmp count] > 0) {
warnings = [warningsTmp copy];
}
}
} else if (deserializationError) {
error = [NSError
errorWithDomain:kFirebaseDurableDeepLinkErrorDomain
code:0
userInfo:@{
NSLocalizedFailureReasonErrorKey : NSLocalizedString(
@"Unrecognized server response",
@"Error reason message when server response can't be parsed"),
NSUnderlyingErrorKey : deserializationError,
}];
}
}
if (!shortURL && !error) {
// provide generic error message if we have no additional details about failure
error = [NSError errorWithDomain:kFirebaseDurableDeepLinkErrorDomain
code:0
userInfo:nil];
}
dispatch_async(dispatch_get_main_queue(), ^{
completion(shortURL, warnings, error);
});
}];
}
- (void)shortenWithCompletion:(FIRDynamicLinkShortenerCompletion)completion {
NSURL *url = [self url];
if (!url) {
NSError *error = [NSError errorWithDomain:kFirebaseDurableDeepLinkErrorDomain
code:0
userInfo:@{
NSLocalizedFailureReasonErrorKey : NSLocalizedString(
@"Unable to produce long URL",
@"Error reason when long url can't be produced"),
}];
completion(nil, nil, error);
return;
}
return [FIRDynamicLinkComponents shortenURL:url options:_options completion:completion];
}
- (NSURL *)url {
static NSString *const kFDLURLComponentsLinkKey = @"link";
NSMutableDictionary *queryDictionary =
[NSMutableDictionary dictionaryWithObject:self.link.absoluteString
forKey:kFDLURLComponentsLinkKey];
void (^addEntriesFromDictionaryRepresentingConformerToDictionary)(id<FDLDictionaryRepresenting>) =
^(id<FDLDictionaryRepresenting> _Nullable dictionaryRepresentingConformer) {
NSDictionary *dictionary = dictionaryRepresentingConformer.dictionaryRepresentation;
if (dictionary.count > 0) {
[queryDictionary addEntriesFromDictionary:dictionary];
}
};
addEntriesFromDictionaryRepresentingConformerToDictionary(_analyticsParameters);
addEntriesFromDictionaryRepresentingConformerToDictionary(_socialMetaTagParameters);
addEntriesFromDictionaryRepresentingConformerToDictionary(_iOSParameters);
addEntriesFromDictionaryRepresentingConformerToDictionary(_iTunesConnectParameters);
addEntriesFromDictionaryRepresentingConformerToDictionary(_androidParameters);
addEntriesFromDictionaryRepresentingConformerToDictionary(_navigationInfoParameters);
addEntriesFromDictionaryRepresentingConformerToDictionary(_otherPlatformParameters);
NSString *queryString = FIRDLURLQueryStringFromDictionary(queryDictionary);
NSString *urlString = [NSString stringWithFormat:@"%@/%@", _domain, queryString];
return [NSURL URLWithString:urlString];
}
#pragma mark Helper Methods
+ (void)sendHTTPRequest:(NSURLRequest *)request
completion:(void (^)(NSData *_Nullable data, NSError *_Nullable error))completion {
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task =
[session dataTaskWithRequest:request
completionHandler:^(NSData *_Nullable data, NSURLResponse *_Nullable response,
NSError *_Nullable error) {
completion(data, error);
}];
[task resume];
}
+ (NSURLRequest *)shorteningRequestForLongURL:(NSURL *)url
options:(nullable FIRDynamicLinkComponentsOptions *)options {
if (!url) {
return nil;
}
static NSString *const kFDLURLShortenerAPIHost = @"https://firebasedynamiclinks.googleapis.com";
static NSString *const kFDLURLShortenerAPIPath = @"/v1/shortLinks";
static NSString *const kFDLURLShortenerAPIQuery = @"?key=";
NSString *apiKey = [FIRDynamicLinkComponentsKeyProvider APIKey];
NSString *postURLString =
[NSString stringWithFormat:@"%@%@%@%@", kFDLURLShortenerAPIHost, kFDLURLShortenerAPIPath,
kFDLURLShortenerAPIQuery, apiKey];
NSURL *postURL = [NSURL URLWithString:postURLString];
NSMutableDictionary *payloadDictionary =
[NSMutableDictionary dictionaryWithObject:url.absoluteString forKey:@"longDynamicLink"];
switch (options.pathLength) {
case FIRShortDynamicLinkPathLengthShort:
payloadDictionary[@"suffix"] = @{@"option" : @"SHORT"};
break;
case FIRShortDynamicLinkPathLengthUnguessable:
payloadDictionary[@"suffix"] = @{@"option" : @"UNGUESSABLE"};
break;
default:
break;
}
NSData *payload = [NSJSONSerialization dataWithJSONObject:payloadDictionary options:0 error:0];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:postURL];
request.HTTPMethod = @"POST";
request.HTTPBody = payload;
[request setValue:[NSBundle mainBundle].bundleIdentifier
forHTTPHeaderField:@"X-Ios-Bundle-Identifier"];
NSString *contentType = @"application/json";
[request setValue:contentType forHTTPHeaderField:@"Accept"];
[request setValue:contentType forHTTPHeaderField:@"Content-Type"];
return [request copy];
}
@end
#endif // TARGET_OS_IOS
@@ -1,23 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
@interface FIRDynamicLinkComponentsKeyProvider : NSObject
+ (nullable NSString *)APIKey;
@end
@@ -1,39 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <TargetConditionals.h>
#if TARGET_OS_IOS
#import "FirebaseDynamicLinks/Sources/FDLURLComponents/FIRDynamicLinkComponentsKeyProvider.h"
#import "FirebaseCore/Extension/FirebaseCoreInternal.h"
@implementation FIRDynamicLinkComponentsKeyProvider
+ (nullable NSString *)APIKey {
// If there's no default app, immediately return nil since reading from the default app will cause
// an error to be logged.
if (![FIRApp isDefaultAppConfigured]) {
return nil;
}
// FDL only supports the default app, use the options from it.
return [FIRApp defaultApp].options.APIKey;
}
@end
#endif // TARGET_OS_IOS
@@ -1,41 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "FirebaseDynamicLinks/Sources/FIRDLRetrievalProcessProtocols.h"
#import <Foundation/Foundation.h>
@class FIRDynamicLinkNetworking;
NS_ASSUME_NONNULL_BEGIN
/**
Class to encapsulate logic related to retrieving pending dynamic link.
*/
@interface FIRDLDefaultRetrievalProcessV2 : NSObject <FIRDLRetrievalProcessProtocol>
- (instancetype)initWithNetworkingService:(FIRDynamicLinkNetworking *)networkingService
URLScheme:(NSString *)URLScheme
APIKey:(NSString *)APIKey
FDLSDKVersion:(NSString *)FDLSDKVersion
delegate:(id<FIRDLRetrievalProcessDelegate>)delegate
NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE;
@end
NS_ASSUME_NONNULL_END
@@ -1,256 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <TargetConditionals.h>
#if TARGET_OS_IOS
#import "FirebaseDynamicLinks/Sources/FIRDLDefaultRetrievalProcessV2.h"
#import <UIKit/UIKit.h>
#import "FirebaseDynamicLinks/Sources/FIRDLJavaScriptExecutor.h"
#import "FirebaseDynamicLinks/Sources/FIRDLRetrievalProcessResult+Private.h"
#import "FirebaseDynamicLinks/Sources/FIRDynamicLink+Private.h"
#import "FirebaseDynamicLinks/Sources/FIRDynamicLinkNetworking.h"
#import "FirebaseDynamicLinks/Sources/Utilities/FDLUtilities.h"
// Reason for this string to ensure that only FDL links, copied to clipboard by AppPreview Page
// JavaScript code, are recognized and used in copy-unique-match process. If user copied FDL to
// clipboard by himself, that link must not be used in copy-unique-match process.
// This constant must be kept in sync with constant in the server version at
// durabledeeplink/click/ios/click_page.js
static NSString *expectedCopiedLinkStringSuffix = @"_icp=1";
NS_ASSUME_NONNULL_BEGIN
@interface FIRDLDefaultRetrievalProcessV2 () <FIRDLJavaScriptExecutorDelegate>
@end
@implementation FIRDLDefaultRetrievalProcessV2 {
FIRDynamicLinkNetworking *_networkingService;
NSString *_URLScheme;
NSString *_APIKey;
NSString *_FDLSDKVersion;
NSString *_clipboardContentAtMatchProcessStart;
FIRDLJavaScriptExecutor *_jsExecutor;
NSString *_localeFromWebView;
}
@synthesize delegate = _delegate;
#pragma mark - Initialization
- (instancetype)initWithNetworkingService:(FIRDynamicLinkNetworking *)networkingService
URLScheme:(NSString *)URLScheme
APIKey:(NSString *)APIKey
FDLSDKVersion:(NSString *)FDLSDKVersion
delegate:(id<FIRDLRetrievalProcessDelegate>)delegate {
NSParameterAssert(networkingService);
NSParameterAssert(URLScheme);
NSParameterAssert(APIKey);
if (self = [super init]) {
_networkingService = networkingService;
_URLScheme = [URLScheme copy];
_APIKey = [APIKey copy];
_FDLSDKVersion = [FDLSDKVersion copy];
_delegate = delegate;
}
return self;
}
#pragma mark - FIRDLRetrievalProcessProtocol
- (void)retrievePendingDynamicLink {
if (_localeFromWebView) {
[self retrievePendingDynamicLinkInternal];
} else {
[self fetchLocaleFromWebView];
}
}
#pragma mark - FIRDLJavaScriptExecutorDelegate
- (void)javaScriptExecutor:(FIRDLJavaScriptExecutor *)executor
completedExecutionWithResult:(NSString *)result {
_localeFromWebView = result ?: @"";
_jsExecutor = nil;
[self retrievePendingDynamicLinkInternal];
}
- (void)javaScriptExecutor:(FIRDLJavaScriptExecutor *)executor failedWithError:(NSError *)error {
_localeFromWebView = @"";
_jsExecutor = nil;
[self retrievePendingDynamicLinkInternal];
}
#pragma mark - Internal methods
- (void)retrievePendingDynamicLinkInternal {
CGRect mainScreenBounds = [UIScreen mainScreen].bounds;
NSInteger resolutionWidth = mainScreenBounds.size.width;
NSInteger resolutionHeight = mainScreenBounds.size.height;
if ([[[UIDevice currentDevice] model] isEqualToString:@"iPad"] &&
UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
// iPhone App running in compatibility mode on iPad
// screen resolution reported by UIDevice/UIScreen will be wrong
resolutionWidth = 0;
resolutionHeight = 0;
}
NSURL *uniqueMatchLinkToCheck = [self uniqueMatchLinkToCheck];
__weak __typeof__(self) weakSelf = self;
FIRPostInstallAttributionCompletionHandler completionHandler =
^(NSDictionary *_Nullable dynamicLinkParameters, NSString *_Nullable matchMessage,
NSError *_Nullable error) {
__typeof__(self) strongSelf = weakSelf;
if (!strongSelf) {
return;
}
FIRDynamicLink *dynamicLink;
if (dynamicLinkParameters.count) {
dynamicLink = [[FIRDynamicLink alloc] initWithParametersDictionary:dynamicLinkParameters];
}
FIRDLRetrievalProcessResult *result =
[[FIRDLRetrievalProcessResult alloc] initWithDynamicLink:dynamicLink
error:error
message:matchMessage
matchSource:nil];
[strongSelf handleRetrievalProcessWithResult:result];
if (!error) {
[strongSelf clearUsedUniqueMatchLinkToCheckFromClipboard];
}
};
// Disable deprecated warning for internal methods.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
// If there is not a unique match, we will send an additional request for device heuristics based
// matching.
[_networkingService
retrievePendingDynamicLinkWithIOSVersion:[UIDevice currentDevice].systemVersion
resolutionHeight:resolutionHeight
resolutionWidth:resolutionWidth
locale:FIRDLDeviceLocale()
localeRaw:FIRDLDeviceLocaleRaw()
localeFromWebView:_localeFromWebView
timezone:FIRDLDeviceTimezone()
modelName:FIRDLDeviceModelName()
FDLSDKVersion:_FDLSDKVersion
appInstallationDate:FIRDLAppInstallationDate()
uniqueMatchVisualStyle:FIRDynamicLinkNetworkingUniqueMatchVisualStyleUnknown
retrievalProcessType:
FIRDynamicLinkNetworkingRetrievalProcessTypeImplicitDefault
uniqueMatchLinkToCheck:uniqueMatchLinkToCheck
handler:completionHandler];
#pragma clang pop
}
- (void)handleRetrievalProcessWithResult:(FIRDLRetrievalProcessResult *)result {
if (!result) {
// if we did not get any results, construct one
NSString *message = NSLocalizedString(@"Pending dynamic link not found",
@"Message when dynamic link was not found");
result = [[FIRDLRetrievalProcessResult alloc] initWithDynamicLink:nil
error:nil
message:message
matchSource:nil];
}
[self.delegate retrievalProcess:self completedWithResult:result];
}
- (nullable NSURL *)uniqueMatchLinkToCheck {
_clipboardContentAtMatchProcessStart = nil;
NSString *pasteboardContents = [self retrievePasteboardContents];
if (!pasteboardContents) {
return nil;
}
NSInteger linkStringMinimumLength =
expectedCopiedLinkStringSuffix.length + /* ? or & */ 1 + /* http:// */ 7;
if ((pasteboardContents.length >= linkStringMinimumLength) &&
[pasteboardContents hasSuffix:expectedCopiedLinkStringSuffix] &&
[NSURL URLWithString:pasteboardContents]) {
// remove custom suffix and preceding '&' or '?' character from string
NSString *linkStringWithoutSuffix = [pasteboardContents
substringToIndex:pasteboardContents.length - expectedCopiedLinkStringSuffix.length - 1];
NSURL *URL = [NSURL URLWithString:linkStringWithoutSuffix];
if (URL) {
// check is link matches short link format
if (FIRDLMatchesShortLinkFormat(URL)) {
_clipboardContentAtMatchProcessStart = pasteboardContents;
return URL;
}
// check is link matches long link format
if (FIRDLCanParseUniversalLinkURL(URL)) {
_clipboardContentAtMatchProcessStart = pasteboardContents;
return URL;
}
}
}
return nil;
}
- (nullable NSString *)retrievePasteboardContents {
if (![self isPasteboardRetrievalEnabled]) {
// Pasteboard check for dynamic link is disabled by user.
return nil;
}
if ([[UIPasteboard generalPasteboard] hasURLs]) {
return [UIPasteboard generalPasteboard].string;
} else {
return nil;
}
}
/**
Property to enable or disable dynamic link retrieval from Pasteboard.
This property is added because of iOS 14 feature where pop up is displayed while accessing
Pasteboard. So if developers don't want their users to see the Pasteboard popup, they can set
"FirebaseDeepLinkPasteboardRetrievalEnabled" to false in their plist.
*/
- (BOOL)isPasteboardRetrievalEnabled {
id retrievalEnabledValue =
[[NSBundle mainBundle] infoDictionary][@"FirebaseDeepLinkPasteboardRetrievalEnabled"];
if ([retrievalEnabledValue respondsToSelector:@selector(boolValue)]) {
return [retrievalEnabledValue boolValue];
}
return YES;
}
- (void)clearUsedUniqueMatchLinkToCheckFromClipboard {
// See discussion in b/65304652
// We will clear clipboard after we used the unique match link from the clipboard
if (_clipboardContentAtMatchProcessStart.length > 0 &&
[_clipboardContentAtMatchProcessStart isEqualToString:_clipboardContentAtMatchProcessStart]) {
[UIPasteboard generalPasteboard].string = @"";
}
}
- (void)fetchLocaleFromWebView {
if (_jsExecutor) {
return;
}
NSString *jsString = @"window.generateDeviceHeuristics=()=>navigator.language||''";
_jsExecutor = [[FIRDLJavaScriptExecutor alloc] initWithDelegate:self script:jsString];
}
@end
NS_ASSUME_NONNULL_END
#endif // TARGET_OS_IOS
@@ -1,38 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
@class FIRDLJavaScriptExecutor;
NS_ASSUME_NONNULL_BEGIN
@protocol FIRDLJavaScriptExecutorDelegate <NSObject>
- (void)javaScriptExecutor:(FIRDLJavaScriptExecutor *)executor
completedExecutionWithResult:(NSString *)result;
- (void)javaScriptExecutor:(FIRDLJavaScriptExecutor *)executor failedWithError:(NSError *)error;
@end
@interface FIRDLJavaScriptExecutor : NSObject
- (instancetype)initWithDelegate:(id<FIRDLJavaScriptExecutorDelegate>)delegate
script:(NSString *)script;
@end
NS_ASSUME_NONNULL_END
@@ -1,174 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <TargetConditionals.h>
#if TARGET_OS_IOS
#import <sys/sysctl.h>
#import <WebKit/WebKit.h>
#import "FirebaseDynamicLinks/Sources/FIRDLJavaScriptExecutor.h"
NS_ASSUME_NONNULL_BEGIN
static NSString *const kJSMethodName = @"generateDeviceHeuristics";
/** Creates and returns the FDL JS method name. */
NSString *FIRDLTypeofDeviceHeuristicsJSMethodNameString(void) {
static NSString *methodName;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
methodName = [NSString stringWithFormat:@"typeof(%@)", kJSMethodName];
});
return methodName;
}
/** Creates and returns the FDL JS method definition. */
NSString *GINDeviceHeuristicsJSMethodString(void) {
static NSString *methodString;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
methodString = [NSString stringWithFormat:@"%@()", kJSMethodName];
});
return methodString;
}
@interface FIRDLJavaScriptExecutor () <WKNavigationDelegate>
@end
@implementation FIRDLJavaScriptExecutor {
__weak id<FIRDLJavaScriptExecutorDelegate> _delegate;
NSString *_script;
// Web view with which to run JavaScript.
WKWebView *_wkWebView;
}
- (instancetype)initWithDelegate:(id<FIRDLJavaScriptExecutorDelegate>)delegate
script:(NSString *)script {
NSParameterAssert(delegate);
NSParameterAssert(script);
NSParameterAssert(script.length > 0);
NSAssert([NSThread isMainThread], @"%@ must be used in main thread",
NSStringFromClass([self class]));
if (self = [super init]) {
_delegate = delegate;
_script = [script copy];
[self start];
}
return self;
}
#pragma mark - Internal methods
- (void)start {
// Initializing a `WKWebView` causes a memory allocation error when the process
// is running under Rosetta translation on Apple Silicon.
// The issue only occurs on the simulator in apps targeting below iOS 14. (Issue #7618)
#if TARGET_OS_SIMULATOR
BOOL systemVersionAtLeastiOS14 = [NSProcessInfo.processInfo
isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){14, 0, 0}];
// Perform an early exit if the process is running under Rosetta translation and targeting
// under iOS 14.
if (processIsTranslated() && !systemVersionAtLeastiOS14) {
[self handleExecutionError:nil];
return;
}
#endif
NSString *htmlContent =
[NSString stringWithFormat:@"<html><head><script>%@</script></head></html>", _script];
_wkWebView = [[WKWebView alloc] init];
_wkWebView.navigationDelegate = self;
[_wkWebView loadHTMLString:htmlContent baseURL:nil];
}
- (void)handleExecutionResult:(NSString *)result {
[self cleanup];
[_delegate javaScriptExecutor:self completedExecutionWithResult:result];
}
- (void)handleExecutionError:(nullable NSError *)error {
[self cleanup];
if (!error) {
error = [NSError errorWithDomain:@"com.firebase.durabledeeplink" code:-1 userInfo:nil];
}
[_delegate javaScriptExecutor:self failedWithError:error];
}
- (void)cleanup {
_wkWebView.navigationDelegate = nil;
_wkWebView = nil;
}
#pragma mark - WKNavigationDelegate
- (void)webView:(WKWebView *)webView
didFinishNavigation:(null_unspecified WKNavigation *)navigation {
__weak __typeof__(self) weakSelf = self;
// Make sure that the javascript was loaded successfully before calling the method.
[webView evaluateJavaScript:FIRDLTypeofDeviceHeuristicsJSMethodNameString()
completionHandler:^(id _Nullable typeofResult, NSError *_Nullable typeError) {
if (typeError) {
[weakSelf handleExecutionError:typeError];
return;
}
if ([typeofResult isEqual:@"function"]) {
[webView
evaluateJavaScript:GINDeviceHeuristicsJSMethodString()
completionHandler:^(id _Nullable result, NSError *_Nullable functionError) {
__typeof__(self) strongSelf = weakSelf;
if ([result isKindOfClass:[NSString class]]) {
[strongSelf handleExecutionResult:result];
} else {
[strongSelf handleExecutionError:nil];
}
}];
} else {
[weakSelf handleExecutionError:nil];
}
}];
}
- (void)webView:(WKWebView *)webView
didFailNavigation:(null_unspecified WKNavigation *)navigation
withError:(NSError *)error {
[self handleExecutionError:error];
}
// Determine whether a process is running under Rosetta translation.
// Returns 0 for a native process, 1 for a translated process,
// and -1 when an error occurs.
// From:
// https://developer.apple.com/documentation/apple-silicon/about-the-rosetta-translation-environment
#if TARGET_OS_SIMULATOR
static int processIsTranslated(void) {
int ret = 0;
size_t size = sizeof(ret);
if (sysctlbyname("sysctl.proc_translated", &ret, &size, NULL, 0) == -1) {
if (errno == ENOENT) return 0;
return -1;
}
return ret;
}
#endif
@end
NS_ASSUME_NONNULL_END
#endif // TARGET_OS_IOS
@@ -1,39 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
#import "FirebaseDynamicLinks/Sources/FIRDLRetrievalProcessProtocols.h"
@class FIRDynamicLinkNetworking;
NS_ASSUME_NONNULL_BEGIN
@interface FIRDLRetrievalProcessFactory : NSObject
- (instancetype)initWithNetworkingService:(FIRDynamicLinkNetworking *)networkingService
URLScheme:(NSString *)URLScheme
APIKey:(NSString *)APIKey
FDLSDKVersion:(NSString *)FDLSDKVersion
delegate:(id<FIRDLRetrievalProcessDelegate>)delegate
NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE;
- (id<FIRDLRetrievalProcessProtocol>)automaticRetrievalProcess;
@end
NS_ASSUME_NONNULL_END
@@ -1,61 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <TargetConditionals.h>
#if TARGET_OS_IOS
#import "FirebaseDynamicLinks/Sources/FIRDLRetrievalProcessFactory.h"
#import "FirebaseDynamicLinks/Sources/FIRDLDefaultRetrievalProcessV2.h"
NS_ASSUME_NONNULL_BEGIN
@implementation FIRDLRetrievalProcessFactory {
FIRDynamicLinkNetworking *_networkingService;
NSString *_URLScheme;
NSString *_APIKey;
NSString *_FDLSDKVersion;
id<FIRDLRetrievalProcessDelegate> _delegate;
}
- (instancetype)initWithNetworkingService:(FIRDynamicLinkNetworking *)networkingService
URLScheme:(NSString *)URLScheme
APIKey:(NSString *)APIKey
FDLSDKVersion:(NSString *)FDLSDKVersion
delegate:(id<FIRDLRetrievalProcessDelegate>)delegate {
if (self = [super init]) {
_networkingService = networkingService;
_URLScheme = URLScheme;
_APIKey = APIKey;
_FDLSDKVersion = FDLSDKVersion;
_delegate = delegate;
}
return self;
}
- (id<FIRDLRetrievalProcessProtocol>)automaticRetrievalProcess {
return [[FIRDLDefaultRetrievalProcessV2 alloc] initWithNetworkingService:_networkingService
URLScheme:_URLScheme
APIKey:_APIKey
FDLSDKVersion:_FDLSDKVersion
delegate:_delegate];
}
@end
NS_ASSUME_NONNULL_END
#endif // TARGET_OS_IOS
@@ -1,39 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@protocol FIRDLRetrievalProcessProtocol;
@class FIRDLRetrievalProcessResult;
@protocol FIRDLRetrievalProcessDelegate <NSObject>
- (void)retrievalProcess:(id<FIRDLRetrievalProcessProtocol>)retrievalProcess
completedWithResult:(FIRDLRetrievalProcessResult *)result;
@end
@protocol FIRDLRetrievalProcessProtocol <NSObject>
@property(weak, nonatomic, readonly) id<FIRDLRetrievalProcessDelegate> delegate;
- (void)retrievePendingDynamicLink;
@end
NS_ASSUME_NONNULL_END
@@ -1,30 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "FirebaseDynamicLinks/Sources/FIRDLRetrievalProcessResult.h"
NS_ASSUME_NONNULL_BEGIN
@interface FIRDLRetrievalProcessResult ()
- (instancetype)initWithDynamicLink:(nullable FIRDynamicLink *)dynamicLink
error:(nullable NSError *)error
message:(nullable NSString *)message
matchSource:(nullable NSString *)matchSource NS_DESIGNATED_INITIALIZER;
@end
NS_ASSUME_NONNULL_END
@@ -1,38 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
@class FIRDynamicLink;
NS_ASSUME_NONNULL_BEGIN
@interface FIRDLRetrievalProcessResult : NSObject
- (instancetype)init NS_UNAVAILABLE;
// Method to present retrieval result as custom URL scheme URL.
// Produced URL will be passed to an application via [UIApplicationDelegate openURL:] method.
- (NSURL *)URLWithCustomURLScheme:(NSString *)customURLScheme;
@property(nonatomic, nullable, readonly) FIRDynamicLink *dynamicLink;
@property(nonatomic, nullable, readonly) NSError *error;
@property(nonatomic, nullable, copy, readonly) NSString *message;
@property(nonatomic, nullable, copy, readonly) NSString *matchSource;
@end
NS_ASSUME_NONNULL_END
@@ -1,65 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <TargetConditionals.h>
#if TARGET_OS_IOS
#import "FirebaseDynamicLinks/Sources/FIRDLRetrievalProcessResult.h"
#import "FirebaseDynamicLinks/Sources/FIRDLRetrievalProcessResult+Private.h"
#import "FirebaseDynamicLinks/Sources/FIRDynamicLink+Private.h"
#import "FirebaseDynamicLinks/Sources/Utilities/FDLUtilities.h"
NS_ASSUME_NONNULL_BEGIN
@implementation FIRDLRetrievalProcessResult
- (instancetype)initWithDynamicLink:(nullable FIRDynamicLink *)dynamicLink
error:(nullable NSError *)error
message:(nullable NSString *)message
matchSource:(nullable NSString *)matchSource {
if (self = [super init]) {
_dynamicLink = dynamicLink;
_error = error;
_message = [message copy];
_matchSource = [matchSource copy];
}
return self;
}
- (NSURL *)URLWithCustomURLScheme:(NSString *)customURLScheme {
NSURL *URL;
if (_dynamicLink) {
NSString *queryString = FIRDLURLQueryStringFromDictionary(_dynamicLink.parametersDictionary);
NSMutableString *URLString = [[NSMutableString alloc] init];
[URLString appendString:customURLScheme];
[URLString appendString:@"://google/link/"];
[URLString appendString:queryString];
URL = [NSURL URLWithString:URLString];
} else {
NSMutableString *URLString = [[NSMutableString alloc] init];
[URLString appendString:customURLScheme];
[URLString appendString:@"://google/link/?dismiss=1&is_weak_match=1"];
URL = [NSURL URLWithString:URLString];
}
return URL;
}
@end
NS_ASSUME_NONNULL_END
#endif // TARGET_OS_IOS
@@ -1,45 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
#import "Interop/Analytics/Public/FIRAnalyticsInterop.h"
/**
* @enum FIRDLLogEvent
* @abstract Types of events that may be logged to Scion.
*/
typedef NS_ENUM(NSInteger, FIRDLLogEvent) {
/** Log event where the app is first opened with a DL. */
FIRDLLogEventFirstOpen,
/** Log event where the app is subsequently opened with a DL. */
FIRDLLogEventAppOpen,
};
/**
* @fn FIRDLLogEventToScion
* @abstract Logs a given event to Scion
* @param event The event type that occurred.
* @param source The utm_source URL parameter value.
* @param medium The utm_medium URL parameter value.
* @param campaign The utm_campaign URL parameter value.
* @param analytics The class to be used as the receiver of the logging method.
*/
void FIRDLLogEventToScion(FIRDLLogEvent event,
NSString* _Nullable source,
NSString* _Nullable medium,
NSString* _Nullable campaign,
id<FIRAnalyticsInterop> _Nullable analytics);
@@ -1,59 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <TargetConditionals.h>
#if TARGET_OS_IOS
#import "FirebaseDynamicLinks/Sources/FIRDLScionLogging.h"
#import "Interop/Analytics/Public/FIRInteropParameterNames.h"
static NSString *const kFIRDLLogEventFirstOpenCampaign = @"dynamic_link_first_open";
static NSString *const kFIRDLLogEventAppOpenCampaign = @"dynamic_link_app_open";
void FIRDLLogEventToScion(FIRDLLogEvent event,
NSString *_Nullable source,
NSString *_Nullable medium,
NSString *_Nullable campaign,
id<FIRAnalyticsInterop> _Nullable analytics) {
NSMutableDictionary *parameters = [NSMutableDictionary dictionary];
if (source) {
parameters[kFIRIParameterSource] = source;
}
if (medium) {
parameters[kFIRIParameterMedium] = medium;
}
if (campaign) {
parameters[kFIRIParameterCampaign] = campaign;
}
NSString *name;
switch (event) {
case FIRDLLogEventFirstOpen:
name = kFIRDLLogEventFirstOpenCampaign;
break;
case FIRDLLogEventAppOpen:
name = kFIRDLLogEventAppOpenCampaign;
break;
}
if (name) {
[analytics logEventWithOrigin:@"fdl" name:name parameters:parameters];
}
}
#endif // TARGET_OS_IOS
@@ -1,52 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "FirebaseDynamicLinks/Sources/Public/FirebaseDynamicLinks/FIRDynamicLink.h"
NS_ASSUME_NONNULL_BEGIN
@interface FIRDynamicLink ()
typedef NS_ENUM(NSUInteger, FIRDynamicLinkMatchConfidence) {
FIRDynamicLinkMatchConfidenceWeak,
FIRDynamicLinkMatchConfidenceStrong
} NS_SWIFT_NAME(DynamicLinkMatchConfidence) DEPRECATED_MSG_ATTRIBUTE("Use FIRDLMatchType instead.");
@property(nonatomic, assign, readonly)
FIRDynamicLinkMatchConfidence matchConfidence DEPRECATED_MSG_ATTRIBUTE(
"Use FIRDynamicLink.matchType (DynamicLink.DLMatchType) instead.");
@property(nonatomic, copy, nullable) NSURL *url;
@property(nonatomic, copy, readwrite, nullable) NSString *minimumAppVersion;
// The invite ID retrieved from the dynamic link.
@property(nonatomic, copy, nullable) NSString *inviteId;
// Whether the received invite is matched via ipv4 or ipv6 endpoint.
@property(nonatomic, copy, nullable) NSString *weakMatchEndpoint;
@property(nonatomic, copy, nullable) NSString *matchMessage;
@property(nonatomic, copy, readonly) NSDictionary<NSString *, id> *parametersDictionary;
@property(nonatomic, assign, readwrite) FIRDLMatchType matchType;
- (instancetype)initWithParametersDictionary:(NSDictionary<NSString *, id> *)parametersDictionary;
@end
NS_ASSUME_NONNULL_END
@@ -1,153 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <TargetConditionals.h>
#if TARGET_OS_IOS
#import "FirebaseDynamicLinks/Sources/FIRDynamicLink+Private.h"
#import "FirebaseDynamicLinks/Sources/Utilities/FDLUtilities.h"
@implementation FIRDynamicLink
NSString *const FDLUTMParamPrefix = @"utm_";
- (NSString *)description {
return [NSString stringWithFormat:@"<%@: %p, url [%@], match type: %@, minimumAppVersion: %@, "
"match message: %@>",
NSStringFromClass([self class]), self, self.url,
[[self class] stringWithMatchType:_matchType],
self.minimumAppVersion ?: @"N/A", self.matchMessage];
}
- (instancetype)initWithParametersDictionary:(NSDictionary<NSString *, id> *)parameters {
NSParameterAssert(parameters.count > 0);
if (self = [super init]) {
_parametersDictionary = [parameters copy];
_utmParametersDictionary = [[self class] extractUTMParams:parameters];
NSString *urlString = parameters[kFIRDLParameterDeepLinkIdentifier];
_url = [NSURL URLWithString:urlString];
_inviteId = parameters[kFIRDLParameterInviteId];
_weakMatchEndpoint = parameters[kFIRDLParameterWeakMatchEndpoint];
_minimumAppVersion = parameters[kFIRDLParameterMinimumAppVersion];
if (parameters[kFIRDLParameterMatchType]) {
[self setMatchType:[[self class] matchTypeWithString:parameters[kFIRDLParameterMatchType]]];
} else if (_url || _inviteId) {
// If matchType not present assume unique match for compatibility with server side behavior
// on iOS 8.
[self setMatchType:FIRDLMatchTypeUnique];
}
_matchMessage = parameters[kFIRDLParameterMatchMessage];
}
return self;
}
#pragma mark - Properties
- (void)setUrl:(NSURL *)url {
_url = [url copy];
[self setParametersDictionaryValue:[_url absoluteString]
forKey:kFIRDLParameterDeepLinkIdentifier];
}
- (void)setMinimumAppVersion:(NSString *)minimumAppVersion {
_minimumAppVersion = [minimumAppVersion copy];
[self setParametersDictionaryValue:_minimumAppVersion forKey:kFIRDLParameterMinimumAppVersion];
}
- (void)setInviteId:(NSString *)inviteId {
_inviteId = [inviteId copy];
[self setParametersDictionaryValue:_inviteId forKey:kFIRDLParameterInviteId];
}
- (void)setWeakMatchEndpoint:(NSString *)weakMatchEndpoint {
_weakMatchEndpoint = [weakMatchEndpoint copy];
[self setParametersDictionaryValue:_weakMatchEndpoint forKey:kFIRDLParameterWeakMatchEndpoint];
}
- (void)setMatchType:(FIRDLMatchType)matchType {
_matchType = matchType;
[self setParametersDictionaryValue:[[self class] stringWithMatchType:_matchType]
forKey:kFIRDLParameterMatchType];
}
- (void)setMatchMessage:(NSString *)matchMessage {
_matchMessage = [matchMessage copy];
[self setParametersDictionaryValue:_matchMessage forKey:kFIRDLParameterMatchMessage];
}
- (void)setParametersDictionaryValue:(id)value forKey:(NSString *)key {
NSMutableDictionary<NSString *, id> *parametersDictionary =
[self.parametersDictionary mutableCopy];
if (value == nil) {
[parametersDictionary removeObjectForKey:key];
} else {
parametersDictionary[key] = value;
}
_parametersDictionary = [parametersDictionary copy];
}
- (FIRDynamicLinkMatchConfidence)matchConfidence {
return (_matchType == FIRDLMatchTypeUnique) ? FIRDynamicLinkMatchConfidenceStrong
: FIRDynamicLinkMatchConfidenceWeak;
}
+ (NSString *)stringWithMatchType:(FIRDLMatchType)matchType {
switch (matchType) {
case FIRDLMatchTypeNone:
return @"none";
case FIRDLMatchTypeWeak:
return @"weak";
case FIRDLMatchTypeDefault:
return @"default";
case FIRDLMatchTypeUnique:
return @"unique";
}
}
+ (FIRDLMatchType)matchTypeWithString:(NSString *)string {
static NSDictionary *matchMap;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
matchMap = @{
@"none" : @(FIRDLMatchTypeNone),
@"weak" : @(FIRDLMatchTypeWeak),
@"default" : @(FIRDLMatchTypeDefault),
@"unique" : @(FIRDLMatchTypeUnique),
};
});
return [matchMap[string] integerValue] ?: FIRDLMatchTypeNone;
}
+ (NSDictionary<NSString *, id> *)extractUTMParams:(NSDictionary<NSString *, id> *)parameters {
NSMutableDictionary<NSString *, id> *utmParamsDictionary = [[NSMutableDictionary alloc] init];
for (NSString *key in parameters) {
if ([key hasPrefix:FDLUTMParamPrefix]) {
[utmParamsDictionary setObject:[parameters valueForKey:key] forKey:key];
}
}
return [[NSDictionary alloc] initWithDictionary:utmParamsDictionary];
}
@end
#endif // TARGET_OS_IOS
@@ -1,49 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "FirebaseDynamicLinks/Sources/FIRDynamicLinkNetworking.h"
NS_ASSUME_NONNULL_BEGIN
/** The key for the DL URL. */
FOUNDATION_EXPORT NSString *const kFDLResolvedLinkDeepLinkURLKey;
/** The key for the minimum iOS app version. */
FOUNDATION_EXPORT NSString *const kFDLResolvedLinkMinAppVersionKey;
// Private interface for testing.
@interface FIRDynamicLinkNetworking ()
/**
* @method executeOnePlatformRequest:forURL:eventString:completionHandler:
* @abstract Creates and sends a OnePlatform HTTP request. Also adds the necessary header.
* @param requestBody The body of the request. Values may be added to this.
* @param requestURLString The URL to which to send the request.
* @param handler A block to be executed upon completion. Guaranteed to be called, but not
* always on the main thread.
*/
- (void)executeOnePlatformRequest:(NSDictionary *)requestBody
forURL:(NSString *)requestURLString
completionHandler:(FIRNetworkRequestCompletionHandler)handler;
@end
/** Encodes the API key in a query parameter string. */
NSString *_Nullable FIRDynamicLinkAPIKeyParameter(NSString *apiKey);
/** Creates and returns an NSData object from an NSDictionary along with any error. */
NSData *_Nullable FIRDataWithDictionary(NSDictionary *dictionary, NSError **_Nullable error);
NS_ASSUME_NONNULL_END
@@ -1,133 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
#import "FirebaseDynamicLinks/Sources/Public/FirebaseDynamicLinks/FIRDynamicLinksCommon.h"
NS_ASSUME_NONNULL_BEGIN
/** A definition for a block used by methods that are asynchronous and may produce errors. */
typedef void (^FIRDynamicLinkNetworkingErrorHandler)(NSError *_Nullable error);
/** A definition for a block used to return a pending Dynamic Link. */
typedef void (^FIRPostInstallAttributionCompletionHandler)(
NSDictionary *_Nullable dynamicLinkParameters,
NSString *_Nullable matchMessage,
NSError *_Nullable error);
/** A definition for a block used to return data and errors after an asynchronous task. */
typedef void (^FIRNetworkRequestCompletionHandler)(NSData *_Nullable data,
NSURLResponse *_Nullable response,
NSError *_Nullable error);
// these enums must be in sync with google/firebase/dynamiclinks/v1/dynamic_links.proto
typedef NS_ENUM(NSInteger, FIRDynamicLinkNetworkingUniqueMatchVisualStyle) {
// Unknown style.
FIRDynamicLinkNetworkingUniqueMatchVisualStyleUnknown = 0,
// Default style.
FIRDynamicLinkNetworkingUniqueMatchVisualStyleDefault = 1,
// Custom style.
FIRDynamicLinkNetworkingUniqueMatchVisualStyleCustom = 2,
};
typedef NS_ENUM(NSInteger, FIRDynamicLinkNetworkingRetrievalProcessType) {
// Unknown method.
FIRDynamicLinkNetworkingRetrievalProcessTypeUnknown = 0,
// iSDK performs a server lookup using default match in the background
// when app is first-opened; no API called by developer.
FIRDynamicLinkNetworkingRetrievalProcessTypeImplicitDefault = 1,
// iSDK performs a server lookup by device heuristics upon a dev API call.
FIRDynamicLinkNetworkingRetrievalProcessTypeExplicitDefault = 2,
// iSDK performs a unique match only if default match is found upon a dev
// API call.
FIRDynamicLinkNetworkingRetrievalProcessTypeOptionalUnique = 3,
};
/**
* @fn FIRMakeHTTPRequest
* @abstract A basic and simple network request method.
* @param request The NSURLRequest with which to perform the network request.
* @param completion The handler executed after the request has completed.
*/
void FIRMakeHTTPRequest(NSURLRequest *request, FIRNetworkRequestCompletionHandler completion);
/** The base of the FDL API URL */
FOUNDATION_EXPORT NSString *const kApiaryRestBaseUrl;
/**
* @class FIRDynamicLinkNetworking
* @abstract The class used to handle all network communications for the service.
*/
@interface FIRDynamicLinkNetworking : NSObject
/**
* @method initWithAPIKey:URLScheme:
* @param URLScheme Custom URL scheme of the app.
* @param APIKey API Key value.
*/
- (instancetype)initWithAPIKey:(NSString *)APIKey
URLScheme:(NSString *)URLScheme NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE;
/**
* @method resolveShortLink:URLScheme:APIKey:completion:
* @abstract Retrieves the details of the durable link that the shortened URL represents
* @param url A Short Dynamic Link.
* @param completion Block to be run upon completion.
*/
- (void)resolveShortLink:(NSURL *)url
FDLSDKVersion:(NSString *)FDLSDKVersion
completion:(FIRDynamicLinkResolverHandler)completion;
/**
* @method
* retrievePendingDynamicLinkWithIOSVersion:resolutionHeight:resolutionWidth:locale:localeRaw:timezone:modelName:FDLSDKVersion:appInstallationDate:uniqueMatchVisualStyle:retrievalProcessType:handler:
* @abstract Retrieves a pending link from the server using the supplied device info and returns it
* by executing the completion handler.
*/
- (void)retrievePendingDynamicLinkWithIOSVersion:(NSString *)IOSVersion
resolutionHeight:(NSInteger)resolutionHeight
resolutionWidth:(NSInteger)resolutionWidth
locale:(NSString *)locale
localeRaw:(NSString *)localeRaw
localeFromWebView:(NSString *)localeFromWebView
timezone:(NSString *)timezone
modelName:(NSString *)modelName
FDLSDKVersion:(NSString *)FDLSDKVersion
appInstallationDate:(NSDate *_Nullable)appInstallationDate
uniqueMatchVisualStyle:
(FIRDynamicLinkNetworkingUniqueMatchVisualStyle)uniqueMatchVisualStyle
retrievalProcessType:
(FIRDynamicLinkNetworkingRetrievalProcessType)retrievalProcessType
uniqueMatchLinkToCheck:(NSURL *)uniqueMatchLinkToCheck
handler:
(FIRPostInstallAttributionCompletionHandler)handler;
/**
* @method convertInvitation:handler:
* @abstract Marks an invitation as converted. You should call this method in your application after
* the user performs an action that represents a successful conversion.
* @param invitationID The invitation ID of the link.
* @param handler A block that is called upon completion. If successful, the error parameter will be
* nil. This is always executed on the main thread.
*/
- (void)convertInvitation:(NSString *)invitationID
handler:(nullable FIRDynamicLinkNetworkingErrorHandler)handler;
@end
NS_ASSUME_NONNULL_END
@@ -1,369 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <TargetConditionals.h>
#if TARGET_OS_IOS
#import "FirebaseDynamicLinks/Sources/FIRDynamicLinkNetworking+Private.h"
#import "FirebaseDynamicLinks/Sources/GINInvocation/GINArgument.h"
#import "FirebaseDynamicLinks/Sources/GINInvocation/GINInvocation.h"
#import "FirebaseDynamicLinks/Sources/Utilities/FDLDeviceHeuristicsHelper.h"
#import "FirebaseDynamicLinks/Sources/Utilities/FDLUtilities.h"
NS_ASSUME_NONNULL_BEGIN
NSString *const kApiaryRestBaseUrl = @"https://appinvite-pa.googleapis.com/v1";
static NSString *const kiOSReopenRestBaseUrl = @"https://firebasedynamiclinks.googleapis.com/v1";
// Endpoint for default retrieval process V2. (Endpoint version is V1)
static NSString *const kIosPostInstallAttributionRestBaseUrl =
@"https://firebasedynamiclinks.googleapis.com/v1";
static NSString *const kReasonString = @"reason";
static NSString *const kiOSInviteReason = @"ios_invite";
NSString *const kFDLResolvedLinkDeepLinkURLKey = @"deepLink";
NSString *const kFDLResolvedLinkMinAppVersionKey = @"iosMinAppVersion";
static NSString *const kFDLAnalyticsDataSourceKey = @"utmSource";
static NSString *const kFDLAnalyticsDataMediumKey = @"utmMedium";
static NSString *const kFDLAnalyticsDataCampaignKey = @"utmCampaign";
static NSString *const kFDLAnalyticsDataTermKey = @"utmTerm";
static NSString *const kFDLAnalyticsDataContentKey = @"utmContent";
static NSString *const kHeaderIosBundleIdentifier = @"X-Ios-Bundle-Identifier";
static NSString *const kGenericErrorDomain = @"com.firebase.dynamicLinks";
typedef NSDictionary *_Nullable (^FIRDLNetworkingParserBlock)(
NSString *requestURLString,
NSData *data,
NSString *_Nullable *_Nonnull matchMessagePtr,
NSError *_Nullable *_Nullable errorPtr);
NSString *FIRURLParameterString(NSString *key, NSString *value) {
if (key.length > 0) {
return [NSString stringWithFormat:@"?%@=%@", key, value];
}
return @"";
}
NSString *_Nullable FIRDynamicLinkAPIKeyParameter(NSString *apiKey) {
return apiKey ? FIRURLParameterString(@"key", apiKey) : nil;
}
void FIRMakeHTTPRequest(NSURLRequest *request, FIRNetworkRequestCompletionHandler completion) {
NSURLSessionConfiguration *sessionConfig =
[NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig];
NSURLSessionDataTask *dataTask =
[session dataTaskWithRequest:request
completionHandler:^(NSData *_Nullable data, NSURLResponse *_Nullable response,
NSError *_Nullable error) {
completion(data, response, error);
}];
[dataTask resume];
}
NSData *_Nullable FIRDataWithDictionary(NSDictionary *dictionary, NSError **_Nullable error) {
return [NSJSONSerialization dataWithJSONObject:dictionary options:0 error:error];
}
@implementation FIRDynamicLinkNetworking {
NSString *_APIKey;
NSString *_URLScheme;
}
- (instancetype)initWithAPIKey:(NSString *)APIKey URLScheme:(NSString *)URLScheme {
NSParameterAssert(APIKey);
NSParameterAssert(URLScheme);
if (self = [super init]) {
_APIKey = [APIKey copy];
_URLScheme = [URLScheme copy];
}
return self;
}
+ (nullable NSError *)extractErrorForShortLink:(NSURL *)url
data:(NSData *)data
response:(NSURLResponse *)response
error:(nullable NSError *)error {
if (error) {
return error;
}
NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode];
NSError *customError = nil;
if (![response isKindOfClass:[NSHTTPURLResponse class]]) {
customError =
[NSError errorWithDomain:kGenericErrorDomain
code:0
userInfo:@{@"message" : @"Response should be of type NSHTTPURLResponse."}];
} else if ((statusCode < 200 || statusCode >= 300) && data) {
NSDictionary *result = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
if ([result isKindOfClass:[NSDictionary class]] && [result objectForKey:@"error"]) {
id err = [result objectForKey:@"error"];
customError = [NSError errorWithDomain:kGenericErrorDomain code:statusCode userInfo:err];
} else {
customError = [NSError
errorWithDomain:kGenericErrorDomain
code:0
userInfo:@{
@"message" :
[NSString stringWithFormat:@"Failed to resolve link: %@", url.absoluteString]
}];
}
}
return customError;
}
#pragma mark - Public interface
- (void)resolveShortLink:(NSURL *)url
FDLSDKVersion:(NSString *)FDLSDKVersion
completion:(FIRDynamicLinkResolverHandler)handler {
NSParameterAssert(handler);
if (!url) {
handler(nil, nil);
return;
}
NSDictionary *requestBody = @{
@"requestedLink" : url.absoluteString,
@"bundle_id" : [NSBundle mainBundle].bundleIdentifier,
@"sdk_version" : FDLSDKVersion
};
FIRNetworkRequestCompletionHandler resolveLinkCallback =
^(NSData *data, NSURLResponse *response, NSError *error) {
NSURL *resolvedURL = nil;
NSError *extractedError = [FIRDynamicLinkNetworking extractErrorForShortLink:url
data:data
response:response
error:error];
if (!extractedError && data) {
NSDictionary *result = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
if ([result isKindOfClass:[NSDictionary class]]) {
id invitationIDObject = [result objectForKey:@"invitationId"];
NSString *invitationIDString;
if ([invitationIDObject isKindOfClass:[NSDictionary class]]) {
NSDictionary *invitationIDDictionary = invitationIDObject;
invitationIDString = invitationIDDictionary[@"id"];
} else if ([invitationIDObject isKindOfClass:[NSString class]]) {
invitationIDString = invitationIDObject;
}
NSString *deepLinkString = result[kFDLResolvedLinkDeepLinkURLKey];
NSString *minAppVersion = result[kFDLResolvedLinkMinAppVersionKey];
NSString *utmSource = result[kFDLAnalyticsDataSourceKey];
NSString *utmMedium = result[kFDLAnalyticsDataMediumKey];
NSString *utmCampaign = result[kFDLAnalyticsDataCampaignKey];
NSString *utmContent = result[kFDLAnalyticsDataContentKey];
NSString *utmTerm = result[kFDLAnalyticsDataTermKey];
resolvedURL = FIRDLDeepLinkURLWithInviteID(
invitationIDString, deepLinkString, utmSource, utmMedium, utmCampaign, utmContent,
utmTerm, NO, nil, minAppVersion, self->_URLScheme, nil);
}
}
handler(resolvedURL, extractedError);
};
NSString *requestURLString =
[NSString stringWithFormat:@"%@/reopenAttribution%@", kiOSReopenRestBaseUrl,
FIRDynamicLinkAPIKeyParameter(_APIKey)];
[self executeOnePlatformRequest:requestBody
forURL:requestURLString
completionHandler:resolveLinkCallback];
}
- (void)retrievePendingDynamicLinkWithIOSVersion:(NSString *)IOSVersion
resolutionHeight:(NSInteger)resolutionHeight
resolutionWidth:(NSInteger)resolutionWidth
locale:(NSString *)locale
localeRaw:(NSString *)localeRaw
localeFromWebView:(NSString *)localeFromWebView
timezone:(NSString *)timezone
modelName:(NSString *)modelName
FDLSDKVersion:(NSString *)FDLSDKVersion
appInstallationDate:(NSDate *_Nullable)appInstallationDate
uniqueMatchVisualStyle:
(FIRDynamicLinkNetworkingUniqueMatchVisualStyle)uniqueMatchVisualStyle
retrievalProcessType:
(FIRDynamicLinkNetworkingRetrievalProcessType)retrievalProcessType
uniqueMatchLinkToCheck:(NSURL *)uniqueMatchLinkToCheck
handler:
(FIRPostInstallAttributionCompletionHandler)handler {
NSParameterAssert(handler);
NSMutableDictionary *requestBody = [@{
@"bundleId" : [NSBundle mainBundle].bundleIdentifier,
@"device" :
[FDLDeviceHeuristicsHelper FDLDeviceInfoDictionaryFromResolutionHeight:resolutionHeight
resolutionWidth:resolutionWidth
locale:locale
localeRaw:localeRaw
localeFromWebview:localeFromWebView
timeZone:timezone
modelName:modelName],
@"iosVersion" : IOSVersion,
@"sdkVersion" : FDLSDKVersion,
@"visualStyle" : @(uniqueMatchVisualStyle),
@"retrievalMethod" : @(retrievalProcessType),
} mutableCopy];
if (appInstallationDate) {
requestBody[@"appInstallationTime"] = @((NSInteger)[appInstallationDate timeIntervalSince1970]);
}
if (uniqueMatchLinkToCheck) {
requestBody[@"uniqueMatchLinkToCheck"] = uniqueMatchLinkToCheck.absoluteString;
}
FIRDLNetworkingParserBlock responseParserBlock = ^NSDictionary *_Nullable(
NSString *requestURLString, NSData *data, NSString **matchMessagePtr, NSError **errorPtr) {
NSError *serializationError;
NSDictionary *result = [NSJSONSerialization JSONObjectWithData:data
options:0
error:&serializationError];
if (serializationError) {
if (errorPtr != nil) {
*errorPtr = serializationError;
}
return nil;
}
NSString *matchMessage = result[@"matchMessage"];
if (matchMessage.length) {
*matchMessagePtr = matchMessage;
}
// Create the dynamic link parameters
NSMutableDictionary *dynamicLinkParameters = [[NSMutableDictionary alloc] init];
dynamicLinkParameters[kFIRDLParameterInviteId] = result[@"invitationId"];
dynamicLinkParameters[kFIRDLParameterDeepLinkIdentifier] = result[@"deepLink"];
if (result[@"deepLink"]) {
dynamicLinkParameters[kFIRDLParameterMatchType] =
FIRDLMatchTypeStringFromServerString(result[@"attributionConfidence"]);
}
dynamicLinkParameters[kFIRDLParameterSource] = result[@"utmSource"];
dynamicLinkParameters[kFIRDLParameterMedium] = result[@"utmMedium"];
dynamicLinkParameters[kFIRDLParameterCampaign] = result[@"utmCampaign"];
dynamicLinkParameters[kFIRDLParameterMinimumAppVersion] = result[@"appMinimumVersion"];
dynamicLinkParameters[kFIRDLParameterRequestIPVersion] = result[@"requestIpVersion"];
dynamicLinkParameters[kFIRDLParameterMatchMessage] = matchMessage;
return [dynamicLinkParameters copy];
};
[self sendRequestWithBaseURLString:kIosPostInstallAttributionRestBaseUrl
requestBody:requestBody
endpointPath:@"installAttribution"
parserBlock:responseParserBlock
completion:handler];
}
- (void)convertInvitation:(NSString *)invitationID
handler:(nullable FIRDynamicLinkNetworkingErrorHandler)handler {
if (!invitationID) {
return;
}
NSDictionary *requestBody = @{
@"invitationId" : @{@"id" : invitationID},
@"containerClientId" : @{
@"type" : @"IOS",
}
};
FIRNetworkRequestCompletionHandler convertInvitationCallback =
^(NSData *data, NSURLResponse *response, NSError *error) {
if (handler) {
dispatch_async(dispatch_get_main_queue(), ^{
handler(error);
});
}
};
NSString *requestURL = [NSString stringWithFormat:@"%@/convertInvitation%@", kApiaryRestBaseUrl,
FIRDynamicLinkAPIKeyParameter(_APIKey)];
[self executeOnePlatformRequest:requestBody
forURL:requestURL
completionHandler:convertInvitationCallback];
}
#pragma mark - Internal methods
- (void)sendRequestWithBaseURLString:(NSString *)baseURL
requestBody:(NSDictionary *)requestBody
endpointPath:(NSString *)endpointPath
parserBlock:(FIRDLNetworkingParserBlock)parserBlock
completion:(FIRPostInstallAttributionCompletionHandler)handler {
NSParameterAssert(handler);
NSString *requestURLString = [NSString
stringWithFormat:@"%@/%@%@", baseURL, endpointPath, FIRDynamicLinkAPIKeyParameter(_APIKey)];
FIRNetworkRequestCompletionHandler completeInvitationByDeviceCallback =
^(NSData *data, NSURLResponse *response, NSError *error) {
if (error || !data) {
dispatch_async(dispatch_get_main_queue(), ^{
handler(nil, nil, error);
});
return;
}
NSString *matchMessage = nil;
NSError *parsingError = nil;
NSDictionary *parsedDynamicLinkParameters =
parserBlock(requestURLString, data, &matchMessage, &parsingError);
dispatch_async(dispatch_get_main_queue(), ^{
handler(parsedDynamicLinkParameters, matchMessage, parsingError);
});
};
[self executeOnePlatformRequest:requestBody
forURL:requestURLString
completionHandler:completeInvitationByDeviceCallback];
}
- (void)executeOnePlatformRequest:(NSDictionary *)requestBody
forURL:(NSString *)requestURLString
completionHandler:(FIRNetworkRequestCompletionHandler)handler {
NSURL *requestURL = [NSURL URLWithString:requestURLString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:requestURL];
// TODO: Verify that HTTPBody and HTTPMethod are iOS 8+ and find an alternative.
request.HTTPBody = FIRDataWithDictionary(requestBody, nil);
request.HTTPMethod = @"POST";
[request setValue:@"application/json; charset=utf-8" forHTTPHeaderField:@"Content-Type"];
// Set the iOS bundleID as a request header.
NSString *bundleID = [[NSBundle mainBundle] bundleIdentifier];
if (bundleID) {
[request setValue:bundleID forHTTPHeaderField:kHeaderIosBundleIdentifier];
}
FIRMakeHTTPRequest(request, handler);
}
@end
NS_ASSUME_NONNULL_END
#endif // TARGET_OS_IOS
@@ -1,122 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "FirebaseDynamicLinks/Sources/Public/FirebaseDynamicLinks/FIRDynamicLinks.h" // IWYU pragma: export
#import "FirebaseDynamicLinks/Sources/FIRDynamicLink+Private.h"
@class UIViewController;
NS_ASSUME_NONNULL_BEGIN
@interface FIRDynamicLinks (FirstParty)
/**
* @method setUpWithLaunchOptions::apiKey:clientID:urlScheme:userDefaults:
* @abstract Set up Dynamic Links.
* @param launchOptions launchOptions from |application:didFinishLaunchingWithOptions:|. If nil, the
* deep link may appear twice on iOS 9 if a user clicks on a link before opening the app.
* @param apiKey API key for API access.
* @param clientID client ID for API access.
* @param urlScheme A custom url scheme used by the application. If nil, bundle id will be used.
* @param userDefaults The defaults from a user’s defaults database. If nil, standard
* NSUserDefaults will be used.
* @return whether the Dynamic Links was set up successfully.
*/
- (BOOL)setUpWithLaunchOptions:(nullable NSDictionary *)launchOptions
apiKey:(NSString *)apiKey
clientID:(NSString *)clientID
urlScheme:(nullable NSString *)urlScheme
userDefaults:(nullable NSUserDefaults *)userDefaults
DEPRECATED_MSG_ATTRIBUTE(
"Use [FIRDynamicLinks setUpWithLaunchOptions::apiKey:urlScheme:userDefaults:] instead.");
/**
* @method setUpWithLaunchOptions::apiKey:urlScheme:userDefaults:
* @abstract Set up Dynamic Links.
* @param launchOptions launchOptions from |application:didFinishLaunchingWithOptions:|. If nil, the
* deep link may appear twice on iOS 9 if a user clicks on a link before opening the app.
* @param apiKey API key for API access.
* @param urlScheme A custom url scheme used by the application. If nil, bundle id will be used.
* @param userDefaults The defaults from a user’s defaults database. If nil, standard
* NSUserDefaults will be used.
* @return whether the Dynamic Links was set up successfully.
*/
- (BOOL)setUpWithLaunchOptions:(nullable NSDictionary *)launchOptions
apiKey:(NSString *)apiKey
urlScheme:(nullable NSString *)urlScheme
userDefaults:(nullable NSUserDefaults *)userDefaults;
/**
* @method checkForPendingDynamicLink
* @abstract check for a pending Dynamic Link. This method should be called from your
* |UIApplicationDelegate|'s |application:didFinishLaunchingWithOptions:|. If a Dynamic Link is
* found, you'll receive an URL in |application:openURL:options:| on iOS9 or later, and
* |application:openURL:sourceApplication:annotation| on iOS 8 and earlier. From there you could
* get a |GINDeepLink| object by calling |dynamicLinkFromCustomSchemeURL:|. If no Dynamic Link
* is found, you will receive callback with "dismiss link". For "dismiss link" the
* FIRDynamicLink.url property is nil.
* For new integrations prefer to use method
* retrievePendingDynamicLinkWithRetrievalProcessType:retrievalOptions:delegate: . This method
* will be the only way to use FDL in near future.
*/
- (void)checkForPendingDynamicLink;
/**
@method checkForPendingDynamicLinkUsingExperimentalRetrievalProcess
@abstract The same as checkForPendingDynamicLink. Will be using experimental retrieval process.
*/
- (void)checkForPendingDynamicLinkUsingExperimentalRetrievalProcess;
/**
* @method sharedInstance
* @abstract Method for compatibility with old interface of the GINDurableDeepLinkService
*/
+ (instancetype)
sharedInstance DEPRECATED_MSG_ATTRIBUTE("Use [FIRDynamicLinks dynamicLinks] instead.");
/**
* @method checkForPendingDeepLink
* @abstract Method for compatibility with old interface of the GINDurableDeepLinkService
*/
- (void)checkForPendingDeepLink DEPRECATED_MSG_ATTRIBUTE(
"Use [FIRDynamicLinks checkForPendingDynamicLink] instead.");
/**
* @method deepLinkFromCustomSchemeURL:
* @abstract Method for compatibility with old interface of the GINDurableDeepLinkService
*/
- (nullable FIRDynamicLink *)deepLinkFromCustomSchemeURL:(NSURL *)url
DEPRECATED_MSG_ATTRIBUTE("Use [FIRDynamicLinks dynamicLinkFromCustomSchemeURL:] instead.");
/**
* @method deepLinkFromUniversalLinkURL:
* @abstract Method for compatibility with old interface of the GINDurableDeepLinkService
*/
- (nullable FIRDynamicLink *)deepLinkFromUniversalLinkURL:(NSURL *)url
DEPRECATED_MSG_ATTRIBUTE("Use [FIRDynamicLinks dynamicLinkFromUniversalLinkURL:] instead.");
/**
* @method shouldHandleDeepLinkFromCustomSchemeURL:
* @abstract Method for compatibility with old interface of the GINDurableDeepLinkService
*/
- (BOOL)shouldHandleDeepLinkFromCustomSchemeURL:(NSURL *)url
DEPRECATED_MSG_ATTRIBUTE("Use [FIRDynamicLinks shouldHandleDynamicLinkFromCustomSchemeURL:]"
" instead.");
@end
NS_ASSUME_NONNULL_END
@@ -1,55 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "FirebaseDynamicLinks/Sources/Public/FirebaseDynamicLinks/FIRDynamicLinks.h"
@class UIViewController;
NS_ASSUME_NONNULL_BEGIN
/**
* The version of the Firebase Dynamic Link Service SDK.
*/
FOUNDATION_EXPORT NSString *const kFIRDLVersion;
/**
* Exposed for Unit Tests usage.
*/
FOUNDATION_EXPORT NSString *const kFIRDLReadDeepLinkAfterInstallKey;
@interface FIRDynamicLinks (Private)
/**
* @abstract Internal method to return is automatic retrieval of dynamic link enabled or not.
* To be used for internal purposes.
*/
+ (BOOL)isAutomaticRetrievalEnabled;
/**
* @property APIKey
* @abstract API Key for API access.
*/
@property(nonatomic, copy, readonly) NSString *APIKey;
/**
* @property URLScheme
* @abstract Custom URL scheme.
*/
@property(nonatomic, copy, readonly, nullable) NSString *URLScheme;
@end
NS_ASSUME_NONNULL_END
@@ -1,805 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <TargetConditionals.h>
#if TARGET_OS_IOS
#import "FirebaseDynamicLinks/Sources/Public/FirebaseDynamicLinks/FIRDynamicLinks.h"
#import <UIKit/UIKit.h>
#ifdef FIRDynamicLinks3P
#import "FirebaseCore/Extension/FirebaseCoreInternal.h"
#import "FirebaseDynamicLinks/Sources/FIRDLScionLogging.h"
#import "Interop/Analytics/Public/FIRAnalyticsInterop.h"
#else
#import "FirebaseCore/Sources/Public/FirebaseCore/FIRVersion.h"
#endif
#ifdef FIRDynamicLinks3P
#import "FirebaseDynamicLinks/Sources/FDLURLComponents/FDLURLComponents+Private.h"
#endif
#import "FirebaseDynamicLinks/Sources/FIRDLRetrievalProcessFactory.h"
#import "FirebaseDynamicLinks/Sources/FIRDLRetrievalProcessProtocols.h"
#import "FirebaseDynamicLinks/Sources/FIRDLRetrievalProcessResult.h"
#import "FirebaseDynamicLinks/Sources/FIRDynamicLink+Private.h"
#import "FirebaseDynamicLinks/Sources/FIRDynamicLinkNetworking.h"
#import "FirebaseDynamicLinks/Sources/FIRDynamicLinks+FirstParty.h"
#import "FirebaseDynamicLinks/Sources/FIRDynamicLinks+Private.h"
#import "FirebaseDynamicLinks/Sources/Logging/FDLLogging.h"
#import "FirebaseDynamicLinks/Sources/Utilities/FDLUtilities.h"
// We should only read the deeplink after install once. We use the following key to store the state
// in the user defaults.
NSString *const kFIRDLReadDeepLinkAfterInstallKey =
@"com.google.appinvite.readDeeplinkAfterInstall";
// We should only open url once. We use the following key to store the state in the user defaults.
static NSString *const kFIRDLOpenURLKey = @"com.google.appinvite.openURL";
// Custom domains to be allowed are optionally added as an array to the info.plist.
static NSString *const kInfoPlistCustomDomainsKey = @"FirebaseDynamicLinksCustomDomains";
NS_ASSUME_NONNULL_BEGIN
@interface FIRDynamicLinks () <FIRDLRetrievalProcessDelegate>
// API Key for API access.
@property(nonatomic, copy) NSString *APIKey;
// Custom URL scheme.
@property(nonatomic, copy) NSString *URLScheme;
// Networking object for Dynamic Links
@property(nonatomic, readonly) FIRDynamicLinkNetworking *dynamicLinkNetworking;
@property(atomic, assign) BOOL retrievingPendingDynamicLink;
@end
#ifdef FIRDynamicLinks3P
// Error code from FDL.
static const NSInteger FIRErrorCodeDurableDeepLinkFailed = -119;
@interface FIRDynamicLinks () {
/// Stored Analytics reference, if it exists.
id<FIRAnalyticsInterop> _Nullable _analytics;
}
@end
// DynamicLinks doesn't provide any functionality to other components,
// so it provides a private, empty protocol that it conforms to and use it for registration.
@protocol FIRDynamicLinksInstanceProvider
@end
@interface FIRDynamicLinks () <FIRDynamicLinksInstanceProvider, FIRLibrary>
@end
#endif
@implementation FIRDynamicLinks {
// User defaults passed.
NSUserDefaults *_userDefaults;
FIRDynamicLinkNetworking *_dynamicLinkNetworking;
id<FIRDLRetrievalProcessProtocol> _retrievalProcess;
}
#pragma mark - Object lifecycle
#ifdef FIRDynamicLinks3P
+ (void)load {
[FIRApp registerInternalLibrary:self withName:@"fire-dl"];
}
+ (nonnull NSArray<FIRComponent *> *)componentsToRegister {
FIRComponentCreationBlock creationBlock =
^id _Nullable(FIRComponentContainer *container, BOOL *isCacheable) {
// Don't return an instance when it's not the default app.
if (!container.app.isDefaultApp) {
// Only configure for the default FIRApp.
FDLLog(FDLLogLevelInfo, FDLLogIdentifierSetupNonDefaultApp,
@"Firebase Dynamic Links only "
"works with the default app.");
return nil;
}
// Ensure it's cached so it returns the same instance every time dynamicLinks is called.
*isCacheable = YES;
id<FIRAnalyticsInterop> analytics = FIR_COMPONENT(FIRAnalyticsInterop, container);
FIRDynamicLinks *dynamicLinks = [[FIRDynamicLinks alloc] initWithAnalytics:analytics];
[dynamicLinks configureDynamicLinks:container.app];
if ([FIRDynamicLinks isAutomaticRetrievalEnabled]) {
[dynamicLinks checkForPendingDynamicLink];
}
return dynamicLinks;
};
FIRComponent *dynamicLinksProvider =
[FIRComponent componentWithProtocol:@protocol(FIRDynamicLinksInstanceProvider)
instantiationTiming:FIRInstantiationTimingEagerInDefaultApp
creationBlock:creationBlock];
return @[ dynamicLinksProvider ];
}
- (void)configureDynamicLinks:(FIRApp *)app {
FIROptions *options = app.options;
NSError *error;
NSMutableString *errorDescription;
NSString *urlScheme;
if (options.APIKey.length == 0) {
errorDescription = [@"API key must not be nil or empty." mutableCopy];
}
if (!errorDescription) {
// setup FDL if no error detected
urlScheme = options.deepLinkURLScheme ?: [NSBundle mainBundle].bundleIdentifier;
[self setUpWithLaunchOptions:nil apiKey:options.APIKey urlScheme:urlScheme userDefaults:nil];
} else {
NSString *description =
[NSString stringWithFormat:@"Configuration failed for service DynamicLinks."];
NSDictionary *errorDict = @{
NSLocalizedDescriptionKey : description,
NSLocalizedFailureReasonErrorKey : errorDescription
};
error = [NSError errorWithDomain:kFirebaseDurableDeepLinkErrorDomain
code:FIRErrorCodeDurableDeepLinkFailed
userInfo:errorDict];
}
if (error) {
NSString *message =
[NSString stringWithFormat:
@"Firebase Dynamic Links has stopped your project "
@"because there are incorrect values provided in Firebase's configuration "
@"options that may prevent your app from behaving as expected:\n\n"
@"Error: %@\n\n"
@"Please fix these issues to ensure that Firebase is correctly configured in "
@"your project.",
error.localizedFailureReason];
[NSException raise:kFirebaseDurableDeepLinkErrorDomain format:@"%@", message];
}
[self checkForCustomDomainEntriesInInfoPlist];
}
- (instancetype)initWithAnalytics:(nullable id<FIRAnalyticsInterop>)analytics {
self = [super init];
if (self) {
_analytics = analytics;
}
return self;
}
+ (instancetype)dynamicLinks {
FIRApp *defaultApp = [FIRApp defaultApp]; // Missing configure will be logged here.
id<FIRDynamicLinksInstanceProvider> instance =
FIR_COMPONENT(FIRDynamicLinksInstanceProvider, defaultApp.container);
return (FIRDynamicLinks *)instance;
}
#else
+ (instancetype)dynamicLinks {
static FIRDynamicLinks *dynamicLinks;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
dynamicLinks = [[self alloc] init];
});
return dynamicLinks;
}
#endif
#pragma mark - Custom domains
- (instancetype)init {
self = [super init];
if (self) {
[self checkForCustomDomainEntriesInInfoPlist];
}
return self;
}
// Check for custom domains entry in PLIST file.
- (void)checkForCustomDomainEntriesInInfoPlist {
// Check to see if FirebaseDynamicLinksCustomDomains array is present.
NSDictionary *infoDictionary = [NSBundle mainBundle].infoDictionary;
NSArray *customDomains = infoDictionary[kInfoPlistCustomDomainsKey];
if (customDomains) {
FIRDLAddToAllowListForCustomDomainsArray(customDomains);
}
}
#pragma mark - First party interface
- (BOOL)setUpWithLaunchOptions:(nullable NSDictionary *)launchOptions
apiKey:(NSString *)apiKey
urlScheme:(nullable NSString *)urlScheme
userDefaults:(nullable NSUserDefaults *)userDefaults {
if (apiKey == nil) {
FDLLog(FDLLogLevelError, FDLLogIdentifierSetupNilAPIKey, @"API Key must not be nil.");
return NO;
}
_APIKey = [apiKey copy];
_URLScheme = urlScheme.length ? [urlScheme copy] : [NSBundle mainBundle].bundleIdentifier;
if (!userDefaults) {
_userDefaults = [NSUserDefaults standardUserDefaults];
} else {
_userDefaults = userDefaults;
}
NSURL *url = launchOptions[UIApplicationLaunchOptionsURLKey];
if (url) {
if ([self canParseCustomSchemeURL:url] || [self canParseUniversalLinkURL:url]) {
// Make sure we don't call |checkForPendingDynamicLink| again if
// a strong deep link is found.
[_userDefaults setBool:YES forKey:kFIRDLReadDeepLinkAfterInstallKey];
}
}
return YES;
}
- (void)checkForPendingDynamicLinkUsingExperimentalRetrievalProcess {
[self checkForPendingDynamicLink];
}
- (void)checkForPendingDynamicLink {
// Make sure this method is called only once after the application was installed.
// kFIRDLOpenURLKey marks checkForPendingDynamic link had been called already so no need to do it
// again. kFIRDLReadDeepLinkAfterInstallKey marks we have already read a deeplink after the
// install and so no need to do check for pending dynamic link.
BOOL appInviteDeepLinkRead = [_userDefaults boolForKey:kFIRDLOpenURLKey] ||
[_userDefaults boolForKey:kFIRDLReadDeepLinkAfterInstallKey];
if (appInviteDeepLinkRead || self.retrievingPendingDynamicLink) {
NSString *errorDescription =
appInviteDeepLinkRead ? NSLocalizedString(@"Link was already retrieved", @"Error message")
: NSLocalizedString(@"Already retrieving link", @"Error message");
[self handlePendingDynamicLinkRetrievalFailureWithErrorCode:-1
errorDescription:errorDescription
underlyingError:nil];
return;
}
self.retrievingPendingDynamicLink = YES;
FIRDLRetrievalProcessFactory *factory =
[[FIRDLRetrievalProcessFactory alloc] initWithNetworkingService:self.dynamicLinkNetworking
URLScheme:_URLScheme
APIKey:_APIKey
FDLSDKVersion:FIRFirebaseVersion()
delegate:self];
_retrievalProcess = [factory automaticRetrievalProcess];
[_retrievalProcess retrievePendingDynamicLink];
}
// Disable deprecated warning for internal methods.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-implementations"
+ (instancetype)sharedInstance {
return [self dynamicLinks];
}
- (BOOL)setUpWithLaunchOptions:(nullable NSDictionary *)launchOptions
apiKey:(NSString *)apiKey
clientID:(NSString *)clientID
urlScheme:(nullable NSString *)urlScheme
userDefaults:(nullable NSUserDefaults *)userDefaults {
return [self setUpWithLaunchOptions:launchOptions
apiKey:apiKey
urlScheme:urlScheme
userDefaults:userDefaults];
}
- (void)checkForPendingDeepLink {
[self checkForPendingDynamicLink];
}
- (nullable FIRDynamicLink *)deepLinkFromCustomSchemeURL:(NSURL *)url {
return [self dynamicLinkFromCustomSchemeURL:url];
}
- (nullable FIRDynamicLink *)deepLinkFromUniversalLinkURL:(NSURL *)url {
return [self dynamicLinkFromUniversalLinkURL:url];
}
- (BOOL)shouldHandleDeepLinkFromCustomSchemeURL:(NSURL *)url {
return [self shouldHandleDynamicLinkFromCustomSchemeURL:url];
}
#pragma clang pop
#pragma mark - Public interface
- (BOOL)shouldHandleDynamicLinkFromCustomSchemeURL:(NSURL *)url {
// Return NO if the URL scheme does not match.
if (![self canParseCustomSchemeURL:url]) {
return NO;
}
// We can handle "/link" and "/link/dismiss". The latter will return a nil deep link.
return ([url.path hasPrefix:@"/link"] && [url.host isEqualToString:@"google"]);
}
- (nullable FIRDynamicLink *)dynamicLinkFromCustomSchemeURL:(NSURL *)url {
// Return nil if the URL scheme does not match.
if (![self canParseCustomSchemeURL:url]) {
return nil;
}
if ([url.path isEqualToString:@"/link"] && [url.host isEqualToString:@"google"]) {
// This URL is a callback url from a device heuristics based match
// Extract information from query.
NSString *query = url.query;
NSDictionary *parameters = FIRDLDictionaryFromQuery(query);
// As long as the deepLink has some parameter, return it.
if (parameters.count > 0) {
FIRDynamicLink *dynamicLink =
[[FIRDynamicLink alloc] initWithParametersDictionary:parameters];
#ifdef GIN_SCION_LOGGING
if (dynamicLink.url) {
BOOL isFirstOpen = ![_userDefaults boolForKey:kFIRDLReadDeepLinkAfterInstallKey];
FIRDLLogEvent event = isFirstOpen ? FIRDLLogEventFirstOpen : FIRDLLogEventAppOpen;
FIRDLLogEventToScion(event, parameters[kFIRDLParameterSource],
parameters[kFIRDLParameterMedium], parameters[kFIRDLParameterCampaign],
_analytics);
}
#endif
// Make sure we don't call |checkForPendingDynamicLink| again if we did this already.
if ([_userDefaults boolForKey:kFIRDLOpenURLKey]) {
[_userDefaults setBool:YES forKey:kFIRDLReadDeepLinkAfterInstallKey];
}
return dynamicLink;
}
}
return nil;
}
- (nullable FIRDynamicLink *)
dynamicLinkInternalFromUniversalLinkURL:(NSURL *)url
completion:
(nullable FIRDynamicLinkUniversalLinkHandler)completion {
// Make sure the completion is always called on the main queue.
FIRDynamicLinkUniversalLinkHandler mainQueueCompletion =
^(FIRDynamicLink *_Nullable dynamicLink, NSError *_Nullable error) {
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(dynamicLink, error);
});
}
};
if ([self canParseUniversalLinkURL:url]) {
if (url.query.length > 0) {
NSDictionary<NSString *, NSString *> *parameters = FIRDLDictionaryFromQuery(url.query);
if (parameters[kFIRDLParameterLink]) {
NSString *urlString = parameters[kFIRDLParameterLink];
NSURL *deepLinkURL = [NSURL URLWithString:urlString];
if (deepLinkURL) {
NSMutableDictionary *paramsDictionary = [[NSMutableDictionary alloc]
initWithDictionary:@{kFIRDLParameterDeepLinkIdentifier : urlString}];
if (parameters[kFIRDLParameterSource] != nil) {
[paramsDictionary setValue:parameters[kFIRDLParameterSource]
forKey:kFIRDLParameterSource];
}
if (parameters[kFIRDLParameterMedium] != nil) {
[paramsDictionary setValue:parameters[kFIRDLParameterMedium]
forKey:kFIRDLParameterMedium];
}
if (parameters[kFIRDLParameterTerm] != nil) {
[paramsDictionary setValue:parameters[kFIRDLParameterTerm] forKey:kFIRDLParameterTerm];
}
if (parameters[kFIRDLParameterCampaign] != nil) {
[paramsDictionary setValue:parameters[kFIRDLParameterCampaign]
forKey:(kFIRDLParameterCampaign)];
}
if (parameters[kFIRDLParameterContent] != nil) {
[paramsDictionary setValue:parameters[kFIRDLParameterContent]
forKey:kFIRDLParameterContent];
}
FIRDynamicLink *dynamicLink =
[[FIRDynamicLink alloc] initWithParametersDictionary:paramsDictionary];
dynamicLink.matchType = FIRDLMatchTypeUnique;
dynamicLink.minimumAppVersion = parameters[kFIRDLParameterMinimumAppVersion];
// Call resolveShortLink:completion: to do logging.
// TODO: Create dedicated logging function to prevent this.
[self.dynamicLinkNetworking
resolveShortLink:url
FDLSDKVersion:FIRFirebaseVersion()
completion:^(NSURL *_Nullable resolverURL, NSError *_Nullable resolverError) {
mainQueueCompletion(dynamicLink, resolverError);
}];
#ifdef GIN_SCION_LOGGING
FIRDLLogEventToScion(FIRDLLogEventAppOpen, parameters[kFIRDLParameterSource],
parameters[kFIRDLParameterMedium],
parameters[kFIRDLParameterCampaign], _analytics);
#endif
return dynamicLink;
}
}
}
}
mainQueueCompletion(
nil, [[NSError alloc] initWithDomain:@"com.firebase.dynamicLinks"
code:1
userInfo:@{
NSLocalizedFailureReasonErrorKey :
@"Universal link URL could not be parsed by Dynamic Links."
}]);
return nil;
}
- (nullable FIRDynamicLink *)dynamicLinkFromUniversalLinkURL:(NSURL *)url {
return [self dynamicLinkInternalFromUniversalLinkURL:url completion:nil];
}
- (void)dynamicLinkFromUniversalLinkURL:(NSURL *)url
completion:(FIRDynamicLinkUniversalLinkHandler)completion {
[self dynamicLinkInternalFromUniversalLinkURL:url completion:completion];
}
- (BOOL)handleUniversalLink:(NSURL *)universalLinkURL
completion:(FIRDynamicLinkUniversalLinkHandler)completion {
if ([self matchesShortLinkFormat:universalLinkURL]) {
__weak __typeof__(self) weakSelf = self;
[self resolveShortLink:universalLinkURL
completion:^(NSURL *url, NSError *error) {
__typeof__(self) strongSelf = weakSelf;
if (strongSelf) {
FIRDynamicLink *dynamicLink = [strongSelf dynamicLinkFromCustomSchemeURL:url];
dispatch_async(dispatch_get_main_queue(), ^{
completion(dynamicLink, error);
});
} else {
completion(nil, nil);
}
}];
return YES;
} else {
[self dynamicLinkFromUniversalLinkURL:universalLinkURL completion:completion];
BOOL canHandleUniversalLink =
[self canParseUniversalLinkURL:universalLinkURL] && universalLinkURL.query.length > 0 &&
FIRDLDictionaryFromQuery(universalLinkURL.query)[kFIRDLParameterLink];
return canHandleUniversalLink;
}
}
- (void)resolveShortLink:(NSURL *)url completion:(FIRDynamicLinkResolverHandler)completion {
[self.dynamicLinkNetworking resolveShortLink:url
FDLSDKVersion:FIRFirebaseVersion()
completion:completion];
}
- (BOOL)matchesShortLinkFormat:(NSURL *)url {
return FIRDLMatchesShortLinkFormat(url);
}
#pragma mark - Private interface
+ (BOOL)isAutomaticRetrievalEnabled {
id retrievalEnabledValue =
[[NSBundle mainBundle] infoDictionary][@"FirebaseDeepLinkAutomaticRetrievalEnabled"];
if ([retrievalEnabledValue respondsToSelector:@selector(boolValue)]) {
return [retrievalEnabledValue boolValue];
}
return YES;
}
#pragma mark - Internal methods
- (FIRDynamicLinkNetworking *)dynamicLinkNetworking {
if (!_dynamicLinkNetworking) {
_dynamicLinkNetworking = [[FIRDynamicLinkNetworking alloc] initWithAPIKey:_APIKey
URLScheme:_URLScheme];
}
return _dynamicLinkNetworking;
}
- (BOOL)canParseCustomSchemeURL:(nullable NSURL *)url {
if (url.scheme.length) {
NSString *bundleIdentifier = [NSBundle mainBundle].bundleIdentifier;
if ([url.scheme.lowercaseString isEqualToString:_URLScheme.lowercaseString] ||
[url.scheme.lowercaseString isEqualToString:bundleIdentifier.lowercaseString]) {
return YES;
}
}
return NO;
}
- (BOOL)canParseUniversalLinkURL:(nullable NSURL *)url {
return FIRDLCanParseUniversalLinkURL(url);
}
- (BOOL)handleIncomingCustomSchemeDeepLink:(NSURL *)url {
return [self canParseCustomSchemeURL:url];
}
- (void)passRetrievedDynamicLinkToApplication:(NSURL *)url {
id<UIApplicationDelegate> applicationDelegate = [UIApplication sharedApplication].delegate;
if ([self isOpenUrlMethodPresentInAppDelegate:applicationDelegate]) {
// pass url directly to application delegate to avoid hop into
// iOS handling of the universal links
[applicationDelegate application:[UIApplication sharedApplication] openURL:url options:@{}];
return;
}
[[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
}
- (BOOL)isOpenUrlMethodPresentInAppDelegate:(id<UIApplicationDelegate>)applicationDelegate {
return applicationDelegate &&
[applicationDelegate respondsToSelector:@selector(application:openURL:options:)];
}
- (void)handlePendingDynamicLinkRetrievalFailureWithErrorCode:(NSInteger)errorCode
errorDescription:(NSString *)errorDescription
underlyingError:(nullable NSError *)underlyingError {
self.retrievingPendingDynamicLink = NO;
}
#pragma mark - FIRDLRetrievalProcessDelegate
- (void)retrievalProcess:(id<FIRDLRetrievalProcessProtocol>)retrievalProcess
completedWithResult:(FIRDLRetrievalProcessResult *)result {
self.retrievingPendingDynamicLink = NO;
_retrievalProcess = nil;
if (![_userDefaults boolForKey:kFIRDLOpenURLKey]) {
// Once we complete the Pending dynamic link retrieval, regardless of whether the retrieval is
// success or failure, we don't want to do the retrieval again on next app start.
// If we try to redo the retrieval again because of some error, the user will experience
// unwanted deeplinking when they restart the app next time.
[_userDefaults setBool:YES forKey:kFIRDLOpenURLKey];
}
NSURL *linkToPassToApp = [result URLWithCustomURLScheme:_URLScheme];
[self passRetrievedDynamicLinkToApplication:linkToPassToApp];
}
#pragma mark - Diagnostics methods
static NSString *kSelfDiagnoseOutputHeader =
@"---- Firebase Dynamic Links diagnostic output start ----\n";
static NSString *kSelfDiagnoseOutputFooter =
@"---- Firebase Dynamic Links diagnostic output end ----\n";
+ (NSString *)genericDiagnosticInformation {
NSMutableString *genericDiagnosticInfo = [[NSMutableString alloc] init];
[genericDiagnosticInfo
appendFormat:@"Firebase Dynamic Links framework version %@\n", FIRFirebaseVersion()];
[genericDiagnosticInfo appendFormat:@"System information: OS %@, OS version %@, model %@\n",
[UIDevice currentDevice].systemName,
[UIDevice currentDevice].systemVersion,
[UIDevice currentDevice].model];
[genericDiagnosticInfo appendFormat:@"Current date %@\n", [NSDate date]];
// TODO: bring this diagnostic info back when we shipped non-automatic retrieval
// [genericDiagnosticInfo appendFormat:@"AutomaticRetrievalEnabled: %@\n",
// [self isAutomaticRetrievalEnabled] ? @"YES" : @"NO"];
// Disable deprecated warning for internal methods.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[genericDiagnosticInfo appendFormat:@"Device locale %@ (raw %@), timezone %@\n",
FIRDLDeviceLocale(), FIRDLDeviceLocaleRaw(),
FIRDLDeviceTimezone()];
#pragma clang pop
return genericDiagnosticInfo;
}
+ (NSString *)diagnosticAnalyzeEntitlements {
NSString *embeddedMobileprovisionFilePath = [[[NSBundle mainBundle] bundlePath]
stringByAppendingPathComponent:@"embedded.mobileprovision"];
NSError *error;
NSMutableData *profileData = [NSMutableData dataWithContentsOfFile:embeddedMobileprovisionFilePath
options:0
error:&error];
if (!profileData.length || error) {
return @"\tSKIPPED: Not able to read entitlements (embedded.mobileprovision).\n";
}
// The "embedded.mobileprovision" sometimes contains characters with value 0, which signals the
// end of a c-string and halts the ASCII parser, or with value > 127, which violates strict 7-bit
// ASCII. Replace any 0s or invalid characters in the input.
uint8_t *profileBytes = (uint8_t *)profileData.bytes;
for (int i = 0; i < profileData.length; i++) {
uint8_t currentByte = profileBytes[i];
if (!currentByte || currentByte > 127) {
profileBytes[i] = '.';
}
}
NSString *embeddedProfile = [[NSString alloc] initWithBytesNoCopy:profileBytes
length:profileData.length
encoding:NSASCIIStringEncoding
freeWhenDone:NO];
if (error || !embeddedProfile.length) {
return @"\tSKIPPED: Not able to read entitlements (embedded.mobileprovision).\n";
}
NSScanner *scanner = [NSScanner scannerWithString:embeddedProfile];
NSString *plistContents;
if ([scanner scanUpToString:@"<plist" intoString:nil]) {
if ([scanner scanUpToString:@"</plist>" intoString:&plistContents]) {
plistContents = [plistContents stringByAppendingString:@"</plist>"];
}
}
if (!plistContents.length) {
return @"\tWARNING: Not able to read plist entitlements (embedded.mobileprovision).\n";
}
NSData *data = [plistContents dataUsingEncoding:NSUTF8StringEncoding];
if (!data.length) {
return @"\tWARNING: Not able to parse entitlements (embedded.mobileprovision).\n";
}
NSError *plistMapError;
id plistData = [NSPropertyListSerialization propertyListWithData:data
options:NSPropertyListImmutable
format:nil
error:&plistMapError];
if (plistMapError || ![plistData isKindOfClass:[NSDictionary class]]) {
return @"\tWARNING: Not able to deserialize entitlements (embedded.mobileprovision).\n";
}
NSDictionary *plistMap = (NSDictionary *)plistData;
// analyze entitlements and print diagnostic information
// we can't detect errors, information p[rinted here may hint developer or will help support
// to identify the issue
NSMutableString *outputString = [[NSMutableString alloc] init];
NSArray *appIdentifierPrefixes = plistMap[@"ApplicationIdentifierPrefix"];
NSString *teamID = plistMap[@"Entitlements"][@"com.apple.developer.team-identifier"];
if (appIdentifierPrefixes.count > 1) {
// is this possible? anyway, we can handle it
[outputString
appendFormat:@"\tAppID Prefixes: %@, Team ID: %@, AppId Prefixes contains to Team ID: %@\n",
appIdentifierPrefixes, teamID,
([appIdentifierPrefixes containsObject:teamID] ? @"YES" : @"NO")];
} else {
[outputString
appendFormat:@"\tAppID Prefix: %@, Team ID: %@, AppId Prefix equal to Team ID: %@\n",
appIdentifierPrefixes[0], teamID,
([appIdentifierPrefixes[0] isEqualToString:teamID] ? @"YES" : @"NO")];
}
return outputString;
}
+ (NSString *)performDiagnosticsIncludingHeaderFooter:(BOOL)includingHeaderFooter
detectedErrors:(nullable NSInteger *)detectedErrors {
NSMutableString *diagnosticString = [[NSMutableString alloc] init];
if (includingHeaderFooter) {
[diagnosticString appendString:@"\n"];
[diagnosticString appendString:kSelfDiagnoseOutputHeader];
}
NSInteger detectedErrorsCnt = 0;
[diagnosticString appendString:[self genericDiagnosticInformation]];
#if TARGET_IPHONE_SIMULATOR
// check is Simulator and print WARNING that Universal Links is not supported on Simulator
[diagnosticString
appendString:@"WARNING: iOS Simulator does not support Universal Links. Firebase "
@"Dynamic Links SDK functionality will be limited. Some FDL "
@"features may be missing or will not work correctly.\n"];
#endif // TARGET_IPHONE_SIMULATOR
id<UIApplicationDelegate> applicationDelegate = [UIApplication sharedApplication].delegate;
if (![applicationDelegate respondsToSelector:@selector(application:openURL:options:)]) {
detectedErrorsCnt++;
[diagnosticString appendFormat:@"ERROR: UIApplication delegate %@ does not implements selector "
@"%@. FDL depends on this implementation to retrieve pending "
@"dynamic link.\n",
applicationDelegate,
NSStringFromSelector(@selector(application:openURL:options:))];
}
// check that Info.plist has custom URL scheme and the scheme is the same as bundleID or
// as customURLScheme passed to FDL iOS SDK
NSString *URLScheme = [FIRDynamicLinks dynamicLinks].URLScheme;
BOOL URLSchemeFoundInPlist = NO;
NSArray *URLSchemesFromInfoPlist = [[NSBundle mainBundle] infoDictionary][@"CFBundleURLTypes"];
for (NSDictionary *schemeDetails in URLSchemesFromInfoPlist) {
NSArray *arrayOfSchemes = schemeDetails[@"CFBundleURLSchemes"];
for (NSString *scheme in arrayOfSchemes) {
if ([scheme isEqualToString:URLScheme]) {
URLSchemeFoundInPlist = YES;
break;
}
}
if (URLSchemeFoundInPlist) {
break;
}
}
if (!URLSchemeFoundInPlist) {
detectedErrorsCnt++;
[diagnosticString appendFormat:@"ERROR: Specified custom URL scheme is %@ but Info.plist do "
@"not contain such scheme in "
"CFBundleURLTypes key.\n",
URLScheme];
} else {
[diagnosticString appendFormat:@"\tSpecified custom URL scheme is %@ and Info.plist contains "
@"such scheme in CFBundleURLTypes key.\n",
URLScheme];
}
#if !TARGET_IPHONE_SIMULATOR
// analyse information in entitlements file
NSString *entitlementsAnalysis = [self diagnosticAnalyzeEntitlements];
if (entitlementsAnalysis.length) {
[diagnosticString appendString:entitlementsAnalysis];
}
#endif // TARGET_IPHONE_SIMULATOR
if (includingHeaderFooter) {
if (detectedErrorsCnt == 0) {
[diagnosticString
appendString:@"performDiagnostic completed successfully! No errors found.\n"];
} else {
[diagnosticString
appendFormat:@"performDiagnostic detected %ld ERRORS.\n", (long)detectedErrorsCnt];
}
[diagnosticString appendString:kSelfDiagnoseOutputFooter];
}
if (detectedErrors) {
*detectedErrors = detectedErrorsCnt;
}
return [diagnosticString copy];
}
+ (void)performDiagnosticsWithCompletion:(void (^_Nullable)(NSString *diagnosticOutput,
BOOL hasErrors))completionHandler;
{
NSInteger detectedErrorsCnt = 0;
NSString *diagnosticString = [self performDiagnosticsIncludingHeaderFooter:YES
detectedErrors:&detectedErrorsCnt];
if (completionHandler) {
completionHandler(diagnosticString, detectedErrorsCnt > 0);
} else {
NSLog(@"%@", diagnosticString);
}
}
@end
NS_ASSUME_NONNULL_END
#endif // TARGET_OS_IOS
@@ -1,52 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
/**
* @class GINArgument
* @abstract Encapsulates an argument that is passed to a method.
*/
@interface GINArgument : NSObject
/**
* @method argumentWithObject:
* @abstract Creates an GINArgument with an NSObject.
* @param object The NSObject representing the value of the argument.
* @return An instance of GINArgument.
*/
+ (instancetype)argumentWithObject:(NSObject *)object;
/**
* @method argumentWithInteger:
* @abstract Creates an GINArgument with an NSObject.
* @param integer The NSInteger representing the value of the argument.
* @return An instance of GINArgument.
*/
+ (instancetype)argumentWithInteger:(NSInteger)integer;
/**
* @method setNextArgumentInList:inInvocation:
* @abstract Reads the next argument in |argumentList| and sets it in the |invocation| object.
* @param argumentList The list of arguments. Each entry must be of type GINArgument.
* @param index The argument index to set on the |invocation| object.
* @param invocation The invocation object to set the argument to.
* @return YES if the argument was set, NO if there were no arguments left in the list.
*/
+ (BOOL)setNextArgumentInList:(va_list)argumentList
atIndex:(NSUInteger)index
inInvocation:(NSInvocation *)invocation;
@end
@@ -1,84 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "FirebaseDynamicLinks/Sources/GINInvocation/GINArgument.h"
// Currently only supporting arguments of types id and integer.
// Will support more argument types when it is needed.
typedef NS_ENUM(NSUInteger, GINArgumentType) {
kGINArgumentTypeObject = 0,
kGINArgumentTypeInteger
};
@interface GINArgument ()
@property(nonatomic, assign) GINArgumentType type;
@property(nonatomic, strong) id object;
@property(nonatomic, assign) NSInteger integer;
@end
@implementation GINArgument
+ (instancetype)argumentWithObject:(NSObject *)object {
GINArgument *arg = [[GINArgument alloc] init];
arg.type = kGINArgumentTypeObject;
arg.object = object;
return arg;
}
+ (instancetype)argumentWithInteger:(NSInteger)integer {
GINArgument *arg = [[GINArgument alloc] init];
arg.type = kGINArgumentTypeInteger;
arg.integer = integer;
return arg;
}
+ (BOOL)setNextArgumentInList:(va_list)argumentList
atIndex:(NSUInteger)index
inInvocation:(NSInvocation *)invocation {
id argument = va_arg(argumentList, id);
if (!argument) {
return NO;
}
if (![argument isKindOfClass:[GINArgument class]]) {
[NSException raise:@"InvalidArgumentException"
format:@"Invalid argument type at index %lu", (unsigned long)index];
}
[argument setArgumentInInvocation:invocation atIndex:index];
return YES;
}
- (void)setArgumentInInvocation:(NSInvocation *)invocation atIndex:(NSUInteger)index {
switch (self.type) {
case kGINArgumentTypeObject:
[invocation setArgument:&_object atIndex:index];
break;
case kGINArgumentTypeInteger:
[invocation setArgument:&_integer atIndex:index];
break;
default:
break;
}
}
@end
@@ -1,71 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
// Calls a method on a class or object.
#define GINPerformSelector(target, selector) \
GINPerformSelectorWithArguments(target, selector, 0, nil)
// Calls a method on a class or object, that takes arguments.
#define GINPerformSelectorWithArguments(target, selector, numArgs, args...) \
[GINInvocation objectByPerformingSelector:selector \
onTarget:target \
numberOfArguments:numArgs, args, nil]
// Calls a method that returns double on a class or object.
#define GINDoubleByPerformingSelector(target, selector) \
GINDoubleByPerformingSelectorWithArguments(target, selector, 0, nil)
// Calls a method that returns double on a class or object, that takes arguments.
#define GINDoubleByPerformingSelectorWithArguments(target, selector, numArgs, args...) \
[GINInvocation doubleByPerformingSelector:selector \
onTarget:target \
numberOfArguments:numArgs, args, nil]
/**
* @class GINInvocation
* @abstract A utility class that provide helper methods to invoke methods on objects and classes.
*/
@interface GINInvocation : NSObject
/**
* @method objectByPerformingSelector:onTarget:numberOfArguments:...
* @abstract Performs a selector on a class or object.
* @param selector The selector to perform.
* @param target The target class or object to perform the selector on.
* @param numberOfArguments Number of arguments in the argument list.
* @param ... An optional argument list, each argument should be of type GINArgument.
* @return id The result of the selector.
*/
+ (id)objectByPerformingSelector:(SEL)selector
onTarget:(id)target
numberOfArguments:(NSInteger)numberOfArguments, ...;
/**
* @method doubleByPerformingSelector:onTarget:numberOfArguments:...
* @abstract Performs a selector on a class or object.
* @param selector The selector to perform.
* @param target The target class or object to perform the selector on.
* @param numberOfArguments Number of arguments in the argument list.
* @param ... An optional argument list, each argument should be of type GINArgument.
* @return double The result of the selector.
*/
+ (double)doubleByPerformingSelector:(SEL)selector
onTarget:(id)target
numberOfArguments:(NSInteger)numberOfArguments, ...;
@end
@@ -1,98 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "FirebaseDynamicLinks/Sources/GINInvocation/GINInvocation.h"
#import "FirebaseDynamicLinks/Sources/GINInvocation/GINArgument.h"
@implementation GINInvocation
// A method that performs a selector on a target object, and return the result.
+ (id)objectByPerformingSelector:(SEL)selector
onTarget:(id)target
numberOfArguments:(NSInteger)numberOfArguments, ... {
if (![target respondsToSelector:selector]) {
#if DEBUG
[NSException raise:@"InvalidSelectorException" format:@"Invalid selector send to target"];
#endif
return nil;
}
NSMethodSignature *methodSignature = [target methodSignatureForSelector:selector];
NSInvocation *inv = [NSInvocation invocationWithMethodSignature:methodSignature];
[inv setSelector:selector];
[inv setTarget:target];
int index = 2;
va_list argumentList;
va_start(argumentList, numberOfArguments);
for (NSInteger i = 0; i < numberOfArguments; i++) {
[GINArgument setNextArgumentInList:argumentList atIndex:index inInvocation:inv];
}
va_end(argumentList);
[inv invoke];
// This method only returns object.
if ([methodSignature methodReturnLength]) {
CFTypeRef result;
[inv getReturnValue:&result];
if (result) {
CFRetain(result);
}
return (__bridge_transfer id)result;
}
return nil;
}
// A method that performs a selector on a target object, and return the result.
+ (double)doubleByPerformingSelector:(SEL)selector
onTarget:(id)target
numberOfArguments:(NSInteger)numberOfArguments, ... {
if (![target respondsToSelector:selector]) {
#if DEBUG
[NSException raise:@"InvalidSelectorException" format:@"Invalid selector send to target"];
#endif
return 0;
}
NSMethodSignature *methodSignature = [target methodSignatureForSelector:selector];
NSInvocation *inv = [NSInvocation invocationWithMethodSignature:methodSignature];
[inv setSelector:selector];
[inv setTarget:target];
int index = 2;
va_list argumentList;
va_start(argumentList, numberOfArguments);
for (NSInteger i = 0; i < numberOfArguments; i++) {
[GINArgument setNextArgumentInList:argumentList atIndex:index inInvocation:inv];
}
va_end(argumentList);
[inv invoke];
// This method only returns double.
if ([methodSignature methodReturnLength]) {
double doubleValue;
[inv getReturnValue:&doubleValue];
return doubleValue;
}
return 0;
}
@end
@@ -1,48 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
/** Log level for FIRLogger. */
typedef NS_ENUM(NSInteger, FDLLogLevel) {
FDLLogLevelError = 0,
FDLLogLevelWarning,
FDLLogLevelNotice,
FDLLogLevelInfo,
FDLLogLevelDebug,
};
/**
Used to specify a unique integer for FIRLogger. Add entries ONLY to the end of the enum.
Unique values are specified so that items can be safely removed without affecting the others.
*/
typedef NS_ENUM(NSInteger, FDLLogIdentifier) {
FDLLogIdentifierSetupNilAPIKey = 0,
FDLLogIdentifierSetupNilClientID = 1, // Not used anymore
FDLLogIdentifierSetupNonDefaultApp = 2,
FDLLogIdentifierSetupInvalidDomainURIPrefixScheme = 3,
FDLLogIdentifierSetupInvalidDomainURIPrefix = 4,
FDLLogIdentifierSetupWarnHTTPSScheme = 5,
};
/** The appropriate formatter for using NSInteger in FIRLogger. */
FOUNDATION_EXPORT NSString *const FDLMessageCodeIntegerFormat;
/** Logs a message with FIRLogger. */
FOUNDATION_EXPORT void FDLLog(FDLLogLevel logLevel,
FDLLogIdentifier identifier,
NSString *message,
...) NS_FORMAT_FUNCTION(3, 4);
@@ -1,73 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <TargetConditionals.h>
#if TARGET_OS_IOS
#import "FirebaseDynamicLinks/Sources/Logging/FDLLogging.h"
#ifdef GIN_SCION_LOGGING
#import "FirebaseCore/Extension/FirebaseCoreInternal.h"
FIRLoggerService kFIRLoggerDynamicLinks = @"[FirebaseDynamicLinks]";
#endif // GIN_SCION_LOGGING
#ifdef GIN_SCION_LOGGING
#if __LP64__ // 64-bit
NSString *const FDLMessageCodeIntegerFormat = @"%06ld";
#else // 32-bit
NSString *const FDLMessageCodeIntegerFormat = @"%06d";
#endif // #if __LP64__
NSString *FDLMessageCodeForLogIdentifier(FDLLogIdentifier identifier) {
static NSString *const kMessageCodePrefix = @"I-FDL";
NSString *intString = [NSString stringWithFormat:FDLMessageCodeIntegerFormat, identifier];
return [kMessageCodePrefix stringByAppendingString:intString];
}
#endif // GIN_SCION_LOGGING
void FDLLog(FDLLogLevel logLevel, FDLLogIdentifier identifier, NSString *message, ...) {
va_list args_ptr;
va_start(args_ptr, message);
#ifdef GIN_SCION_LOGGING
NSString *messageCode = FDLMessageCodeForLogIdentifier(identifier);
switch (logLevel) {
case FDLLogLevelError:
FIRLogError(kFIRLoggerDynamicLinks, messageCode, message, args_ptr);
break;
case FDLLogLevelWarning:
FIRLogWarning(kFIRLoggerDynamicLinks, messageCode, message, args_ptr);
break;
case FDLLogLevelNotice:
FIRLogNotice(kFIRLoggerDynamicLinks, messageCode, message, args_ptr);
break;
case FDLLogLevelInfo:
FIRLogInfo(kFIRLoggerDynamicLinks, messageCode, message, args_ptr);
break;
case FDLLogLevelDebug:
FIRLogDebug(kFIRLoggerDynamicLinks, messageCode, message, args_ptr);
break;
}
#else
NSLogv(message, args_ptr);
#endif // GIN_SCION_LOGGING
va_end(args_ptr);
}
#endif // TARGET_OS_IOS
@@ -1,560 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/**
* @abstract Enum used to define the desired path length for shortened Dynamic Link URLs.
*/
typedef NS_ENUM(NSInteger, FIRShortDynamicLinkPathLength) {
/**
* Uses the server-default for the path length. See https://goo.gl/8yDAqC for more information.
*/
FIRShortDynamicLinkPathLengthDefault = 0,
/** Typical short link for non-sensitive links. */
FIRShortDynamicLinkPathLengthShort,
/** Short link with an extra long path for great difficulty in guessing. */
FIRShortDynamicLinkPathLengthUnguessable,
} NS_SWIFT_NAME(ShortDynamicLinkPathLength);
/**
* @abstract The definition of the completion block used by URL shortener.
* @param shortURL Shortened URL.
* @param warnings Warnings that describe usability or function limitations of the generated
* short link. Usually presence of warnings means parameters format error, parameters value
* error or missing parameter.
* @param error Error if URL can't be shortened.
*/
typedef void (^FIRDynamicLinkShortenerCompletion)(NSURL *_Nullable shortURL,
NSArray<NSString *> *_Nullable warnings,
NSError *_Nullable error)
NS_SWIFT_UNAVAILABLE("Use Swift's closure syntax instead.");
/**
* @class FIRDynamicLinkGoogleAnalyticsParameters
* @abstract The Dynamic Link analytics parameters.
*/
NS_SWIFT_NAME(DynamicLinkGoogleAnalyticsParameters)
@interface FIRDynamicLinkGoogleAnalyticsParameters : NSObject
/**
* @property source
* @abstract The utm_source analytics parameter.
*/
@property(nonatomic, copy, nullable) NSString *source;
/**
* @property medium
* @abstract The utm_medium analytics parameter.
*/
@property(nonatomic, copy, nullable) NSString *medium;
/**
* @property campaign
* @abstract The utm_campaign analytics parameter.
*/
@property(nonatomic, copy, nullable) NSString *campaign;
/**
* @property term
* @abstract The utm_term analytics parameter.
*/
@property(nonatomic, copy, nullable) NSString *term;
/**
* @property content
* @abstract The utm_content analytics parameter.
*/
@property(nonatomic, copy, nullable) NSString *content;
/**
* @method parametersWithSource:medium:campaign:
* @abstract The preferred factory method for creating the analytics parameters object. It includes
* the commonly-used source, medium, and campaign fields.
* @param source The utm_source analytics parameter.
* @param medium The utm_medium analytics parameter.
* @param campaign The utm_campaign analytics parameter.
* @return Returns An object to be used with FIRDynamicLinkURLComponents to add analytics parameters
* to a generated Dynamic Link URL.
*/
+ (instancetype)parametersWithSource:(NSString *)source
medium:(NSString *)medium
campaign:(NSString *)campaign
NS_SWIFT_UNAVAILABLE("Use init(source:medium:campaign:)");
/**
* @method parameters
* @abstract A factory method for creating the analytics parameters object.
* @return Returns an object to be used with FIRDynamicLinkURLComponents to add analytics parameters
* to a generated Dynamic Link URL.
*/
+ (instancetype)parameters NS_SWIFT_UNAVAILABLE("Use init()");
/**
* @method initWithSource:medium:campaign:
* @abstract The preferred instance method for creating the analytics parameters object. It
* includes the commonly-used source, medium, and campaign fields.
* @param source The utm_source analytics parameter.
* @param medium The utm_medium analytics parameter.
* @param campaign The utm_campaign analytics parameter.
* @return Returns An object to be used with FIRDynamicLinkURLComponents to add analytics parameters
* to a generated Dynamic Link URL.
*/
- (instancetype)initWithSource:(NSString *)source
medium:(NSString *)medium
campaign:(NSString *)campaign;
/**
* @method init
* @return Returns an object to be used with FIRDynamicLinkURLComponents to add analytics parameters
* to a generated Dynamic Link URL.
*/
- (instancetype)init;
@end
/**
* @class FIRDynamicLinkIOSParameters
* @abstract The Dynamic Link iOS parameters.
*/
NS_SWIFT_NAME(DynamicLinkIOSParameters)
@interface FIRDynamicLinkIOSParameters : NSObject
/**
* @property bundleID
* @abstract The bundle ID of the iOS app to use to open the link.
*/
@property(nonatomic, copy, nullable, readonly) NSString *bundleID;
/**
* @property appStoreID
* @abstract The appStore ID of the iOS app in AppStore.
*/
@property(nonatomic, copy, nullable) NSString *appStoreID;
/**
* @property fallbackURL
* @abstract The link to open when the app isn't installed. Specify this to do something other than
* install the app from the App Store when the app isn't installed, such as open the mobile
* web version of the content, or display a promotional page for the app.
*/
@property(nonatomic, nullable) NSURL *fallbackURL;
/**
* @property customScheme
* @abstract The target app's custom URL scheme, if defined to be something other than the app's
* bundle ID
*/
@property(nonatomic, copy, nullable) NSString *customScheme;
/**
* @property iPadBundleID
* @abstract The bundle ID of the iOS app to use on iPads to open the link. This is only required if
* there are separate iPhone and iPad applications.
*/
@property(nonatomic, copy, nullable) NSString *iPadBundleID;
/**
* @property iPadFallbackURL
* @abstract The link to open on iPads when the app isn't installed. Specify this to do something
* other than install the app from the App Store when the app isn't installed, such as open the
* web version of the content, or display a promotional page for the app.
*/
@property(nonatomic, nullable) NSURL *iPadFallbackURL;
/**
@property minimumAppVersion
@abstract The minimum version of your app that can open the link. If the
* installed app is an older version, the user is taken to the AppStore to upgrade the app.
* Note: It is app's developer responsibility to open AppStore when received link declares
* higher minimumAppVersion than currently installed.
*/
@property(nonatomic, copy, nullable) NSString *minimumAppVersion;
/**
* @method parametersWithBundleID:
* @abstract A method for creating the iOS parameters object.
* @param bundleID The bundle ID of the iOS app to use to open the link.
* @return Returns an object to be used with FIRDynamicLinkURLComponents to add iOS parameters to a
* generated Dynamic Link URL.
*/
+ (instancetype)parametersWithBundleID:(NSString *)bundleID
NS_SWIFT_UNAVAILABLE("Use initWithBundleID()");
/**
* @method initWithBundleID:
* @abstract A method for creating the iOS parameters object.
* @param bundleID The bundle ID of the iOS app to use to open the link.
* @return Returns an object to be used with FIRDynamicLinkURLComponents to add iOS parameters to a
* generated Dynamic Link URL.
*/
- (instancetype)initWithBundleID:(NSString *)bundleID;
@end
/**
* @class FIRDynamicLinkItunesConnectAnalyticsParameters
* @abstract The Dynamic Link iTunes Connect parameters.
*/
NS_SWIFT_NAME(DynamicLinkItunesConnectAnalyticsParameters)
@interface FIRDynamicLinkItunesConnectAnalyticsParameters : NSObject
/**
* @property affiliateToken
* @abstract The iTunes Connect affiliate token.
*/
@property(nonatomic, copy, nullable) NSString *affiliateToken;
/**
* @property campaignToken
* @abstract The iTunes Connect campaign token.
*/
@property(nonatomic, copy, nullable) NSString *campaignToken;
/**
* @property providerToken
* @abstract The iTunes Connect provider token.
*/
@property(nonatomic, copy, nullable) NSString *providerToken;
/**
* @method parameters
* @abstract A method for creating the iTunes Connect parameters object.
* @return Returns an object to be used with FIRDynamicLinkURLComponents to add iTunes Connect
* parameters to a generated Dynamic Link URL.
*/
+ (instancetype)parameters NS_SWIFT_UNAVAILABLE("Use init()");
/**
* @method init
* @abstract A method for creating the iTunes Connect parameters object.
* @return Returns an object to be used with FIRDynamicLinkURLComponents to add iTunes Connect
* parameters to a generated Dynamic Link URL.
*/
- (instancetype)init;
@end
/**
* @class FIRDynamicLinkAndroidParameters
* @abstract The Dynamic Link Android parameters.
*/
NS_SWIFT_NAME(DynamicLinkAndroidParameters)
@interface FIRDynamicLinkAndroidParameters : NSObject
/**
* @property packageName
* @abstract The Android app's package name.
*/
@property(nonatomic, copy, nullable, readonly) NSString *packageName;
/**
* @property fallbackURL
* @abstract The link to open when the app isn't installed. Specify this to do something other than
* install the app from the Play Store when the app isn't installed, such as open the mobile web
* version of the content, or display a promotional page for the app.
*/
@property(nonatomic, nullable) NSURL *fallbackURL;
/**
@property minimumVersion
@abstract The version code of the minimum version of your app that can open the link. If the
* installed app is an older version, the user is taken to the Play Store to upgrade the app.
*/
@property(nonatomic) NSInteger minimumVersion;
/**
* @method parametersWithPackageName:
* @abstract A method for creating the Android parameters object.
* @param packageName The Android app's package name.
* @return Returns an object to be used with FIRDynamicLinkURLComponents to add Android parameters
* to a generated Dynamic Link URL.
*/
+ (instancetype)parametersWithPackageName:(NSString *)packageName
NS_SWIFT_UNAVAILABLE("Use initWithPackageName()");
/**
* @method initWithPackageName:
* @abstract A method for creating the Android parameters object.
* @param packageName The Android app's package name.
* @return Returns an object to be used with FIRDynamicLinkURLComponents to add Android parameters
* to a generated Dynamic Link URL.
*/
- (instancetype)initWithPackageName:(NSString *)packageName;
@end
/**
* @class FIRDynamicLinkSocialMetaTagParameters
* @abstract The Dynamic Link Social Meta Tag parameters.
*/
NS_SWIFT_NAME(DynamicLinkSocialMetaTagParameters)
@interface FIRDynamicLinkSocialMetaTagParameters : NSObject
/**
* @property title
* @abstract The title to use when the Dynamic Link is shared in a social post.
*/
@property(nonatomic, copy, nullable) NSString *title;
/**
* @property descriptionText
* @abstract The description to use when the Dynamic Link is shared in a social post.
*/
@property(nonatomic, copy, nullable) NSString *descriptionText;
/**
* @property imageURL
* @abstract The URL to an image related to this link.
*/
@property(nonatomic, nullable) NSURL *imageURL;
/**
* @method parameters
* @abstract A method for creating the Social Meta Tag parameters object.
* @return Returns an object to be used with FIRDynamicLinkURLComponents to add Social Meta Tag
* parameters to a generated Dynamic Link URL.
*/
+ (instancetype)parameters NS_SWIFT_UNAVAILABLE("Use init()");
/**
* @method init
* @abstract A method for creating the Social Meta Tag parameters object.
* @return Returns an object to be used with FIRDynamicLinkURLComponents to add Social Meta Tag
* parameters to a generated Dynamic Link URL.
*/
- (instancetype)init;
@end
/**
* @class FIRDynamicLinkNavigationInfoParameters
* @abstract Options class for defining navigation behavior of the Dynamic Link.
*/
NS_SWIFT_NAME(DynamicLinkNavigationInfoParameters)
@interface FIRDynamicLinkNavigationInfoParameters : NSObject
/**
* @property forcedRedirectEnabled
* @abstract Property defines should forced non-interactive redirect be used when link is tapped on
* mobile device. Default behavior is to disable force redirect and show interstitial page where
* user tap will initiate navigation to the App (or AppStore if not installed). Disabled force
* redirect normally improves reliability of the click.
*/
@property(nonatomic, getter=isForcedRedirectEnabled) BOOL forcedRedirectEnabled;
/**
* @method parameters
* @abstract A method for creating the Navigation Info parameters object.
* @return Returns an object to be used with FIRDynamicLinkURLComponents to add Navigation Info
* parameters to a generated Dynamic Link URL.
*/
+ (instancetype)parameters NS_SWIFT_UNAVAILABLE("Use init()");
/**
* @method init
* @abstract A method for creating the Navigation Info parameters object.
* @return Returns an object to be used with FIRDynamicLinkURLComponents to add Navigation Info
* parameters to a generated Dynamic Link URL.
*/
- (instancetype)init;
@end
/**
* @class FIRDynamicLinkOtherPlatformParameters
* @abstract Options class for defining other platform(s) parameters of the Dynamic Link.
* Other here means not covered by specific parameters (not iOS and not Android).
*/
NS_SWIFT_NAME(DynamicLinkOtherPlatformParameters)
@interface FIRDynamicLinkOtherPlatformParameters : NSObject
/**
* @property fallbackUrl
* @abstract Property defines fallback URL to navigate to when Dynamic Link is clicked on
* other platform.
*/
@property(nonatomic, nullable) NSURL *fallbackUrl;
/**
* @method parameters
* @abstract A method for creating the Other platform parameters object.
* @return Returns an object to be used with FIRDynamicLinkURLComponents to add Other Platform
* parameters to a generated Dynamic Link URL.
*/
+ (instancetype)parameters NS_SWIFT_UNAVAILABLE("Use init()");
/**
* @method init
* @abstract A method for creating the Other platform parameters object.
* @return Returns an object to be used with FIRDynamicLinkURLComponents to add Other Platform
* parameters to a generated Dynamic Link URL.
*/
- (instancetype)init;
@end
/**
* @class FIRDynamicLinkComponentsOptions
* @abstract Options class for defining how Dynamic Link URLs are generated.
*/
NS_SWIFT_NAME(DynamicLinkComponentsOptions)
@interface FIRDynamicLinkComponentsOptions : NSObject
/**
* @property pathLength
* @abstract Specifies the length of the path component of a short Dynamic Link.
*/
@property(nonatomic) FIRShortDynamicLinkPathLength pathLength;
/**
* @method options
* @abstract A method for creating the Dynamic Link components options object.
* @return Returns an object to be used with FIRDynamicLinkURLComponents to specify options related
* to the generation of Dynamic Link URLs.
*/
+ (instancetype)options NS_SWIFT_UNAVAILABLE("Use init()");
/**
* @method init
* @abstract A method for creating the Dynamic Link components options object.
* @return Returns an object to be used with FIRDynamicLinkURLComponents to specify options related
* to the generation of Dynamic Link URLs.
*/
- (instancetype)init;
@end
/**
* @class FIRDynamicLinkComponents
* @abstract The class used for Dynamic Link URL generation; supports creation of short and long
* Dynamic Link URLs. Short URLs will have a domain and a randomized path; long URLs will have a
* domain and a query that contains all of the Dynamic Link parameters.
*/
NS_SWIFT_NAME(DynamicLinkComponents)
@interface FIRDynamicLinkComponents : NSObject
/**
* @property analyticsParameters
* @abstract Applies Analytics parameters to a generated Dynamic Link URL.
*/
@property(nonatomic, nullable) FIRDynamicLinkGoogleAnalyticsParameters *analyticsParameters;
/**
* @property socialMetaTagParameters
* @abstract Applies Social Meta Tag parameters to a generated Dynamic Link URL.
*/
@property(nonatomic, nullable) FIRDynamicLinkSocialMetaTagParameters *socialMetaTagParameters;
/**
* @property iOSParameters
* @abstract Applies iOS parameters to a generated Dynamic Link URL.
*/
@property(nonatomic, nullable) FIRDynamicLinkIOSParameters *iOSParameters;
/**
* @property iTunesConnectParameters
* @abstract Applies iTunes Connect parameters to a generated Dynamic Link URL.
*/
@property(nonatomic, nullable)
FIRDynamicLinkItunesConnectAnalyticsParameters *iTunesConnectParameters;
/**
* @property androidParameters
* @abstract Applies Android parameters to a generated Dynamic Link URL.
*/
@property(nonatomic, nullable) FIRDynamicLinkAndroidParameters *androidParameters;
/**
* @property navigationInfoParameters
* @abstract Applies Navigation Info parameters to a generated Dynamic Link URL.
*/
@property(nonatomic, nullable) FIRDynamicLinkNavigationInfoParameters *navigationInfoParameters;
/**
* @property otherPlatformParameters
* @abstract Applies Other platform parameters to a generated Dynamic Link URL.
*/
@property(nonatomic, nullable) FIRDynamicLinkOtherPlatformParameters *otherPlatformParameters;
/**
* @property options
* @abstract Defines behavior for generating Dynamic Link URLs.
*/
@property(nonatomic, nullable) FIRDynamicLinkComponentsOptions *options;
/**
* @property link
* @abstract The link the target app will open. You can specify any URL the app can handle, such as
* a link to the app's content, or a URL that initiates some app-specific logic such as
* crediting the user with a coupon, or displaying a specific welcome screen. This link must be
* a well-formatted URL, be properly URL-encoded, and use the HTTP or HTTPS scheme.
*/
@property(nonatomic) NSURL *link;
/**
* @property domain
* @abstract The Firebase project's Dynamic Links domain. You can find this value in the Dynamic
* Links section of the Firebase console.
* https://console.firebase.google.com/
*/
@property(nonatomic, nullable, copy) NSString *domain;
/**
* @property url
* @abstract A generated long Dynamic Link URL.
*/
@property(nonatomic, nullable, readonly) NSURL *url;
/**
* @method componentsWithLink:domainURIPrefix:
* @abstract Generates a Dynamic Link URL components object with the minimum necessary parameters
* set to generate a fully-functional Dynamic Link.
* @param link Deep link to be stored in created Dynamic link. This link also called "payload" of
* the Dynamic link.
* @param domainURIPrefix Domain URI Prefix of your App. This value must be your assigned
* domain from the Firebase console. (e.g. https://xyz.page.link) The domain URI prefix must
* start with a valid HTTPS scheme (https://).
* @return Returns an instance of FIRDynamicLinkComponents if the parameters succeed validation,
* else returns nil.
*/
+ (nullable instancetype)componentsWithLink:(NSURL *)link
domainURIPrefix:(NSString *)domainURIPrefix
NS_SWIFT_UNAVAILABLE("Use init(link:domainURIPrefix:)");
/**
* @method initWithLink:domainURIPrefix:
* @abstract Generates a Dynamic Link URL components object with the minimum necessary parameters
* set to generate a fully-functional Dynamic Link.
* @param link Deep link to be stored in created Dynamic link. This link also called "payload" of
* the Dynamic link.
* @param domainURIPrefix Domain URI Prefix of your App. This value must be your assigned
* domain from the Firebase console. (e.g. https://xyz.page.link) The domain URI prefix must
* start with a valid HTTPS scheme (https://).
* @return Returns an instance of FIRDynamicLinkComponents if the parameters succeed validation,
* else returns nil.
*/
- (nullable instancetype)initWithLink:(NSURL *)link domainURIPrefix:(NSString *)domainURIPrefix;
/**
* @method shortenURL:options:completion:
* @abstract Shortens a Dynamic Link URL. This method may be used for shortening a custom URL that
* was not generated using FIRDynamicLinkComponents.
* @param url A properly-formatted long Dynamic Link URL.
* @param completion A block to be executed upon completion of the shortening attempt. It is
* guaranteed to be executed once and on the main thread.
*/
+ (void)shortenURL:(NSURL *)url
options:(FIRDynamicLinkComponentsOptions *_Nullable)options
completion:(void (^)(NSURL *_Nullable shortURL,
NSArray<NSString *> *_Nullable warnings,
NSError *_Nullable error))completion;
/**
* @method shortenWithCompletion:
* @abstract Generates a short Dynamic Link URL using all set parameters.
* @param completion A block to be executed upon completion of the shortening attempt. It is
* guaranteed to be executed once and on the main thread.
*/
- (void)shortenWithCompletion:(void (^)(NSURL *_Nullable shortURL,
NSArray<NSString *> *_Nullable warnings,
NSError *_Nullable error))completion;
@end
NS_ASSUME_NONNULL_END
@@ -1,90 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/**
* @file FIRDynamicLink.h
* @abstract Dynamic Link object used in Firebase Dynamic Links.
*/
/**
* @abstract The match type of the Dynamic Link.
*/
typedef NS_ENUM(NSUInteger, FIRDLMatchType) {
/**
* The match has not been achieved.
*/
FIRDLMatchTypeNone,
/**
* The match between the Dynamic Link and this device may not be perfect, hence you should not
* reveal any personal information related to the Dynamic Link.
*/
FIRDLMatchTypeWeak,
/**
* The match between the Dynamic Link and this device has high confidence but small possibility of
* error still exist.
*/
FIRDLMatchTypeDefault,
/**
* The match between the Dynamic Link and this device is exact, hence you may reveal personal
* information related to the Dynamic Link.
*/
FIRDLMatchTypeUnique,
} NS_SWIFT_NAME(DLMatchType);
/**
* @class FIRDynamicLink
* @abstract A received Dynamic Link.
*/
NS_SWIFT_NAME(DynamicLink)
@interface FIRDynamicLink : NSObject
/**
* @property url
* @abstract The URL that was passed to the app.
*/
@property(nonatomic, copy, readonly, nullable) NSURL *url;
/**
* @property matchType
* @abstract The match type of the received Dynamic Link.
*/
@property(nonatomic, assign, readonly) FIRDLMatchType matchType;
/**
* @property utmParametersDictionary
* @abstract UTM parameters associated with a Firebase Dynamic Link.
*/
@property(nonatomic, copy, readonly) NSDictionary<NSString *, id> *utmParametersDictionary;
/**
* @property minimumAppVersion
* @abstract The minimum iOS application version that supports the Dynamic Link. This is retrieved
* from the imv= parameter of the Dynamic Link URL. Note: This is not the minimum iOS system
* version, but the minimum app version. If app version of the opening app is less than the
* value of this property, then app expected to open AppStore to allow user to download most
* recent version. App can notify or ask user before opening AppStore.
*/
@property(nonatomic, copy, readonly, nullable) NSString *minimumAppVersion;
- (instancetype)init NS_UNAVAILABLE;
@end
NS_ASSUME_NONNULL_END
@@ -1,147 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
#import "FIRDynamicLink.h"
#import "FIRDynamicLinksCommon.h"
NS_ASSUME_NONNULL_BEGIN
/**
* @file FIRDynamicLinks.h
* @abstract Firebase Dynamic Links
*/
/**
* @class FIRDynamicLinks
* @abstract A class that checks for pending Dynamic Links and parses URLs.
* This class is available on iOS only.
*/
DEPRECATED_MSG_ATTRIBUTE(
"Firebase Dynamic Links is deprecated and the service will shut down on August 25, 2025.")
NS_EXTENSION_UNAVAILABLE_IOS("Firebase Dynamic Links is not supported for iOS extensions.")
API_UNAVAILABLE(macos, tvos, watchos)
NS_SWIFT_NAME(DynamicLinks)
@interface FIRDynamicLinks : NSObject
/**
* @method dynamicLinks
* @abstract Shared instance of FIRDynamicLinks.
* @return Shared instance of FIRDynamicLinks.
*/
+ (instancetype)dynamicLinks NS_SWIFT_NAME(dynamicLinks());
/**
* @method shouldHandleDynamicLinkFromCustomSchemeURL:
* @abstract Determine whether FIRDynamicLinks should handle the given URL. This does not
* guarantee that |dynamicLinkFromCustomSchemeURL:| will return a non-nil value, but it means
* the client should not attempt to handle the URL.
* @param url Custom scheme URL.
* @return Whether the URL can be handled by FIRDynamicLinks.
*/
- (BOOL)shouldHandleDynamicLinkFromCustomSchemeURL:(NSURL *)url
NS_SWIFT_NAME(shouldHandleDynamicLink(fromCustomSchemeURL:));
/**
* @method dynamicLinkFromCustomSchemeURL:
* @abstract Get a Dynamic Link from a custom scheme URL. This method parses URLs with a custom
* scheme, for instance, "comgoogleapp://google/link?deep_link_id=abc123". It is suggested to
* call it inside your |UIApplicationDelegate|'s
* |application:openURL:sourceApplication:annotation| and |application:openURL:options:|
* methods.
* @param url Custom scheme URL.
* @return Dynamic Link object if the URL is valid and has link parameter, otherwise nil.
*/
- (nullable FIRDynamicLink *)dynamicLinkFromCustomSchemeURL:(NSURL *)url
NS_SWIFT_NAME(dynamicLink(fromCustomSchemeURL:));
/**
* @method dynamicLinkFromUniversalLinkURL:completion:
* @abstract Get a Dynamic Link from a universal link URL. This method parses universal link
* URLs, for instance,
* "https://example.page.link?link=https://www.google.com&ibi=com.google.app&ius=comgoogleapp".
* It is suggested to call it inside your |UIApplicationDelegate|'s
* |application:continueUserActivity:restorationHandler:| method.
* @param url Custom scheme URL.
* @param completion A block that handles the outcome of attempting to get a Dynamic Link from a
* universal link URL.
*/
- (void)dynamicLinkFromUniversalLinkURL:(NSURL *)url
completion:(void (^)(FIRDynamicLink *_Nullable dynamicLink,
NSError *_Nullable error))completion
NS_SWIFT_NAME(dynamicLink(fromUniversalLink:completion:));
/**
* @method dynamicLinkFromUniversalLinkURL:
* @abstract Get a Dynamic Link from a universal link URL. This method parses universal link
* URLs, for instance,
* "https://example.page.link?link=https://www.google.com&ibi=com.google.app&ius=comgoogleapp".
* It is suggested to call it inside your |UIApplicationDelegate|'s
* |application:continueUserActivity:restorationHandler:| method.
* @param url Custom scheme URL.
* @return Dynamic Link object if the URL is valid and has link parameter, otherwise nil.
*/
- (nullable FIRDynamicLink *)dynamicLinkFromUniversalLinkURL:(NSURL *)url
NS_SWIFT_NAME(dynamicLink(fromUniversalLink:))
DEPRECATED_MSG_ATTRIBUTE("Use dynamicLinkFromUniversalLinkURL:completion: instead.");
/**
* @method handleUniversalLink:completion:
* @abstract Convenience method to handle a Universal Link whether it is long or short.
* @param url A Universal Link URL.
* @param completion A block that handles the outcome of attempting to create a FIRDynamicLink.
* @return YES if FIRDynamicLinks is handling the link, otherwise, NO.
*/
- (BOOL)handleUniversalLink:(NSURL *)url
completion:(void (^)(FIRDynamicLink *_Nullable dynamicLink,
NSError *_Nullable error))completion;
/**
* @method resolveShortLink:completion:
* @abstract Retrieves the details of the Dynamic Link that the shortened URL represents.
* @param url A Short Dynamic Link.
* @param completion Block to be run upon completion.
*/
- (void)resolveShortLink:(NSURL *)url
completion:(void (^)(NSURL *_Nullable url, NSError *_Nullable error))completion;
/**
* @method matchesShortLinkFormat:
* @abstract Determines if a given URL matches the given short Dynamic Link format.
* @param url A URL.
* @return YES if the URL is a short Dynamic Link, otherwise, NO.
*/
- (BOOL)matchesShortLinkFormat:(NSURL *)url;
/**
* @method performDiagnosticsWithCompletion:
* @abstract Performs basic FDL self diagnostic. Method effect on startup latency is quite small
* and no user-visible UI is presented. This method should be used for debugging purposes.
* App developers are encouraged to include output, generated by this method, to the support
* requests sent to Firebase support.
* @param completionHandler Handler that will be called when diagnostic completes.
* If value of the completionHandler is nil than diagnostic output will be printed to
* the standard output.
* diagnosticOutput String that includes diagnostic information.
* hasErrors Param will have YES value if diagnostic method detected error, NO otherwise.
*/
+ (void)performDiagnosticsWithCompletion:(void (^_Nullable)(NSString *diagnosticOutput,
BOOL hasErrors))completionHandler;
@end
NS_ASSUME_NONNULL_END
@@ -1,41 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
@class FIRDynamicLink;
NS_ASSUME_NONNULL_BEGIN
/**
* @file FIRDynamicLinksCommon.h
* @abstract Commonly shared definitions within Firebase Dynamic Links.
*/
/**
* @abstract The definition of the block used by |resolveShortLink:completion:|
*/
typedef void (^FIRDynamicLinkResolverHandler)(NSURL* _Nullable url, NSError* _Nullable error)
NS_SWIFT_UNAVAILABLE("Use Swift's closure syntax instead.");
/**
* @abstract The definition of the block used by |handleUniversalLink:completion:|
*/
typedef void (^FIRDynamicLinkUniversalLinkHandler)(FIRDynamicLink* _Nullable dynamicLink,
NSError* _Nullable error)
NS_SWIFT_UNAVAILABLE("Use Swift's closure syntax instead.");
NS_ASSUME_NONNULL_END
@@ -1,20 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "FDLURLComponents.h"
#import "FIRDynamicLink.h"
#import "FIRDynamicLinks.h"
#import "FIRDynamicLinksCommon.h"
@@ -1,46 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyTracking</key>
<false/>
<key>NSPrivacyTrackingDomains</key>
<array>
</array>
<key>NSPrivacyCollectedDataTypes</key>
<array>
<dict>
<key>NSPrivacyCollectedDataType</key>
<string>NSPrivacyCollectedDataTypeOtherDataTypes</string>
<key>NSPrivacyCollectedDataTypeLinked</key>
<false/>
<key>NSPrivacyCollectedDataTypeTracking</key>
<false/>
<key>NSPrivacyCollectedDataTypePurposes</key>
<array>
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
</array>
</dict>
</array>
<key>NSPrivacyAccessedAPITypes</key>
<array>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>C617.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>1C8F.1</string>
</array>
</dict>
</array>
</dict>
</plist>
@@ -1,33 +0,0 @@
/*
* Copyright 2021 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
@interface FDLDeviceHeuristicsHelper : NSObject
/**
* Creates DeviceInfo dictionary based on the provided information.
*/
+ (NSDictionary<NSString *, NSObject *> *)
FDLDeviceInfoDictionaryFromResolutionHeight:(NSInteger)resolutionHeight
resolutionWidth:(NSInteger)resolutionWidth
locale:(NSString *)locale
localeRaw:(NSString *)localeRaw
localeFromWebview:(NSString *)localeFromWebView
timeZone:(NSString *)timezone
modelName:(NSString *)modelName;
@end
@@ -1,43 +0,0 @@
/*
* Copyright 2021 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "FirebaseDynamicLinks/Sources/Utilities/FDLDeviceHeuristicsHelper.h"
#import <Foundation/Foundation.h>
@implementation FDLDeviceHeuristicsHelper
/**
* Creates DeviceInfo dictionary based on the provided information.
*/
+ (NSDictionary<NSString *, NSObject *> *)
FDLDeviceInfoDictionaryFromResolutionHeight:(NSInteger)resolutionHeight
resolutionWidth:(NSInteger)resolutionWidth
locale:(NSString *)locale
localeRaw:(NSString *)localeRaw
localeFromWebview:(NSString *)localeFromWebView
timeZone:(NSString *)timezone
modelName:(NSString *)modelName {
return @{
@"screenResolutionHeight" : @(resolutionHeight),
@"screenResolutionWidth" : @(resolutionWidth),
@"languageCode" : locale,
@"languageCodeRaw" : localeRaw,
@"languageCodeFromWebview" : localeFromWebView,
@"timezone" : timezone,
@"deviceModelName" : modelName,
};
}
@end
@@ -1,148 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
FOUNDATION_EXPORT NSString *const kFIRDLParameterDeepLinkIdentifier;
FOUNDATION_EXPORT NSString *const kFIRDLParameterLink;
FOUNDATION_EXPORT NSString *const kFIRDLParameterMinimumAppVersion;
FOUNDATION_EXPORT NSString *const kFIRDLParameterCampaign;
FOUNDATION_EXPORT NSString *const kFIRDLParameterContent;
FOUNDATION_EXPORT NSString *const kFIRDLParameterMedium;
FOUNDATION_EXPORT NSString *const kFIRDLParameterTerm;
FOUNDATION_EXPORT NSString *const kFIRDLParameterSource;
FOUNDATION_EXPORT NSString *const kFIRDLParameterMatchType;
FOUNDATION_EXPORT NSString *const kFIRDLParameterInviteId;
FOUNDATION_EXPORT NSString *const kFIRDLParameterWeakMatchEndpoint;
FOUNDATION_EXPORT NSString *const kFIRDLParameterMatchMessage;
FOUNDATION_EXPORT NSString *const kFIRDLParameterRequestIPVersion;
/**
* After a Dynamic Link URL is opened in Safari, a cookie is dropped on the domain goo.gl. When a
* specific URL is used, JavaScript checks if there's a cookie and, if it exists, redirects to the
* custom-scheme URL stored in it. That causes application:openURL:options: to be called in
* AppDelegate with the custom-scheme URL. This method creates and returns the URL required to check
* for the presence of the FDL cookie on goo.gl.
*/
NSURL *FIRDLCookieRetrievalURL(NSString *urlScheme, NSString *bundleID);
/**
* Creates a URL query string from the contents of an NSDictionary. Single-percent-encoded using
* allowed query characters.
*/
NSString *FIRDLURLQueryStringFromDictionary(NSDictionary<NSString *, NSString *> *dictionary);
/**
* @fn FIRDLDictionaryFromQuery
* @abstract This receives a URL query parameter string and parses it into a dictionary that
* represents the query. This method is necessary as |gtm_dictionaryWithHttpArgumentsString:|
* removes the pluses with spaces and, as a result, cannot be used without first replacing all
* instances of the plus character with '%2B'.
* @param queryString The query string of a URL.
* @return returns a dictionary of type <NSString *, NSString *> that represents the query.
*/
NSDictionary *FIRDLDictionaryFromQuery(NSString *queryString);
/**
* @fn FIRDLDeepLinkURLWithInviteID
* @abstract A method that takes the given parameters and constructs a url-scheme-based URL that can
* be opened within the containing app, so that the correct link handlers are fired. This is
* used after Firebase Dynamic Links either has found a pending deep link, or no link was found.
* @param inviteID The invitation ID associated with the Dynamic Link. Included in App Invite URLs.
* @param deepLinkString The deep link, if any, found in the response from a server lookup.
* @param utmSource The UTM source, if any, found in the response from a server lookup.
* @param utmMedium The UTM medium, if any, found in the response from a server lookup.
* @param utmCampaign The UTM campaign, if any, found in the response from a server lookup.
* @param isWeakLink This value provides information is deep link was weak-matched.
* @param weakMatchEndpoint This value provides information about which endpoint, IPv4 or IPv6, was
* used to perform the lookup if weak match is used.
* @param minAppVersion The minimum app version string, if any, found in the response from a server
* lookup. If this value is provided, the app developer can use it to determine whether or not
* to handle the deep link, or to encourage their users to perhaps upgrade their app.
* @param URLScheme Custom URL scheme of the Application.
*/
NSURL *FIRDLDeepLinkURLWithInviteID(NSString *_Nullable inviteID,
NSString *_Nullable deepLinkString,
NSString *_Nullable utmSource,
NSString *_Nullable utmMedium,
NSString *_Nullable utmCampaign,
NSString *_Nullable utmContent,
NSString *_Nullable utmTerm,
BOOL isWeakLink,
NSString *_Nullable weakMatchEndpoint,
NSString *_Nullable minAppVersion,
NSString *URLScheme,
NSString *_Nullable matchMessage);
/**
* @fn FIRDLOSVersionSupported(NSString *systemVersion, NSString *minSupportedVersion)
* @abstract Determines if the system version is greater than or equal to the minSupportedVersion.
* @param systemVersion The iOS version to use as the current version in the comparison.
* @param minSupportedVersion The minimum iOS system version that is supported.
* @return YES if the system version is greater than or equal to the minimum, otherwise, NO.
*/
BOOL FIRDLOSVersionSupported(NSString *_Nullable systemVersion, NSString *minSupportedVersion);
/**
Returns date of the App installation. Return value may be nil in case of failure.
*/
NSDate *_Nullable FIRDLAppInstallationDate(void);
/**
Returns current device model name.
*/
NSString *FIRDLDeviceModelName(void);
/**
Returns current device locale. The method will try to bring locale format to the same format as
reported by Safari/WebView.
*/
NSString *FIRDLDeviceLocale(void) __deprecated_msg("Use FIRDeviceLocaleRaw instead");
/**
Returns current device locale as reported by iOS.
*/
NSString *FIRDLDeviceLocaleRaw(void);
/**
Returns current device timezone.
*/
NSString *FIRDLDeviceTimezone(void);
/**
Returns is universal link (long FDL link) parsable.
*/
BOOL FIRDLCanParseUniversalLinkURL(NSURL *_Nullable URL);
/**
Return is link matches FDL short link format.
*/
BOOL FIRDLMatchesShortLinkFormat(NSURL *URL);
/**
Returns match type string using server side match type string.
Returned string can be used as customURLScheme URL with parameter kFIRDLParameterMatchType.
*/
NSString *FIRDLMatchTypeStringFromServerString(NSString *_Nullable serverMatchTypeString);
/**
Add custom domains from the info.plist to the internal allowlist.
*/
void FIRDLAddToAllowListForCustomDomainsArray(NSArray *customDomains);
NS_ASSUME_NONNULL_END
@@ -1,368 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <TargetConditionals.h>
#if TARGET_OS_IOS
#import "FirebaseDynamicLinks/Sources/Utilities/FDLUtilities.h"
#import <UIKit/UIDevice.h>
#include <sys/sysctl.h>
NS_ASSUME_NONNULL_BEGIN
NSString *const kFIRDLParameterDeepLinkIdentifier = @"deep_link_id";
NSString *const kFIRDLParameterLink = @"link";
NSString *const kFIRDLParameterMinimumAppVersion = @"imv";
NSString *const kFIRDLParameterCampaign = @"utm_campaign";
NSString *const kFIRDLParameterContent = @"utm_content";
NSString *const kFIRDLParameterMedium = @"utm_medium";
NSString *const kFIRDLParameterSource = @"utm_source";
NSString *const kFIRDLParameterTerm = @"utm_term";
NSString *const kFIRDLParameterMatchType = @"match_type";
NSString *const kFIRDLParameterInviteId = @"invitation_id";
NSString *const kFIRDLParameterWeakMatchEndpoint = @"invitation_weakMatchEndpoint";
NSString *const kFIRDLParameterMatchMessage = @"match_message";
NSString *const kFIRDLParameterRequestIPVersion = @"request_ip_version";
static NSSet *FIRDLCustomDomains = nil;
NSURL *FIRDLCookieRetrievalURL(NSString *urlScheme, NSString *bundleID) {
static NSString *const kFDLBundleIDQueryParameterName = @"fdl_ios_bundle_id";
static NSString *const kFDLURLSchemeQueryParameterName = @"fdl_ios_url_scheme";
NSURLComponents *components = [[NSURLComponents alloc] init];
components.scheme = @"https";
components.host = @"goo.gl";
components.path = @"/app/_/deeplink";
NSMutableArray *queryItems = [NSMutableArray array];
[queryItems addObject:[NSURLQueryItem queryItemWithName:kFDLBundleIDQueryParameterName
value:bundleID]];
[queryItems addObject:[NSURLQueryItem queryItemWithName:kFDLURLSchemeQueryParameterName
value:urlScheme]];
[components setQueryItems:queryItems];
return [components URL];
}
NSString *FIRDLURLQueryStringFromDictionary(NSDictionary<NSString *, NSString *> *dictionary) {
NSMutableString *parameters = [NSMutableString string];
NSCharacterSet *queryCharacterSet = [NSCharacterSet alphanumericCharacterSet];
NSString *parameterFormatString = @"%@%@=%@";
__block NSUInteger index = 0;
[dictionary enumerateKeysAndObjectsUsingBlock:^(NSString *_Nonnull key, NSString *_Nonnull value,
BOOL *_Nonnull stop) {
NSString *delimiter = index++ == 0 ? @"?" : @"&";
NSString *encodedValue =
[value stringByAddingPercentEncodingWithAllowedCharacters:queryCharacterSet];
NSString *parameter =
[NSString stringWithFormat:parameterFormatString, delimiter, key, encodedValue];
[parameters appendString:parameter];
}];
return parameters;
}
NSDictionary *FIRDLDictionaryFromQuery(NSString *queryString) {
NSArray<NSString *> *keyValuePairs = [queryString componentsSeparatedByString:@"&"];
NSMutableDictionary *queryDictionary =
[NSMutableDictionary dictionaryWithCapacity:keyValuePairs.count];
for (NSString *pair in keyValuePairs) {
NSArray *keyValuePair = [pair componentsSeparatedByString:@"="];
if (keyValuePair.count == 2) {
NSString *key = keyValuePair[0];
NSString *value = [keyValuePair[1] stringByRemovingPercentEncoding];
[queryDictionary setObject:value forKey:key];
}
}
return [queryDictionary copy];
}
NSURL *FIRDLDeepLinkURLWithInviteID(NSString *_Nullable inviteID,
NSString *_Nullable deepLinkString,
NSString *_Nullable utmSource,
NSString *_Nullable utmMedium,
NSString *_Nullable utmCampaign,
NSString *_Nullable utmContent,
NSString *_Nullable utmTerm,
BOOL isWeakLink,
NSString *_Nullable weakMatchEndpoint,
NSString *_Nullable minAppVersion,
NSString *URLScheme,
NSString *_Nullable matchMessage) {
// We are unable to use NSURLComponents as NSURLQueryItem is available beginning in iOS 8 and
// appending our query string with NSURLComponents improperly formats the query by adding
// a second '?' in the query.
NSMutableDictionary *queryDictionary = [NSMutableDictionary dictionary];
if (inviteID != nil) {
queryDictionary[kFIRDLParameterInviteId] = inviteID;
}
if (deepLinkString != nil) {
queryDictionary[kFIRDLParameterDeepLinkIdentifier] = deepLinkString;
}
if (utmSource != nil) {
queryDictionary[kFIRDLParameterSource] = utmSource;
}
if (utmMedium != nil) {
queryDictionary[kFIRDLParameterMedium] = utmMedium;
}
if (utmCampaign != nil) {
queryDictionary[kFIRDLParameterCampaign] = utmCampaign;
}
if (utmContent != nil) {
queryDictionary[kFIRDLParameterContent] = utmContent;
}
if (utmTerm != nil) {
queryDictionary[kFIRDLParameterTerm] = utmTerm;
}
if (isWeakLink) {
queryDictionary[kFIRDLParameterMatchType] = @"weak";
} else {
queryDictionary[kFIRDLParameterMatchType] = @"unique";
}
if (weakMatchEndpoint != nil) {
queryDictionary[kFIRDLParameterWeakMatchEndpoint] = weakMatchEndpoint;
}
if (minAppVersion != nil) {
queryDictionary[kFIRDLParameterMinimumAppVersion] = minAppVersion;
}
if (matchMessage != nil) {
queryDictionary[kFIRDLParameterMatchMessage] = matchMessage;
}
NSString *scheme = [URLScheme lowercaseString];
NSString *queryString = FIRDLURLQueryStringFromDictionary(queryDictionary);
NSString *urlString = [NSString stringWithFormat:@"%@://google/link/%@", scheme, queryString];
return [NSURL URLWithString:urlString];
}
BOOL FIRDLOSVersionSupported(NSString *_Nullable systemVersion, NSString *minSupportedVersion) {
systemVersion = systemVersion ?: [UIDevice currentDevice].systemVersion;
return [systemVersion compare:minSupportedVersion options:NSNumericSearch] != NSOrderedAscending;
}
NSDate *_Nullable FIRDLAppInstallationDate(void) {
NSURL *documentsDirectoryURL =
[[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory
inDomains:NSUserDomainMask] firstObject];
if (!documentsDirectoryURL) {
return nil;
}
NSDictionary<NSString *, id> *attributes =
[[NSFileManager defaultManager] attributesOfItemAtPath:documentsDirectoryURL.path error:NULL];
if (attributes[NSFileCreationDate] &&
[attributes[NSFileCreationDate] isKindOfClass:[NSDate class]]) {
return attributes[NSFileCreationDate];
}
return nil;
}
NSString *FIRDLDeviceModelName(void) {
// this method will return string like iPad3,3
// for Simulator this will be x86_64
static NSString *machineString = @"";
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
size_t size;
// compute string size
if (sysctlbyname("hw.machine", NULL, &size, NULL, 0) == 0) {
// get device name
char *machine = calloc(1, size);
if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == 0) {
machineString = [NSString stringWithCString:machine encoding:NSUTF8StringEncoding];
}
free(machine);
}
});
return machineString;
}
NSString *FIRDLDeviceLocale(void) {
// expected return value from this method looks like: @"en-US"
return [[[NSLocale currentLocale] localeIdentifier] stringByReplacingOccurrencesOfString:@"_"
withString:@"-"];
}
NSString *FIRDLDeviceLocaleRaw(void) {
return [[NSLocale currentLocale] localeIdentifier];
}
NSString *FIRDLDeviceTimezone(void) {
NSString *timeZoneName = [[NSTimeZone localTimeZone] name];
return timeZoneName;
}
BOOL FIRDLIsURLForAllowedCustomDomain(NSURL *URL) {
if (URL) {
for (NSURL *allowedCustomDomain in FIRDLCustomDomains) {
// At least one custom domain host name should match at a minimum.
if ([URL.absoluteString hasPrefix:allowedCustomDomain.absoluteString]) {
NSString *urlWithoutDomainURIPrefix =
[URL.absoluteString substringFromIndex:allowedCustomDomain.absoluteString.length];
// The urlWithoutDomainURIPrefix should be starting with '/' or '?' otherwise it means the
// allowed domain is not exactly matching the incoming URL domain prefix.
if ([urlWithoutDomainURIPrefix hasPrefix:@"/"] ||
[urlWithoutDomainURIPrefix hasPrefix:@"?"]) {
// For a valid custom domain DL Suffix the urlWithoutDomainURIPrefix should have:
// 1. At least one path exists OR
// 2. Should have a link query param with an http/https link
NSURLComponents *components =
[[NSURLComponents alloc] initWithString:urlWithoutDomainURIPrefix];
if (components.path && components.path.length > 1) {
// Have a path exists. So valid custom domain.
return true;
}
if (components.queryItems && components.queryItems.count > 0) {
for (NSURLQueryItem *queryItem in components.queryItems) {
// Checks whether we have a link query param
if ([queryItem.name caseInsensitiveCompare:@"link"] == NSOrderedSame) {
// Checks whether link query param value starts with http/https
if (queryItem.value && ([queryItem.value hasPrefix:@"http://"] ||
[queryItem.value hasPrefix:@"https://"])) {
return true;
}
}
}
}
}
}
}
}
return false;
}
/* We are validating following domains in proper format.
*.page.link
*.app.goo.gl
*.page.link/i/
*.app.goo.gl/i/
*/
BOOL FIRDLIsAValidDLWithFDLDomain(NSURL *_Nullable URL) {
BOOL matchesRegularExpression = false;
NSString *urlStr = URL.absoluteString;
if ([URL.host containsString:@".page.link"] || [URL.host containsString:@".app.goo.gl"] ||
[URL.host containsString:@".app.google"]) {
// Matches the *.page.link and *.app.goo.gl domains.
matchesRegularExpression =
([urlStr rangeOfString:
@"^https?://"
@"[a-zA-Z0-9]+((\\.app\\.goo\\.gl)|(\\.page\\.link)|(\\.app\\.google))((\\/"
@"?\\?.*link=https?.*)|(\\/[a-zA-Z0-9-_]+)((\\/?\\?.*=.*)?$|$))"
options:NSRegularExpressionSearch]
.location != NSNotFound);
if (!matchesRegularExpression) {
// Matches the *.page.link/i/ and *.app.goo.gl/i/ domains.
// Checks whether the URL is of the format :
// http(s)://$DOMAIN(.page.link or .app.goo.gl)/i/$ANYTHING
matchesRegularExpression =
([urlStr
rangeOfString:
@"^https?:\\/\\/"
@"[a-zA-Z0-9]+((\\.app\\.goo\\.gl)|(\\.page\\.link)|(\\.app\\.google))\\/i\\/.*$"
options:NSRegularExpressionSearch]
.location != NSNotFound);
}
}
return matchesRegularExpression;
}
/*
DL can be parsed if it :
1. Has http(s)://goo.gl/app* or http(s)://page.link/app* format
2. OR http(s)://$DomainPrefix.page.link or http(s)://$DomainPrefix.app.goo.gl domain with specific
format
3. OR the domain is a listed custom domain
*/
BOOL FIRDLCanParseUniversalLinkURL(NSURL *_Nullable URL) {
// Handle universal links with format |https://goo.gl/app/<appcode>?<parameters>|.
// Also support page.link format.
BOOL isDDLWithAppcodeInPath = ([URL.host isEqual:@"goo.gl"] || [URL.host isEqual:@"page.link"] ||
[URL.host isEqual:@"app.google"]) &&
[URL.path hasPrefix:@"/app"];
return isDDLWithAppcodeInPath || FIRDLIsAValidDLWithFDLDomain(URL) ||
FIRDLIsURLForAllowedCustomDomain(URL);
}
BOOL FIRDLMatchesShortLinkFormat(NSURL *URL) {
// Short Durable Link URLs always have a path or it should be a custom domain.
BOOL hasPathOrCustomDomain = URL.path.length > 0 || FIRDLIsURLForAllowedCustomDomain(URL);
// Must be able to parse (also checks if the URL conforms to *.app.goo.gl/* or goo.gl/app/* or
// *.page.link or custom domain with valid suffix)
BOOL canParse = FIRDLCanParseUniversalLinkURL(URL);
// Path cannot be prefixed with /link/dismiss
BOOL isDismiss = [[URL.path lowercaseString] hasPrefix:@"/link/dismiss"];
// Checks short link format by having only one path after domain prefix.
BOOL matchesRegularExpression =
([URL.path rangeOfString:@"/[^/]+" options:NSRegularExpressionSearch].location != NSNotFound);
return hasPathOrCustomDomain && !isDismiss && canParse && matchesRegularExpression;
}
NSString *FIRDLMatchTypeStringFromServerString(NSString *_Nullable serverMatchTypeString) {
static NSDictionary *matchMap;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
matchMap = @{
@"WEAK" : @"weak",
@"DEFAULT" : @"default",
@"UNIQUE" : @"unique",
};
});
return matchMap[serverMatchTypeString] ?: @"none";
}
void FIRDLAddToAllowListForCustomDomainsArray(NSArray *_Nonnull customDomains) {
// Duplicates will be weeded out when converting to a set.
NSMutableArray *validCustomDomains =
[[NSMutableArray alloc] initWithCapacity:customDomains.count];
for (NSString *customDomainEntry in customDomains) {
// We remove trailing slashes in the path if present.
NSString *domainEntry =
[customDomainEntry hasSuffix:@"/"]
? [customDomainEntry substringToIndex:[customDomainEntry length] - 1]
: customDomainEntry;
NSURL *customDomainURL = [NSURL URLWithString:domainEntry];
// We require a valid scheme for each custom domain enumerated in the info.plist file.
if (customDomainURL && customDomainURL.scheme) {
[validCustomDomains addObject:customDomainURL];
}
}
// Duplicates will be weeded out when converting to a set.
FIRDLCustomDomains = [NSSet setWithArray:validCustomDomains];
}
NS_ASSUME_NONNULL_END
#endif // TARGET_OS_IOS
@@ -1,442 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 51;
objects = {
/* Begin PBXBuildFile section */
5B41DB1823E3A71C00929EC5 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B41DB1723E3A71C00929EC5 /* AppDelegate.m */; };
5B41DB1B23E3A71C00929EC5 /* SceneDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B41DB1A23E3A71C00929EC5 /* SceneDelegate.m */; };
5B41DB1E23E3A71C00929EC5 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B41DB1D23E3A71C00929EC5 /* ViewController.m */; };
5B41DB2123E3A71C00929EC5 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5B41DB1F23E3A71C00929EC5 /* Main.storyboard */; };
5B41DB2323E3A71E00929EC5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5B41DB2223E3A71E00929EC5 /* Assets.xcassets */; };
5B41DB2623E3A71E00929EC5 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5B41DB2423E3A71E00929EC5 /* LaunchScreen.storyboard */; };
5B41DB2923E3A71E00929EC5 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B41DB2823E3A71E00929EC5 /* main.m */; };
5B41DB3323E3A7FA00929EC5 /* LinkTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B41DB2F23E3A7F900929EC5 /* LinkTableViewCell.m */; };
5B41DB3423E3A7FA00929EC5 /* ParamTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B41DB3123E3A7F900929EC5 /* ParamTableViewCell.m */; };
5B41DB3623E3A8F400929EC5 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5B41DB3523E3A8F400929EC5 /* GoogleService-Info.plist */; };
CF16533A355AFED181ABDD38 /* Pods_FDLBuilderTestAppObjC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B90D2E2F25A43DD8A8773AF2 /* Pods_FDLBuilderTestAppObjC.framework */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
5B3E9BB723E3ADDC00FF3A1E /* FDLBuilderTestAppObjC.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = FDLBuilderTestAppObjC.entitlements; sourceTree = "<group>"; };
5B41DB1323E3A71C00929EC5 /* FDLBuilderTestAppObjC.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FDLBuilderTestAppObjC.app; sourceTree = BUILT_PRODUCTS_DIR; };
5B41DB1623E3A71C00929EC5 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
5B41DB1723E3A71C00929EC5 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
5B41DB1923E3A71C00929EC5 /* SceneDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SceneDelegate.h; sourceTree = "<group>"; };
5B41DB1A23E3A71C00929EC5 /* SceneDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SceneDelegate.m; sourceTree = "<group>"; };
5B41DB1C23E3A71C00929EC5 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
5B41DB1D23E3A71C00929EC5 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
5B41DB2023E3A71C00929EC5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
5B41DB2223E3A71E00929EC5 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
5B41DB2523E3A71E00929EC5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
5B41DB2723E3A71E00929EC5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
5B41DB2823E3A71E00929EC5 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
5B41DB2F23E3A7F900929EC5 /* LinkTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LinkTableViewCell.m; sourceTree = "<group>"; };
5B41DB3023E3A7F900929EC5 /* LinkTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LinkTableViewCell.h; sourceTree = "<group>"; };
5B41DB3123E3A7F900929EC5 /* ParamTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ParamTableViewCell.m; sourceTree = "<group>"; };
5B41DB3223E3A7FA00929EC5 /* ParamTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ParamTableViewCell.h; sourceTree = "<group>"; };
5B41DB3523E3A8F400929EC5 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
8C3596C191E82DCECC6735C8 /* Pods-FDLBuilderTestAppObjC.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FDLBuilderTestAppObjC.release.xcconfig"; path = "Target Support Files/Pods-FDLBuilderTestAppObjC/Pods-FDLBuilderTestAppObjC.release.xcconfig"; sourceTree = "<group>"; };
B90D2E2F25A43DD8A8773AF2 /* Pods_FDLBuilderTestAppObjC.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_FDLBuilderTestAppObjC.framework; sourceTree = BUILT_PRODUCTS_DIR; };
E8ED1BD19C174903FC5BDF2C /* Pods-FDLBuilderTestAppObjC.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FDLBuilderTestAppObjC.debug.xcconfig"; path = "Target Support Files/Pods-FDLBuilderTestAppObjC/Pods-FDLBuilderTestAppObjC.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
5B41DB1023E3A71C00929EC5 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
CF16533A355AFED181ABDD38 /* Pods_FDLBuilderTestAppObjC.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
165ECBBDCCBFA3107802A5B8 /* Pods */ = {
isa = PBXGroup;
children = (
E8ED1BD19C174903FC5BDF2C /* Pods-FDLBuilderTestAppObjC.debug.xcconfig */,
8C3596C191E82DCECC6735C8 /* Pods-FDLBuilderTestAppObjC.release.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
};
5B41DB0A23E3A71C00929EC5 = {
isa = PBXGroup;
children = (
5B41DB3523E3A8F400929EC5 /* GoogleService-Info.plist */,
5B41DB1523E3A71C00929EC5 /* FDLBuilderTestAppObjC */,
5B41DB1423E3A71C00929EC5 /* Products */,
165ECBBDCCBFA3107802A5B8 /* Pods */,
8AB095C8794DB7CBE5853D77 /* Frameworks */,
);
sourceTree = "<group>";
};
5B41DB1423E3A71C00929EC5 /* Products */ = {
isa = PBXGroup;
children = (
5B41DB1323E3A71C00929EC5 /* FDLBuilderTestAppObjC.app */,
);
name = Products;
sourceTree = "<group>";
};
5B41DB1523E3A71C00929EC5 /* FDLBuilderTestAppObjC */ = {
isa = PBXGroup;
children = (
5B3E9BB723E3ADDC00FF3A1E /* FDLBuilderTestAppObjC.entitlements */,
5B41DB3023E3A7F900929EC5 /* LinkTableViewCell.h */,
5B41DB2F23E3A7F900929EC5 /* LinkTableViewCell.m */,
5B41DB3223E3A7FA00929EC5 /* ParamTableViewCell.h */,
5B41DB3123E3A7F900929EC5 /* ParamTableViewCell.m */,
5B41DB1623E3A71C00929EC5 /* AppDelegate.h */,
5B41DB1723E3A71C00929EC5 /* AppDelegate.m */,
5B41DB1923E3A71C00929EC5 /* SceneDelegate.h */,
5B41DB1A23E3A71C00929EC5 /* SceneDelegate.m */,
5B41DB1C23E3A71C00929EC5 /* ViewController.h */,
5B41DB1D23E3A71C00929EC5 /* ViewController.m */,
5B41DB1F23E3A71C00929EC5 /* Main.storyboard */,
5B41DB2223E3A71E00929EC5 /* Assets.xcassets */,
5B41DB2423E3A71E00929EC5 /* LaunchScreen.storyboard */,
5B41DB2723E3A71E00929EC5 /* Info.plist */,
5B41DB2823E3A71E00929EC5 /* main.m */,
);
path = FDLBuilderTestAppObjC;
sourceTree = "<group>";
};
8AB095C8794DB7CBE5853D77 /* Frameworks */ = {
isa = PBXGroup;
children = (
B90D2E2F25A43DD8A8773AF2 /* Pods_FDLBuilderTestAppObjC.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
5B41DB1223E3A71C00929EC5 /* FDLBuilderTestAppObjC */ = {
isa = PBXNativeTarget;
buildConfigurationList = 5B41DB2C23E3A71E00929EC5 /* Build configuration list for PBXNativeTarget "FDLBuilderTestAppObjC" */;
buildPhases = (
064F9DD0B6255FE37CE6E06D /* [CP] Check Pods Manifest.lock */,
5B41DB0F23E3A71C00929EC5 /* Sources */,
5B41DB1023E3A71C00929EC5 /* Frameworks */,
5B41DB1123E3A71C00929EC5 /* Resources */,
94C2948974AD9FEF43500B54 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = FDLBuilderTestAppObjC;
productName = FDLBuilderTestAppObjC;
productReference = 5B41DB1323E3A71C00929EC5 /* FDLBuilderTestAppObjC.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
5B41DB0B23E3A71C00929EC5 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1130;
ORGANIZATIONNAME = "Google Inc";
TargetAttributes = {
5B41DB1223E3A71C00929EC5 = {
CreatedOnToolsVersion = 11.3.1;
};
};
};
buildConfigurationList = 5B41DB0E23E3A71C00929EC5 /* Build configuration list for PBXProject "FDLBuilderTestAppObjC" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 5B41DB0A23E3A71C00929EC5;
productRefGroup = 5B41DB1423E3A71C00929EC5 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
5B41DB1223E3A71C00929EC5 /* FDLBuilderTestAppObjC */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
5B41DB1123E3A71C00929EC5 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5B41DB2623E3A71E00929EC5 /* LaunchScreen.storyboard in Resources */,
5B41DB3623E3A8F400929EC5 /* GoogleService-Info.plist in Resources */,
5B41DB2323E3A71E00929EC5 /* Assets.xcassets in Resources */,
5B41DB2123E3A71C00929EC5 /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
064F9DD0B6255FE37CE6E06D /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-FDLBuilderTestAppObjC-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
94C2948974AD9FEF43500B54 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-FDLBuilderTestAppObjC/Pods-FDLBuilderTestAppObjC-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-FDLBuilderTestAppObjC/Pods-FDLBuilderTestAppObjC-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-FDLBuilderTestAppObjC/Pods-FDLBuilderTestAppObjC-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
5B41DB0F23E3A71C00929EC5 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5B41DB1E23E3A71C00929EC5 /* ViewController.m in Sources */,
5B41DB3423E3A7FA00929EC5 /* ParamTableViewCell.m in Sources */,
5B41DB1823E3A71C00929EC5 /* AppDelegate.m in Sources */,
5B41DB3323E3A7FA00929EC5 /* LinkTableViewCell.m in Sources */,
5B41DB2923E3A71E00929EC5 /* main.m in Sources */,
5B41DB1B23E3A71C00929EC5 /* SceneDelegate.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
5B41DB1F23E3A71C00929EC5 /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
5B41DB2023E3A71C00929EC5 /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
5B41DB2423E3A71E00929EC5 /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
5B41DB2523E3A71E00929EC5 /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
5B41DB2A23E3A71E00929EC5 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.2;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
};
name = Debug;
};
5B41DB2B23E3A71E00929EC5 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.2;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
5B41DB2D23E3A71E00929EC5 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = E8ED1BD19C174903FC5BDF2C /* Pods-FDLBuilderTestAppObjC.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = FDLBuilderTestAppObjC/FDLBuilderTestAppObjC.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
DEVELOPMENT_TEAM = EQHXZ8M8AV;
INFOPLIST_FILE = FDLBuilderTestAppObjC/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.dynamiclinks.demo.dev;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "Firebase Dynamic Links Demo Dev";
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
5B41DB2E23E3A71E00929EC5 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 8C3596C191E82DCECC6735C8 /* Pods-FDLBuilderTestAppObjC.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = FDLBuilderTestAppObjC/FDLBuilderTestAppObjC.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
DEVELOPMENT_TEAM = EQHXZ8M8AV;
INFOPLIST_FILE = FDLBuilderTestAppObjC/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.dynamiclinks.demo.dev;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "Firebase Dynamic Links Demo Dev";
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
5B41DB0E23E3A71C00929EC5 /* Build configuration list for PBXProject "FDLBuilderTestAppObjC" */ = {
isa = XCConfigurationList;
buildConfigurations = (
5B41DB2A23E3A71E00929EC5 /* Debug */,
5B41DB2B23E3A71E00929EC5 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
5B41DB2C23E3A71E00929EC5 /* Build configuration list for PBXNativeTarget "FDLBuilderTestAppObjC" */ = {
isa = XCConfigurationList;
buildConfigurations = (
5B41DB2D23E3A71E00929EC5 /* Debug */,
5B41DB2E23E3A71E00929EC5 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 5B41DB0B23E3A71C00929EC5 /* Project object */;
}
@@ -1,23 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property(strong, nonatomic) UIWindow *window;
@end
@@ -1,103 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "AppDelegate.h"
#import "ViewController.h"
#import <FirebaseCore/FirebaseCore.h>
#import <FirebaseDynamicLinks/FIRDynamicLinks.h>
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[FIRApp configure];
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
UINavigationController *navController = [[UINavigationController alloc]
initWithRootViewController:[[ViewController alloc] initWithNibName:nil bundle:nil]];
self.window.rootViewController = navController;
[self.window makeKeyAndVisible];
#ifdef DEBUG
[FIRDynamicLinks performDiagnosticsWithCompletion:nil];
#endif // DEBUG
return YES;
}
- (BOOL)application:(UIApplication *)app
openURL:(NSURL *)url
options:(NSDictionary<NSString *, id> *)options {
FIRDynamicLink *dynamicLink = [[FIRDynamicLinks dynamicLinks] dynamicLinkFromCustomSchemeURL:url];
if (dynamicLink) {
[self _showDynamicLinkInfo:dynamicLink];
}
return YES;
}
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation {
return [self application:application openURL:url options:@{}];
}
- (BOOL)application:(UIApplication *)application
continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:
#if __has_include(<UIKit/UIUserActivity.h>)
(void (^)(NSArray<id<UIUserActivityRestoring>> *_Nullable))restorationHandler {
#else
(void (^)(NSArray *))restorationHandler {
#endif
BOOL handled = [[FIRDynamicLinks dynamicLinks]
handleUniversalLink:userActivity.webpageURL
completion:^(FIRDynamicLink *_Nullable dynamicLink, NSError *_Nullable error) {
[self _showDynamicLinkInfo:dynamicLink];
}];
if (!handled) {
// Show the deep link URL from userActivity.
NSLog(@"Unhandled link %@", userActivity.webpageURL);
}
return handled;
}
- (void)_showDynamicLinkInfo:(FIRDynamicLink *)dynamicLink {
NSLog(@"Got dynamic link %@", dynamicLink);
UIAlertController *alertVC = [UIAlertController
alertControllerWithTitle:@"Got Dynamic Link!"
message:[NSString stringWithFormat:@"URL [%@], matchType [%ld], "
@"minimumAppVersion [%@], utmParams [%@]",
dynamicLink.url,
(unsigned long)dynamicLink.matchType,
dynamicLink.minimumAppVersion,
dynamicLink.utmParametersDictionary]
preferredStyle:UIAlertControllerStyleAlert];
[alertVC addAction:[UIAlertAction actionWithTitle:@"Dismiss"
style:UIAlertActionStyleCancel
handler:NULL]];
[[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alertVC
animated:YES
completion:NULL];
}
@end
@@ -1,98 +0,0 @@
{
"images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "83.5x83.5",
"scale" : "2x"
},
{
"idiom" : "ios-marketing",
"size" : "1024x1024",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
@@ -1,6 +0,0 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
@@ -1,25 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>
@@ -1,26 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15705" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15706"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC" customClass="UITableView">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="132" y="66"/>
</scene>
</scenes>
</document>
@@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>application-identifier</key>
<string>$(AppIdentifierPrefix)$(CFBundleIdentifier)</string>
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:goo.gl</string>
<string>applinks:testfdl.app.goo.gl</string>
<string>applinks:testfdl.page.link</string>
</array>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)$(CFBundleIdentifier)</string>
<string>$(AppIdentifierPrefix)$(CFBundleIdentifier).sso</string>
<string>$(AppIdentifierPrefix)com.google.common.SSO</string>
</array>
</dict>
</plist>
@@ -1,83 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>Dev url scheme</string>
<key>CFBundleURLSchemes</key>
<array>
<string>com.google.firebase.dynamiclinks.demo.dev</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>SceneDelegate</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
</dict>
</dict>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>FirebaseDynamicLinksCustomDomains</key>
<array>
<string>https://google.com</string>
<string>https://google.com/one/</string>
<string>https://a.firebase.com/mypath</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
@@ -1,23 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <UIKit/UIKit.h>
@interface LinkTableViewCell : UITableViewCell <UITextViewDelegate>
- (void)setTitle:(NSString *)title link:(NSString *)link;
@end
@@ -1,69 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "LinkTableViewCell.h"
static const NSUInteger kHInset = 10;
static const NSUInteger kVInset = 4;
@implementation LinkTableViewCell {
UILabel *_titleLabel;
UITextView *_linkTextView;
}
- (instancetype)init {
self = [super initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:NSStringFromClass(self.class)];
if (self) {
_titleLabel = [[UILabel alloc] init];
_titleLabel.font = [UIFont systemFontOfSize:15];
_linkTextView = [[UITextView alloc] init];
_linkTextView.font = [UIFont boldSystemFontOfSize:15];
_linkTextView.editable = NO;
_linkTextView.scrollEnabled = NO;
_linkTextView.dataDetectorTypes = UIDataDetectorTypeLink;
[self.contentView addSubview:_titleLabel];
[self.contentView addSubview:_linkTextView];
}
return self;
}
- (void)layoutSubviews {
_titleLabel.frame = CGRectMake(kHInset, kVInset, self.contentView.frame.size.width - 2 * kHInset,
(self.contentView.frame.size.height / 2) - 2 * kVInset);
_linkTextView.frame = CGRectMake(kHInset, (self.contentView.frame.size.height / 2) + kVInset,
self.contentView.frame.size.width - 2 * kHInset,
(self.contentView.frame.size.height / 2) - 2 * kVInset);
}
- (void)setTitle:(NSString *)title link:(NSString *)link {
self.accessibilityIdentifier =
[NSString stringWithFormat:@"%@-%@", NSStringFromClass(self.class), title];
_linkTextView.accessibilityIdentifier =
[NSString stringWithFormat:@"%@-LinkTextView-%@", NSStringFromClass(self.class), title];
_titleLabel.text = title;
if (link) {
NSURL *URL = [NSURL URLWithString:link];
NSAttributedString *attributedLink =
[[NSAttributedString alloc] initWithString:link attributes:@{NSLinkAttributeName : URL}];
_linkTextView.attributedText = attributedLink;
}
_linkTextView.accessibilityValue = link;
}
@end
@@ -1,33 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <UIKit/UIKit.h>
@class ParamTableViewCell;
@protocol ParamTableViewCellDelegate <NSObject>
- (void)paramTableViewCellUpdatedValue:(ParamTableViewCell *)cell;
@end
@interface ParamTableViewCell : UITableViewCell
@property(nonatomic, readwrite, copy) NSDictionary *paramConfig;
@property(nonatomic, readwrite, copy) NSString *textFieldValue;
@property(nonatomic, weak) id<ParamTableViewCellDelegate> delegate;
@end
@@ -1,82 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "ParamTableViewCell.h"
static const NSUInteger kHInset = 10;
static const NSUInteger kVInset = 4;
@implementation ParamTableViewCell {
UILabel *_label;
UITextField *_textField;
}
@synthesize paramConfig = _paramConfig;
- (instancetype)init {
self = [super initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:NSStringFromClass(self.class)];
if (self) {
self.selectionStyle = UITableViewCellSelectionStyleNone;
_label = [[UILabel alloc] init];
_label.font = [UIFont italicSystemFontOfSize:[UIFont systemFontSize]];
_textField = [[UITextField alloc] init];
_textField.autocapitalizationType = UITextAutocapitalizationTypeNone;
[self.contentView addSubview:_label];
[self.contentView addSubview:_textField];
[_textField addTarget:self
action:@selector(onTextFieldValueChanged)
forControlEvents:UIControlEventEditingChanged];
[_textField addTarget:self
action:@selector(onTextFieldDidEndOnExit)
forControlEvents:UIControlEventEditingDidEndOnExit];
}
return self;
}
- (void)layoutSubviews {
_label.frame = CGRectMake(kHInset, kVInset, self.contentView.frame.size.width - 2 * kHInset,
(self.contentView.frame.size.height / 2) - 2 * kVInset);
_textField.frame = CGRectMake(kHInset, (self.contentView.frame.size.height / 2) + kVInset,
self.contentView.frame.size.width - 2 * kHInset,
(self.contentView.frame.size.height / 2) - 2 * kVInset);
}
- (void)onTextFieldValueChanged {
if (![self.textFieldValue isEqualToString:_textField.text]) {
self.textFieldValue = _textField.text;
[_delegate paramTableViewCellUpdatedValue:self];
}
}
- (void)onTextFieldDidEndOnExit {
[_textField resignFirstResponder];
}
- (void)setTextFieldValue:(NSString *)textFieldValue {
_textFieldValue = textFieldValue;
if (![_textFieldValue isEqualToString:_textField.text]) {
_textField.text = self.textFieldValue;
}
}
- (void)setParamConfig:(NSDictionary *)paramConfig {
_paramConfig = [paramConfig copy];
self.accessibilityIdentifier = _paramConfig[@"id"];
_label.text = _paramConfig[@"label"];
}
@end
@@ -1,23 +0,0 @@
/*
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <UIKit/UIKit.h>
@interface SceneDelegate : UIResponder <UIWindowSceneDelegate>
@property(strong, nonatomic) UIWindow* window;
@end
@@ -1,108 +0,0 @@
/*
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "SceneDelegate.h"
#import <FirebaseDynamicLinks/FIRDynamicLinks.h>
@interface SceneDelegate ()
@end
@implementation SceneDelegate
- (void)scene:(UIScene *)scene
willConnectToSession:(UISceneSession *)session
options:(UISceneConnectionOptions *)connectionOptions {
// Use this method to optionally configure and attach the UIWindow `window` to the provided
// UIWindowScene `scene`. If using a storyboard, the `window` property will automatically be
// initialized and attached to the scene. This delegate does not imply the connecting scene or
// session are new (see `application:configurationForConnectingSceneSession` instead).
if (connectionOptions.userActivities && connectionOptions.userActivities.count > 0) {
NSUserActivity *userActivity = connectionOptions.userActivities.allObjects.firstObject;
[self handleDynamicLinkFromActivity:userActivity];
}
}
- (void)sceneDidDisconnect:(UIScene *)scene {
// Called as the scene is being released by the system.
// This occurs shortly after the scene enters the background, or when its session is discarded.
// Release any resources associated with this scene that can be re-created the next time the scene
// connects. The scene may re-connect later, as its session was not necessarily discarded (see
// `application:didDiscardSceneSessions` instead).
}
- (void)sceneDidBecomeActive:(UIScene *)scene {
// Called when the scene has moved from an inactive state to an active state.
// Use this method to restart any tasks that were paused (or not yet started) when the scene was
// inactive.
}
- (void)sceneWillResignActive:(UIScene *)scene {
// Called when the scene will move from an active state to an inactive state.
// This may occur due to temporary interruptions (ex. an incoming phone call).
}
- (void)sceneWillEnterForeground:(UIScene *)scene {
// Called as the scene transitions from the background to the foreground.
// Use this method to undo the changes made on entering the background.
}
- (void)sceneDidEnterBackground:(UIScene *)scene {
// Called as the scene transitions from the foreground to the background.
// Use this method to save data, release shared resources, and store enough scene-specific state
// information to restore the scene back to its current state.
}
- (void)scene:(UIScene *)scene continueUserActivity:(NSUserActivity *)userActivity {
[self handleDynamicLinkFromActivity:userActivity];
}
- (void)handleDynamicLinkFromActivity:(NSUserActivity *)userActivity {
if (!userActivity) {
return;
}
BOOL handled = [[FIRDynamicLinks dynamicLinks]
handleUniversalLink:userActivity.webpageURL
completion:^(FIRDynamicLink *_Nullable dynamicLink, NSError *_Nullable error) {
[self _showDynamicLinkInfo:dynamicLink];
}];
if (!handled) {
// Show the deep link URL from userActivity.
NSLog(@"Unhandled link %@", userActivity.webpageURL);
}
}
- (void)_showDynamicLinkInfo:(FIRDynamicLink *)dynamicLink {
NSLog(@"Got dynamic link %@", dynamicLink);
UIAlertController *alertVC = [UIAlertController
alertControllerWithTitle:@"Got Dynamic Link!"
message:[NSString stringWithFormat:@"URL [%@], matchType [%ld], "
@"minimumAppVersion [%@], utmParams [%@]",
dynamicLink.url,
(unsigned long)dynamicLink.matchType,
dynamicLink.minimumAppVersion,
dynamicLink.utmParametersDictionary]
preferredStyle:UIAlertControllerStyleAlert];
[alertVC addAction:[UIAlertAction actionWithTitle:@"Dismiss"
style:UIAlertActionStyleCancel
handler:NULL]];
[self.window.rootViewController presentViewController:alertVC animated:YES completion:NULL];
}
@end
@@ -1,21 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <UIKit/UIKit.h>
@interface ViewController : UITableViewController
@end
@@ -1,399 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "ViewController.h"
#import <FirebaseDynamicLinks/FDLURLComponents.h>
#import <FirebaseDynamicLinks/FIRDynamicLinks.h>
#import "LinkTableViewCell.h"
#import "ParamTableViewCell.h"
static NSArray *kParamsConfiguration;
@interface ViewController () <ParamTableViewCellDelegate>
@end
@implementation ViewController {
NSArray *_paramsConfiguration;
NSMutableDictionary *_paramValues;
NSURL *_longLink;
NSURL *_shortLink;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
self.title = @"FDL Builder";
self.tableView.rowHeight = 60;
[self _initDefaultValues];
}
#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 0 && indexPath.row == (kParamsConfiguration.count + 0)) {
[self _buildFDLLink];
}
if (indexPath.section == 0 && indexPath.row == (kParamsConfiguration.count + 1)) {
// copy long link
if (_longLink) {
[UIPasteboard generalPasteboard].string = _longLink.absoluteString;
[self _presentMessage:@"Long Link copied to Clipboard" description:nil];
} else {
[self _presentMessage:@"Long Link is empty" description:nil];
}
}
if (indexPath.section == 0 && indexPath.row == (kParamsConfiguration.count + 2)) {
// copy short link
if (_shortLink) {
[UIPasteboard generalPasteboard].string = _shortLink.absoluteString;
[self _presentMessage:@"Short Link copied to Clipboard" description:nil];
} else {
[self _presentMessage:@"Short Link is empty" description:nil];
}
}
}
#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (section == 0) {
return kParamsConfiguration.count + 3;
} else {
return 0;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row >= kParamsConfiguration.count) {
return [self _customCellForRow:indexPath.row - kParamsConfiguration.count];
} else {
ParamTableViewCell *cell =
(ParamTableViewCell *)[tableView dequeueReusableCellWithIdentifier:@"ParamTableViewCell"];
if (!cell) {
cell = [[ParamTableViewCell alloc] init];
}
NSDictionary *paramConfig = kParamsConfiguration[indexPath.row];
cell.paramConfig = paramConfig;
cell.textFieldValue = _paramValues[paramConfig[@"id"]];
cell.delegate = self;
return cell;
}
}
- (void)paramTableViewCellUpdatedValue:(ParamTableViewCell *)cell;
{ _paramValues[cell.paramConfig[@"id"]] = cell.textFieldValue; }
#pragma mark - Private methods
- (UITableViewCell *)_customCellForRow:(NSUInteger)row {
switch (row) {
case 0: {
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:@"cell"];
cell.textLabel.text = @"Generate Link";
cell.accessibilityIdentifier = @"generate-link";
cell.textLabel.textAlignment = NSTextAlignmentCenter;
cell.textLabel.font = [UIFont boldSystemFontOfSize:22];
return cell;
} break;
case 1:
case 2: {
LinkTableViewCell *cell = (LinkTableViewCell *)[self.tableView
dequeueReusableCellWithIdentifier:@"LinkTableViewCell"];
if (!cell) {
cell = [[LinkTableViewCell alloc] init];
}
if (row == 1) {
[cell setTitle:@"Long link" link:_longLink.absoluteString];
} else {
[cell setTitle:@"Short link" link:_shortLink.absoluteString];
}
return cell;
} break;
case 3: {
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:@"cell"];
cell.textLabel.text = @"Perform FDL self diagnostic";
cell.textLabel.textAlignment = NSTextAlignmentCenter;
cell.textLabel.font = [UIFont systemFontOfSize:22];
return cell;
} break;
}
return nil;
}
- (void)_initDefaultValues {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
kParamsConfiguration = @[
// general link params
@{
@"id" : @"linkString",
@"label" : @"Link value (required)",
@"defaultValue" : @"https://www.google.com?q=jump",
},
// The default value of domain appcode belongs to project: app-invites-qa
@{
@"id" : @"domainURIPrefix",
@"label" : @"App domainURIPrefix (required)",
@"defaultValue" : @"https://testfdl.page.link",
},
// analytics params
@{
@"id" : @"FIRDynamicLinkGoogleAnalyticsParameters.source",
@"label" : @"Google Analytics source (optional)",
@"defaultValue" : @"",
},
@{
@"id" : @"FIRDynamicLinkGoogleAnalyticsParameters.medium",
@"label" : @"Google Analytics medium (optional)",
@"defaultValue" : @"",
},
@{
@"id" : @"FIRDynamicLinkGoogleAnalyticsParameters.campaign",
@"label" : @"Google Analytics campaign (optional)",
@"defaultValue" : @"",
},
@{
@"id" : @"FIRDynamicLinkGoogleAnalyticsParameters.term",
@"label" : @"Google Analytics term (optional)",
@"defaultValue" : @"",
},
@{
@"id" : @"FIRDynamicLinkGoogleAnalyticsParameters.content",
@"label" : @"Google Analytics content (optional)",
@"defaultValue" : @"",
},
// iOS params
@{
@"id" : @"FIRDynamicLinkIOSParameters.bundleId",
@"label" : @"iOS App bundle ID",
@"defaultValue" : [[NSBundle mainBundle] bundleIdentifier] ?: @"",
},
@{
@"id" : @"FIRDynamicLinkIOSParameters.fallbackURL",
@"label" : @"Fallback URL iOS (optional)",
},
@{
@"id" : @"FIRDynamicLinkIOSParameters.minimumAppVersion",
@"label" : @"minimum version of iOS App (optional)",
@"defaultValue" : @"1.0",
},
@{
@"id" : @"FIRDynamicLinkIOSParameters.customScheme",
@"label" : @"iOS App custom scheme (optional)",
},
@{
@"id" : @"FIRDynamicLinkIOSParameters.iPadBundleID",
@"label" : @"iPad App bundleID (optional)",
@"defaultValue" : @"",
},
@{
@"id" : @"FIRDynamicLinkIOSParameters.iPadFallbackURL",
@"label" : @"Fallback URL on iPad (optional)",
},
@{
@"id" : @"FIRDynamicLinkIOSParameters.appStoreID",
@"label" : @"iOS AppStore ID (optional)",
},
// iTunesConnect params
@{
@"id" : @"FIRDynamicLinkItunesConnectAnalyticsParameters.affiliateToken",
@"label" : @"iTunesConnect affiliate token (optional)",
@"defaultValue" : @"",
},
@{
@"id" : @"FIRDynamicLinkItunesConnectAnalyticsParameters.campaignToken",
@"label" : @"iTunesConnect campaign token (optional)",
@"defaultValue" : @"",
},
@{
@"id" : @"FIRDynamicLinkItunesConnectAnalyticsParameters.providerToken",
@"label" : @"iTunesConnect provider token (optional)",
@"defaultValue" : @"",
},
// Android params
@{
@"id" : @"FIRDynamicLinkAndroidParameters.packageName",
@"label" : @"Android App package name (optional)",
@"defaultValue" : @"",
},
@{
@"id" : @"FIRDynamicLinkAndroidParameters.fallbackURL",
@"label" : @"Android fallback URL (optional)",
@"defaultValue" : @"",
},
@{
@"id" : @"FIRDynamicLinkAndroidParameters.minimumVersion",
@"label" : @"Andropid App minimum version, integer number (optional)",
@"defaultValue" : @"",
},
// social tag params
@{
@"id" : @"FIRDynamicLinkSocialMetaTagParameters.title",
@"label" : @"Social meta tag title (optional)",
@"defaultValue" : @"",
},
@{
@"id" : @"FIRDynamicLinkSocialMetaTagParameters.descriptionText",
@"label" : @"Social meta tag description text (optional)",
@"defaultValue" : @"",
},
@{
@"id" : @"FIRDynamicLinkSocialMetaTagParameters.imageURL",
@"label" : @"Social meta tag image URL (optional)",
@"defaultValue" : @"",
},
// OtherPlatform params
@{
@"id" : @"FIRDynamicLinkOtherPlatformParameters.fallbackUrl",
@"label" : @"OtherPlatform Fallback link (optional)",
@"defaultValue" : @"",
},
];
});
_paramValues = [[NSMutableDictionary alloc] initWithCapacity:kParamsConfiguration.count];
for (NSDictionary *paramConfig in kParamsConfiguration) {
if (paramConfig[@"defaultValue"]) {
_paramValues[paramConfig[@"id"]] = paramConfig[@"defaultValue"];
}
}
}
- (void)_buildFDLLink {
NSURL *link = [NSURL URLWithString:_paramValues[@"linkString"]];
FIRDynamicLinkComponents *components =
[FIRDynamicLinkComponents componentsWithLink:link
domainURIPrefix:_paramValues[@"domainURIPrefix"]];
FIRDynamicLinkGoogleAnalyticsParameters *analyticsParams =
[FIRDynamicLinkGoogleAnalyticsParameters
parametersWithSource:_paramValues[@"FIRDynamicLinkGoogleAnalyticsParameters.source"]
medium:_paramValues[@"FIRDynamicLinkGoogleAnalyticsPara"
@"meters.medium"]
campaign:_paramValues[@"FIRDynamicLinkGoogleAnalyticsPara"
@"meters.campaign"]];
analyticsParams.term = _paramValues[@"FIRDynamicLinkGoogleAnalyticsParameters.term"];
analyticsParams.content = _paramValues[@"FIRDynamicLinkGoogleAnalyticsParameters.content"];
FIRDynamicLinkIOSParameters *iOSParams;
if (_paramValues[@"FIRDynamicLinkIOSParameters.bundleId"]) {
iOSParams = [FIRDynamicLinkIOSParameters
parametersWithBundleID:_paramValues[@"FIRDynamicLinkIOSParameters.bundleId"]];
iOSParams.fallbackURL =
[NSURL URLWithString:_paramValues[@"FIRDynamicLinkIOSParameters.fallbackURL"]];
iOSParams.customScheme = _paramValues[@"FIRDynamicLinkIOSParameters.customScheme"];
iOSParams.iPadBundleID = _paramValues[@"FIRDynamicLinkIOSParameters.iPadBundleID"];
iOSParams.iPadFallbackURL =
[NSURL URLWithString:_paramValues[@"FIRDynamicLinkIOSParameters.iPadFallbackURL"]];
iOSParams.appStoreID = _paramValues[@"FIRDynamicLinkIOSParameters.appStoreId"];
iOSParams.minimumAppVersion = _paramValues[@"FIRDynamicLinkIOSParameters.minimumAppVersion"];
}
FIRDynamicLinkItunesConnectAnalyticsParameters *appStoreParams =
[FIRDynamicLinkItunesConnectAnalyticsParameters parameters];
appStoreParams.affiliateToken =
_paramValues[@"FIRDynamicLinkItunesConnectAnalyticsParameters.affiliateToken"];
appStoreParams.campaignToken =
_paramValues[@"FIRDynamicLinkItunesConnectAnalyticsParameters.campaignToken"];
appStoreParams.providerToken =
_paramValues[@"FIRDynamicLinkItunesConnectAnalyticsParameters.providerToken"];
FIRDynamicLinkAndroidParameters *androidParams;
if (_paramValues[@"FIRDynamicLinkAndroidParameters.packageName"]) {
androidParams = [FIRDynamicLinkAndroidParameters
parametersWithPackageName:_paramValues[@"FIRDynamicLinkAndroidParameters.packageName"]];
androidParams.fallbackURL =
[NSURL URLWithString:_paramValues[@"FIRDynamicLinkAndroidParameters.fallbackURL"]];
if ([_paramValues[@"FIRDynamicLinkAndroidParameters.minimumVersion"] integerValue] > 0) {
androidParams.minimumVersion =
[_paramValues[@"FIRDynamicLinkAndroidParameters.minimumVersion"] integerValue];
}
}
FIRDynamicLinkSocialMetaTagParameters *socialParams =
[FIRDynamicLinkSocialMetaTagParameters parameters];
socialParams.title = _paramValues[@"FIRDynamicLinkSocialMetaTagParameters.title"];
socialParams.descriptionText =
_paramValues[@"FIRDynamicLinkSocialMetaTagParameters.descriptionText"];
socialParams.imageURL =
[NSURL URLWithString:_paramValues[@"FIRDynamicLinkSocialMetaTagParameters.imageURL"]];
FIRDynamicLinkOtherPlatformParameters *otherPlatformParams =
[FIRDynamicLinkOtherPlatformParameters parameters];
otherPlatformParams.fallbackUrl =
[NSURL URLWithString:_paramValues[@"FIRDynamicLinkOtherPlatformParameters.fallbackUrl"]];
FIRDynamicLinkComponentsOptions *options = [FIRDynamicLinkComponentsOptions options];
options.pathLength = FIRShortDynamicLinkPathLengthShort;
components.analyticsParameters = analyticsParams;
components.iOSParameters = iOSParams;
components.iTunesConnectParameters = appStoreParams;
components.androidParameters = androidParams;
components.socialMetaTagParameters = socialParams;
components.otherPlatformParameters = otherPlatformParams;
components.options = options;
NSURL *longURL = components.url;
// Handle longURL.
NSLog(@"Long URL: %@", longURL);
_longLink = longURL;
[self.tableView
reloadRowsAtIndexPaths:@[ [NSIndexPath indexPathForRow:kParamsConfiguration.count + 1
inSection:0] ]
withRowAnimation:UITableViewRowAnimationNone];
[components shortenWithCompletion:^(NSURL *_Nullable shortURL, NSArray *_Nullable warnings,
NSError *_Nullable error) {
// Handle shortURL or error.
NSLog(@"Short URL: %@, warnings: %@ error: %@", shortURL, warnings, error);
if (error) {
[self _presentMessage:@"Error generating short link" description:[error description]];
}
_shortLink = shortURL;
[self.tableView
reloadRowsAtIndexPaths:@[ [NSIndexPath indexPathForRow:kParamsConfiguration.count + 2
inSection:0] ]
withRowAnimation:UITableViewRowAnimationNone];
}];
}
- (void)_presentMessage:(NSString *)message description:(NSString *)description {
UIAlertController *alertVC =
[UIAlertController alertControllerWithTitle:message
message:description
preferredStyle:UIAlertControllerStyleAlert];
[alertVC addAction:[UIAlertAction actionWithTitle:@"Dismiss"
style:UIAlertActionStyleCancel
handler:NULL]];
[self presentViewController:alertVC animated:YES completion:NULL];
}
@end
@@ -1,27 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char* argv[]) {
NSString* appDelegateClassName;
@autoreleasepool {
// Setup code that might create autoreleased objects goes here.
appDelegateClassName = NSStringFromClass([AppDelegate class]);
}
return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
-11
View File
@@ -1,11 +0,0 @@
source 'https://github.com/firebase/SpecsDev.git'
source 'https://github.com/firebase/SpecsStaging.git'
source 'https://cdn.cocoapods.org/'
target 'FDLBuilderTestAppObjC' do
platform :ios, '13.0'
use_frameworks!
pod 'FirebaseCore', :path => '../../../'
pod 'FirebaseDynamicLinks', :path => '../../../'
end
@@ -1,56 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>org.cocoapods-generate.App-iOS</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>FirebaseDynamicLinksCustomDomains</key>
<array>
<string>https://google.com</string>
<string>https://google.com/one/</string>
<string>https://a.firebase.com/mypath</string>
</array>
</dict>
</plist>
@@ -1,779 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <XCTest/XCTest.h>
#import "FirebaseDynamicLinks/Sources/FDLURLComponents/FDLURLComponents+Private.h"
#import "FirebaseDynamicLinks/Sources/FDLURLComponents/FIRDynamicLinkComponentsKeyProvider.h"
#import <OCMock/OCMock.h>
static NSString *const kFDLURLDomain = @"https://xyz.page.link";
static NSString *const kFDLURLCustomDomain = @"https://foo.com/path";
@interface FDLURLComponentsTests : XCTestCase
@end
@implementation FDLURLComponentsTests
#pragma mark - FIRDynamicLinkGoogleAnalyticsParameters
- (void)testAnalyticsParamsFactoryReturnsInstanceOfCorrectClass {
id returnValue = [FIRDynamicLinkGoogleAnalyticsParameters parameters];
XCTAssertTrue([returnValue isKindOfClass:[FIRDynamicLinkGoogleAnalyticsParameters class]]);
}
- (void)testAnalyticsParamsFactoryReturnsInstanceWithAllNilProperties {
FIRDynamicLinkGoogleAnalyticsParameters *params =
[FIRDynamicLinkGoogleAnalyticsParameters parameters];
XCTAssertNil(params.source);
XCTAssertNil(params.medium);
XCTAssertNil(params.campaign);
XCTAssertNil(params.term);
XCTAssertNil(params.content);
}
- (void)testAnalyticsParamsPropertiesSetProperly {
FIRDynamicLinkGoogleAnalyticsParameters *params =
[FIRDynamicLinkGoogleAnalyticsParameters parameters];
params.source = @"s";
params.medium = @"m";
params.campaign = @"ca";
params.term = @"t";
params.content = @"co";
XCTAssertEqualObjects(params.source, @"s");
XCTAssertEqualObjects(params.medium, @"m");
XCTAssertEqualObjects(params.campaign, @"ca");
XCTAssertEqualObjects(params.term, @"t");
XCTAssertEqualObjects(params.content, @"co");
params.source = nil;
params.medium = nil;
params.campaign = nil;
params.term = nil;
params.content = nil;
XCTAssertNil(params.source);
XCTAssertNil(params.medium);
XCTAssertNil(params.campaign);
XCTAssertNil(params.term);
XCTAssertNil(params.content);
}
- (void)testAnalyticsParamsDictionaryRepresentationReturnsCorrectDictionaryFull {
FIRDynamicLinkGoogleAnalyticsParameters *params =
[FIRDynamicLinkGoogleAnalyticsParameters parameters];
params.source = @"s";
params.medium = @"m";
params.campaign = @"ca";
params.term = @"t";
params.content = @"co";
NSDictionary *expectedDictionary = @{
@"utm_source" : @"s",
@"utm_medium" : @"m",
@"utm_campaign" : @"ca",
@"utm_term" : @"t",
@"utm_content" : @"co",
};
XCTAssertEqualObjects(expectedDictionary, params.dictionaryRepresentation);
}
- (void)testAnalyticsParamsDictionaryRepresentationReturnsCorrectDictionaryEmpty {
FIRDynamicLinkGoogleAnalyticsParameters *params =
[FIRDynamicLinkGoogleAnalyticsParameters parameters];
XCTAssertEqualObjects(@{}, params.dictionaryRepresentation);
}
- (void)testAnalyticsParamsFactoryWithParamsReturnsInstanceOfCorrectClass {
id returnValue = [FIRDynamicLinkGoogleAnalyticsParameters parametersWithSource:@"s"
medium:@"m"
campaign:@"c"];
XCTAssertTrue([returnValue isKindOfClass:[FIRDynamicLinkGoogleAnalyticsParameters class]]);
}
- (void)testAnalyticsParamsFactoryWithParamsReturnsInstanceWithCorrectInitialPropertyValues {
FIRDynamicLinkGoogleAnalyticsParameters *params =
[FIRDynamicLinkGoogleAnalyticsParameters parametersWithSource:@"s" medium:@"m" campaign:@"c"];
XCTAssertEqualObjects(params.source, @"s");
XCTAssertEqualObjects(params.medium, @"m");
XCTAssertEqualObjects(params.campaign, @"c");
XCTAssertNil(params.term);
XCTAssertNil(params.content);
}
#pragma mark - FIRDynamicLinkIOSParameters
- (void)testIOSParamsFactoryReturnsInstanceOfCorrectClass {
id returnValue = [FIRDynamicLinkIOSParameters parametersWithBundleID:@"com.iphone.app"];
XCTAssertTrue([returnValue isKindOfClass:[FIRDynamicLinkIOSParameters class]]);
}
- (void)testIOSParamsFactoryReturnsInstanceWithAllOptionalNilProperties {
FIRDynamicLinkIOSParameters *params =
[FIRDynamicLinkIOSParameters parametersWithBundleID:@"com.iphone.app"];
XCTAssertNil(params.fallbackURL);
XCTAssertNil(params.customScheme);
XCTAssertNil(params.minimumAppVersion);
XCTAssertNil(params.iPadBundleID);
XCTAssertNil(params.iPadFallbackURL);
XCTAssertNil(params.appStoreID);
}
- (void)testIOSParamsPropertiesSetProperly {
FIRDynamicLinkIOSParameters *params =
[FIRDynamicLinkIOSParameters parametersWithBundleID:@"com.iphone.app"];
params.fallbackURL = [NSURL URLWithString:@"https://google.com/iphone"];
params.customScheme = @"mycustomsheme";
params.minimumAppVersion = @"1.2.3";
params.iPadBundleID = @"com.ipad.app";
params.iPadFallbackURL = [NSURL URLWithString:@"https://google.com/ipad"];
params.appStoreID = @"666";
XCTAssertEqualObjects(params.bundleID, @"com.iphone.app");
XCTAssertEqualObjects(params.fallbackURL, [NSURL URLWithString:@"https://google.com/iphone"]);
XCTAssertEqualObjects(params.customScheme, @"mycustomsheme");
XCTAssertEqualObjects(params.minimumAppVersion, @"1.2.3");
XCTAssertEqualObjects(params.iPadBundleID, @"com.ipad.app");
XCTAssertEqualObjects(params.iPadFallbackURL, [NSURL URLWithString:@"https://google.com/ipad"]);
XCTAssertEqualObjects(params.appStoreID, @"666");
params.fallbackURL = nil;
params.customScheme = nil;
params.minimumAppVersion = nil;
params.iPadBundleID = nil;
params.iPadFallbackURL = nil;
params.appStoreID = nil;
XCTAssertNil(params.fallbackURL);
XCTAssertNil(params.customScheme);
XCTAssertNil(params.minimumAppVersion);
XCTAssertNil(params.iPadBundleID);
XCTAssertNil(params.iPadFallbackURL);
XCTAssertNil(params.appStoreID);
}
- (void)testIOSParamsDictionaryRepresentationReturnsCorrectDictionaryFull {
FIRDynamicLinkIOSParameters *params =
[FIRDynamicLinkIOSParameters parametersWithBundleID:@"com.iphone.app"];
params.fallbackURL = [NSURL URLWithString:@"https://google.com/iphone"];
params.customScheme = @"mycustomscheme";
params.minimumAppVersion = @"1.2.3";
params.iPadBundleID = @"com.ipad.app";
params.iPadFallbackURL = [NSURL URLWithString:@"https://google.com/ipad"];
params.appStoreID = @"666";
NSDictionary *expectedDictionary = @{
@"ibi" : @"com.iphone.app",
@"ifl" : [NSURL URLWithString:@"https://google.com/iphone"].absoluteString,
@"ius" : @"mycustomscheme",
@"imv" : @"1.2.3",
@"ipbi" : @"com.ipad.app",
@"ipfl" : [NSURL URLWithString:@"https://google.com/ipad"].absoluteString,
@"isi" : @"666"
};
XCTAssertEqualObjects(expectedDictionary, params.dictionaryRepresentation);
}
- (void)testIOSParamsDictionaryRepresentationReturnsCorrectDictionaryOnlyReqParams {
FIRDynamicLinkIOSParameters *params =
[FIRDynamicLinkIOSParameters parametersWithBundleID:@"com.iphone.app"];
XCTAssertEqualObjects(@{@"ibi" : @"com.iphone.app"}, params.dictionaryRepresentation);
}
#pragma mark - FIRDynamicLinkItunesConnectAnalyticsParameters
- (void)testIOSAppStoreParamsFactoryReturnsInstanceOfCorrectClass {
id returnValue = [FIRDynamicLinkItunesConnectAnalyticsParameters parameters];
XCTAssertTrue([returnValue isKindOfClass:[FIRDynamicLinkItunesConnectAnalyticsParameters class]]);
}
- (void)testIOSAppStoreParamsFactoryReturnsInstanceWithAllNilProperties {
FIRDynamicLinkItunesConnectAnalyticsParameters *params =
[FIRDynamicLinkItunesConnectAnalyticsParameters parameters];
XCTAssertNil(params.affiliateToken);
XCTAssertNil(params.campaignToken);
XCTAssertNil(params.providerToken);
}
- (void)testIOSAppStoreParamsPropertiesSetProperly {
FIRDynamicLinkItunesConnectAnalyticsParameters *params =
[FIRDynamicLinkItunesConnectAnalyticsParameters parameters];
params.affiliateToken = @"affiliate";
params.campaignToken = @"campaign";
params.providerToken = @"provider";
XCTAssertEqualObjects(params.affiliateToken, @"affiliate");
XCTAssertEqualObjects(params.campaignToken, @"campaign");
XCTAssertEqualObjects(params.providerToken, @"provider");
params.affiliateToken = nil;
params.campaignToken = nil;
params.providerToken = nil;
XCTAssertNil(params.affiliateToken);
XCTAssertNil(params.campaignToken);
XCTAssertNil(params.providerToken);
}
- (void)testIOSAppStoreDictionaryRepresentationReturnsCorrectDictionaryFull {
FIRDynamicLinkItunesConnectAnalyticsParameters *params =
[FIRDynamicLinkItunesConnectAnalyticsParameters parameters];
params.affiliateToken = @"affiliate";
params.campaignToken = @"campaign";
params.providerToken = @"provider";
NSDictionary *expectedDictionary = @{
@"at" : @"affiliate",
@"ct" : @"campaign",
@"pt" : @"provider",
};
XCTAssertEqualObjects(expectedDictionary, params.dictionaryRepresentation);
}
- (void)testIOSAppStoreDictionaryRepresentationReturnsCorrectDictionaryEmpty {
FIRDynamicLinkItunesConnectAnalyticsParameters *params =
[FIRDynamicLinkItunesConnectAnalyticsParameters parameters];
XCTAssertEqualObjects(@{}, params.dictionaryRepresentation);
}
#pragma mark - FIRDynamicLinkAndroidParameters
- (void)testAndroidParamsFactoryReturnsInstanceOfCorrectClass {
id returnValue =
[FIRDynamicLinkAndroidParameters parametersWithPackageName:@"com.google.android.gms"];
XCTAssertTrue([returnValue isKindOfClass:[FIRDynamicLinkAndroidParameters class]]);
}
- (void)testAndroidParamsFactoryReturnsInstanceWithAllOptionalNilProperties {
FIRDynamicLinkAndroidParameters *params =
[FIRDynamicLinkAndroidParameters parametersWithPackageName:@"com.google.android.gms"];
XCTAssertNil(params.fallbackURL);
XCTAssertEqual(params.minimumVersion, 0);
}
- (void)testAndroidParamsPropertiesSetProperly {
FIRDynamicLinkAndroidParameters *params =
[FIRDynamicLinkAndroidParameters parametersWithPackageName:@"com.google.android.gms"];
params.fallbackURL = [NSURL URLWithString:@"https://google.com/android"];
params.minimumVersion = 14;
XCTAssertEqualObjects(params.packageName, @"com.google.android.gms");
XCTAssertEqualObjects(params.fallbackURL, [NSURL URLWithString:@"https://google.com/android"]);
XCTAssertEqual(params.minimumVersion, 14);
params.fallbackURL = nil;
params.minimumVersion = 0;
XCTAssertNil(params.fallbackURL);
XCTAssertEqual(params.minimumVersion, 0);
}
- (void)testAndroidParamsDictionaryRepresentationReturnsCorrectDictionaryFull {
FIRDynamicLinkAndroidParameters *params =
[FIRDynamicLinkAndroidParameters parametersWithPackageName:@"com.google.android.gms"];
params.fallbackURL = [NSURL URLWithString:@"https://google.com/android"];
params.minimumVersion = 14;
NSDictionary *expectedDictionary = @{
@"apn" : @"com.google.android.gms",
@"afl" : [NSURL URLWithString:@"https://google.com/android"].absoluteString,
@"amv" : @"14",
};
XCTAssertEqualObjects(expectedDictionary, params.dictionaryRepresentation);
}
- (void)testAndroidParamsDictionaryRepresentationReturnsCorrectDictionaryEmpty {
FIRDynamicLinkAndroidParameters *params =
[FIRDynamicLinkAndroidParameters parametersWithPackageName:@"com.google.android.gms"];
XCTAssertEqualObjects(@{@"apn" : @"com.google.android.gms"}, params.dictionaryRepresentation);
}
#pragma mark - FIRDynamicLinkSocialMetaTagParameters
- (void)testSocialParamsFactoryReturnsInstanceOfCorrectClass {
id returnValue = [FIRDynamicLinkSocialMetaTagParameters parameters];
XCTAssertTrue([returnValue isKindOfClass:[FIRDynamicLinkSocialMetaTagParameters class]]);
}
- (void)testSocialParamsFactoryReturnsInstanceWithAllNilProperties {
FIRDynamicLinkSocialMetaTagParameters *params =
[FIRDynamicLinkSocialMetaTagParameters parameters];
XCTAssertNil(params.title);
XCTAssertNil(params.descriptionText);
XCTAssertNil(params.imageURL);
}
- (void)testSocialParamsPropertiesSetProperly {
FIRDynamicLinkSocialMetaTagParameters *params =
[FIRDynamicLinkSocialMetaTagParameters parameters];
params.title = @"title";
params.descriptionText = @"description";
params.imageURL = [NSURL URLWithString:@"https://google.com/someimage"];
XCTAssertEqualObjects(params.title, @"title");
XCTAssertEqualObjects(params.descriptionText, @"description");
XCTAssertEqualObjects(params.imageURL, [NSURL URLWithString:@"https://google.com/someimage"]);
params.title = nil;
params.descriptionText = nil;
params.imageURL = nil;
XCTAssertNil(params.title);
XCTAssertNil(params.descriptionText);
XCTAssertNil(params.imageURL);
}
- (void)testSocialParamsDictionaryRepresentationReturnsCorrectDictionaryFull {
FIRDynamicLinkSocialMetaTagParameters *params =
[FIRDynamicLinkSocialMetaTagParameters parameters];
params.title = @"title";
params.descriptionText = @"description";
params.imageURL = [NSURL URLWithString:@"https://google.com/someimage"];
NSDictionary *expectedDictionary = @{
@"st" : @"title",
@"sd" : @"description",
@"si" : [NSURL URLWithString:@"https://google.com/someimage"].absoluteString,
};
XCTAssertEqualObjects(expectedDictionary, params.dictionaryRepresentation);
}
- (void)testSocialParamsDictionaryRepresentationReturnsCorrectDictionaryEmpty {
FIRDynamicLinkSocialMetaTagParameters *params =
[FIRDynamicLinkSocialMetaTagParameters parameters];
XCTAssertEqualObjects(@{}, params.dictionaryRepresentation);
}
#pragma mark - FIRDynamicLinkNavigationInfoParameters
- (void)testNavigationOptionsReturnsCorrectClass {
id returnValue = [FIRDynamicLinkNavigationInfoParameters parameters];
XCTAssertTrue([returnValue isKindOfClass:[FIRDynamicLinkNavigationInfoParameters class]]);
}
- (void)testNavigationOptionsFactoryReturnsInstanceWithAllNilProperties {
FIRDynamicLinkNavigationInfoParameters *options =
[FIRDynamicLinkNavigationInfoParameters parameters];
XCTAssertEqual(options.forcedRedirectEnabled, NO);
}
- (void)testNavigationOptionsParamsPropertiesSetProperly {
FIRDynamicLinkNavigationInfoParameters *options =
[FIRDynamicLinkNavigationInfoParameters parameters];
options.forcedRedirectEnabled = YES;
XCTAssertEqual(options.forcedRedirectEnabled, YES);
options.forcedRedirectEnabled = NO;
XCTAssertEqual(options.forcedRedirectEnabled, NO);
}
#pragma mark - FIRDynamicLinkOtherPlatformParameters
- (void)testOtherPlatformParametersReturnsCorrectClass {
id returnValue = [FIRDynamicLinkOtherPlatformParameters parameters];
XCTAssertTrue([returnValue isKindOfClass:[FIRDynamicLinkOtherPlatformParameters class]]);
}
- (void)testOtherPlatformParametersFactoryReturnsInstanceWithAllNilProperties {
FIRDynamicLinkOtherPlatformParameters *options =
[FIRDynamicLinkOtherPlatformParameters parameters];
XCTAssertNil(options.fallbackUrl);
}
- (void)testOtherPlatformParametersParamsPropertiesSetProperly {
FIRDynamicLinkOtherPlatformParameters *options =
[FIRDynamicLinkOtherPlatformParameters parameters];
options.fallbackUrl = [NSURL URLWithString:@"https://google.com"];
XCTAssertEqualObjects(options.fallbackUrl, [NSURL URLWithString:@"https://google.com"]);
options.fallbackUrl = nil;
XCTAssertNil(options.fallbackUrl);
}
#pragma mark - FIRDynamicLinkComponentsOptions
- (void)testLinkOptionsFactoryReturnsInstanceOfCorrectClass {
id returnValue = [FIRDynamicLinkComponentsOptions options];
XCTAssertTrue([returnValue isKindOfClass:[FIRDynamicLinkComponentsOptions class]]);
}
- (void)testLinkOptionsParamsFactoryReturnsInstanceWithAllNilProperties {
FIRDynamicLinkComponentsOptions *options = [FIRDynamicLinkComponentsOptions options];
XCTAssertEqual(options.pathLength, FIRShortDynamicLinkPathLengthDefault);
}
- (void)testLinkOptionsParamsPropertiesSetProperly {
FIRDynamicLinkComponentsOptions *options = [FIRDynamicLinkComponentsOptions options];
options.pathLength = FIRShortDynamicLinkPathLengthUnguessable;
XCTAssertEqual(options.pathLength, FIRShortDynamicLinkPathLengthUnguessable);
options.pathLength = FIRShortDynamicLinkPathLengthShort;
XCTAssertEqual(options.pathLength, FIRShortDynamicLinkPathLengthShort);
}
#pragma mark - FIRDynamicLinkComponents
- (void)testFDLComponentsFactoryReturnsInstanceOfCorrectClass {
NSURL *link = [NSURL URLWithString:@"https://google.com"];
id returnValue = [FIRDynamicLinkComponents componentsWithLink:link domainURIPrefix:kFDLURLDomain];
XCTAssertTrue([returnValue isKindOfClass:[FIRDynamicLinkComponents class]]);
}
- (void)testFDLComponentsFactoryReturnsInstanceWithAllNilProperties {
NSURL *link = [NSURL URLWithString:@"https://google.com"];
FIRDynamicLinkComponents *components =
[FIRDynamicLinkComponents componentsWithLink:link domainURIPrefix:kFDLURLDomain];
XCTAssertNil(components.analyticsParameters);
XCTAssertNil(components.socialMetaTagParameters);
XCTAssertNil(components.iOSParameters);
XCTAssertNil(components.iTunesConnectParameters);
XCTAssertNil(components.analyticsParameters);
XCTAssertNil(components.options);
}
- (void)testFDLComponentsCreatesSimplestLinkCorrectly {
NSString *linkString = @"https://google.com";
NSString *encodedLinkString = @"https%3A%2F%2Fgoogle%2Ecom";
NSURL *link = [NSURL URLWithString:linkString];
NSString *expectedURLString =
[NSString stringWithFormat:@"%@/?link=%@", kFDLURLDomain, encodedLinkString];
NSURL *expectedURL = [NSURL URLWithString:expectedURLString];
FIRDynamicLinkComponents *components =
[FIRDynamicLinkComponents componentsWithLink:link domainURIPrefix:kFDLURLDomain];
NSURL *actualURL = components.url;
XCTAssertEqualObjects(actualURL, expectedURL);
}
- (void)testFDLComponentsCustomDomainWithPath {
NSString *linkString = @"https://google.com";
NSString *encodedLinkString = @"https%3A%2F%2Fgoogle%2Ecom";
NSURL *link = [NSURL URLWithString:linkString];
NSString *expectedURLString =
[NSString stringWithFormat:@"%@/?link=%@", kFDLURLCustomDomain, encodedLinkString];
NSURL *expectedURL = [NSURL URLWithString:expectedURLString];
FIRDynamicLinkComponents *components =
[FIRDynamicLinkComponents componentsWithLink:link domainURIPrefix:kFDLURLCustomDomain];
NSURL *actualURL = components.url;
XCTAssertEqualObjects(actualURL, expectedURL);
}
- (void)testFDLComponentsFailsOnMalformedDomainURIPrefix {
NSString *linkString = @"https://google.com";
NSURL *link = [NSURL URLWithString:linkString];
FIRDynamicLinkComponents *components =
[FIRDynamicLinkComponents componentsWithLink:link
domainURIPrefix:@"this is invalid domain URI Prefix"];
XCTAssertNil(components.url);
}
- (void)testFDLComponentsNotNilOnDomainWithHTTPScheme {
NSString *linkString = @"https://google.com";
NSURL *link = [NSURL URLWithString:linkString];
FIRDynamicLinkComponents *components =
[FIRDynamicLinkComponents componentsWithLink:link domainURIPrefix:@"https://xyz.page.link"];
XCTAssertNotNil(components);
}
- (void)testFDLComponentsNotNilOnDomainWithHTTPSScheme {
NSString *linkString = @"https://google.com";
NSURL *link = [NSURL URLWithString:linkString];
FIRDynamicLinkComponents *components =
[FIRDynamicLinkComponents componentsWithLink:link domainURIPrefix:@"https://xyz.page.link"];
XCTAssertNotNil(components);
}
- (void)testFDLComponentsFailsOnMalformedDomain {
NSString *linkString = @"https://google.com";
NSURL *link = [NSURL URLWithString:linkString];
FIRDynamicLinkComponents *components =
[FIRDynamicLinkComponents componentsWithLink:link
domainURIPrefix:@"this is invalid domain URI Prefix"];
XCTAssertNil(components);
}
- (void)testFDLComponentsCreatesFullLinkCorrectly {
FIRDynamicLinkGoogleAnalyticsParameters *analyticsParams =
[FIRDynamicLinkGoogleAnalyticsParameters parameters];
analyticsParams.source = @"s";
analyticsParams.medium = @"m";
analyticsParams.campaign = @"ca";
analyticsParams.term = @"t";
analyticsParams.content = @"co";
FIRDynamicLinkIOSParameters *iosParams =
[FIRDynamicLinkIOSParameters parametersWithBundleID:@"com.iphone.app"];
iosParams.fallbackURL = [NSURL URLWithString:@"https://google.com/iphone"];
iosParams.customScheme = @"mycustomsheme";
iosParams.minimumAppVersion = @"1.2.3";
iosParams.iPadBundleID = @"com.ipad.app";
iosParams.iPadFallbackURL = [NSURL URLWithString:@"https://google.com/ipad"];
iosParams.appStoreID = @"666";
FIRDynamicLinkItunesConnectAnalyticsParameters *itcParams =
[FIRDynamicLinkItunesConnectAnalyticsParameters parameters];
itcParams.affiliateToken = @"affiliate";
itcParams.campaignToken = @"campaign";
itcParams.providerToken = @"provider";
FIRDynamicLinkAndroidParameters *androidParams =
[FIRDynamicLinkAndroidParameters parametersWithPackageName:@"com.google.android.gms"];
androidParams.fallbackURL = [NSURL URLWithString:@"https://google.com/android"];
androidParams.minimumVersion = 14;
FIRDynamicLinkSocialMetaTagParameters *socialParams =
[FIRDynamicLinkSocialMetaTagParameters parameters];
socialParams.title = @"title";
socialParams.descriptionText = @"description";
socialParams.imageURL = [NSURL URLWithString:@"https://google.com/someimage"];
FIRDynamicLinkOtherPlatformParameters *otherPlatformParams =
[FIRDynamicLinkOtherPlatformParameters parameters];
otherPlatformParams.fallbackUrl =
[NSURL URLWithString:@"https://google.com/fallbackForOtherPlatform"];
FIRDynamicLinkNavigationInfoParameters *navInfo =
[FIRDynamicLinkNavigationInfoParameters parameters];
navInfo.forcedRedirectEnabled = YES;
FIRDynamicLinkComponentsOptions *options = [FIRDynamicLinkComponentsOptions options];
options.pathLength = FIRShortDynamicLinkPathLengthUnguessable;
NSURL *link = [NSURL URLWithString:@"https://google.com"];
FIRDynamicLinkComponents *fdlComponents =
[FIRDynamicLinkComponents componentsWithLink:link domainURIPrefix:kFDLURLDomain];
fdlComponents.analyticsParameters = analyticsParams;
fdlComponents.iOSParameters = iosParams;
fdlComponents.iTunesConnectParameters = itcParams;
fdlComponents.androidParameters = androidParams;
fdlComponents.socialMetaTagParameters = socialParams;
fdlComponents.navigationInfoParameters = navInfo;
fdlComponents.otherPlatformParameters = otherPlatformParams;
fdlComponents.options = options;
// This is a long FDL URL that has been verified to be a correct representation of the expected
// URL. Since the parameters are not guaranteed to be in any specific order, we must compare
// arrays of properties of the URLs rather than the URLs themselves.
NSString *possibleExpectedURLString =
@"https://xyz.page.link/?afl=https%3A%2F%2Fgoogle%2Ecom%2F"
"android&amv=14&apn=com.google.android.gms&ibi=com%2Eiphone%2Eapp&utm_term=t&link=https%3A%"
"2F"
"%2Fgoogle%2Ecom&ipbi=com%2Eipad%2Eapp&ius=mycustomsheme&ifl=https%3A%2F%2Fgoogle%2Ecom%2"
"Fiphone&isi=666&utm_content=co&utm_source=s&utm_medium=m&imv=1%2E2%2E3&ct=campaign&ipfl="
"http"
"s%3A%2F%2Fgoogle%2Ecom%2Fipad&si=https%3A%2F%2Fgoogle%2Ecom%2Fsomeimage&at=affiliate&pt="
"prov"
"ider&st=title&utm_campaign=ca&sd=description&efr=1&ofl=https%3A%2F%2Fgoogle%2Ecom%"
"2Ffallback"
"ForOtherPlatform";
NSURL *possibleExpectedURL = [NSURL URLWithString:possibleExpectedURLString];
NSURLComponents *expectedURLComponents =
[NSURLComponents componentsWithString:possibleExpectedURLString];
// sort both expected/actual arrays to prevent order influencing the test results
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES];
NSArray<NSURLQueryItem *> *expectedURLQueryItems =
[expectedURLComponents.queryItems sortedArrayUsingDescriptors:@[ sort ]];
NSURL *actualURL = fdlComponents.url;
NSURLComponents *actualURLComponents =
[NSURLComponents componentsWithString:actualURL.absoluteString];
NSArray<NSURLQueryItem *> *actualQueryItems =
[actualURLComponents.queryItems sortedArrayUsingDescriptors:@[ sort ]];
XCTAssertEqualObjects(actualQueryItems, expectedURLQueryItems);
XCTAssertEqualObjects(actualURL.host, possibleExpectedURL.host);
}
- (void)testFDLComponentsCorrectlySetsPathLengthInRequest {
NSURL *url = [NSURL URLWithString:@"https://google.com/abc"];
NSURLRequest *request;
NSDictionary *JSON;
FIRDynamicLinkComponentsOptions *options = [FIRDynamicLinkComponentsOptions options];
// Default path-length
request = [FIRDynamicLinkComponents shorteningRequestForLongURL:url options:options];
JSON = [NSJSONSerialization JSONObjectWithData:request.HTTPBody options:0 error:nil];
XCTAssertNil(JSON[@"suffix"]);
// Unguessable
options.pathLength = FIRShortDynamicLinkPathLengthUnguessable;
request = [FIRDynamicLinkComponents shorteningRequestForLongURL:url options:options];
JSON = [NSJSONSerialization JSONObjectWithData:request.HTTPBody options:0 error:nil];
XCTAssertTrue([JSON[@"suffix"][@"option"] isEqualToString:@"UNGUESSABLE"]);
// Short
options.pathLength = FIRShortDynamicLinkPathLengthShort;
request = [FIRDynamicLinkComponents shorteningRequestForLongURL:url options:options];
JSON = [NSJSONSerialization JSONObjectWithData:request.HTTPBody options:0 error:nil];
XCTAssertTrue([JSON[@"suffix"][@"option"] isEqualToString:@"SHORT"]);
}
- (void)testShortenURL {
NSString *shortURLString = @"https://xyz.page.link/abcd";
// Mock key provider
id keyProviderClassMock = OCMClassMock([FIRDynamicLinkComponentsKeyProvider class]);
[[[keyProviderClassMock expect] andReturn:@"fake-api-key"] APIKey];
id componentsClassMock = OCMClassMock([FIRDynamicLinkComponents class]);
[[componentsClassMock expect]
sendHTTPRequest:OCMOCK_ANY
completion:[OCMArg checkWithBlock:^BOOL(id obj) {
void (^completion)(NSData *_Nullable, NSError *_Nullable) = obj;
NSDictionary *JSON = @{@"shortLink" : shortURLString};
NSData *JSONData = [NSJSONSerialization dataWithJSONObject:JSON options:0 error:0];
completion(JSONData, nil);
return YES;
}]];
XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"];
NSURL *link = [NSURL URLWithString:@"https://google.com/abc"];
FIRDynamicLinkComponents *components =
[FIRDynamicLinkComponents componentsWithLink:link domainURIPrefix:kFDLURLDomain];
[components
shortenWithCompletion:^(NSURL *_Nullable shortURL, NSArray<NSString *> *_Nullable warnings,
NSError *_Nullable error) {
XCTAssertEqualObjects(shortURL.absoluteString, shortURLString);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:0.1 handler:nil];
[keyProviderClassMock verify];
[keyProviderClassMock stopMocking];
[componentsClassMock verify];
[componentsClassMock stopMocking];
}
- (void)testShortenURLReturnsErrorWhenAPIKeyMissing {
NSString *shortURLString = @"https://xyz.page.link/abcd";
// Mock key provider
id keyProviderClassMock = OCMClassMock([FIRDynamicLinkComponentsKeyProvider class]);
[[[keyProviderClassMock expect] andReturn:nil] APIKey];
id componentsClassMock = OCMClassMock([FIRDynamicLinkComponents class]);
[[componentsClassMock stub]
sendHTTPRequest:OCMOCK_ANY
completion:[OCMArg checkWithBlock:^BOOL(id obj) {
void (^completion)(NSData *_Nullable, NSError *_Nullable) = obj;
NSDictionary *JSON = @{@"shortLink" : shortURLString};
NSData *JSONData = [NSJSONSerialization dataWithJSONObject:JSON options:0 error:0];
completion(JSONData, nil);
return YES;
}]];
XCTestExpectation *expectation =
[self expectationWithDescription:@"completion called with error"];
NSURL *link = [NSURL URLWithString:@"https://google.com/abc"];
FIRDynamicLinkComponents *components =
[FIRDynamicLinkComponents componentsWithLink:link domainURIPrefix:kFDLURLDomain];
[components
shortenWithCompletion:^(NSURL *_Nullable shortURL, NSArray<NSString *> *_Nullable warnings,
NSError *_Nullable error) {
XCTAssertNil(shortURL);
if (error) {
[expectation fulfill];
}
}];
[self waitForExpectationsWithTimeout:0.1 handler:nil];
[keyProviderClassMock verify];
[keyProviderClassMock stopMocking];
[componentsClassMock verify];
[componentsClassMock stopMocking];
}
- (void)testShortenURLReturnsErrorWhenDomainIsMalformed {
NSString *shortURLString = @"https://xyz.page.link/abcd";
// Mock key provider
id keyProviderClassMock = OCMClassMock([FIRDynamicLinkComponentsKeyProvider class]);
[[keyProviderClassMock reject] APIKey];
id componentsClassMock = OCMClassMock([FIRDynamicLinkComponents class]);
[[componentsClassMock reject]
sendHTTPRequest:OCMOCK_ANY
completion:[OCMArg checkWithBlock:^BOOL(id obj) {
void (^completion)(NSData *_Nullable, NSError *_Nullable) = obj;
NSDictionary *JSON = @{@"shortLink" : shortURLString};
NSData *JSONData = [NSJSONSerialization dataWithJSONObject:JSON options:0 error:0];
completion(JSONData, nil);
return YES;
}]];
NSURL *link = [NSURL URLWithString:@"https://google.com/abc"];
FIRDynamicLinkComponents *components =
[FIRDynamicLinkComponents componentsWithLink:link
domainURIPrefix:@"this is invalid domain URI Prefix"];
XCTAssertNil(components);
[keyProviderClassMock verify];
[keyProviderClassMock stopMocking];
[componentsClassMock verify];
[componentsClassMock stopMocking];
}
@end
@@ -1,182 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <XCTest/XCTest.h>
#import <OCMock/OCMock.h>
#import "FirebaseDynamicLinks/Sources/FIRDLScionLogging.h"
static const NSTimeInterval kAsyncTestTimeout = 0.5;
typedef void (^FakeAnalyticsLogEventWithOriginNameParametersHandler)(NSString *origin,
NSString *name,
NSDictionary *parameters);
@interface FakeAnalytics : NSObject <FIRAnalyticsInterop>
- (instancetype)initWithHandler:(FakeAnalyticsLogEventWithOriginNameParametersHandler)handler;
@end
@implementation FakeAnalytics
static FakeAnalyticsLogEventWithOriginNameParametersHandler _handler;
- (instancetype)initWithHandler:(FakeAnalyticsLogEventWithOriginNameParametersHandler)handler {
self = [super init];
if (self) {
_handler = handler;
}
return self;
}
- (void)logEventWithOrigin:(nonnull NSString *)origin
name:(nonnull NSString *)name
parameters:(nullable NSDictionary<NSString *, id> *)parameters {
if (_handler) {
_handler(origin, name, parameters);
}
}
// Stubs
- (void)clearConditionalUserProperty:(nonnull NSString *)userPropertyName
clearEventName:(nonnull NSString *)clearEventName
clearEventParameters:(nonnull NSDictionary *)clearEventParameters {
}
- (NSInteger)maxUserProperties:(nonnull NSString *)origin {
return -1;
}
- (void)setUserPropertyWithOrigin:(nonnull NSString *)origin
name:(nonnull NSString *)name
value:(nonnull id)value {
}
- (void)checkLastNotificationForOrigin:(nonnull NSString *)origin
queue:(nonnull dispatch_queue_t)queue
callback:(nonnull void (^)(NSString *_Nullable))
currentLastNotificationProperty {
}
- (void)registerAnalyticsListener:(nonnull id<FIRAnalyticsInteropListener>)listener
withOrigin:(nonnull NSString *)origin {
}
- (void)unregisterAnalyticsListenerWithOrigin:(nonnull NSString *)origin {
}
- (void)clearConditionalUserProperty:(nonnull NSString *)userPropertyName
forOrigin:(nonnull NSString *)origin
clearEventName:(nonnull NSString *)clearEventName
clearEventParameters:
(nonnull NSDictionary<NSString *, NSString *> *)clearEventParameters {
}
- (nonnull NSArray<NSDictionary<NSString *, NSString *> *> *)
conditionalUserProperties:(nonnull NSString *)origin
propertyNamePrefix:(nonnull NSString *)propertyNamePrefix {
return nil;
}
- (void)setConditionalUserProperty:(nonnull NSDictionary<NSString *, id> *)conditionalUserProperty {
}
- (void)getUserPropertiesWithCallback:(nonnull FIRAInteropUserPropertiesCallback)callback {
}
@end
@interface FIRDLScionLoggingTest : XCTestCase
@end
@implementation FIRDLScionLoggingTest
- (void)testGINLogEventToScionCallsLogMethodWithFirstOpen {
XCTestExpectation *expectation = [self expectationWithDescription:@"completion"];
FakeAnalytics *analytics = [[FakeAnalytics alloc]
initWithHandler:^(NSString *origin, NSString *name, NSDictionary *parameters) {
[expectation fulfill];
}];
FIRDLLogEventToScion(FIRDLLogEventFirstOpen, nil, nil, nil, analytics);
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
}
- (void)testGINLogEventToScionContainsCorrectNameWithFirstOpen {
XCTestExpectation *expectation = [self expectationWithDescription:@"completion"];
FakeAnalytics *analytics = [[FakeAnalytics alloc]
initWithHandler:^(NSString *origin, NSString *name, NSDictionary *parameters) {
XCTAssertEqualObjects(name, @"dynamic_link_first_open", @"scion name param was incorrect");
[expectation fulfill];
}];
FIRDLLogEventToScion(FIRDLLogEventFirstOpen, nil, nil, nil, analytics);
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
}
- (void)testGINLogEventToScionCallsLogMethodWithAppOpen {
XCTestExpectation *expectation = [self expectationWithDescription:@"completion"];
FakeAnalytics *analytics = [[FakeAnalytics alloc]
initWithHandler:^(NSString *origin, NSString *name, NSDictionary *parameters) {
[expectation fulfill];
}];
FIRDLLogEventToScion(FIRDLLogEventAppOpen, nil, nil, nil, analytics);
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
}
- (void)testGINLogEventToScionContainsCorrectNameWithAppOpen {
XCTestExpectation *expectation = [self expectationWithDescription:@"completion"];
FakeAnalytics *analytics = [[FakeAnalytics alloc]
initWithHandler:^(NSString *origin, NSString *name, NSDictionary *parameters) {
XCTAssertEqualObjects(name, @"dynamic_link_app_open", @"scion name param was incorrect");
[expectation fulfill];
}];
FIRDLLogEventToScion(FIRDLLogEventAppOpen, nil, nil, nil, analytics);
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
}
- (void)testGINLogEventToScionLogsParametersCorrectly {
NSString *source = @"9-2nkg";
NSString *medium = @"fjg0";
NSString *campaign = @"gjoo3u5";
NSString *sourceKey = @"source";
NSString *mediumKey = @"medium";
NSString *campaignKey = @"campaign";
XCTestExpectation *expectation = [self expectationWithDescription:@"completion"];
FakeAnalytics *analytics = [[FakeAnalytics alloc]
initWithHandler:^(NSString *origin, NSString *name, NSDictionary *params) {
XCTAssertEqualObjects(params[sourceKey], source, @"scion logger has incorrect source.");
XCTAssertEqualObjects(params[mediumKey], medium, @"scion logger has incorrect medium.");
XCTAssertEqualObjects(params[campaignKey], campaign,
@"scion logger has incorrect campaign.");
[expectation fulfill];
}];
FIRDLLogEventToScion(FIRDLLogEventAppOpen, source, medium, campaign, analytics);
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
}
@end
@@ -1,100 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <XCTest/XCTest.h>
#import <OCMock/OCMock.h>
#import <GoogleUtilities/GULSwizzler.h>
#import <GoogleUtilities/GULSwizzler+Unswizzle.h>
#import "FirebaseDynamicLinks/Sources/FIRDynamicLinkNetworking+Private.h"
static NSString *const kAPIKey = @"myfakeapikey";
const NSInteger kJSONParsingErrorCode = 3840;
static NSString *const kURLScheme = @"gindeeplinkurl";
static const NSTimeInterval kAsyncTestTimeout = 5.0;
@interface FIRDynamicLinkNetworkingTests : XCTestCase
@property(strong, nonatomic) FIRDynamicLinkNetworking *service;
@end
@implementation FIRDynamicLinkNetworkingTests
- (void)tearDown {
self.service = nil;
}
- (FIRDynamicLinkNetworking *)service {
if (!_service) {
_service = [[FIRDynamicLinkNetworking alloc] initWithAPIKey:kAPIKey URLScheme:kURLScheme];
}
return _service;
}
- (void)testFIRDynamicLinkAPIKeyParameterReturnsCorrectlyFormattedParameterString {
NSString *expectedValue = [NSString stringWithFormat:@"?key=%@", kAPIKey];
NSString *parameter = FIRDynamicLinkAPIKeyParameter(kAPIKey);
XCTAssertEqualObjects(parameter, expectedValue,
@"FIRDynamicLinkAPIKeyParameter() returned incorrect parameter string");
}
- (void)testFIRDynamicLinkAPIKeyParameterReturnsNilParameterStringWhenAPIKeyIsNil {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wnonnull"
NSString *parameter = FIRDynamicLinkAPIKeyParameter(nil);
#pragma clang diagnostic pop
XCTAssertNil(parameter,
@"FIRDynamicLinkAPIKeyParameter() returned non-nil result when API key was nil");
}
- (void)testResolveShortLinkServiceCompletionDoesntCrashWhenNilDataIsRetrieved {
NSURL *url = [NSURL URLWithString:@"https://google.com"];
void (^executeRequestBlock)(id, NSDictionary *, NSString *, FIRNetworkRequestCompletionHandler) =
^(id p1, NSDictionary *requestBody, NSString *requestURLString,
FIRNetworkRequestCompletionHandler handler) {
handler(nil, nil, nil);
};
SEL executeRequestSelector = @selector(executeOnePlatformRequest:forURL:completionHandler:);
[GULSwizzler swizzleClass:[FIRDynamicLinkNetworking class]
selector:executeRequestSelector
isClassSelector:NO
withBlock:executeRequestBlock];
XCTestExpectation *expectation = [self expectationWithDescription:@"completion called"];
[self.service resolveShortLink:url
FDLSDKVersion:@"1.0.0"
completion:^(NSURL *_Nullable url, NSError *_Nullable error) {
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
[GULSwizzler unswizzleClass:[FIRDynamicLinkNetworking class]
selector:executeRequestSelector
isClassSelector:NO];
}
@end
@@ -1,79 +0,0 @@
/*
* Copyright 2021 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
#import <XCTest/XCTest.h>
#import "FirebaseDynamicLinks/Sources/FIRDynamicLink+Private.h"
@interface FIRDynamicLinkTest : XCTestCase {
}
@end
@implementation FIRDynamicLinkTest
NSMutableDictionary<NSString *, NSString *> *fdlParameters = nil;
NSDictionary<NSString *, NSString *> *linkParameters = nil;
NSDictionary<NSString *, NSString *> *utmParameters = nil;
- (void)setUp {
[super setUp];
linkParameters = @{
@"deep_link_id" : @"https://mmaksym.com/test-app1",
@"match_message" : @"Link is uniquely matched for this device.",
@"match_type" : @"unique",
@"a_parameter" : @"a_value"
};
utmParameters = @{
@"utm_campaign" : @"eldhosembabu Test",
@"utm_medium" : @"test_medium",
@"utm_source" : @"test_source",
};
fdlParameters = [[NSMutableDictionary alloc] initWithDictionary:linkParameters];
[fdlParameters addEntriesFromDictionary:utmParameters];
}
- (void)testDynamicLinkParameters_InitWithParameters {
FIRDynamicLink *dynamicLink = [[FIRDynamicLink alloc] initWithParametersDictionary:fdlParameters];
XCTAssertEqual([fdlParameters count], [[dynamicLink parametersDictionary] count]);
for (NSString *key in fdlParameters) {
NSString *expectedValue = [fdlParameters valueForKey:key];
NSString *derivedValue = [[dynamicLink parametersDictionary] valueForKey:key];
XCTAssertNotNil(derivedValue, @"Cannot be null!");
XCTAssertEqualObjects(derivedValue, expectedValue);
}
}
- (void)testDynamicLinkUtmParameters_InitWithParameters {
FIRDynamicLink *dynamicLink = [[FIRDynamicLink alloc] initWithParametersDictionary:fdlParameters];
XCTAssertEqual([[dynamicLink utmParametersDictionary] count], [utmParameters count]);
for (NSString *key in utmParameters) {
NSString *expectedValue = [utmParameters valueForKey:key];
NSString *derivedValue = [[dynamicLink utmParametersDictionary] valueForKey:key];
XCTAssertNotNil(derivedValue, @"Cannot be null!");
XCTAssertEqualObjects(derivedValue, expectedValue);
}
}
- (void)testDynamicLinkParameters_InitWithNoUtmParameters {
FIRDynamicLink *dynamicLink =
[[FIRDynamicLink alloc] initWithParametersDictionary:linkParameters];
XCTAssertEqual([[dynamicLink parametersDictionary] count], [linkParameters count]);
XCTAssertEqual([[dynamicLink utmParametersDictionary] count], 0);
}
@end
@@ -1,33 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <XCTest/XCTest.h>
#import "FirebaseDynamicLinks/Sources/Public/FirebaseDynamicLinks/FIRDynamicLinks.h"
@interface FIRDynamicLinksImportsTest3P : XCTestCase
@end
@implementation FIRDynamicLinksImportsTest3P
- (void)testPlaceholder {
// This is empty test case.
// The test file designed to test successful build if source file, that includes
// FIRDynamicLinks.h, does not includes any other headers, like UIKit.
}
@end
File diff suppressed because it is too large. Load diff
@@ -1,198 +0,0 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <XCTest/XCTest.h>
#import "FirebaseDynamicLinks/Sources/Utilities/FDLUtilities.h"
static NSString *const kURLScheme = @"gindeeplinkurl";
@interface FDLUtilitiesTests : XCTestCase
@end
@implementation FDLUtilitiesTests
- (void)testFDLCookieRetrievalURLCreatesCorrectURL {
static NSString *const kCustomScheme = @"customscheme";
static NSString *const kBundleID = @"com.My.Bundle.ID";
NSString *expectedURLString = [NSString stringWithFormat:@"https://goo.gl/app/_/deeplink?fdl_ios_"
"bundle_id=%@&fdl_ios_url_scheme=%@",
kBundleID, kCustomScheme];
NSURL *url = FIRDLCookieRetrievalURL(kCustomScheme, kBundleID);
XCTAssertEqualObjects(url.absoluteString, expectedURLString);
}
- (void)testFDLURLQueryStringFromDictionaryReturnsEmptyStringWithEmptyDictionary {
NSString *query = FIRDLURLQueryStringFromDictionary(@{});
XCTAssertEqualObjects(query, @"");
}
- (void)testFDLURLQueryStringFromDictionaryReturnsCorrectStringWithSingleKVP {
NSString *key = @"key";
NSString *value = @"value";
NSDictionary *queryDict = @{key : value};
NSString *query = FIRDLURLQueryStringFromDictionary(queryDict);
NSString *expectedQuery = [NSString stringWithFormat:@"?%@=%@", key, value];
XCTAssertEqualObjects(query, expectedQuery);
}
- (void)testFDLURLQueryStringFromDictionary {
NSDictionary *expectedQueryDict = @{
@"key1" : @"va!lue1",
@"key2" : @"val=ue2",
@"key3" : @"val&ue3",
@"key4" : @"valu?e4",
@"key5" : @"val$ue5",
};
NSString *query = FIRDLURLQueryStringFromDictionary(expectedQueryDict);
NSString *prefixToRemove = @"?";
NSString *queryWithoutPrefix = [query substringFromIndex:prefixToRemove.length];
NSDictionary *retrievedQueryDict = FIRDLDictionaryFromQuery(queryWithoutPrefix);
XCTAssertEqualObjects(retrievedQueryDict, expectedQueryDict);
}
- (void)testGINDictionaryFromQueryWithNormalQuery {
NSString *query = @"key1=value1&key2=value2";
NSDictionary *returnedDictionary = FIRDLDictionaryFromQuery(query);
NSDictionary *expectedDictionary = @{@"key1" : @"value1", @"key2" : @"value2"};
XCTAssertEqualObjects(returnedDictionary, expectedDictionary);
}
- (void)testGINDictionaryFromQueryWithQueryMissingValue {
NSString *query = @"key1=value1&key2=";
NSDictionary *returnedDictionary = FIRDLDictionaryFromQuery(query);
NSDictionary *expectedDictionary = @{@"key1" : @"value1", @"key2" : @""};
XCTAssertEqualObjects(returnedDictionary, expectedDictionary);
}
- (void)testGINDictionaryFromQueryWithQueryMissingKey {
NSString *query = @"key1=value1&=value2";
NSDictionary *returnedDictionary = FIRDLDictionaryFromQuery(query);
NSDictionary *expectedDictionary = @{@"key1" : @"value1", @"" : @"value2"};
XCTAssertEqualObjects(returnedDictionary, expectedDictionary);
}
- (void)testGINDictionaryFromQueryWithQueryMissingKeyAndValue {
NSString *query = @"key1=value1&=";
NSDictionary *returnedDictionary = FIRDLDictionaryFromQuery(query);
NSDictionary *expectedDictionary = @{@"key1" : @"value1", @"" : @""};
XCTAssertEqualObjects(returnedDictionary, expectedDictionary);
}
- (void)testGINDictionaryFromQueryWithQueryMissingPairAtTheEnd {
NSString *query = @"key1=value1&";
NSDictionary *returnedDictionary = FIRDLDictionaryFromQuery(query);
NSDictionary *expectedDictionary = @{@"key1" : @"value1"};
XCTAssertEqualObjects(returnedDictionary, expectedDictionary);
}
- (void)testGINDictionaryFromQueryWithQueryMissingPairAtTheBeginning {
NSString *query = @"&key1=value1";
NSDictionary *returnedDictionary = FIRDLDictionaryFromQuery(query);
NSDictionary *expectedDictionary = @{@"key1" : @"value1"};
XCTAssertEqualObjects(returnedDictionary, expectedDictionary);
}
- (void)testGINDictionaryFromQueryWithQueryMissingPairInTheMiddle {
NSString *query = @"key1=value1&&key2=value2";
NSDictionary *returnedDictionary = FIRDLDictionaryFromQuery(query);
NSDictionary *expectedDictionary = @{@"key1" : @"value1", @"key2" : @"value2"};
XCTAssertEqualObjects(returnedDictionary, expectedDictionary);
}
- (void)testDeepLinkURLWithInviteIDDeepLinkStringWeakMatchEndpointCreatesExpectedCustomSchemeURL {
NSString *inviteID = @"3082906yht4i02";
NSString *deepLinkString = @"https://google.com/a%b!c=d";
NSString *encodedDeepLinkString = @"https%3A%2F%2Fgoogle%2Ecom%2Fa%25b%21c%3Dd";
NSString *weakMatchEndpoint = @"IPV6";
NSString *utmSource = @"firebase";
NSString *utmMedium = @"email";
NSString *utmCampaign = @"testCampaign";
NSString *utmTerm = @"testTerm";
NSString *utmContent = @"testContent";
NSString *matchType = @"unique";
NSString *expectedURLString = [NSString
stringWithFormat:@"%@://google/link/?utm_campaign=%@"
@"&deep_link_id=%@&utm_medium=%@&invitation_weakMatchEndpoint=%@"
@"&utm_source=%@&invitation_id=%@&match_type=%@"
@"&utm_content=%@&utm_term=%@",
kURLScheme, utmCampaign, encodedDeepLinkString, utmMedium, weakMatchEndpoint,
utmSource, inviteID, matchType, utmContent, utmTerm];
NSURLComponents *expectedURLComponents = [NSURLComponents componentsWithString:expectedURLString];
NSURL *actualURL = FIRDLDeepLinkURLWithInviteID(inviteID, deepLinkString, utmSource, utmMedium,
utmCampaign, utmContent, utmTerm, NO,
weakMatchEndpoint, nil, kURLScheme, nil);
NSURLComponents *actualURLComponents = [NSURLComponents componentsWithURL:actualURL
resolvingAgainstBaseURL:NO];
// Since the parameters are not guaranteed to be in any specific order, we must compare
// arrays of properties of the URLs rather than the URLs themselves.
// sort both expected/actual arrays to prevent order influencing the test results
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES];
NSArray<NSURLQueryItem *> *expectedURLQueryItems =
[expectedURLComponents.queryItems sortedArrayUsingDescriptors:@[ sort ]];
NSArray<NSURLQueryItem *> *actualQueryItems =
[actualURLComponents.queryItems sortedArrayUsingDescriptors:@[ sort ]];
XCTAssertEqualObjects(actualQueryItems, expectedURLQueryItems);
XCTAssertEqualObjects(actualURLComponents.host, expectedURLComponents.host);
}
- (void)testGINOSVersionSupportedReturnsYESWhenCurrentIsGreaterThanMin {
BOOL supported = FIRDLOSVersionSupported(@"8.0.1", @"8.0");
XCTAssertTrue(supported, @"FIRDLOSVersionSupported() returned NO when the OS was supported.");
}
- (void)testGINOSVersionSupportedReturnsYESWhenCurrentIsEqualToMin {
BOOL supported = FIRDLOSVersionSupported(@"8.0", @"8.0");
XCTAssertTrue(supported, @"FIRDLOSVersionSupported() returned NO when the OS was supported.");
}
- (void)testGINOSVersionSupportedReturnsNOWhenCurrentIsLessThanMin {
BOOL supported = FIRDLOSVersionSupported(@"7.1", @"8.1");
XCTAssertFalse(supported,
@"FIRDLOSVersionSupported() returned YES when the OS was not supported.");
}
@end
@@ -29,7 +29,6 @@
EA7DF58D29EF3326005664A7 /* FirebaseAppDistribution-Beta in Frameworks */ = {isa = PBXBuildFile; platformFilter = ios; productRef = EA7DF58C29EF3326005664A7 /* FirebaseAppDistribution-Beta */; };
EA7DF59329EF3326005664A7 /* FirebaseCrashlytics in Frameworks */ = {isa = PBXBuildFile; productRef = EA7DF59229EF3326005664A7 /* FirebaseCrashlytics */; };
EA7DF59529EF3326005664A7 /* FirebaseDatabase in Frameworks */ = {isa = PBXBuildFile; productRef = EA7DF59429EF3326005664A7 /* FirebaseDatabase */; };
EA7DF59929EF3326005664A7 /* FirebaseDynamicLinks in Frameworks */ = {isa = PBXBuildFile; platformFilter = ios; productRef = EA7DF59829EF3326005664A7 /* FirebaseDynamicLinks */; };
EA7DF59B29EF3326005664A7 /* FirebaseFirestore in Frameworks */ = {isa = PBXBuildFile; productRef = EA7DF59A29EF3326005664A7 /* FirebaseFirestore */; };
EA7DF59D29EF3326005664A7 /* FirebaseFirestoreCombine-Community in Frameworks */ = {isa = PBXBuildFile; productRef = EA7DF59C29EF3326005664A7 /* FirebaseFirestoreCombine-Community */; };
EA7DF5A129EF3327005664A7 /* FirebaseFunctions in Frameworks */ = {isa = PBXBuildFile; productRef = EA7DF5A029EF3327005664A7 /* FirebaseFunctions */; };
@@ -81,7 +80,6 @@
EA7DF5A129EF3327005664A7 /* FirebaseFunctions in Frameworks */,
EA7DF58D29EF3326005664A7 /* FirebaseAppDistribution-Beta in Frameworks */,
EA7DF5AF29EF3328005664A7 /* FirebasePerformance in Frameworks */,
EA7DF59929EF3326005664A7 /* FirebaseDynamicLinks in Frameworks */,
EA7DF59D29EF3326005664A7 /* FirebaseFirestoreCombine-Community in Frameworks */,
EA7DF5A929EF3327005664A7 /* FirebaseInstallations in Frameworks */,
);
@@ -203,7 +201,6 @@
EA7DF58C29EF3326005664A7 /* FirebaseAppDistribution-Beta */,
EA7DF59229EF3326005664A7 /* FirebaseCrashlytics */,
EA7DF59429EF3326005664A7 /* FirebaseDatabase */,
EA7DF59829EF3326005664A7 /* FirebaseDynamicLinks */,
EA7DF59A29EF3326005664A7 /* FirebaseFirestore */,
EA7DF59C29EF3326005664A7 /* FirebaseFirestoreCombine-Community */,
EA7DF5A029EF3327005664A7 /* FirebaseFunctions */,
@@ -661,10 +658,6 @@
isa = XCSwiftPackageProductDependency;
productName = FirebaseDatabase;
};
EA7DF59829EF3326005664A7 /* FirebaseDynamicLinks */ = {
isa = XCSwiftPackageProductDependency;
productName = FirebaseDynamicLinks;
};
EA7DF59A29EF3326005664A7 /* FirebaseFirestore */ = {
isa = XCSwiftPackageProductDependency;
productName = FirebaseFirestore;
-1
View File
@@ -22,7 +22,6 @@ target 'ClientApp-CocoaPods' do
pod 'FirebaseAuth', :path => '../../'
pod 'FirebaseCrashlytics', :path => '../../'
pod 'FirebaseDatabase', :path => '../../'
pod 'FirebaseDynamicLinks', :path => '../../'
pod 'FirebaseFirestore', :path => '../../'
pod 'FirebaseFirestoreInternal', :path => '../../'
pod 'FirebaseFunctions', :path => '../../'
@@ -31,16 +31,12 @@
#import <FirebaseCore/FirebaseCore.h>
#import <FirebaseCrashlytics/FirebaseCrashlytics.h>
#import <FirebaseDatabase/FirebaseDatabase.h>
#import "FirebaseCore/FirebaseCore.h"
#import "FirebaseCrashlytics/FirebaseCrashlytics.h"
#import "FirebaseDatabase/FirebaseDatabase.h"
#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST
#import <FirebaseDynamicLinks/FirebaseDynamicLinks.h>
#import "FirebaseDynamicLinks/FirebaseDynamicLinks.h"
#endif
#import <FirebaseFirestore/FirebaseFirestore.h>
#import <FirebaseInstallations/FirebaseInstallations.h>
#import <FirebaseMessaging/FirebaseMessaging.h>
#import "FirebaseCore/FirebaseCore.h"
#import "FirebaseCrashlytics/FirebaseCrashlytics.h"
#import "FirebaseDatabase/FirebaseDatabase.h"
#import "FirebaseFirestore/FirebaseFirestore.h"
#import "FirebaseInstallations/FirebaseInstallations.h"
#import "FirebaseMessaging/FirebaseMessaging.h"
@@ -27,9 +27,6 @@
@import FirebaseAuth;
@import FirebaseCore;
@import FirebaseCrashlytics;
#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST
@import FirebaseDynamicLinks;
#endif
#if (TARGET_OS_IOS && !TARGET_OS_MACCATALYST) || TARGET_OS_TV
@import FirebasePerformance;
@import FirebaseInAppMessaging;
@@ -34,16 +34,12 @@
#import <FirebaseCore/FirebaseCore.h>
#import <FirebaseCrashlytics/FirebaseCrashlytics.h>
#import <FirebaseDatabase/FirebaseDatabase.h>
#import "FirebaseCore/FirebaseCore.h"
#import "FirebaseCrashlytics/FirebaseCrashlytics.h"
#import "FirebaseDatabase/FirebaseDatabase.h"
#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST
#import <FirebaseDynamicLinks/FirebaseDynamicLinks.h>
#import "FirebaseDynamicLinks/FirebaseDynamicLinks.h"
#endif
#import <FirebaseFirestore/FirebaseFirestore.h>
#import <FirebaseInstallations/FirebaseInstallations.h>
#import <FirebaseMessaging/FirebaseMessaging.h>
#import "FirebaseCore/FirebaseCore.h"
#import "FirebaseCrashlytics/FirebaseCrashlytics.h"
#import "FirebaseDatabase/FirebaseDatabase.h"
#import "FirebaseFirestore/FirebaseFirestore.h"
#import "FirebaseInstallations/FirebaseInstallations.h"
#import "FirebaseMessaging/FirebaseMessaging.h"
@@ -32,9 +32,6 @@ import FirebaseAuth
import FirebaseCore
import FirebaseCrashlytics
import FirebaseDatabase
#if os(iOS) && !targetEnvironment(macCatalyst)
import FirebaseDynamicLinks
#endif
import FirebaseFirestore
#if SWIFT_PACKAGE
import FirebaseFirestoreCombineSwift
@@ -18,7 +18,6 @@ target 'CocoapodsIntegrationTest' do
pod 'FirebaseAuth', :path => '../../'
pod 'FirebaseAuthInterop', :path => '../../'
pod 'FirebaseDatabase', :path => '../../'
pod 'FirebaseDynamicLinks', :path => '../../'
pod 'FirebaseFirestore', :path => '../../'
pod 'FirebaseFunctions', :path => '../../'
pod 'FirebaseInAppMessaging', :path => '../../'
-28
View File
@@ -108,10 +108,6 @@ let package = Package(
name: "FirebaseDatabase",
targets: ["FirebaseDatabase"]
),
.library(
name: "FirebaseDynamicLinks",
targets: ["FirebaseDynamicLinksTarget"]
),
.library(
name: "FirebaseFirestore",
targets: ["FirebaseFirestoreTarget"]
@@ -777,28 +773,6 @@ let package = Package(
dependencies: ["FirebaseSharedSwift"],
path: "FirebaseSharedSwift/Tests/"
),
.target(
name: "FirebaseDynamicLinksTarget",
dependencies: [.target(name: "FirebaseDynamicLinks",
condition: .when(platforms: [.iOS]))],
path: "SwiftPM-PlatformExclude/FirebaseDynamicLinksWrap"
),
.target(
name: "FirebaseDynamicLinks",
dependencies: ["FirebaseCore"],
path: "FirebaseDynamicLinks/Sources",
resources: [.process("Resources/PrivacyInfo.xcprivacy")],
publicHeadersPath: "Public",
cSettings: [
.headerSearchPath("../../"),
.define("FIRDynamicLinks3P", to: "1"),
.define("GIN_SCION_LOGGING", to: "1"),
],
linkerSettings: [
.linkedFramework("QuartzCore"),
]
),
firestoreWrapperTarget(),
@@ -1292,7 +1266,6 @@ let package = Package(
"FirebaseCrashlytics",
"FirebaseCore",
"FirebaseDatabase",
"FirebaseDynamicLinks",
"FirebaseFirestoreTarget",
"FirebaseFunctions",
.target(name: "FirebaseInAppMessaging",
@@ -1328,7 +1301,6 @@ let package = Package(
"FirebaseCrashlytics",
"FirebaseCore",
"FirebaseDatabase",
"FirebaseDynamicLinks",
"FirebaseFirestoreTarget",
"FirebaseFunctions",
.target(name: "FirebaseInAppMessaging",
@@ -45,7 +45,6 @@ public let shared = Manifest(
Pod("FirebaseAuth", zip: true),
Pod("FirebaseCrashlytics", zip: true),
Pod("FirebaseDatabase", platforms: ["ios", "macos", "tvos"], zip: true),
Pod("FirebaseDynamicLinks", allowWarnings: true, platforms: ["ios"], zip: true),
Pod("FirebaseFirestoreInternal", allowWarnings: true, platforms: ["ios", "macos", "tvos"]),
Pod("FirebaseFirestore", allowWarnings: true, platforms: ["ios", "macos", "tvos"], zip: true),
Pod("FirebaseFunctions", zip: true),
@@ -1,18 +0,0 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import <TargetConditionals.h>
#if !TARGET_OS_IOS
#warning "Firebase Dynamic Links only supports the iOS platform"
#endif
@@ -1,15 +0,0 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Prevent a missing umbrella header warning.
@@ -21,7 +21,6 @@
#import "FirebaseCore/FirebaseCore.h"
#import "FirebaseCrashlytics/FirebaseCrashlytics.h"
#import "FirebaseDatabase/FirebaseDatabase.h"
#import "FirebaseDynamicLinks/FirebaseDynamicLinks.h"
#import "FirebaseFirestore/FirebaseFirestore.h"
#import "FirebaseInstallations/FirebaseInstallations.h"
#import "FirebaseMessaging/FirebaseMessaging.h"
@@ -40,7 +39,6 @@
#import <FirebaseCore/FirebaseCore.h>
#import <FirebaseCrashlytics/FirebaseCrashlytics.h>
#import <FirebaseDatabase/FirebaseDatabase.h>
#import <FirebaseDynamicLinks/FirebaseDynamicLinks.h>
#import <FirebaseFirestore/FirebaseFirestore.h>
#if TARGET_OS_IOS || TARGET_OS_TV
#import <FirebaseInAppMessaging/FirebaseInAppMessaging.h>
@@ -22,7 +22,6 @@
@import FirebaseCrashlytics;
@import FirebaseCore;
@import FirebaseDatabase;
@import FirebaseDynamicLinks;
@import FirebaseFirestore;
@import FirebaseFunctions;
@import FirebaseInstallations;
@@ -23,7 +23,6 @@ import Foundation
#endif
import FirebaseCrashlytics
import FirebaseDatabase
import FirebaseDynamicLinks
import FirebaseFirestore
import FirebaseFunctions
import FirebaseInstallations
-1
View File
@@ -18,7 +18,6 @@ target 'SymbolCollisionTest' do
pod 'FirebaseCoreInternal', :path => '../'
pod 'FirebaseCrashlytics', :path => '../'
pod 'FirebaseDatabase', :path => '../'
pod 'FirebaseDynamicLinks', :path => '../'
pod 'FirebaseFirestore', :path => '../'
pod 'FirebaseFunctions', :path => '../'
pod 'FirebaseInAppMessaging', :path => '../'
+1 -1
View File
@@ -97,7 +97,7 @@ repo.
The previous setup will run podspecs testing nightly. This enables presubmits of pod spec lint
podspecs and accelerates the testing process. This is to run presubmit tests for Firebase Apple SDKs
in the SDK repo. A job to run `pod spec lint` is added to SDK testing workflows, including ABTesting,
Analytics, Auth, Core, Crashlytics, Database, DynamicLinks, Firestore, Functions, GoogleUtilities,
Analytics, Auth, Core, Crashlytics, Database, Firestore, Functions, GoogleUtilities,
InAppMessaging, Installations, Messaging, MLModelDownloader, Performance, RemoteConfig and Storage.
These jobs will be triggered in presubmit and run pod spec lint with a source of
Firebase/SpecsTesting repo, which is updated to the head of main nightly in the prerelease
-1
View File
@@ -31,7 +31,6 @@ MODULE_LIST = [
'FirebaseCore',
'FirebaseCrashlytics',
'FirebaseDatabase',
'FirebaseDynamicLinks',
'FirebaseFirestoreInternal',
'FirebaseFirestore',
'FirebaseFunctions',
Loaded 100 of 104 files, more files were not shown because too many files have changed in this diff. Show more