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(