From f1db6f72e4cec89e05743082f2ed48b49eb2dbf5 Mon Sep 17 00:00:00 2001 From: Alexander Romanov Date: Mon, 25 Sep 2023 01:13:54 +0300 Subject: [PATCH] Add buld Example and NetworkKit --- .github/workflows/build-example.yml | 32 +++ .swiftformat | 2 +- Example/.gitignore | 8 + Example/.swiftformat | 2 + Example/.swiftlint.yml | 27 ++ .../AccentColor.colorset/Contents.json | 20 ++ .../Contents.json | 13 + .../Contents.json | 13 + .../Contents.json | 13 + .../Contents.json | 13 + .../Contents.json | 13 + .../Contents.json | 13 + .../Contents.json | 13 + .../AppIcon.appiconset/Contents.json | 63 +++++ Example/Example/Assets.xcassets/Contents.json | 6 + .../Contents.json | 36 +++ Example/Example/ExampleApp.swift | 120 ++++++++ .../Preview Assets.xcassets/Contents.json | 6 + Example/Example/Resources/AppConfig.plist | 258 ++++++++++++++++++ Example/Example/Resources/Info.plist | 10 + Example/Example/Resources/Products.storekit | 175 ++++++++++++ .../Example/Resources/Scripts/swiftgen.yml | 14 + Example/Example/Router/Alerts.swift | 48 ++++ Example/Example/Router/Router.swift | 214 +++++++++++++++ Example/Example/Router/Screens.swift | 50 ++++ Example/Example/Router/Tabs.swift | 59 ++++ .../AppSettings/AppSettingsPageView.swift | 37 +++ .../AppSettingsPageViewModel.swift | 11 + .../Screens/AppSettings/AppSettingsView.swift | 31 +++ .../AppSettings/AppSettingsViewModel.swift | 10 + Example/Example/Screens/Main/MainView.swift | 35 +++ .../Example/Screens/Main/MainViewModel.swift | 11 + .../Screens/Onboarding/OnboardingView.swift | 172 ++++++++++++ Package.swift | 20 +- Sources/OversizeAdsKit/AdView.swift | 29 +- Sources/OversizeAdsKit/AdViewModel.swift | 44 ++- .../AttachmentScreen/AttachmentView.swift | 2 +- .../CreateEventScreen/CreateEventView.swift | 18 +- .../SettingsKit/Views/About/AboutView.swift | 2 +- 39 files changed, 1610 insertions(+), 53 deletions(-) create mode 100644 .github/workflows/build-example.yml create mode 100644 Example/.gitignore create mode 100644 Example/.swiftformat create mode 100644 Example/.swiftlint.yml create mode 100644 Example/Example/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 Example/Example/Assets.xcassets/AlternateAppIcon1.appiconset/Contents.json create mode 100644 Example/Example/Assets.xcassets/AlternateAppIcon2.appiconset/Contents.json create mode 100644 Example/Example/Assets.xcassets/AlternateAppIcon3.appiconset/Contents.json create mode 100644 Example/Example/Assets.xcassets/AlternateAppIcon4.appiconset/Contents.json create mode 100644 Example/Example/Assets.xcassets/AlternateAppIcon5.appiconset/Contents.json create mode 100644 Example/Example/Assets.xcassets/AlternateAppIcon6.appiconset/Contents.json create mode 100644 Example/Example/Assets.xcassets/AlternateAppIcon7.appiconset/Contents.json create mode 100644 Example/Example/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 Example/Example/Assets.xcassets/Contents.json create mode 100644 Example/Example/Assets.xcassets/OnbardingBackground.imageset/Contents.json create mode 100644 Example/Example/ExampleApp.swift create mode 100644 Example/Example/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 Example/Example/Resources/AppConfig.plist create mode 100644 Example/Example/Resources/Info.plist create mode 100644 Example/Example/Resources/Products.storekit create mode 100644 Example/Example/Resources/Scripts/swiftgen.yml create mode 100644 Example/Example/Router/Alerts.swift create mode 100644 Example/Example/Router/Router.swift create mode 100644 Example/Example/Router/Screens.swift create mode 100644 Example/Example/Router/Tabs.swift create mode 100644 Example/Example/Screens/AppSettings/AppSettingsPageView.swift create mode 100644 Example/Example/Screens/AppSettings/AppSettingsPageViewModel.swift create mode 100644 Example/Example/Screens/AppSettings/AppSettingsView.swift create mode 100644 Example/Example/Screens/AppSettings/AppSettingsViewModel.swift create mode 100644 Example/Example/Screens/Main/MainView.swift create mode 100644 Example/Example/Screens/Main/MainViewModel.swift create mode 100644 Example/Example/Screens/Onboarding/OnboardingView.swift diff --git a/.github/workflows/build-example.yml b/.github/workflows/build-example.yml new file mode 100644 index 0000000..f2e7923 --- /dev/null +++ b/.github/workflows/build-example.yml @@ -0,0 +1,32 @@ +name: Build Example + +on: + push: + branches: [ develop ] + pull_request: + branches: [ main ] + +env: + DEVELOPER_DIR: /Applications/Xcode_15.0.app/Contents/Developer + PROJECT_DIR: Example + PROJECT_NAME: Example.xcodeproj + iOSSCHEME: Example + +jobs: + + example: + name: Run examples + runs-on: macOS-latest + strategy: + matrix: + iosDestination: ['platform=iOS Simulator,OS=16,name=iPhone X','platform=iOS Simulator,OS=17.0,name=iPhone 14'] + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Build iOS + run: | + xcodebuild clean build -project "${{ env.PROJECT_DIR }}/${{ env.PROJECT_NAME }}" -scheme "${{ env.iOSSCHEME }}" | xcpretty + env: + destination: ${{ matrix.iosDestination }} diff --git a/.swiftformat b/.swiftformat index 2f6ed6f..21f72f9 100644 --- a/.swiftformat +++ b/.swiftformat @@ -1,2 +1,2 @@ ---swiftversion 5.7 +--swiftversion 5.8 --disable preferKeyPath \ No newline at end of file diff --git a/Example/.gitignore b/Example/.gitignore new file mode 100644 index 0000000..4d62f3e --- /dev/null +++ b/Example/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/.build +/Packages +/*.xcodeproj +xcuserdata/ +DerivedData/ +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +/.idea/ \ No newline at end of file diff --git a/Example/.swiftformat b/Example/.swiftformat new file mode 100644 index 0000000..2f6ed6f --- /dev/null +++ b/Example/.swiftformat @@ -0,0 +1,2 @@ +--swiftversion 5.7 +--disable preferKeyPath \ No newline at end of file diff --git a/Example/.swiftlint.yml b/Example/.swiftlint.yml new file mode 100644 index 0000000..9f9669f --- /dev/null +++ b/Example/.swiftlint.yml @@ -0,0 +1,27 @@ +excluded: # paths to ignore during linting. Takes precedence over `included`. + - Carthage + - Pods +opt_in_rules: + - empty_count + - file_header + - explicit_init + - closure_spacing + - overridden_super_call + - redundant_nil_coalescing + - private_outlet + - nimble_operator + - attributes + - operator_usage_whitespace + - first_where + - object_literal + - number_separator + - prohibited_super_call + - fatal_error_message + - vertical_parameter_alignment_on_call + - let_var_whitespace +identifier_name: + excluded: + - id +line_length: 196 +number_separator: + minimum_length: 5 diff --git a/Example/Example/Assets.xcassets/AccentColor.colorset/Contents.json b/Example/Example/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..5b26f04 --- /dev/null +++ b/Example/Example/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.961", + "green" : "0.537", + "red" : "0.173" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/Example/Assets.xcassets/AlternateAppIcon1.appiconset/Contents.json b/Example/Example/Assets.xcassets/AlternateAppIcon1.appiconset/Contents.json new file mode 100644 index 0000000..13613e3 --- /dev/null +++ b/Example/Example/Assets.xcassets/AlternateAppIcon1.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/Example/Assets.xcassets/AlternateAppIcon2.appiconset/Contents.json b/Example/Example/Assets.xcassets/AlternateAppIcon2.appiconset/Contents.json new file mode 100644 index 0000000..13613e3 --- /dev/null +++ b/Example/Example/Assets.xcassets/AlternateAppIcon2.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/Example/Assets.xcassets/AlternateAppIcon3.appiconset/Contents.json b/Example/Example/Assets.xcassets/AlternateAppIcon3.appiconset/Contents.json new file mode 100644 index 0000000..13613e3 --- /dev/null +++ b/Example/Example/Assets.xcassets/AlternateAppIcon3.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/Example/Assets.xcassets/AlternateAppIcon4.appiconset/Contents.json b/Example/Example/Assets.xcassets/AlternateAppIcon4.appiconset/Contents.json new file mode 100644 index 0000000..13613e3 --- /dev/null +++ b/Example/Example/Assets.xcassets/AlternateAppIcon4.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/Example/Assets.xcassets/AlternateAppIcon5.appiconset/Contents.json b/Example/Example/Assets.xcassets/AlternateAppIcon5.appiconset/Contents.json new file mode 100644 index 0000000..13613e3 --- /dev/null +++ b/Example/Example/Assets.xcassets/AlternateAppIcon5.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/Example/Assets.xcassets/AlternateAppIcon6.appiconset/Contents.json b/Example/Example/Assets.xcassets/AlternateAppIcon6.appiconset/Contents.json new file mode 100644 index 0000000..13613e3 --- /dev/null +++ b/Example/Example/Assets.xcassets/AlternateAppIcon6.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/Example/Assets.xcassets/AlternateAppIcon7.appiconset/Contents.json b/Example/Example/Assets.xcassets/AlternateAppIcon7.appiconset/Contents.json new file mode 100644 index 0000000..13613e3 --- /dev/null +++ b/Example/Example/Assets.xcassets/AlternateAppIcon7.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/Example/Assets.xcassets/AppIcon.appiconset/Contents.json b/Example/Example/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..532cd72 --- /dev/null +++ b/Example/Example/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,63 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "512x512" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "512x512" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/Example/Assets.xcassets/Contents.json b/Example/Example/Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/Example/Example/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Example/Example/Assets.xcassets/OnbardingBackground.imageset/Contents.json b/Example/Example/Assets.xcassets/OnbardingBackground.imageset/Contents.json new file mode 100644 index 0000000..409cb43 --- /dev/null +++ b/Example/Example/Assets.xcassets/OnbardingBackground.imageset/Contents.json @@ -0,0 +1,36 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "1x" + }, + { + "idiom" : "iphone", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "scale" : "2x" + }, + { + "idiom" : "mac", + "scale" : "1x" + }, + { + "idiom" : "mac", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/Example/ExampleApp.swift b/Example/Example/ExampleApp.swift new file mode 100644 index 0000000..498cb60 --- /dev/null +++ b/Example/Example/ExampleApp.swift @@ -0,0 +1,120 @@ +// +// Copyright © 2023 Alexander Romanov +// ExampleApp.swift, created on 25.09.2023 +// + +import Factory +import OversizeKit +import OversizeServices +import OversizeUI +import SwiftUI + +@main +struct ExampleApp: App { + + @Injected(\.appStateService) var appStateService: AppStateService + @ObservedObject private var router = Router() + @StateObject private var appSettingsViewModel = AppSettingsViewModel() + let pub = NotificationCenter.default.publisher(for: NSNotification.Name("Deeplink")) + + var body: some Scene { + WindowGroup { + TabView(selection: $router.tab) { + NavigationStack(path: $router.mainPath) { + MainView() + .navigationDestination(for: Screen.self) { destination in + router.resolve(pathItem: destination) + } + } + .tag(RootTab.main) + .tabItem { + RootTab.main.image + .renderingMode(.template) + Text(RootTab.main.title) + } + + NavigationStack(path: $router.secondaryPath) { + EmptyView() + .navigationDestination(for: Screen.self) { destination in + router.resolve(pathItem: destination) + } + } + .tag(RootTab.secondary) + .tabItem { + RootTab.secondary.image + .renderingMode(.template) + Text(RootTab.secondary.title) + } + + NavigationStack(path: $router.tertiaryPath) { + EmptyView() + .navigationDestination(for: Screen.self) { destination in + router.resolve(pathItem: destination) + } + } + .tag(RootTab.tertiary) + .tabItem { + RootTab.tertiary.image + .renderingMode(.template) + Text(RootTab.tertiary.title) + } + + NavigationStack(path: $router.quaternaryPath) { + EmptyView() + .navigationDestination(for: Screen.self) { destination in + router.resolve(pathItem: destination) + } + } + .tag(RootTab.quaternary) + .tabItem { + RootTab.quaternary.image + .renderingMode(.template) + Text(RootTab.quaternary.title) + } + + NavigationStack(path: $router.settingsPath) { + SettingsView { + AppSettingsView() + } + .navigationDestination(for: Screen.self) { destination in + router.resolve(pathItem: destination) + } + } + .tag(RootTab.settings) + .tabItem { + RootTab.settings.image + .renderingMode(.template) + Text(RootTab.settings.title) + } + } + .appLaunch { + OnboardingView() + } + .hud(router.hudText, isPresented: $router.isShowHud) + .sheet(item: $router.sheet) { sheet in + router.resolveSheet(pathItem: sheet, detents: router.sheetDetents, dragIndicator: router.dragIndicator, dismissDisabled: router.dismissDisabled) + .hud(router.hudText, isPresented: $router.isShowHud) + .alert(item: $router.alert) { $0.alert } + .environmentObject(appSettingsViewModel) + .systemServices() + } + .fullScreenCover(item: $router.fullScreenCover) { fullScreenCover in + router.resolve(pathItem: fullScreenCover) + .hud(router.hudText, isPresented: $router.isShowHud) + .alert(item: $router.alert) { $0.alert } + .environmentObject(appSettingsViewModel) + .systemServices() + } + .alert(item: $router.alert) { $0.alert } + .onOpenURL { router.handle($0) } + .environmentObject(router) + .environmentObject(appSettingsViewModel) + .onReceive(pub) { output in + if let userInfo = output.userInfo, let info = userInfo["link"] { + let url = URL(string: info as! String)! + router.handle(url) + } + } + } + } +} diff --git a/Example/Example/Preview Content/Preview Assets.xcassets/Contents.json b/Example/Example/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Example/Example/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/Example/Resources/AppConfig.plist b/Example/Example/Resources/AppConfig.plist new file mode 100644 index 0000000..55a1ad6 --- /dev/null +++ b/Example/Example/Resources/AppConfig.plist @@ -0,0 +1,258 @@ + + + + + Links + + App + + Url + https://oversize.app/example + AppStoreID + + TelegramChat + + + Developer + + Name + Alexander Romanov + Url + romanov.cc + Email + hello@oversize.app + Fecebook + + Telegram + + + Company + + Name + Oversize + Url + https://oversize.app + CDNUrl + https://cdn.oversize.app + Email + support@oversize.app + Facebook + oversizedev + Telegram + oversizeapp + Dribbble + oversizedesign + Instagram + oversize.design + Twitter + oversizedesign + AppStoreID + 1459928735 + + + FeatureFlags + + Onboarding + + Apperance + + StoreKit + + CloudKit + + HealthKit + + FaceID + + FaceIDSecureCodes + + Notifications + + Lookscreen + + Vibration + + Sounds + + Spotlight + + BlurMinimize + + AlertPINCode + + CVVCodes + + BruteForceSecure + + PhotoBreaker + + AlternateAppIcons + 7 + PremiumFeatures + + BlurMinimize + + Lookscreen + + + + Store + + BannerLabel + Store.BannerLabelDressWeathrt + SubscriptionsName + Pro + SubscriptionsDescription + No limits, without Ads, iСloud, application design setting and much more + ProductIdentifiers + + app.oversize.Example.monthly + app.oversize.Example.yearly + app.oversize.Example.forever + app.oversize.Example.offerYearly + + Features + + + image + Solid/ShoppingandEcommerce/Megaphone + title + No Ads + subtitle + Advertising will not be displayed + topScreenAlignment + + illustrationURL + https://cdn.oversize.design/assets/illustrations/objects/tools/speaker/large.png + backgroundColor + FF7A20 + + + image + Solid/DateandTime/Calendar02 + title + No limits for subtasks + subtitle + Save any number of subtasks + illustrationURL + https://cdn.oversize.design/assets/illustrations/objects/education/calendar-b/large.png + backgroundColor + FB616B + + + image + Solid/UserInterface/Lock + title + PIN code + subtitle + Increase the security of personal data + illustrationURL + https://cdn.oversize.design/assets/illustrations/objects/houseware/lock-a/large.png + backgroundColor + FF5795 + + + image + Solid/Weather/Cloudy01 + title + Sync with iCloud + subtitle + Data will not be lost + illustrationURL + https://cdn.oversize.design/assets/illustrations/objects/tools/cloud/large.png + backgroundColor + AF7AFF + + + image + Solid/Design/Brush + title + Custumize theme and fonts + subtitle + Setting app design + illustrationURL + https://cdn.oversize.design/assets/illustrations/objects/tools/bucket/large.png + backgroundColor + 5E92FE + + + image + Solid/Design/Palette + title + Alternate app icon + subtitle + Choose from a selection app icons for your homescreen + illustrationURL + https://cdn.oversize.design/assets/illustrations/objects/tools/roller/large.png + backgroundColor + 5E92FE + + + image + Solid/FoodandDrinks/CupToGo + title + Help developer :) + subtitle + Coffe for single worker + illustrationURL + https://cdn.oversize.design/assets/illustrations/objects/food/coffee-cup/large.png + backgroundColor + 3FB4FF + + + + Onboarding + + Pages + + + image + + title + + subtitle + + + + + Apps + + + Id + 6443709281 + Name + Scale Down + Title + Control weight easily, follow the successes of every month + Subtitle + It is very difficult to reset everything, but if you move in small steps every day, then it is easier to reach the goal. The Scale Down application will help with this, with it the whole process is clearly visible + Path + scaledown + + + Id + 1477792790 + Name + PIN Wallet + Title + Allows you to store information about your bank cards + Subtitle + All data is saved in the phone or is possible synchronization with iCloud + Path + pinwallet + + + Id + 1552617598 + Name + Dress Weather + Title + How often are you smell in the rain? + Subtitle + We will tell you when to take an umbrella + Path + dressweather + + + + diff --git a/Example/Example/Resources/Info.plist b/Example/Example/Resources/Info.plist new file mode 100644 index 0000000..688fcf7 --- /dev/null +++ b/Example/Example/Resources/Info.plist @@ -0,0 +1,10 @@ + + + + + ITSAppUsesNonExemptEncryption + + NSFaceIDUsageDescription + $(PRODUCT_NAME) Authentication with TouchId or FaceID + + diff --git a/Example/Example/Resources/Products.storekit b/Example/Example/Resources/Products.storekit new file mode 100644 index 0000000..f9dfc04 --- /dev/null +++ b/Example/Example/Resources/Products.storekit @@ -0,0 +1,175 @@ +{ + "identifier" : "D65ED633", + "nonRenewingSubscriptions" : [ + + ], + "products" : [ + { + "displayPrice" : "19.99", + "familyShareable" : false, + "internalID" : "A413F754", + "localizations" : [ + { + "description" : "", + "displayName" : "", + "locale" : "en_US" + } + ], + "productID" : "app.oversize.Example.forever", + "referenceName" : "Forever", + "type" : "NonConsumable" + } + ], + "settings" : { + "_failTransactionsEnabled" : false, + "_storeKitErrors" : [ + { + "current" : null, + "enabled" : false, + "name" : "Load Products" + }, + { + "current" : null, + "enabled" : false, + "name" : "Purchase" + }, + { + "current" : null, + "enabled" : false, + "name" : "Verification" + }, + { + "current" : null, + "enabled" : false, + "name" : "App Store Sync" + }, + { + "current" : null, + "enabled" : false, + "name" : "Subscription Status" + }, + { + "current" : null, + "enabled" : false, + "name" : "App Transaction" + }, + { + "current" : null, + "enabled" : false, + "name" : "Manage Subscriptions Sheet" + }, + { + "current" : null, + "enabled" : false, + "name" : "Refund Request Sheet" + }, + { + "current" : null, + "enabled" : false, + "name" : "Offer Code Redeem Sheet" + } + ] + }, + "subscriptionGroups" : [ + { + "id" : "A866A70B", + "localizations" : [ + + ], + "name" : "Pro", + "subscriptions" : [ + { + "adHocOffers" : [ + + ], + "codeOffers" : [ + + ], + "displayPrice" : "9.99", + "familyShareable" : false, + "groupNumber" : 1, + "internalID" : "917AE642", + "introductoryOffer" : { + "internalID" : "09E486B7", + "paymentMode" : "free", + "subscriptionPeriod" : "P1W" + }, + "localizations" : [ + { + "description" : "", + "displayName" : "", + "locale" : "en_US" + } + ], + "productID" : "app.oversize.Example.yearly", + "recurringSubscriptionPeriod" : "P1Y", + "referenceName" : "Yearly", + "subscriptionGroupID" : "A866A70B", + "type" : "RecurringSubscription" + }, + { + "adHocOffers" : [ + + ], + "codeOffers" : [ + + ], + "displayPrice" : "0.99", + "familyShareable" : false, + "groupNumber" : 1, + "internalID" : "DCD986A0", + "introductoryOffer" : { + "internalID" : "AD2453D4", + "paymentMode" : "free", + "subscriptionPeriod" : "P3D" + }, + "localizations" : [ + { + "description" : "", + "displayName" : "", + "locale" : "en_US" + } + ], + "productID" : "app.oversize.Example.monthly", + "recurringSubscriptionPeriod" : "P1M", + "referenceName" : "Monthly", + "subscriptionGroupID" : "A866A70B", + "type" : "RecurringSubscription" + }, + { + "adHocOffers" : [ + + ], + "codeOffers" : [ + + ], + "displayPrice" : "3.99", + "familyShareable" : false, + "groupNumber" : 1, + "internalID" : "DA6156FB", + "introductoryOffer" : { + "internalID" : "ED52E0DA", + "paymentMode" : "free", + "subscriptionPeriod" : "P1W" + }, + "localizations" : [ + { + "description" : "", + "displayName" : "", + "locale" : "en_US" + } + ], + "productID" : "app.oversize.Example.offerYearly", + "recurringSubscriptionPeriod" : "P1Y", + "referenceName" : "Yearly Offer", + "subscriptionGroupID" : "A866A70B", + "type" : "RecurringSubscription" + } + ] + } + ], + "version" : { + "major" : 3, + "minor" : 0 + } +} diff --git a/Example/Example/Resources/Scripts/swiftgen.yml b/Example/Example/Resources/Scripts/swiftgen.yml new file mode 100644 index 0000000..1cd638c --- /dev/null +++ b/Example/Example/Resources/Scripts/swiftgen.yml @@ -0,0 +1,14 @@ +#strings: +# inputs: ${PROJECT_DIR}/${PROJECT_NAME}/Resources/en.lproj/Localizable.strings +# outputs: +# - templateName: structured-swift5 +# output: ${PROJECT_DIR}/${PROJECT_NAME}/Resources/SwiftGen/Localizable.swift +# params: +# enumName: L +xcassets: + - inputs: ${PROJECT_DIR}/${PROJECT_NAME}/Resources/Assets.xcassets + outputs: + - templateName: swift5 + output: ${PROJECT_DIR}/${PROJECT_NAME}/Resources/SwiftGen/Assets.swift + params: + enumName: Asset diff --git a/Example/Example/Router/Alerts.swift b/Example/Example/Router/Alerts.swift new file mode 100644 index 0000000..7d97763 --- /dev/null +++ b/Example/Example/Router/Alerts.swift @@ -0,0 +1,48 @@ +// +// Copyright © 2023 Alexander Romanov +// Alerts.swift, created on 25.09.2023 +// + +import OversizeLocalizable +import OversizeServices +import SwiftUI + +enum RootAlert: Identifiable { + case dismiss(_ action: () -> Void) + case delete(_ action: () -> Void) + case appError(error: AppError) + + var id: String { + switch self { + case .dismiss: + return "dismiss" + case .delete: + return "delete" + case .appError: + return "appError" + } + } + + var alert: Alert { + switch self { + case let .dismiss(action): + return Alert( + title: Text("Are you sure you want to dismiss?"), + primaryButton: .destructive(Text("Dismiss"), action: action), + secondaryButton: .cancel() + ) + case let .delete(action): + return Alert( + title: Text("Are you sure you want to delete?"), + primaryButton: .destructive(Text("\(L10n.Button.delete)"), action: action), + secondaryButton: .cancel() + ) + case let .appError(error: error): + return Alert( + title: Text(error.title), + message: Text(error.subtitle.valueOrEmpty), + dismissButton: .cancel() + ) + } + } +} diff --git a/Example/Example/Router/Router.swift b/Example/Example/Router/Router.swift new file mode 100644 index 0000000..e359b20 --- /dev/null +++ b/Example/Example/Router/Router.swift @@ -0,0 +1,214 @@ +// +// Copyright © 2023 Alexander Romanov +// Router.swift, created on 25.09.2023 +// + +import SwiftUI + +@MainActor +final class Router: ObservableObject { + + // Route and Tabs + @Published var mainPath: [Screen] = [] + @Published var secondaryPath: [Screen] = [] + @Published var tertiaryPath: [Screen] = [] + @Published var quaternaryPath: [Screen] = [] + @Published var settingsPath: [Screen] = [] + @Published var tab: RootTab = .main + + // Sheets + @Published var sheet: Screen? + @Published var fullScreenCover: Screen? + @Published private(set) var sheetDetents: Set = [] + @Published private(set) var dragIndicator: Visibility = .hidden + @Published private(set) var dismissDisabled: Bool = false + + // Hud + @Published var isShowHud: Bool = false + @Published var hudText: String = "" + + // Alert + @Published var alert: RootAlert? = nil +} + +// MARK: - Alert + +extension Router { + func presentAlert(_ alert: RootAlert) { + self.alert = alert + } + + func dismissAlert() { + alert = nil + } +} + +// MARK: - HUD + +extension Router { + func presentHud(_ text: String) { + hudText = text + isShowHud = true + } +} + +// MARK: - Route and Tabs + +extension Router { + func changeTab(_ tab: RootTab) { + self.tab = tab + } + + func move(_ screen: Screen) { + switch tab { + case .main: + mainPath.append(screen) + case .secondary: + secondaryPath.append(screen) + case .tertiary: + tertiaryPath.append(screen) + case .quaternary: + quaternaryPath.append(screen) + case .settings: + settingsPath.append(screen) + } + } + + func backToRoot() { + switch tab { + case .main: + mainPath.removeAll() + case .secondary: + secondaryPath.removeAll() + case .tertiary: + tertiaryPath.removeAll() + case .quaternary: + quaternaryPath.removeAll() + case .settings: + settingsPath.removeAll() + } + } + + func back(_ count: Int = 1) { + switch tab { + case .main: + let pathCount = mainPath.count - count + if pathCount > -1 { + mainPath.removeLast(count) + } + case .secondary: + let pathCount = secondaryPath.count - count + if pathCount > -1 { + secondaryPath.removeLast(count) + } + case .tertiary: + let pathCount = tertiaryPath.count - count + if pathCount > -1 { + tertiaryPath.removeLast(count) + } + case .quaternary: + let pathCount = quaternaryPath.count - count + if pathCount > -1 { + quaternaryPath.removeLast(count) + } + case .settings: + let pathCount = settingsPath.count - count + if pathCount > -1 { + settingsPath.removeLast(count) + } + } + } +} + +// MARK: - Sheets + +extension Router { + func present(_ sheet: Screen, fullScreen: Bool = false) { + if fullScreen { + if fullScreenCover != nil { + fullScreenCover = nil + } + fullScreenCover = sheet + } else { + restSheet() + self.sheet = sheet + } + } + + func present(_ sheet: Screen, detents: Set = [.large], indicator: Visibility = .hidden, dismissDisabled: Bool = false) { + restSheet() + sheetDetents = detents + dragIndicator = indicator + self.dismissDisabled = dismissDisabled + self.sheet = sheet + } + + func dismiss() { + sheet = nil + fullScreenCover = nil + } + + func dismissSheet() { + sheet = nil + } + + func dismissFullScreenCover() { + fullScreenCover = nil + } + + func dismissDisabled(_ isDismissDisabled: Bool = true) { + dismissDisabled = isDismissDisabled + } + + private func restSheet() { + if sheet != nil { + sheet = nil + } + if fullScreenCover != nil { + fullScreenCover = nil + } + if dragIndicator != .hidden { + dragIndicator = .hidden + } + if dismissDisabled { + dismissDisabled = false + } + if sheetDetents.isEmpty == false { + sheetDetents = [] + } + } +} + +extension Router { + func handle(_ url: URL) { + guard let components = URLComponents(url: url, resolvingAgainstBaseURL: true), let host = components.host else { + return + } + var pathComponents = components.path.components(separatedBy: "/") + pathComponents.removeFirst() + + switch host { + case "settings": + changeTab(.settings) + backToRoot() + case "premium": + present(.premium) + default: + break + } + } +} + +extension Screen: Hashable, Equatable { + static func == (lhs: Screen, rhs: Screen) -> Bool { + if lhs.id == rhs.id { + return true + } else { + return false + } + } + + func hash(into hasher: inout Hasher) { + hasher.combine(id) + } +} diff --git a/Example/Example/Router/Screens.swift b/Example/Example/Router/Screens.swift new file mode 100644 index 0000000..db98182 --- /dev/null +++ b/Example/Example/Router/Screens.swift @@ -0,0 +1,50 @@ +// +// Copyright © 2023 Alexander Romanov +// Screens.swift, created on 25.09.2023 +// + +import OversizeComponents +import OversizeKit +import SwiftUI + +enum Screen { + case settings + case premium +} + +extension Screen: Identifiable { + var id: String { + switch self { + case .settings: + return "settings" + case .premium: + return "premium" + } + } +} + +extension Router { + @ViewBuilder + func resolve(pathItem: Screen) -> some View { + switch pathItem { + case .settings: + SettingsView { + AppSettingsView() + } + case .premium: + StoreView() + } + } + + func resolveSheet( + pathItem: Screen, + detents: Set, + dragIndicator: Visibility = .automatic, + dismissDisabled: Bool + ) -> some View { + resolve(pathItem: pathItem) + .presentationDetents(detents) + .presentationDragIndicator(dragIndicator) + .interactiveDismissDisabled(dismissDisabled) + } +} diff --git a/Example/Example/Router/Tabs.swift b/Example/Example/Router/Tabs.swift new file mode 100644 index 0000000..cadcacf --- /dev/null +++ b/Example/Example/Router/Tabs.swift @@ -0,0 +1,59 @@ +// +// Copyright © 2023 Alexander Romanov +// Tabs.swift, created on 25.09.2023 +// + +import SwiftUI + +public enum RootTab: String { + case main + case secondary + case tertiary + case quaternary + case settings + + var id: String { + switch self { + case .main: + return "home" + case .secondary: + return "secondary" + case .tertiary: + return "tertiary" + case .quaternary: + return "quaternary" + case .settings: + return "settings" + } + } + + var title: String { + switch self { + case .main: + return "Home" + case .secondary: + return "Secondary" + case .tertiary: + return "Tertiary" + case .quaternary: + return "Quaternary" + case .settings: + return "Settings" + } + } + + var image: Image { + switch self { + case .main: + return Image(systemName: "") + case .secondary: + return Image(systemName: "") + case .tertiary: + return Image(systemName: "") + case .quaternary: + return Image(systemName: "") + case .settings: + return Image(systemName: "") + } + } +} diff --git a/Example/Example/Screens/AppSettings/AppSettingsPageView.swift b/Example/Example/Screens/AppSettings/AppSettingsPageView.swift new file mode 100644 index 0000000..f5d6dfe --- /dev/null +++ b/Example/Example/Screens/AppSettings/AppSettingsPageView.swift @@ -0,0 +1,37 @@ +// +// Copyright © 2023 Alexander Romanov +// AppSettingsPageView.swift, created on 25.09.2023 +// + +import OversizeUI +import SwiftUI + +struct AppSettingsPageView: View { + @StateObject var viewModel: AppSettingsPageViewModel + + init() { + _viewModel = StateObject(wrappedValue: AppSettingsPageViewModel()) + } + + var body: some View { + PageView("Option") { + SectionView { + VStack(spacing: .zero) { + Row("Default option") { + Image(systemName: "") + } + } + } + .sectionContentCompactRowMargins() + } + .leadingBar { + BarButton(.back) + } + } +} + +struct AppSettingsPageView_ViewPreviews: PreviewProvider { + static var previews: some View { + AppSettingsPageView() + } +} diff --git a/Example/Example/Screens/AppSettings/AppSettingsPageViewModel.swift b/Example/Example/Screens/AppSettings/AppSettingsPageViewModel.swift new file mode 100644 index 0000000..820bc81 --- /dev/null +++ b/Example/Example/Screens/AppSettings/AppSettingsPageViewModel.swift @@ -0,0 +1,11 @@ +// +// Copyright © 2023 Alexander Romanov +// AppSettingsPageViewModel.swift, created on 25.09.2023 +// + +import SwiftUI + +@MainActor +class AppSettingsPageViewModel: ObservableObject { + @AppStorage("AppState.Option") var option: String = "" +} diff --git a/Example/Example/Screens/AppSettings/AppSettingsView.swift b/Example/Example/Screens/AppSettings/AppSettingsView.swift new file mode 100644 index 0000000..679db07 --- /dev/null +++ b/Example/Example/Screens/AppSettings/AppSettingsView.swift @@ -0,0 +1,31 @@ +// +// Copyright © 2023 Alexander Romanov +// AppSettingsView.swift, created on 25.09.2023 +// + +import OversizeUI +import SwiftUI + +struct AppSettingsView: View { + @EnvironmentObject var viewModel: AppSettingsViewModel + + var body: some View { + Group { + NavigationLink(destination: AppSettingsPageView()) { + Row("Option") { + Image(systemName: "") + } + .rowArrow() + + .multilineTextAlignment(.leading) + } + .buttonStyle(.row) + } + } +} + +struct AppSettings_ViewPreviews: PreviewProvider { + static var previews: some View { + AppSettingsView() + } +} diff --git a/Example/Example/Screens/AppSettings/AppSettingsViewModel.swift b/Example/Example/Screens/AppSettings/AppSettingsViewModel.swift new file mode 100644 index 0000000..64065f9 --- /dev/null +++ b/Example/Example/Screens/AppSettings/AppSettingsViewModel.swift @@ -0,0 +1,10 @@ +// +// Copyright © 2023 Alexander Romanov +// AppSettingsViewModel.swift, created on 25.09.2023 +// + +import Foundation +import SwiftUI + +@MainActor +class AppSettingsViewModel: ObservableObject {} diff --git a/Example/Example/Screens/Main/MainView.swift b/Example/Example/Screens/Main/MainView.swift new file mode 100644 index 0000000..835ffad --- /dev/null +++ b/Example/Example/Screens/Main/MainView.swift @@ -0,0 +1,35 @@ +// +// Copyright © 2023 Alexander Romanov +// MainView.swift, created on 25.09.2023 +// + +import Factory +import OversizeKit +import OversizeLocalizable +import OversizeServices +import OversizeUI +import SwiftUI + +struct MainView: View { + + @Injected(\.appStateService) var appStateService: AppStateService + @Environment(\.screenSize) var screenSize + @EnvironmentObject var router: Router + @EnvironmentObject var appSettins: AppSettingsViewModel + @StateObject var viewModel: MainViewModel + + init() { + _viewModel = StateObject(wrappedValue: MainViewModel()) + } + + var body: some View { + Text("Hello, Oversize Kit!") + } + +} + +struct MainView_Previews: PreviewProvider { + static var previews: some View { + MainView() + } +} diff --git a/Example/Example/Screens/Main/MainViewModel.swift b/Example/Example/Screens/Main/MainViewModel.swift new file mode 100644 index 0000000..dee89aa --- /dev/null +++ b/Example/Example/Screens/Main/MainViewModel.swift @@ -0,0 +1,11 @@ +// +// Copyright © 2023 Alexander Romanov +// MainViewModel.swift, created on 25.09.2023 +// + +import SwiftUI + +@MainActor +class MainViewModel: ObservableObject { + +} diff --git a/Example/Example/Screens/Onboarding/OnboardingView.swift b/Example/Example/Screens/Onboarding/OnboardingView.swift new file mode 100644 index 0000000..3767f1d --- /dev/null +++ b/Example/Example/Screens/Onboarding/OnboardingView.swift @@ -0,0 +1,172 @@ +// +// Copyright © 2023 Alexander Romanov +// OnboardingView.swift, created on 25.09.2023 +// + +import OversizeServices +import OversizeUI +import SwiftUI +import Factory + +struct OnboardingView: View { + @Injected(\.appStateService) var appStateService: AppStateService + @Environment(\.screenSize) var screenSize: ScreenSize + @Environment(\.verticalSizeClass) var verticalSizeClass: UserInterfaceSizeClass? + @Environment(\.horizontalSizeClass) var horizontalSizeClass: UserInterfaceSizeClass? + + var body: some View { + if horizontalSizeClass == .compact, verticalSizeClass == .regular { + phoneRegular + } else if horizontalSizeClass == .regular, verticalSizeClass == .compact { + phoneCompact + } else { + ipad + } + } + + var phoneRegular: some View { + ZStack { + VStack { + Spacer() + Image("OnbardingBackground", bundle: .main) + + Spacer() + + VStack(spacing: .medium) { + Text("Welcome to\nExample") + .largeTitle() + .onBackgroundHighEmphasisForegroundColor() + + Text("Welcome text") + .title2(.semibold) + .onBackgroundMediumEmphasisForegroundColor() + } + .paddingContent(.horizontal) + .multilineTextAlignment(.center) + + Spacer() + + Button("Contine") { + appStateService.completedOnbarding() + } + .buttonStyle(.primary) + .accent() + .paddingContent(.bottom) + .paddingContent(.horizontal) + .elevation(.z2) + } + + // VStack { + // + // Spacer() + // Image("OnbardingBackground", bundle: .main) + // Spacer() + // } + } + .ignoresSafeArea(edges: .top) + .background { + Color.backgroundSecondary.ignoresSafeArea() + } + } + + var phoneCompact: some View { + HStack(spacing: .zero) { + Image("OnbardingBackground", bundle: .main) + + Divider() + + VStack { + Spacer() + + VStack(spacing: .medium) { + Text("Welcome to\nExample") + .largeTitle() + .onBackgroundHighEmphasisForegroundColor() + + Text("Welcome text") + .title2(.semibold) + .onBackgroundMediumEmphasisForegroundColor() + } + .paddingContent(.horizontal) + .multilineTextAlignment(.center) + + Spacer() + + HStack { + Spacer() + Button("Contine") { + appStateService.completedOnbarding() + } + .buttonStyle(.primary) + .accent() + .elevation(.z2) + .frame(maxWidth: 300) + .paddingContent(.horizontal) + .paddingContent(.bottom) + Spacer() + } + } + } + .background { + Color.backgroundSecondary.ignoresSafeArea() + } + } + + var ipad: some View { + ZStack { + VStack { + VStack {} + .frame(maxHeight: screenSize.height < 1000 ? 320 : 490) + + Spacer() + + VStack(spacing: .medium) { + Text("Example") + .largeTitle() + .onBackgroundHighEmphasisForegroundColor() + + Text("Welcome text") + .title2(.semibold) + .onBackgroundMediumEmphasisForegroundColor() + } + .paddingContent(.horizontal) + .multilineTextAlignment(.center) + + Spacer() + + Button("Contine") { + appStateService.completedOnbarding() + } + .buttonStyle(.primary) + .accent() + .paddingContent(.bottom) + .paddingContent(.horizontal) + .elevation(.z2) + } + + VStack { + Surface { + Image("OnbardingBackground", bundle: .main) + .offset(y: -44) + .frame(width: screenSize.height < 1000 ? 320 : 490, height: screenSize.height < 1000 ? 320 : 490) + .cornerRadius(.xLarge) + .clipped() + } + .elevation(.z2) + .surfaceContentMargins(.zero) + .controlRadius(.xLarge) + .padding(.top, .large) + Spacer() + } + } + .background { + Color.backgroundSecondary.ignoresSafeArea() + } + } +} + +struct OnboardingView_Previews: PreviewProvider { + static var previews: some View { + OnboardingView() + } +} diff --git a/Package.swift b/Package.swift index c71d0ea..3d81c15 100644 --- a/Package.swift +++ b/Package.swift @@ -12,12 +12,7 @@ let productionDependencies: [PackageDescription.Package.Dependency] = { [ .package(url: "https://github.com/oversizedev/OversizeResources.git", .upToNextMajor(from: "2.0.0")), .package(url: "https://github.com/hmlongco/Factory.git", .upToNextMajor(from: "2.1.3")), .package(url: "https://github.com/lorenzofiamingo/swiftui-cached-async-image.git", .upToNextMajor(from: "2.1.1")), - /* - .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")) - */ + .package(url: "https://github.com/oversizedev/OversizeNetwork.git", .upToNextMajor(from: "0.6.0")) ] }() let developmentDependencies: [PackageDescription.Package.Dependency] = { [ @@ -30,10 +25,6 @@ let developmentDependencies: [PackageDescription.Package.Dependency] = { [ .package(name: "OversizeNetwork", path: "../OversizeNetwork"), .package(url: "https://github.com/lorenzofiamingo/swiftui-cached-async-image.git", .upToNextMajor(from: "2.1.1")), .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( @@ -81,14 +72,7 @@ let package = Package( .product(name: "OversizeServices", package: "OversizeServices"), .product(name: "CachedAsyncImage", package: "swiftui-cached-async-image"), .product(name: "OversizeCore", package: "OversizeCore"), - /* - .product(name: "OversizeNetwork", package: "OversizeNetwork"), - .product(name: "OpenAPIRuntime", package: "swift-openapi-runtime"), - .product( - name: "OpenAPIURLSession", - package: "swift-openapi-urlsession" - ), - */ + .product(name: "OversizeNetwork", package: "OversizeNetwork"), ] ), .target( diff --git a/Sources/OversizeAdsKit/AdView.swift b/Sources/OversizeAdsKit/AdView.swift index 2dbd06c..4a41716 100644 --- a/Sources/OversizeAdsKit/AdView.swift +++ b/Sources/OversizeAdsKit/AdView.swift @@ -6,6 +6,7 @@ import CachedAsyncImage import OversizeCore import OversizeKit +import OversizeNetwork import OversizeServices import OversizeUI import SwiftUI @@ -21,24 +22,36 @@ public struct AdView: View { } public var body: some View { - if isPremium { EmptyView() } else { + switch viewModel.state { + case .initial: + VStack {} + .task { + if !isPremium { + await viewModel.fetchAd() + } + } + case let .result(appAd) : #if os(iOS) Surface { isShowProduct.toggle() } label: { - premiumBanner + premiumBanner(appAd: appAd) } - .surfaceContentInsets(.xSmall) - .appStoreOverlay(isPresent: $isShowProduct, appId: viewModel.appAd?.id ?? "") + .surfaceContentMargins(.xSmall) + .appStoreOverlay(isPresent: $isShowProduct, appId: appAd.appStoreId) + #else EmptyView() #endif + + case .loading, .error: + EmptyView() } } - var premiumBanner: some View { + func premiumBanner(appAd: Components.Schemas.AppShort) -> some View { HStack(spacing: .zero) { - CachedAsyncImage(url: URL(string: "\(Info.links?.company.cdnString ?? "")/assets/apps/\(viewModel.appAd?.path ?? "")/icon.png"), urlCache: .imageCache, content: { + CachedAsyncImage(url: URL(string: "\(Info.links?.company.cdnString ?? "")/assets/apps/\(appAd.address)/icon.png"), urlCache: .imageCache, content: { $0 .resizable() .frame(width: 64, height: 64) @@ -62,7 +75,7 @@ public struct AdView: View { VStack(alignment: .leading, spacing: .xxxSmall) { HStack { - Text(viewModel.appAd?.name ?? "") + Text(appAd.name) .subheadline(.bold) .onSurfaceHighEmphasisForegroundColor() @@ -72,7 +85,7 @@ public struct AdView: View { } } - Text(viewModel.appAd?.title ?? "") + Text(appAd.title) .subheadline() .onSurfaceMediumEmphasisForegroundColor() } diff --git a/Sources/OversizeAdsKit/AdViewModel.swift b/Sources/OversizeAdsKit/AdViewModel.swift index ad0ee09..5beacfe 100644 --- a/Sources/OversizeAdsKit/AdViewModel.swift +++ b/Sources/OversizeAdsKit/AdViewModel.swift @@ -3,25 +3,39 @@ // AdViewModel.swift, created on 30.06.2023 // +import Factory +import OversizeNetwork import OversizeServices import SwiftUI -// import OversizeNetwork -// import Factory @MainActor public class AdViewModel: ObservableObject { - let appAd = Info.all?.apps.filter { $0.id != Info.app.appStoreID }.randomElement() - /* - @Injected(\.networkService) var networkService + @Injected(\.networkService) var networkService - 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 - } - } - */ + @Published var state = State.initial + + public init() {} + + public func fetchAd() async { + let result = await networkService.fetchApps() + switch result { + case let .success(ads): + guard let ad = ads.filter({ $0.appStoreId != Info.app.appStoreID }).randomElement() else { + state = .error(.custom(title: "Not ad")) + return + } + state = .result(ad) + case let .failure(error): + state = .error(error) + } + } +} + +extension AdViewModel { + enum State { + case initial + case loading + case result(Components.Schemas.AppShort) + case error(AppError) + } } diff --git a/Sources/OversizeCalendarKit/CreateEventScreen/AttachmentScreen/AttachmentView.swift b/Sources/OversizeCalendarKit/CreateEventScreen/AttachmentScreen/AttachmentView.swift index 74fa535..e9404bc 100644 --- a/Sources/OversizeCalendarKit/CreateEventScreen/AttachmentScreen/AttachmentView.swift +++ b/Sources/OversizeCalendarKit/CreateEventScreen/AttachmentScreen/AttachmentView.swift @@ -25,7 +25,7 @@ public struct AttachmentView: View { } } } - .surfaceContentRowInsets() + .surfaceContentRowMargins() } .backgroundSecondary() .leadingBar { diff --git a/Sources/OversizeCalendarKit/CreateEventScreen/CreateEventView.swift b/Sources/OversizeCalendarKit/CreateEventScreen/CreateEventView.swift index c6549ab..188b9cf 100644 --- a/Sources/OversizeCalendarKit/CreateEventScreen/CreateEventView.swift +++ b/Sources/OversizeCalendarKit/CreateEventScreen/CreateEventView.swift @@ -136,7 +136,7 @@ public struct CreateEventView: View { } .surfaceBorderColor(Color.surfaceSecondary) .surfaceBorderWidth(1) - .surfaceContentInsets(.init(horizontal: .xSmall, vertical: .xSmall)) + .surfaceContentMargins(.init(horizontal: .xSmall, vertical: .xSmall)) .controlRadius(.large) } @@ -191,11 +191,11 @@ public struct CreateEventView: View { viewModel.repitRule = .never viewModel.repitEndRule = .never } - .surfaceContentInsets(.init(horizontal: .small, vertical: .medium)) + .surfaceContentMargins(.init(horizontal: .small, vertical: .medium)) } .surfaceBorderColor(Color.surfaceSecondary) .surfaceBorderWidth(1) - .surfaceContentInsets(.zero) + .surfaceContentMargins(.zero) .controlRadius(.large) } } @@ -216,7 +216,7 @@ public struct CreateEventView: View { .rowClearButton(style: .onSurface) { viewModel.members.remove(email) } - .rowContentInset(.small) + .rowContentMargins(.small) .overlay(alignment: .bottomLeading) { Rectangle() .fillSurfaceSecondary() @@ -228,7 +228,7 @@ public struct CreateEventView: View { } .surfaceBorderColor(Color.surfaceSecondary) .surfaceBorderWidth(1) - .surfaceContentInsets(.zero) + .surfaceContentMargins(.zero) .controlRadius(.large) } } @@ -250,7 +250,7 @@ public struct CreateEventView: View { .rowClearButton(style: .onSurface) { viewModel.alarms.remove(alarm) } - .surfaceContentInsets(.init(horizontal: .small, vertical: .medium)) + .surfaceContentMargins(.init(horizontal: .small, vertical: .medium)) .overlay(alignment: .bottomLeading) { Rectangle() .fillSurfaceSecondary() @@ -262,7 +262,7 @@ public struct CreateEventView: View { } .surfaceBorderColor(Color.surfaceSecondary) .surfaceBorderWidth(1) - .surfaceContentInsets(.zero) + .surfaceContentMargins(.zero) .controlRadius(.large) } } @@ -285,7 +285,7 @@ public struct CreateEventView: View { viewModel.locationName = nil viewModel.location = nil } - .rowContentInset(.init(horizontal: .small, vertical: .xSmall)) + .rowContentMargins(.init(horizontal: .small, vertical: .xSmall)) } } @@ -308,7 +308,7 @@ public struct CreateEventView: View { } .surfaceBorderColor(Color.surfaceSecondary) .surfaceBorderWidth(1) - .surfaceContentInsets(.zero) + .surfaceContentMargins(.zero) .controlRadius(.large) } } diff --git a/Sources/OversizeKit/SettingsKit/Views/About/AboutView.swift b/Sources/OversizeKit/SettingsKit/Views/About/AboutView.swift index eb367ef..871647e 100644 --- a/Sources/OversizeKit/SettingsKit/Views/About/AboutView.swift +++ b/Sources/OversizeKit/SettingsKit/Views/About/AboutView.swift @@ -367,7 +367,7 @@ import SwiftUI } .buttonStyle(.scale) .frame(maxWidth: 300) - .innerPadding(.xSmall) + .controlMargin(.xSmall) .paddingContent(.horizontal) }