Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feat/#31] Admob 추가 #33

Merged
merged 5 commits into from
Oct 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -122,4 +122,5 @@ Derived/
master.key
*.package.resolved

GoogleService-Info.plist
GoogleService-Info.plist
*.storekit
5 changes: 4 additions & 1 deletion Plugins/ModulePlugin/ProjectDescriptionHelpers/Module.swift
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,10 @@ public extension Module {
(.Folio, .OPENAI),
(.Folio, .Admob)
]
case .Toolinder: return []
case .Toolinder:
return [
(.Folio, .Admob)
]
case .Folio:
return [
(.Folio, .OPENAI),
Expand Down
50 changes: 50 additions & 0 deletions Projects/Folio/Core/Admob/Sources/Interstitialed.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//
// Interstitialed.swift
// FolioCoreAdmob
//
// Created by 송영모 on 10/8/23.
//

import SwiftUI

import GoogleMobileAds

public final class Interstitialed: NSObject {
let id: String
var interstitialAd: GADInterstitialAd?

var interstitialed: (() -> Void)?

public init(id: String) {
self.id = id
super.init()
load()
}

private func load(){
let request = GADRequest()
GADInterstitialAd.load(
withAdUnitID: id,
request: request,
completionHandler: { [weak self] ad, errorOrNil in
if let error = errorOrNil {
print("Failed to load interstitial ad with error: \(error.localizedDescription)")
return
}

self?.interstitialAd = ad
}
)
}

public func show(interstitialed: @escaping () -> Void) {
if let ad = interstitialAd,
let root = (UIApplication.shared.connectedScenes.first as? UIWindowScene)?.windows.first?.rootViewController {
ad.present(fromRootViewController: root)
interstitialed()
} else {
print("Ad wasn't ready")
interstitialed()
}
}
}
2 changes: 1 addition & 1 deletion Projects/Folio/Core/Admob/Sources/Rewarded.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
// Created by 송영모 on 2023/08/24.
//
import SwiftUI

import GoogleMobileAds
import UIKit

public final class Rewarded: NSObject {
let rewardedId: String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public struct MinimalButton: View {
Text(self.title)
.font(.title3)
.fontWeight(.semibold)
.foregroundStyle(.white)
.foregroundStyle(.background)

Spacer()
}
Expand Down
12 changes: 7 additions & 5 deletions Projects/Toolinder/App/Support/ToolinderAppIOS-Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,21 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>2</string>
<string>2.1</string>
<key>CFBundleVersion</key>
<string>2.0.0</string>
<string>2.1.0</string>
<key>GADApplicationIdentifier</key>
<string>ca-app-pub-2392187154020666~7018659983</string>
<string>ca-app-pub-2392187154020666~3429915837</string>
<key>GADIsAdManagerApp</key>
<true/>
<key>OPENAI_API_KEY</key>
<string>$(OPENAI_API_KEY)</string>
<key>REMOVEAD_IPA_ID</key>
<string>$(REMOVEAD_IPA_ID)</string>
<key>Primary App Icon Set Name</key>
<string>AppIcon</string>
<key>REWARDED_ID</key>
<string>$(REWARDED_ID)</string>
<key>INTERSTITIAL_ID</key>
<string>$(INTERSTITIAL_ID)</string>
<key>SKAdNetworkItems</key>
<array>
<dict>
Expand Down
3 changes: 2 additions & 1 deletion Projects/Toolinder/Core/Sources/CoreExport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@
// Created by 송영모 on 2023/08/22.
//

import Foundation
@_exported import FolioCoreAdmobInterface
@_exported import FolioCoreAdmob
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
//
// StoreClient.swift
// ToolinderDomainTradeInterface
//
// Created by 송영모 on 10/9/23.
//

import Foundation
import StoreKit

import ComposableArchitecture

import ToolinderShared

public enum StoreError: Error {
case unknown
case failedVerification
}

public struct StoreClient {
public var fetchProducts: () async throws -> [Product]
public var isPurchasedRemoveAD: () async -> Bool

// public init(
// fetchProducts: @escaping () -> [Product],
// purchase: @escaping (Product) -> Result<Product, TradeError>
// ) {
// self.fetchProducts = fetchProducts
// self.purchase = purchase
// }
}

extension StoreClient: TestDependencyKey {
public static var previewValue: StoreClient = Self(
fetchProducts: { return [] },
isPurchasedRemoveAD: {return false }
)

public static var testValue = Self(
fetchProducts: unimplemented("\(Self.self).fetchProducts"),
isPurchasedRemoveAD: unimplemented("\(Self.self).isPurchasedRemoveAD")
)
}

public extension DependencyValues {
var storeClient: StoreClient {
get { self[StoreClient.self] }
set { self[StoreClient.self] = newValue }
}
}

extension StoreClient: DependencyKey {
public static var liveValue = StoreClient(
fetchProducts: {
let tt = try await Product.products(for: [Environment.removeAdIpaId])
return tt
},
isPurchasedRemoveAD: {
for await result in Transaction.currentEntitlements {
do {
let transaction = try checkVerified(result)
if transaction.productID == Environment.removeAdIpaId {
return true
}

} catch {
print(error)
}
}
return false
}
)

private static func checkVerified<T>(_ result: VerificationResult<T>) throws -> T {
//Check whether the JWS passes StoreKit verification.
switch result {
case .unverified:
//StoreKit parses the JWS, but it fails verification.
throw StoreError.failedVerification
case .verified(let safe):
//The result is verified. Return the unwrapped value.
return safe
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@
import ComposableArchitecture

import ToolinderFeatureTradeInterface
import ToolinderCore
import ToolinderShared

public struct CalendarNavigationStackStore: Reducer {
public init() {}

public struct State: Equatable {
var path: StackState<Path.State> = .init()

var main: CalendarMainStore.State = .init()

public init() {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,22 @@ public struct MyPageMainStore: Reducer {
public init() {}

public struct State: Equatable {
@PresentationState var removeAD: RemoveADStore.State?

public init() { }
}

public enum Action: Equatable {
case onAppear
case delegate(Delegate)

case whatIsNew
case removeADTapped
case whatIsNewTapped

case removeAD(PresentationAction<RemoveADStore.Action>)

public enum Delegate: Equatable {
case whatIsNew
case navigateToWhatIsNew
}
}

Expand All @@ -33,12 +38,20 @@ public struct MyPageMainStore: Reducer {
case .onAppear:
return .none

case .whatIsNew:
return .send(.delegate(.whatIsNew))
case .removeADTapped:
state.removeAD = .init()
return .none

case .whatIsNewTapped:
return .send(.delegate(.navigateToWhatIsNew))

default:
return .none
}
}

.ifLet(\.$removeAD, action: /Action.removeAD) {
RemoveADStore()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@
//

import SwiftUI
import StoreKit

import ComposableArchitecture

import ToolinderShared

public struct MyPageMainView: View {
let store: StoreOf<MyPageMainStore>

Expand All @@ -20,48 +23,68 @@ public struct MyPageMainView: View {
WithViewStore(self.store, observe: { $0 }) { viewStore in
List {
Section {
existingUserPolicyItem(viewStore: viewStore)
removeADView(viewStore: viewStore)
}

Section {
whatIsNewView(viewStore: viewStore)

if let url = URL(string: "https://tally.so/r/mJpaR4") {
Link(destination: url, label: {
Label(
title: {
Text("Usability Questionnaire")
}, icon: {
Image(systemName: "doc.text.image.fill")
.foregroundStyle(.blue)
}
)
})
usabilityQuestionnaireView(url: url)
}
}
}
.navigationTitle("MyPage")
.onAppear {
viewStore.send(.onAppear)
}
.sheet(
store: self.store.scope(
state: \.$removeAD,
action: { .removeAD($0) }
)
) {
RemoveADView(store: $0)
}
}
}

private func existingUserPolicyItem(viewStore: ViewStoreOf<MyPageMainStore>) -> some View {
HStack {
private func removeADView(viewStore: ViewStoreOf<MyPageMainStore>) -> some View {
Button(action: {
viewStore.send(.removeADTapped)
}, label: {
Label(
title: {
Text("What's New \(Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "")")
}, icon: {
Image(systemName: "info.circle.fill")
.foregroundStyle(.blue)
title: { Text("Remove AD") },
icon: {
Image(systemName: "crown.fill")
.foregroundStyle(.yellow)
}
)
Spacer()
Button(
action: {
viewStore.send(.whatIsNew)
},
label: {
Image(systemName: "chevron.right")
.foregroundStyle(.black)
})
}

private func whatIsNewView(viewStore: ViewStoreOf<MyPageMainStore>) -> some View {
Button(action: {
viewStore.send(.whatIsNewTapped)
}, label: {
Label(
title: { Text("What's New") },
icon: {
Image(systemName: "info.circle.fill")
.foregroundStyle(.blue)
}
)
}
})
}

private func usabilityQuestionnaireView(url: URL) -> some View {
Link(destination: url, label: {
Label(title: {
Text("Usability Questionnaire")
}, icon: {
Image(systemName: "doc.text.image.fill")
.foregroundStyle(.blue)
})
})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,12 @@ public struct MyPageNavigationStackStore: Reducer {
case .onAppear:
return .none

case .main(.delegate(.whatIsNew)):
state.path.append(.whatIsNew(.init()))
return .none
case let .main(.delegate(action)):
switch action {
case .navigateToWhatIsNew:
state.path.append(.whatIsNew(.init()))
return .none
}

default:
return .none
Expand Down
Loading