diff --git a/Plugins/DependencyPlugin/ProjectDescriptionHelpers/SPM+TargetDependency.swift b/Plugins/DependencyPlugin/ProjectDescriptionHelpers/SPM+TargetDependency.swift
index 8bd26ed1..218ff92d 100644
--- a/Plugins/DependencyPlugin/ProjectDescriptionHelpers/SPM+TargetDependency.swift
+++ b/Plugins/DependencyPlugin/ProjectDescriptionHelpers/SPM+TargetDependency.swift
@@ -17,4 +17,5 @@ public enum ExternalDependency: String {
case composableArchitecture = "ComposableArchitecture"
case kakaoSDK = "KakaoSDK"
case FSCalendar = "FSCalendar"
+ case googleMobileAds = "GoogleMobileAds"
}
diff --git a/Projects/App/Info.plist b/Projects/App/Info.plist
index e1339625..34c9364d 100644
--- a/Projects/App/Info.plist
+++ b/Projects/App/Info.plist
@@ -2,6 +2,8 @@
+ GADApplicationIdentifier
+ $(GOOGLE_ADS_KEY)
FirebaseAppDelegateProxyEnabled
BASE_URL
@@ -39,6 +41,8 @@
KAKAO_NATIVE_APP_KEY
$(KAKAO_NATIVE_APP_KEY)
+ GOOGLE_AD_UNITID
+ $(GOOGLE_AD_UNITID)
LSApplicationCategoryType
LSApplicationQueriesSchemes
@@ -59,5 +63,7 @@
UIUserInterfaceStyle
Light
+ NSUserTrackingUsageDescription
+ Blink에서 사용자에게 맞춤 광고를 위해 추적 권한을 요청합니다
diff --git a/Projects/App/Project.swift b/Projects/App/Project.swift
index 4c923a0c..3ec6b78d 100644
--- a/Projects/App/Project.swift
+++ b/Projects/App/Project.swift
@@ -1,4 +1,4 @@
-import ProjectDescription
+@preconcurrency import ProjectDescription
import ProjectDescriptionHelpers
import DependencyPlugin
diff --git a/Projects/App/Sources/AppDelegate.swift b/Projects/App/Sources/AppDelegate.swift
index 968dde41..093d6b43 100644
--- a/Projects/App/Sources/AppDelegate.swift
+++ b/Projects/App/Sources/AppDelegate.swift
@@ -11,8 +11,6 @@ import SwiftUI
import Feature
import ComposableArchitecture
-import FirebaseCore
-import FirebaseMessaging
final class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
let store = StoreOf.init(
diff --git a/Projects/App/Sources/AppDelegateFeature.swift b/Projects/App/Sources/AppDelegateFeature.swift
index 296ae03c..f95c3436 100644
--- a/Projects/App/Sources/AppDelegateFeature.swift
+++ b/Projects/App/Sources/AppDelegateFeature.swift
@@ -11,8 +11,8 @@ import Foundation
import Services
import ComposableArchitecture
-import FirebaseCore
import FirebaseMessaging
+import FirebaseCore
@Reducer
struct AppDelegateFeature {
@@ -27,6 +27,7 @@ struct AppDelegateFeature {
case initKakaoSDK
case setUpNotificationCenter
case setUpFirebase
+ case setUpGoogleAds
case setUserNotificationCenterDelegate
case setUserNotificationCenterAuthorization
@@ -41,6 +42,8 @@ struct AppDelegateFeature {
@Dependency(\.userDefaultsClient) private var userDefault
@Dependency(\.socialLogin) private var socialLogin
@Dependency(\.userNotificationClient) private var userNotificationClient
+ @Dependency(ATTrackingManagerClient.self) private var attrackingManagerClient
+ @Dependency(GoogleMobileAdsClient.self) private var googleMobileAdsClient
var body: some ReducerOf {
Reduce { state, action in
@@ -53,6 +56,8 @@ struct AppDelegateFeature {
await send(.setUpNotificationCenter)
/// Firebase 설정
await send(.setUpFirebase)
+ /// Google Ads 설정
+ await send(.setUpGoogleAds)
}
case let .didRegisterForRemoteNotificationsWithDeviceToken(deviceToken):
@@ -79,6 +84,12 @@ struct AppDelegateFeature {
await send(.setFirebaseIsAutoInitEnabled)
}
+ case .setUpGoogleAds:
+ return .run { send in
+ await attrackingManagerClient.requestTrackingAuthorization()
+ await googleMobileAdsClient.start()
+ }
+
case .setUserNotificationCenterDelegate:
return .run { send in
for await event in self.userNotificationClient.delegate() {
diff --git a/Projects/Core/Analytics/Project.swift b/Projects/Core/Analytics/Project.swift
index ffbdcec8..6b56b613 100644
--- a/Projects/Core/Analytics/Project.swift
+++ b/Projects/Core/Analytics/Project.swift
@@ -5,7 +5,7 @@
// Created by kyuchul on 11/1/24.
//
-import ProjectDescription
+@preconcurrency import ProjectDescription
import ProjectDescriptionHelpers
import DependencyPlugin
diff --git a/Projects/Core/Models/Project.swift b/Projects/Core/Models/Project.swift
index c3251b00..ffd35fde 100644
--- a/Projects/Core/Models/Project.swift
+++ b/Projects/Core/Models/Project.swift
@@ -5,7 +5,7 @@
// Created by kyuchul on 6/14/24.
//
-import ProjectDescription
+@preconcurrency import ProjectDescription
import ProjectDescriptionHelpers
import DependencyPlugin
diff --git a/Projects/Core/Project.swift b/Projects/Core/Project.swift
index 90cfd796..7138cfdc 100644
--- a/Projects/Core/Project.swift
+++ b/Projects/Core/Project.swift
@@ -5,7 +5,7 @@
// Created by kyuchul on 6/14/24.
//
-import ProjectDescription
+@preconcurrency import ProjectDescription
import ProjectDescriptionHelpers
import DependencyPlugin
diff --git a/Projects/Core/Services/Project.swift b/Projects/Core/Services/Project.swift
index aaf50808..ca72241b 100644
--- a/Projects/Core/Services/Project.swift
+++ b/Projects/Core/Services/Project.swift
@@ -5,7 +5,7 @@
// Created by kyuchul on 6/14/24.
//
-import ProjectDescription
+@preconcurrency import ProjectDescription
import ProjectDescriptionHelpers
import DependencyPlugin
diff --git a/Projects/Core/Services/Sources/ATTrackingManager/ATTrackingManagerClient.swift b/Projects/Core/Services/Sources/ATTrackingManager/ATTrackingManagerClient.swift
new file mode 100644
index 00000000..778ec12c
--- /dev/null
+++ b/Projects/Core/Services/Sources/ATTrackingManager/ATTrackingManagerClient.swift
@@ -0,0 +1,34 @@
+//
+// ATTTrackingManagerClient.swift
+// Services
+//
+// Created by kyuchul on 11/30/24.
+// Copyright © 2024 com.kyuchul.blink. All rights reserved.
+//
+
+import Foundation
+import AppTrackingTransparency
+
+import Dependencies
+import DependenciesMacros
+
+@DependencyClient
+public struct ATTrackingManagerClient {
+ public var requestTrackingAuthorization: @Sendable () async -> Void
+}
+
+extension ATTrackingManagerClient: DependencyKey {
+ public static var liveValue: ATTrackingManagerClient = live()
+ private static func live() -> ATTrackingManagerClient {
+
+ return ATTrackingManagerClient(
+ requestTrackingAuthorization: { @MainActor in
+ return await withCheckedContinuation { continuation in
+ ATTrackingManager.requestTrackingAuthorization { _ in
+ continuation.resume()
+ }
+ }
+ }
+ )
+ }
+}
diff --git a/Projects/Core/Services/Sources/GoogleAds/GoogleAdsClient.swift b/Projects/Core/Services/Sources/GoogleAds/GoogleAdsClient.swift
new file mode 100644
index 00000000..95fafd69
--- /dev/null
+++ b/Projects/Core/Services/Sources/GoogleAds/GoogleAdsClient.swift
@@ -0,0 +1,51 @@
+//
+// GoogleAdsClient.swift
+// Services
+//
+// Created by kyuchul on 11/30/24.
+// Copyright © 2024 com.kyuchul.blink. All rights reserved.
+//
+
+import Foundation
+import GoogleMobileAds
+
+import Dependencies
+import DependenciesMacros
+
+@DependencyClient
+public struct GoogleMobileAdsClient {
+ public var start: @Sendable () async -> Void
+ public var load: @Sendable () async throws -> GoogleAd
+}
+
+extension GoogleMobileAdsClient: DependencyKey {
+ public static var liveValue: GoogleMobileAdsClient = live()
+ private static func live() -> GoogleMobileAdsClient {
+ return GoogleMobileAdsClient(
+ start: {
+ await GADMobileAds.sharedInstance().start()
+ },
+ load: {
+ var adUnitID: String {
+ #if DEBUG
+ return "ca-app-pub-3940256099942544/4411468910"
+ #else
+ return APIKey.googleAdUnitID
+ #endif
+ }
+
+ let ad = try await GADInterstitialAd.load(withAdUnitID: adUnitID, request: GADRequest())
+
+ return GoogleAd(ad: ad)
+ }
+ )
+ }
+}
+
+public struct GoogleAd: Equatable {
+ public var ad: GADInterstitialAd
+
+ public init(ad: GADInterstitialAd) {
+ self.ad = ad
+ }
+}
diff --git a/Projects/Core/Services/Sources/Network/FeedAPI/Response/FeedResponse.swift b/Projects/Core/Services/Sources/Network/FeedAPI/Response/FeedResponse.swift
index eec9e039..c16cbb81 100644
--- a/Projects/Core/Services/Sources/Network/FeedAPI/Response/FeedResponse.swift
+++ b/Projects/Core/Services/Sources/Network/FeedAPI/Response/FeedResponse.swift
@@ -10,8 +10,6 @@ import Foundation
import Models
-
-
struct FeedResponse: Decodable {
let feedId: Int
let thumbnailImage: String
diff --git a/Projects/Core/Services/Sources/SocialLogin/Util/APIKey.swift b/Projects/Core/Services/Sources/SocialLogin/Util/APIKey.swift
deleted file mode 100644
index 0a9b17c7..00000000
--- a/Projects/Core/Services/Sources/SocialLogin/Util/APIKey.swift
+++ /dev/null
@@ -1,17 +0,0 @@
-//
-// APIKey.swift
-// CoreKit
-//
-// Created by kyuchul on 6/17/24.
-// Copyright © 2024 com.jordyma.blink. All rights reserved.
-//
-
-import Foundation
-
-enum APIKey {
- static let kakao = {
- guard let infoDictionary = Bundle.main.infoDictionary else { fatalError("Wrong Info dictionary") }
- guard let key = infoDictionary["KAKAO_NATIVE_APP_KEY"] as? String else { fatalError("Wrong KAKAO_NATIVE_APP_KEY") }
- return key
- }()
-}
diff --git a/Projects/Core/Services/Sources/Util/APIKey.swift b/Projects/Core/Services/Sources/Util/APIKey.swift
new file mode 100644
index 00000000..1ffdbbc3
--- /dev/null
+++ b/Projects/Core/Services/Sources/Util/APIKey.swift
@@ -0,0 +1,22 @@
+//
+// APIKey.swift
+// CoreKit
+//
+// Created by kyuchul on 6/17/24.
+// Copyright © 2024 com.jordyma.blink. All rights reserved.
+//
+
+import Foundation
+
+enum APIKey {
+ static let kakao = getInfoValue(for: "KAKAO_NATIVE_APP_KEY")
+ static let googleAdUnitID = getInfoValue(for: "GOOGLE_AD_UNITID")
+
+ private static func getInfoValue(for key: String) -> String {
+ guard let infoDictionary = Bundle.main.infoDictionary,
+ let value = infoDictionary[key] as? String else {
+ fatalError("Wrong \(key)")
+ }
+ return value
+ }
+}
diff --git a/Projects/Domain/Folder/Project.swift b/Projects/Domain/Folder/Project.swift
index 4e78bbfc..41a13144 100644
--- a/Projects/Domain/Folder/Project.swift
+++ b/Projects/Domain/Folder/Project.swift
@@ -5,7 +5,7 @@
// Created by kyuchul on 10/18/24.
//
-import ProjectDescription
+@preconcurrency import ProjectDescription
import ProjectDescriptionHelpers
import DependencyPlugin
diff --git a/Projects/Domain/Project.swift b/Projects/Domain/Project.swift
index b9ab2ed4..5439e649 100644
--- a/Projects/Domain/Project.swift
+++ b/Projects/Domain/Project.swift
@@ -5,7 +5,7 @@
// Created by kyuchul on 10/18/24.
//
-import ProjectDescription
+@preconcurrency import ProjectDescription
import ProjectDescriptionHelpers
import DependencyPlugin
diff --git a/Projects/Feature/Project.swift b/Projects/Feature/Project.swift
index 0fd14830..dcfaf72a 100644
--- a/Projects/Feature/Project.swift
+++ b/Projects/Feature/Project.swift
@@ -1,4 +1,4 @@
-import ProjectDescription
+@preconcurrency import ProjectDescription
import ProjectDescriptionHelpers
import DependencyPlugin
diff --git a/Projects/Feature/Scene/Common/BKGoogleAdView.swift b/Projects/Feature/Scene/Common/BKGoogleAdView.swift
new file mode 100644
index 00000000..a7a6e185
--- /dev/null
+++ b/Projects/Feature/Scene/Common/BKGoogleAdView.swift
@@ -0,0 +1,63 @@
+//
+// BKGoogleAdView.swift
+// Feature
+//
+// Created by kyuchul on 11/30/24.
+// Copyright © 2024 com.kyuchul.blink. All rights reserved.
+//
+
+import SwiftUI
+
+import Services
+
+import GoogleMobileAds
+
+struct BKGoogleAdView: UIViewControllerRepresentable {
+ @Binding private var isPresented: Bool
+ @Binding private var interstitialAd: GoogleAd?
+ private let dismissAdScreen: () -> Void
+ private let viewController: UIViewController
+
+ init(isPresented: Binding,
+ interstitialAd: Binding,
+ dismissAdScreen: @escaping () -> Void
+ ) {
+ self._isPresented = isPresented
+ self._interstitialAd = interstitialAd
+ self.dismissAdScreen = dismissAdScreen
+ self.viewController = UIViewController()
+ }
+
+ func makeUIViewController(context: Context) -> UIViewController {
+ DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(1)) {
+ if let interstitialAd {
+ interstitialAd.ad.present(fromRootViewController: viewController)
+ }
+ }
+
+ return viewController
+ }
+
+ func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
+
+ func makeCoordinator() -> Coordinator {
+ Coordinator(parent: self)
+ }
+}
+
+extension BKGoogleAdView {
+ final class Coordinator: NSObject, GADFullScreenContentDelegate {
+ private let parent: BKGoogleAdView
+
+ init(parent: BKGoogleAdView) {
+ self.parent = parent
+ super.init()
+ parent.interstitialAd?.ad.fullScreenContentDelegate = self
+ }
+
+ func adDidDismissFullScreenContent(_ ad: GADFullScreenPresentingAd) {
+ parent.isPresented.toggle()
+ parent.dismissAdScreen()
+ }
+ }
+}
diff --git a/Projects/Feature/Scene/SaveLink/View/SaveLinkFeature.swift b/Projects/Feature/Scene/SaveLink/SaveLinkFeature.swift
similarity index 67%
rename from Projects/Feature/Scene/SaveLink/View/SaveLinkFeature.swift
rename to Projects/Feature/Scene/SaveLink/SaveLinkFeature.swift
index 357c2114..25f02a75 100644
--- a/Projects/Feature/Scene/SaveLink/View/SaveLinkFeature.swift
+++ b/Projects/Feature/Scene/SaveLink/SaveLinkFeature.swift
@@ -22,22 +22,30 @@ public struct SaveLinkFeature {
var isValidationURL = true
var validationReasonText = "URL 형식이 올바르지 않아요. 다시 입력해주세요."
var isLoading: Bool = false
+
+ var ad: GoogleAd?
+ var isAdPresented: Bool = false
}
public enum Action: BindableAction {
case binding(BindingAction)
//MARK: UserAction
+ case onAppear
case onTapNextButton
+ case adDismissButtonTapped
case onTapBackButton
// MARK: Inner Business Action
case postLinkSummary
+ case loadAd
case sendAnalyticsLog
// MARK: Inner SetState Action
+ case setAd(GoogleAd)
+ case setAdPresented(Bool)
case setLoading(Bool)
-
+
// MARK: Present Action
case linkSummaryLoadingAlertPresented
case linkSummaryFailAlertPresented
@@ -47,6 +55,7 @@ public struct SaveLinkFeature {
@Dependency(\.alertClient) private var alertClient
@Dependency(\.linkClient) private var linkClient
@Dependency(AnalyticsClient.self) private var analyticsClient
+ @Dependency(GoogleMobileAdsClient.self) private var googleMobileAdsClient
public var body: some ReducerOf {
BindingReducer()
@@ -66,6 +75,9 @@ public struct SaveLinkFeature {
return .none
+ case .onAppear:
+ return .send(.loadAd)
+
case .onTapBackButton:
return .run { _ in await self.dismiss() }
@@ -75,6 +87,18 @@ public struct SaveLinkFeature {
await send(.sendAnalyticsLog)
}
+ case .adDismissButtonTapped:
+ return .run { send in
+ await send(.setLoading(false))
+ await send(.linkSummaryLoadingAlertPresented)
+
+ // 요약 성공 시 LodingAlert 닫힌 후 2초 뒤 메인으로 이동
+ try? await Task.sleep(for: .seconds(2))
+
+ await alertClient.dismiss()
+ await send(.onTapBackButton)
+ }
+
case .postLinkSummary:
return .run(
operation: { [state] send in
@@ -82,14 +106,7 @@ public struct SaveLinkFeature {
_ = try await linkClient.postLinkSummary(state.urlText.trimmingCharacters(in: .whitespaces))
- await send(.setLoading(false))
- await send(.linkSummaryLoadingAlertPresented)
-
- // 요약 성공 시 LodingAlert 닫힌 후 2초 뒤 메인으로 이동
- try? await Task.sleep(for: .seconds(2))
-
- await alertClient.dismiss()
- await send(.onTapBackButton)
+ await send(.setAdPresented(true))
},
catch: { error, send in
await send(.setLoading(false))
@@ -97,15 +114,29 @@ public struct SaveLinkFeature {
}
)
+ case .loadAd:
+ return .run { send in
+ let ad = try await googleMobileAdsClient.load()
+ await send(.setAd(ad))
+ }
+
case .sendAnalyticsLog:
feedSummaryButtonTappedLog()
return .none
+ case let .setAd(ad):
+ state.ad = ad
+ return .none
+
+ case let .setAdPresented(isPresented):
+ state.isAdPresented = isPresented
+ return .none
+
case let .setLoading(isLoading):
state.isLoading = isLoading
return .none
- case .linkSummaryLoadingAlertPresented:
+ case .linkSummaryLoadingAlertPresented:
return .run { send in
await alertClient.present(.init(
isLoadingType: true,
@@ -117,16 +148,16 @@ public struct SaveLinkFeature {
}
case .linkSummaryFailAlertPresented:
- return .run { send in
- await alertClient.present(.init(
- title: "요약 불가",
- imageType: .link,
- description: "링크 요약에 실패했습니다",
- buttonType: .singleButton("메인으로"),
- rightButtonAction: { await send(.onTapBackButton) }
- ))
- }
-
+ return .run { send in
+ await alertClient.present(.init(
+ title: "요약 불가",
+ imageType: .link,
+ description: "링크 요약에 실패했습니다",
+ buttonType: .singleButton("메인으로"),
+ rightButtonAction: { await send(.onTapBackButton) }
+ ))
+ }
+
default:
return .none
}
diff --git a/Projects/Feature/Scene/SaveLink/View/SaveLinkView.swift b/Projects/Feature/Scene/SaveLink/SaveLinkView.swift
similarity index 92%
rename from Projects/Feature/Scene/SaveLink/View/SaveLinkView.swift
rename to Projects/Feature/Scene/SaveLink/SaveLinkView.swift
index b9fab6f5..7f3c33b0 100644
--- a/Projects/Feature/Scene/SaveLink/View/SaveLinkView.swift
+++ b/Projects/Feature/Scene/SaveLink/SaveLinkView.swift
@@ -82,6 +82,15 @@ public struct SaveLinkView: View {
.if(store.isLoading) { view in
view.progressBackground()
}
+ .onAppear { store.send(.onAppear) }
+ .fullScreenCover(isPresented: $store.isAdPresented) {
+ BKGoogleAdView(
+ isPresented: $store.isAdPresented,
+ interstitialAd: $store.ad,
+ dismissAdScreen: { store.send(.adDismissButtonTapped) }
+ )
+ .presentationClearBackground()
+ }
}
}
}
diff --git a/Projects/Feature/Scene/WebView/BKWebView.swift b/Projects/Feature/Scene/WebView/BKWebView.swift
index d95bb3c1..d539e081 100644
--- a/Projects/Feature/Scene/WebView/BKWebView.swift
+++ b/Projects/Feature/Scene/WebView/BKWebView.swift
@@ -39,23 +39,25 @@ struct BKWebView: UIViewRepresentable {
}
}
-final class Coordinator: NSObject, WKNavigationDelegate {
- private let parent: BKWebView
-
- init(parent: BKWebView) {
- self.parent = parent
- }
-
- func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
+extension BKWebView {
+ final class Coordinator: NSObject, WKNavigationDelegate {
+ private let parent: BKWebView
+
+ init(parent: BKWebView) {
+ self.parent = parent
+ }
+
+ func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
if let urlString = webView.url?.absoluteString {
self.parent.viewModel.title = urlString
}
+
+ self.parent.viewModel.canGoBack = webView.canGoBack
+ self.parent.viewModel.canGoForward = webView.canGoForward
+ }
- self.parent.viewModel.canGoBack = webView.canGoBack
- self.parent.viewModel.canGoForward = webView.canGoForward
- }
-
- func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
- self.parent.viewModel.isLoading = false
+ func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
+ self.parent.viewModel.isLoading = false
+ }
}
}
diff --git a/Projects/Shared/Common/Project.swift b/Projects/Shared/Common/Project.swift
index c4232887..9bc7182d 100644
--- a/Projects/Shared/Common/Project.swift
+++ b/Projects/Shared/Common/Project.swift
@@ -5,7 +5,7 @@
// Created by kyuchul on 9/29/24.
//
-import ProjectDescription
+@preconcurrency import ProjectDescription
import ProjectDescriptionHelpers
import DependencyPlugin
diff --git a/Projects/Shared/CommonFeature/Project.swift b/Projects/Shared/CommonFeature/Project.swift
index 1af808f9..a31354a1 100644
--- a/Projects/Shared/CommonFeature/Project.swift
+++ b/Projects/Shared/CommonFeature/Project.swift
@@ -5,7 +5,7 @@
// Created by kyuchul on 9/29/24.
//
-import ProjectDescription
+@preconcurrency import ProjectDescription
import ProjectDescriptionHelpers
import DependencyPlugin
diff --git a/Projects/Shared/CommonFeature/Sources/DesignSystem/Views/Alert/PassThroughWindow.swift b/Projects/Shared/CommonFeature/Sources/DesignSystem/Views/Alert/PassThroughWindow.swift
index 885a177d..8ab385ca 100644
--- a/Projects/Shared/CommonFeature/Sources/DesignSystem/Views/Alert/PassThroughWindow.swift
+++ b/Projects/Shared/CommonFeature/Sources/DesignSystem/Views/Alert/PassThroughWindow.swift
@@ -9,8 +9,23 @@
import SwiftUI
final class PassThroughWindow: UIWindow {
- override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
- guard let hitView = super.hitTest(point, with: event) else { return nil }
- return rootViewController?.view == hitView ? nil : hitView
- }
+ override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
+ guard let hitView = super.hitTest(point, with: event),
+ let rootView = rootViewController?.view
+ else {
+ return nil
+ }
+
+ if #available(iOS 18, *) {
+ for subview in rootView.subviews.reversed() {
+ let convertedPoint = subview.convert(point, from: rootView)
+ if subview.hitTest(convertedPoint, with: event) != nil {
+ return hitView
+ }
+ }
+ return nil
+ } else {
+ return hitView == rootView ? nil : hitView
+ }
+ }
}
diff --git a/Projects/Shared/Project.swift b/Projects/Shared/Project.swift
index 1ae259c5..14ff6448 100644
--- a/Projects/Shared/Project.swift
+++ b/Projects/Shared/Project.swift
@@ -5,7 +5,7 @@
// Created by kyuchul on 9/27/24.
//
-import ProjectDescription
+@preconcurrency import ProjectDescription
import ProjectDescriptionHelpers
import DependencyPlugin
diff --git a/Projects/Shared/ThirdParty/Project.swift b/Projects/Shared/ThirdParty/Project.swift
index 32f067af..659c210d 100644
--- a/Projects/Shared/ThirdParty/Project.swift
+++ b/Projects/Shared/ThirdParty/Project.swift
@@ -5,7 +5,7 @@
// Created by kyuchul on 9/27/24.
//
-import ProjectDescription
+@preconcurrency import ProjectDescription
import ProjectDescriptionHelpers
import DependencyPlugin
@@ -20,9 +20,11 @@ let project = Project.make(
.external(externalDependency: .kakaoSDK),
.external(externalDependency: .firebaseAnalytics),
.external(externalDependency: .firebaseMessaging),
+ .external(externalDependency: .googleMobileAds),
.external(externalDependency: .moya),
.external(externalDependency: .introspect),
- .external(externalDependency: .FSCalendar)
+ .external(externalDependency: .FSCalendar),
+ .sdk(name: "JavaScriptCore", type: .framework)
]
)
)
diff --git a/Tuist/Config.swift b/Tuist/Config.swift
index a1df1048..329e7f6c 100644
--- a/Tuist/Config.swift
+++ b/Tuist/Config.swift
@@ -5,7 +5,7 @@
// Created by kyuchul on 9/29/24.
//
-import ProjectDescription
+@preconcurrency import ProjectDescription
let config = Config(
plugins: [
diff --git a/Tuist/Package.resolved b/Tuist/Package.resolved
index 4ccc2681..2046b1b2 100644
--- a/Tuist/Package.resolved
+++ b/Tuist/Package.resolved
@@ -261,6 +261,24 @@
"version" : "1.0.2"
}
},
+ {
+ "identity" : "swift-package-manager-google-mobile-ads",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/googleads/swift-package-manager-google-mobile-ads.git",
+ "state" : {
+ "revision" : "82c49f703da08f6c20715fd8bb8dfe5d94a2f187",
+ "version" : "11.12.0"
+ }
+ },
+ {
+ "identity" : "swift-package-manager-google-user-messaging-platform",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/googleads/swift-package-manager-google-user-messaging-platform.git",
+ "state" : {
+ "revision" : "708a282840c2171ee63bd93b87afa49fe507d70e",
+ "version" : "2.7.0"
+ }
+ },
{
"identity" : "swift-perception",
"kind" : "remoteSourceControl",
diff --git a/Tuist/Package.swift b/Tuist/Package.swift
index 22a64d68..9da4af25 100644
--- a/Tuist/Package.swift
+++ b/Tuist/Package.swift
@@ -1,8 +1,8 @@
// swift-tools-version: 5.9
-import PackageDescription
+@preconcurrency import PackageDescription
#if TUIST
- import ProjectDescription
+@preconcurrency import ProjectDescription
let packageSettings = PackageSettings(
productTypes: [
@@ -10,6 +10,7 @@ let packageSettings = PackageSettings(
"Moya": .framework,
"FirebaseMessaging": .staticLibrary,
"FirebaseAnalytics": .staticLibrary,
+ "GoogleMobileAds": .framework,
"Lottie": .framework,
"Kingfisher": .framework,
"SwiftUIIntrospect": .framework,
@@ -23,6 +24,7 @@ let package = Package(
name: "blink",
dependencies: [
.package(url: "https://github.com/firebase/firebase-ios-sdk", from: "11.0.0"),
+ .package(url: "https://github.com/googleads/swift-package-manager-google-mobile-ads.git", from: "11.12.0"),
.package(url: "https://github.com/onevcat/Kingfisher", from: "7.9.1"),
.package(url: "https://github.com/airbnb/lottie-ios.git", from: "4.4.3"),
.package(url: "https://github.com/siteline/swiftui-introspect", exact: "1.3.0"),
diff --git a/Tuist/ProjectDescriptionHelpers/DefaultSetting.swift b/Tuist/ProjectDescriptionHelpers/DefaultSetting.swift
index a1019110..a0026e10 100644
--- a/Tuist/ProjectDescriptionHelpers/DefaultSetting.swift
+++ b/Tuist/ProjectDescriptionHelpers/DefaultSetting.swift
@@ -1,4 +1,4 @@
-import ProjectDescription
+@preconcurrency import ProjectDescription
public struct DefaultSetting {
public static let DeploymentTargets: DeploymentTargets = .iOS("16.2")
diff --git a/Tuist/ProjectDescriptionHelpers/SourceFilesList+Template.swift b/Tuist/ProjectDescriptionHelpers/SourceFilesList+Template.swift
index c6a83592..47e7fe3b 100644
--- a/Tuist/ProjectDescriptionHelpers/SourceFilesList+Template.swift
+++ b/Tuist/ProjectDescriptionHelpers/SourceFilesList+Template.swift
@@ -6,7 +6,7 @@
//
import Foundation
-import ProjectDescription
+@preconcurrency import ProjectDescription
public extension SourceFilesList {
static let exampleSources: SourceFilesList = "Example/Sources/**"
diff --git a/Tuist/ProjectDescriptionHelpers/TargetScript+Template.swift b/Tuist/ProjectDescriptionHelpers/TargetScript+Template.swift
index 8e0b0d2e..45878629 100644
--- a/Tuist/ProjectDescriptionHelpers/TargetScript+Template.swift
+++ b/Tuist/ProjectDescriptionHelpers/TargetScript+Template.swift
@@ -5,7 +5,7 @@
// Created by kyuchul on 9/25/24.
//
-import ProjectDescription
+@preconcurrency import ProjectDescription
public extension TargetScript {
static let firebaseCrashlytics: Self = .post(