diff --git a/Package.swift b/Package.swift index 62daea7..c961e5a 100644 --- a/Package.swift +++ b/Package.swift @@ -11,7 +11,10 @@ let productionDependencies: [PackageDescription.Package.Dependency] = { [ .package(url: "https://github.com/oversizedev/OversizeComponents.git", .upToNextMajor(from: "1.2.0")), .package(url: "https://github.com/oversizedev/OversizeResources.git", .upToNextMajor(from: "1.3.0")), .package(url: "https://github.com/hmlongco/Factory.git", .upToNextMajor(from: "2.1.3")), - .package(url: "https://github.com/oversizedev/OversizeNetwork.git", .upToNextMajor(from: "0.1.0")) +// .package(name: "OversizeNetwork", path: "../OversizeNetwork"), +// .package(url: "https://github.com/apple/swift-openapi-runtime", .upToNextMinor(from: "0.1.0")), +// .package(url: "https://github.com/apple/swift-openapi-urlsession", .upToNextMinor(from: "0.1.0")), + // .package(url: "https://github.com/oversizedev/OversizeNetwork.git", .upToNextMajor(from: "0.1.0")) ] }() let developmentDependencies: [PackageDescription.Package.Dependency] = { [ @@ -22,7 +25,9 @@ let developmentDependencies: [PackageDescription.Package.Dependency] = { [ .package(name: "OversizeComponents", path: "../OversizeComponents"), .package(name: "OversizeResources", path: "../OversizeResources"), .package(name: "OversizeNetwork", path: "../OversizeNetwork"), - .package(url: "https://github.com/hmlongco/Factory.git", .upToNextMajor(from: "2.1.3")), +// .package(url: "https://github.com/hmlongco/Factory.git", .upToNextMajor(from: "2.1.3")), +// .package(url: "https://github.com/apple/swift-openapi-runtime", .upToNextMinor(from: "0.1.0")), +// .package(url: "https://github.com/apple/swift-openapi-urlsession", .upToNextMinor(from: "0.1.0")), ] }() let package = Package( @@ -56,6 +61,7 @@ let package = Package( .product(name: "OversizeComponents", package: "OversizeComponents"), .product(name: "OversizeLocalizable", package: "OversizeLocalizable"), .product(name: "OversizeResources", package: "OversizeResources"), + .product(name: "OversizeNotificationService", package: "OversizeServices"), .product(name: "Factory", package: "Factory"), ] ), @@ -66,7 +72,12 @@ let package = Package( .product(name: "Factory", package: "Factory"), .product(name: "OversizeUI", package: "OversizeUI"), .product(name: "OversizeServices", package: "OversizeServices"), - .product(name: "OversizeNetwork", package: "OversizeNetwork"), +// .product(name: "OversizeNetwork", package: "OversizeNetwork"), +// .product(name: "OpenAPIRuntime", package: "swift-openapi-runtime"), +// .product( +// name: "OpenAPIURLSession", +// package: "swift-openapi-urlsession" +// ), ] ), .target( diff --git a/Sources/OversizeAdsKit/AdView.swift b/Sources/OversizeAdsKit/AdView.swift index 2d7d551..9a6f650 100644 --- a/Sources/OversizeAdsKit/AdView.swift +++ b/Sources/OversizeAdsKit/AdView.swift @@ -9,9 +9,8 @@ import OversizeUI import SwiftUI public struct AdView: View { - @Environment(\.isPremium) var isPremium: Bool - + @StateObject var viewModel: AdViewModel @State var isShowProduct = false @@ -79,7 +78,7 @@ public struct AdView: View { } } .surfaceContentInsets(.xSmall) - //.appStoreOverlay(isPresent: $isShowProduct, appId: String(viewModel.appAd?.id ?? 0)) + .appStoreOverlay(isPresent: $isShowProduct, appId: viewModel.appAd?.id ?? "") #else EmptyView() #endif diff --git a/Sources/OversizeAdsKit/AdViewModel.swift b/Sources/OversizeAdsKit/AdViewModel.swift index 9bacddc..7e42b35 100644 --- a/Sources/OversizeAdsKit/AdViewModel.swift +++ b/Sources/OversizeAdsKit/AdViewModel.swift @@ -1,27 +1,27 @@ // // Copyright © 2023 Alexander Romanov // AdViewModel.swift, created on 30.06.2023 -// +// -import SwiftUI -import OversizeNetwork -import Factory import OversizeServices +import SwiftUI +// import OversizeNetwork +// import Factory @MainActor public class AdViewModel: ObservableObject { - @Injected(\.networkService) var networkService + @Published var appAd = Info.all?.apps.filter { $0.id != Info.app.appStoreID }.randomElement() + /* + @Injected(\.networkService) var networkService - @Published var appAd: Components.Schemas.AdBanner? - - public func fetchAdBanners() async { - let status = await networkService.fetchAdsBanners() - switch status { - case .success(let banners): - appAd = banners.filter { $0.id != Int(Info.app.appStoreID ?? "") }.randomElement() - case .failure: - break - } - } - + public func fetchAdBanners() async { + let status = await networkService.fetchAdsBanners() + switch status { + case .success(let banners): + appAd = banners.filter { $0.id != Int(Info.app.appStoreID ?? "") }.randomElement() + case .failure: + break + } + } + */ } diff --git a/Sources/OversizeKit/LauncherKit/LauncherViewModel.swift b/Sources/OversizeKit/LauncherKit/LauncherViewModel.swift index 5a1f715..d32203a 100644 --- a/Sources/OversizeKit/LauncherKit/LauncherViewModel.swift +++ b/Sources/OversizeKit/LauncherKit/LauncherViewModel.swift @@ -77,7 +77,7 @@ public extension LauncherViewModel { } if let subscriptionStatus = status.1 { - if #available(iOS 15.4, *) { + if #available(iOS 15.4, macOS 12.3, *) { log("📝 Subscription: \(subscriptionStatus.localizedDescription)") } subscriptionsState = subscriptionStatus diff --git a/Sources/OversizeKit/LockscreenKit/LockscreenView.swift b/Sources/OversizeKit/LockscreenKit/LockscreenView.swift index a0ea5e7..b5d1968 100644 --- a/Sources/OversizeKit/LockscreenKit/LockscreenView.swift +++ b/Sources/OversizeKit/LockscreenKit/LockscreenView.swift @@ -13,8 +13,10 @@ public enum LockscreenViewState { } public struct LockscreenView: View { - @Environment(\.verticalSizeClass) var verticalSizeClass: UserInterfaceSizeClass? - @Environment(\.horizontalSizeClass) var horizontalSizeClass: UserInterfaceSizeClass? + #if os(iOS) + @Environment(\.verticalSizeClass) var verticalSizeClass: UserInterfaceSizeClass? + @Environment(\.horizontalSizeClass) var horizontalSizeClass: UserInterfaceSizeClass? + #endif @Environment(\.scenePhase) var scenePhase: ScenePhase @Binding private var pinCode: String @@ -44,15 +46,19 @@ public struct LockscreenView: View { private let biometricType: BiometricType private var isShowTitle: Bool { - if horizontalSizeClass == .compact, verticalSizeClass == .regular { - return true - } else if horizontalSizeClass == .regular, verticalSizeClass == .compact { - return false - } else if horizontalSizeClass == .regular, verticalSizeClass == .regular { - return true - } else { + #if os(iOS) + if horizontalSizeClass == .compact, verticalSizeClass == .regular { + return true + } else if horizontalSizeClass == .regular, verticalSizeClass == .compact { + return false + } else if horizontalSizeClass == .regular, verticalSizeClass == .regular { + return true + } else { + return true + } + #else return true - } + #endif } public init(pinCode: Binding, diff --git a/Sources/OversizeKit/StoreKit/StoreScreen/StoreInstuctinsView.swift b/Sources/OversizeKit/StoreKit/StoreScreen/StoreInstuctinsView.swift index e8f551f..90afe6c 100644 --- a/Sources/OversizeKit/StoreKit/StoreScreen/StoreInstuctinsView.swift +++ b/Sources/OversizeKit/StoreKit/StoreScreen/StoreInstuctinsView.swift @@ -57,7 +57,7 @@ public struct StoreInstuctinsView: View { } .bottomToolbar(style: .none) { VStack(spacing: .zero) { - StorePaymentButtonBar { + StorePaymentButtonBar(trialNotification: true) { isShowAllPlans = true DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { withAnimation { diff --git a/Sources/OversizeKit/StoreKit/StoreScreen/ViewModel/StoreViewModel.swift b/Sources/OversizeKit/StoreKit/StoreScreen/ViewModel/StoreViewModel.swift index cf25592..7189397 100644 --- a/Sources/OversizeKit/StoreKit/StoreScreen/ViewModel/StoreViewModel.swift +++ b/Sources/OversizeKit/StoreKit/StoreScreen/ViewModel/StoreViewModel.swift @@ -6,6 +6,7 @@ import Factory import OversizeCore import OversizeLocalizable +import OversizeNotificationService import OversizeServices import OversizeStoreService import StoreKit @@ -21,6 +22,8 @@ class StoreViewModel: ObservableObject { } @Injected(\.storeKitService) var storeKitService: StoreKitService + @Injected(\.localNotificationService) var localNotificationService: LocalNotificationServiceProtocol + @Published var state = State.initial public var updateListenerTask: Task? @@ -68,31 +71,31 @@ extension StoreViewModel { case .subscribed: return L10n.Store.active case .revoked: - if #available(iOS 15.4, *) { + if #available(iOS 15.4, macOS 12.3, *) { return subscriptionStatus.localizedDescription } else { return "Revoked" } case .expired: - if #available(iOS 15.4, *) { + if #available(iOS 15.4, macOS 12.3, *) { return subscriptionStatus.localizedDescription } else { return "Expired" } case .inBillingRetryPeriod: - if #available(iOS 15.4, *) { + if #available(iOS 15.4, macOS 12.3, *) { return subscriptionStatus.localizedDescription } else { return "Billing retry" } case .inGracePeriod: - if #available(iOS 15.4, *) { + if #available(iOS 15.4, macOS 12.3, *) { return subscriptionStatus.localizedDescription } else { return "Grace period" } default: - if #available(iOS 15.4, *) { + if #available(iOS 15.4, macOS 12.3, *) { return subscriptionStatus.localizedDescription } else { return "" @@ -273,7 +276,7 @@ extension StoreViewModel { } } - func buy(product: Product) async -> Bool { + func buy(product: Product, trialNotification: Bool = false) async -> Bool { isBuyLoading = true do { let result = try await storeKitService.purchase(product) @@ -282,6 +285,21 @@ extension StoreViewModel { isPremium = true isPremiumActivated = true isBuyLoading = false + if trialNotification, product.type == .autoRenewable, product.subscription?.introductoryOffer != nil { + let result = await localNotificationService.requestAccess() + if case let .success(status) = result, status, let trialDaysCount = product.trialDaysCount { + let timeInterval = TimeInterval((trialDaysCount - 2) * 24 * 60 * 60) + let notificationTime = Date().addingTimeInterval(timeInterval) + let dateComponents = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute], from: notificationTime) + await localNotificationService.schedule(localNotification: .init( + id: UUID(), + title: "Trial ends soon", + body: "Subscription ends in 2 days", + dateComponents: dateComponents, + repeats: false + )) + } + } return true case .failure: isBuyLoading = false diff --git a/Sources/OversizeKit/StoreKit/Views/PrmiumBannerRow.swift b/Sources/OversizeKit/StoreKit/Views/PrmiumBannerRow.swift index b6e99ea..373edb9 100644 --- a/Sources/OversizeKit/StoreKit/Views/PrmiumBannerRow.swift +++ b/Sources/OversizeKit/StoreKit/Views/PrmiumBannerRow.swift @@ -53,7 +53,7 @@ public struct PrmiumBannerRow: View { #endif #if os(macOS) - Image(nsImage: Illustrations.Images.zap.image) + Resource.Store.zap .padding(.horizontal, Space.xxSmall) .padding(.vertical, Space.xxSmall) #endif @@ -107,7 +107,7 @@ public extension PrmiumBannerRow { #endif #if os(macOS) - Image(nsImage: OversizeCraft.Illustrations.Images.zap.image) + Resource.Store.zap #endif Text(Info.store.subscriptionsName) diff --git a/Sources/OversizeKit/StoreKit/Views/StorePaymentButtonBar.swift b/Sources/OversizeKit/StoreKit/Views/StorePaymentButtonBar.swift index 689405c..53bc101 100644 --- a/Sources/OversizeKit/StoreKit/Views/StorePaymentButtonBar.swift +++ b/Sources/OversizeKit/StoreKit/Views/StorePaymentButtonBar.swift @@ -9,8 +9,10 @@ struct StorePaymentButtonBar: View { @EnvironmentObject var viewModel: StoreViewModel let action: (() -> Void)? + let trialNotification: Bool - init(action: (() -> Void)? = nil) { + init(trialNotification: Bool = false, action: (() -> Void)? = nil) { + self.trialNotification = trialNotification self.action = action } @@ -24,7 +26,7 @@ struct StorePaymentButtonBar: View { Button { if let selectedProduct = viewModel.selectedProduct { Task { - await viewModel.buy(product: selectedProduct) + await viewModel.buy(product: selectedProduct, trialNotification: trialNotification) } } } label: {