├── .ruby-version
├── .swift-version
├── .xcode-version
├── Mintfile
├── docs
├── favicon.ico
├── index
│ ├── data.mdb
│ ├── navigator.index
│ └── availability.index
├── metadata.json
├── img
│ ├── deprecated-icon.015b4f17.svg
│ ├── added-icon.d6f7e47d.svg
│ └── modified-icon.f496e73d.svg
├── js
│ ├── highlight-js-shell.dd7f411f.js
│ ├── highlight-js-json.471128d2.js
│ ├── highlight-js-diff.62d66733.js
│ └── highlight-js-http.163e45b6.js
└── theme-settings.json
├── Example
├── .swiftlint.yml
├── Tools
│ ├── ChatDeeplink (roomID 1, chatID 1).url
│ ├── ChatDeeplink (roomID 1, chatID 2).url
│ ├── ChatDeeplink (roomID 2, chatID 2).url
│ ├── ChatDeeplink (roomID 1, chatID 1).apns
│ ├── ChatDeeplink (roomID 1, chatID 2).apns
│ └── ChatDeeplink (roomID 2, chatID 2).apns
├── NivelirExample
│ ├── Resources
│ │ └── Assets.xcassets
│ │ │ ├── Contents.json
│ │ │ ├── Colors
│ │ │ ├── Contents.json
│ │ │ ├── Icon.colorset
│ │ │ │ └── Contents.json
│ │ │ ├── Title.colorset
│ │ │ │ └── Contents.json
│ │ │ ├── Unimportant.colorset
│ │ │ │ └── Contents.json
│ │ │ ├── Background.colorset
│ │ │ │ └── Contents.json
│ │ │ └── Important.colorset
│ │ │ │ └── Contents.json
│ │ │ ├── Images
│ │ │ ├── Contents.json
│ │ │ ├── User.imageset
│ │ │ │ ├── User.png
│ │ │ │ └── Contents.json
│ │ │ ├── Browser.imageset
│ │ │ │ ├── Browser.pdf
│ │ │ │ └── Contents.json
│ │ │ ├── Turtlerock.imageset
│ │ │ │ ├── turtlerock@2x.jpg
│ │ │ │ └── Contents.json
│ │ │ ├── Share.imageset
│ │ │ │ └── Contents.json
│ │ │ ├── MoreTab.imageset
│ │ │ │ └── Contents.json
│ │ │ ├── Chat.imageset
│ │ │ │ └── Contents.json
│ │ │ ├── Room.imageset
│ │ │ │ └── Contents.json
│ │ │ ├── Close.imageset
│ │ │ │ └── Contents.json
│ │ │ ├── Chevron.imageset
│ │ │ │ └── Contents.json
│ │ │ ├── RoomsTab.imageset
│ │ │ │ └── Contents.json
│ │ │ ├── ProfileTab.imageset
│ │ │ │ └── Contents.json
│ │ │ └── Unauthorized.imageset
│ │ │ │ └── Contents.json
│ │ │ ├── Brand Assets.brandassets
│ │ │ ├── App Icon.imagestack
│ │ │ │ ├── Back.imagestacklayer
│ │ │ │ │ ├── Contents.json
│ │ │ │ │ └── Content.imageset
│ │ │ │ │ │ └── Contents.json
│ │ │ │ ├── Front.imagestacklayer
│ │ │ │ │ ├── Contents.json
│ │ │ │ │ └── Content.imageset
│ │ │ │ │ │ └── Contents.json
│ │ │ │ ├── Middle.imagestacklayer
│ │ │ │ │ ├── Contents.json
│ │ │ │ │ └── Content.imageset
│ │ │ │ │ │ └── Contents.json
│ │ │ │ └── Contents.json
│ │ │ ├── App Icon - App Store.imagestack
│ │ │ │ ├── Back.imagestacklayer
│ │ │ │ │ ├── Contents.json
│ │ │ │ │ └── Content.imageset
│ │ │ │ │ │ └── Contents.json
│ │ │ │ ├── Front.imagestacklayer
│ │ │ │ │ ├── Contents.json
│ │ │ │ │ └── Content.imageset
│ │ │ │ │ │ └── Contents.json
│ │ │ │ ├── Middle.imagestacklayer
│ │ │ │ │ ├── Contents.json
│ │ │ │ │ └── Content.imageset
│ │ │ │ │ │ └── Contents.json
│ │ │ │ └── Contents.json
│ │ │ ├── Top Shelf Image.imageset
│ │ │ │ └── Contents.json
│ │ │ ├── Top Shelf Image Wide.imageset
│ │ │ │ └── Contents.json
│ │ │ └── Contents.json
│ │ │ └── AccentColor.colorset
│ │ │ └── Contents.json
│ ├── Helpers
│ │ ├── DI
│ │ │ ├── Storage
│ │ │ │ ├── ServiceStorage.swift
│ │ │ │ └── ServiceSharedStorage.swift
│ │ │ ├── Scope
│ │ │ │ ├── ServiceScope.swift
│ │ │ │ └── ServiceSharedScope.swift
│ │ │ ├── ServiceKey.swift
│ │ │ └── ServiceContainer.swift
│ │ ├── Reusable
│ │ │ ├── Reusable.swift
│ │ │ └── UITableView+Reusable.swift
│ │ ├── Colors.swift
│ │ └── Images.swift
│ ├── Services
│ │ ├── Authorization
│ │ │ ├── AuthorizationError.swift
│ │ │ ├── AuthorizationService.swift
│ │ │ └── DefaultAuthorizationService.swift
│ │ └── Profile
│ │ │ ├── ProfileError.swift
│ │ │ └── ProfileService.swift
│ ├── Screens
│ │ ├── MoreExampleList
│ │ │ ├── MoreExampleListModel.swift
│ │ │ └── MoreExampleListScreen.swift
│ │ ├── Authorization
│ │ │ ├── AuthorizationObserver.swift
│ │ │ └── AuthorizationScreen.swift
│ │ ├── Landmark
│ │ │ ├── LandmarkScreen.swift
│ │ │ ├── LandmarkMapView.swift
│ │ │ ├── LandmarkCircleImage.swift
│ │ │ └── LandmarkView.swift
│ │ ├── WhatsNewMore
│ │ │ └── WhatsNewMoreScreen.swift
│ │ ├── WhatsNew
│ │ │ └── WhatsNewScreen.swift
│ │ ├── RoomList
│ │ │ └── RoomListScreen.swift
│ │ ├── Chat
│ │ │ ├── ChatScreen.swift
│ │ │ └── ChatViewController.swift
│ │ ├── ChatList
│ │ │ └── ChatListScreen.swift
│ │ └── Profile
│ │ │ └── ProfileScreen.swift
│ ├── Routing
│ │ ├── Authorization
│ │ │ ├── ScreenAuthorizeActionServices.swift
│ │ │ ├── ScreenAuthorizeActionScreens.swift
│ │ │ └── ScreenAuthorizeActionObserver.swift
│ │ ├── Deeplinks
│ │ │ └── ChatDeeplinkPayload.swift
│ │ ├── Extensions
│ │ │ └── HUD+Extensions.swift
│ │ └── Sharing
│ │ │ └── SharingNivelirLinkItem.swift
│ ├── AppDelegate.swift
│ └── Dependencies
│ │ └── Services.swift
├── NivelirExample.xcodeproj
│ └── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── NivelirExample.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── Podfile
└── Podfile.lock
├── Sources
├── Tools
│ ├── Nullable.swift
│ ├── Extensions
│ │ ├── UIKit
│ │ │ ├── UIScreen+Extensions.swift
│ │ │ ├── UIApplication+Extensions.swift
│ │ │ ├── UIEdgeInsets+Extensions.swift
│ │ │ ├── CGSize+Extensions.swift
│ │ │ ├── UIView+Extensions.swift
│ │ │ ├── UIWindow+Extensions.swift
│ │ │ ├── CACornerMask+Extensions.swift
│ │ │ ├── UINavigationController+Extensions.swift
│ │ │ ├── UIScrollView+Extenions.swift
│ │ │ └── UITabBarController+Extensions.swift
│ │ ├── Character+Extensions.swift
│ │ ├── Dictionary+Extensions.swift
│ │ ├── Collection+Extensions.swift
│ │ ├── Optional+Extensions.swift
│ │ ├── Result+Extensions.swift
│ │ └── String+Extensions.swift
│ ├── URLQueryDecoder
│ │ ├── Options
│ │ │ ├── URLQueryDecodingOptions.swift
│ │ │ ├── URLQueryKeyDecodingStrategy.swift
│ │ │ ├── URLQueryDataDecodingStrategy.swift
│ │ │ ├── URLQueryNonConformingFloatDecodingStrategy.swift
│ │ │ └── URLQueryDateDecodingStrategy.swift
│ │ └── URLQueryComponent.swift
│ ├── DictionaryDecoder
│ │ └── Options
│ │ │ ├── DictionaryDecodingOptions.swift
│ │ │ ├── DictionaryKeyDecodingStrategy.swift
│ │ │ ├── DictionaryDataDecodingStrategy.swift
│ │ │ ├── DictionaryNonConformingFloatDecodingStrategy.swift
│ │ │ └── DictionaryDateDecodingStrategy.swift
│ ├── AnyCodingKey.swift
│ ├── ObjectAssociation.swift
│ └── NotificationObserver.swift
├── Screen
│ ├── Container
│ │ ├── Storage
│ │ │ ├── ScreenContainerStorage.swift
│ │ │ ├── ScreenContainerSharedStorage.swift
│ │ │ └── ScreenContainerWeakStorage.swift
│ │ ├── Extensions
│ │ │ ├── UIWindow+ScreenVisibleContainer.swift
│ │ │ ├── UINavigationController+ScreenIterableContainer.swift
│ │ │ ├── UIViewController+ScreenVisibleContainer.swift
│ │ │ └── UITabBarController+ScreenIterableContainer.swift
│ │ ├── ScreenIterableContainer.swift
│ │ ├── ScreenVisibleContainer.swift
│ │ └── ScreenContainer.swift
│ ├── Payload
│ │ ├── Extensions
│ │ │ ├── UIViewController+ScreenPayloadContainer.swift
│ │ │ └── ScreenPayloadedContainer+NSObject.swift
│ │ ├── ScreenPayload.swift
│ │ └── ScreenPayloadedContainer.swift
│ ├── Errors
│ │ ├── ScreenError.swift
│ │ ├── ScreenCanceledError.swift
│ │ ├── ScreenContainerNotFoundError.swift
│ │ ├── ScreenContainerNotSupportedError.swift
│ │ ├── ScreenContainerAlreadyPresentingError.swift
│ │ └── ScreenContainerTypeMismatchError.swift
│ ├── Actions
│ │ ├── Generic
│ │ │ ├── Refresh
│ │ │ │ ├── ScreenRefreshableContainer.swift
│ │ │ │ └── ScreenRefreshAction.swift
│ │ │ ├── ScreenFailAction.swift
│ │ │ └── ScreenGetAction.swift
│ │ ├── Stack
│ │ │ └── SetStack
│ │ │ │ ├── Modifiers
│ │ │ │ ├── ScreenStackModifier.swift
│ │ │ │ └── ScreenStackClearModifier.swift
│ │ │ │ └── Animations
│ │ │ │ ├── ScreenStackCustomAnimation.swift
│ │ │ │ └── ScreenStackTransitionAnimation.swift
│ │ ├── ScreenActionStorage.swift
│ │ ├── Window
│ │ │ ├── SetRoot
│ │ │ │ └── Animations
│ │ │ │ │ ├── ScreenRootCustomAnimation.swift
│ │ │ │ │ ├── ScreenRootAnimation.swift
│ │ │ │ │ └── ScreenRootTransitionAnimation.swift
│ │ │ ├── ScreenMakeKeyAction.swift
│ │ │ └── ScreenMakeKeyAndVisibleAction.swift
│ │ ├── Tabs
│ │ │ └── SelectTab
│ │ │ │ └── Animations
│ │ │ │ ├── ScreenTabCustomAnimation.swift
│ │ │ │ ├── ScreenTabAnimation.swift
│ │ │ │ └── ScreenTabTransitionAnimation.swift
│ │ └── Any
│ │ │ ├── AnyScreenActionBaseBox.swift
│ │ │ └── AnyScreenActionBox.swift
│ ├── Observation
│ │ ├── Storage
│ │ │ ├── ScreenObserverStorage.swift
│ │ │ ├── ScreenObserverSharedStorage.swift
│ │ │ └── ScreenObserverWeakStorage.swift
│ │ ├── ScreenObserver.swift
│ │ └── ScreenObserverToken.swift
│ ├── Route
│ │ ├── Aliases
│ │ │ ├── ScreenWindowRoute.swift
│ │ │ ├── ScreenModalRoute.swift
│ │ │ ├── ScreenTabsRoute.swift
│ │ │ └── ScreenStackRoute.swift
│ │ └── ScreenRouteConvertible.swift
│ ├── Navigator
│ │ ├── WindowProvider
│ │ │ ├── ScreenWindowProvider.swift
│ │ │ ├── ScreenKeyWindowProvider.swift
│ │ │ └── ScreenCustomWindowProvider.swift
│ │ ├── Logger
│ │ │ ├── ScreenLogger.swift
│ │ │ └── DefaultScreenLogger.swift
│ │ └── Iterator
│ │ │ ├── ScreenIterationResult.swift
│ │ │ └── ScreenIterationPredicate.swift
│ ├── Decorators
│ │ └── Modal
│ │ │ ├── ModalStyle
│ │ │ └── ScreenModalStyle.swift
│ │ │ └── ScreenTabBarItemDecorator.swift
│ ├── Any
│ │ ├── AnyScreenBaseBox.swift
│ │ └── AnyScreenBox.swift
│ └── ScreenKey.swift
├── Addons
│ ├── Sharing
│ │ ├── Activity
│ │ │ ├── SharingActivityCategory.swift
│ │ │ ├── SharingVisualActivity.swift
│ │ │ ├── SharingSilentActivity.swift
│ │ │ ├── SharingActivityType.swift
│ │ │ ├── SharingCustomActivity.swift
│ │ │ └── SharingActivity.swift
│ │ └── Item
│ │ │ └── SharingItem.swift
│ ├── DocumentPreview
│ │ └── Extensions
│ │ │ └── UIDocumentInteractionController+ScreenPayloadContainer.swift
│ ├── HUD
│ │ ├── Progress
│ │ │ ├── Footer
│ │ │ │ ├── ProgressFooter.swift
│ │ │ │ └── Empty
│ │ │ │ │ ├── ProgressEmptyFooter.swift
│ │ │ │ │ └── ProgressEmptyFooterView.swift
│ │ │ ├── Header
│ │ │ │ ├── ProgressHeader.swift
│ │ │ │ └── Empty
│ │ │ │ │ ├── ProgressEmptyHeader.swift
│ │ │ │ │ └── ProgressEmptyHeaderView.swift
│ │ │ ├── Indicator
│ │ │ │ ├── ProgressIndicator.swift
│ │ │ │ ├── Failure
│ │ │ │ │ └── ProgressFailureIndicator.swift
│ │ │ │ ├── Spinner
│ │ │ │ │ └── ProgressSpinnerIndicator.swift
│ │ │ │ ├── Success
│ │ │ │ │ └── ProgressSuccessIndicator.swift
│ │ │ │ ├── Activity
│ │ │ │ │ └── ProgressActivityIndicator.swift
│ │ │ │ └── Image
│ │ │ │ │ └── ProgressImageIndicator.swift
│ │ │ ├── Animation
│ │ │ │ ├── ProgressCustomAnimation.swift
│ │ │ │ └── ProgressAnimation.swift
│ │ │ └── Content
│ │ │ │ ├── AnyProgressContent.swift
│ │ │ │ └── ProgressContentView.swift
│ │ └── ScreenHideHUDAction.swift
│ ├── BottomSheet
│ │ ├── Detention
│ │ │ ├── BottomSheetDetentionDelegate.swift
│ │ │ └── Detent
│ │ │ │ ├── BottomSheetDetentContext.swift
│ │ │ │ └── BottomSheetDetentKey.swift
│ │ ├── BottomSheetBorder.swift
│ │ ├── Interaction
│ │ │ ├── BottomSheetInteractionState.swift
│ │ │ └── Interactions
│ │ │ │ ├── BottomSheetInteraction.swift
│ │ │ │ └── BottomSheetDismissedInteraction.swift
│ │ ├── Presentation
│ │ │ └── BottomSheetPresentationState.swift
│ │ ├── Animations
│ │ │ └── BottomSheetAnimationOptions.swift
│ │ ├── BottomSheetGrabber.swift
│ │ ├── Extensions
│ │ │ └── UIViewController+BottomSheet.swift
│ │ ├── BottomSheetRubberBandEffect.swift
│ │ ├── Dimming
│ │ │ └── BottomSheetDimming.swift
│ │ ├── Action
│ │ │ └── InvalidBottomSheetContainerError.swift
│ │ ├── BottomSheetShadow.swift
│ │ ├── BottomSheetCard.swift
│ │ └── Decorators
│ │ │ └── ScreenBottomSheetDecorator.swift
│ ├── MediaPicker
│ │ ├── MediaPickerImageExportPreset.swift
│ │ ├── Errors
│ │ │ ├── UnavailableMediaPickerSourceError.swift
│ │ │ ├── UnavailableMediaPickerTypesError.swift
│ │ │ └── MediaPickerSourceAccessDeniedError.swift
│ │ ├── MediaPickerManager.swift
│ │ ├── MediaPickerProxy.swift
│ │ └── MediaPickerCameraSettings.swift
│ ├── Safari
│ │ └── InvalidSafariURLError.swift
│ ├── URL
│ │ ├── StoreApp
│ │ │ └── InvalidStoreAppIDError.swift
│ │ ├── Call
│ │ │ └── InvalidCallParametersError.swift
│ │ ├── Mail
│ │ │ └── InvalidMailParametersError.swift
│ │ ├── Settings
│ │ │ ├── InvalidOpenSettingsURLError.swift
│ │ │ └── ScreenOpenSettingsAction.swift
│ │ └── FailedToOpenURLError.swift
│ ├── StoreProduct
│ │ ├── InvalidStoreProductIDError.swift
│ │ └── StoreProductManager.swift
│ └── Alert
│ │ ├── AlertTextField.swift
│ │ └── AlertTextFieldsManager.swift
├── Deeplink
│ ├── DeeplinkResult.swift
│ ├── Errors
│ │ ├── DeeplinkError.swift
│ │ ├── DeeplinkDecodingError.swift
│ │ ├── DeeplinkInvalidContextError.swift
│ │ ├── DeeplinkInvalidScreensError.swift
│ │ └── DeeplinkWarningsError.swift
│ ├── DeeplinkType.swift
│ ├── DeeplinkStorage.swift
│ ├── Extensions
│ │ ├── UNNotificationResponse+Extensions.swift
│ │ └── UIApplicationShortcutItem+Extensions.swift
│ ├── URL
│ │ └── Errors
│ │ │ └── URLDeeplinkInvalidComponentsError.swift
│ ├── AnyDeeplink.swift
│ ├── Notification
│ │ └── Errors
│ │ │ └── NotificationDeeplinkInvalidUserInfoError.swift
│ └── DeeplinkInterceptor.swift
└── Info.plist
├── Tests
├── .swiftlint.yml
└── Info.plist
├── Gemfile
├── Nivelir.xcodeproj
└── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ └── IDEWorkspaceChecks.plist
├── Scripts
├── swiftlint.sh
├── Helpers
│ ├── script-paths.sh
│ └── script-run.sh
├── Bootstrap
│ ├── congratulations.sh
│ ├── welcome.sh
│ ├── mintfile.sh
│ ├── gemfile.sh
│ ├── spm.sh
│ ├── bundler.sh
│ ├── macos.sh
│ ├── swift.sh
│ ├── swiftenv.sh
│ ├── ruby.sh
│ ├── mint.sh
│ └── xcode.sh
├── bootstrap.sh
└── generate-docs.sh
├── Dangerfile
├── Package.swift
├── Nivelir.podspec
├── .gitignore
└── LICENSE
/.ruby-version:
--------------------------------------------------------------------------------
1 | 3.1.0
2 |
--------------------------------------------------------------------------------
/.swift-version:
--------------------------------------------------------------------------------
1 | 6.0
2 |
--------------------------------------------------------------------------------
/.xcode-version:
--------------------------------------------------------------------------------
1 | 16.0
2 |
--------------------------------------------------------------------------------
/Mintfile:
--------------------------------------------------------------------------------
1 | realm/SwiftLint@0.52.2
2 |
--------------------------------------------------------------------------------
/docs/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hhru/Nivelir/HEAD/docs/favicon.ico
--------------------------------------------------------------------------------
/docs/index/data.mdb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hhru/Nivelir/HEAD/docs/index/data.mdb
--------------------------------------------------------------------------------
/Example/.swiftlint.yml:
--------------------------------------------------------------------------------
1 | disabled_rules:
2 | - explicit_acl
3 | - explicit_top_level_acl
--------------------------------------------------------------------------------
/Example/Tools/ChatDeeplink (roomID 1, chatID 1).url:
--------------------------------------------------------------------------------
1 | nivelir://chat?room_id=1&chat_id=1
2 |
--------------------------------------------------------------------------------
/Example/Tools/ChatDeeplink (roomID 1, chatID 2).url:
--------------------------------------------------------------------------------
1 | nivelir://chat?room_id=1&chat_id=2
2 |
--------------------------------------------------------------------------------
/Example/Tools/ChatDeeplink (roomID 2, chatID 2).url:
--------------------------------------------------------------------------------
1 | nivelir://chat?room_id=2&chat_id=2
2 |
--------------------------------------------------------------------------------
/docs/index/navigator.index:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hhru/Nivelir/HEAD/docs/index/navigator.index
--------------------------------------------------------------------------------
/docs/index/availability.index:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hhru/Nivelir/HEAD/docs/index/availability.index
--------------------------------------------------------------------------------
/docs/metadata.json:
--------------------------------------------------------------------------------
1 | {"bundleDisplayName":"Nivelir","bundleIdentifier":"ru.hh.Nivelir","schemaVersion":{"major":0,"minor":1,"patch":0}}
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Colors/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Images/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Helpers/DI/Storage/ServiceStorage.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | public protocol ServiceStorage {
4 |
5 | var service: Any? { get }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Services/Authorization/AuthorizationError.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | enum AuthorizationError: Error {
4 |
5 | case unavailable
6 | }
7 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Services/Profile/ProfileError.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | enum ProfileError: Error {
4 |
5 | case unauthorized
6 | case unavailable
7 | }
8 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Helpers/DI/Scope/ServiceScope.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | protocol ServiceScope {
4 |
5 | func storage(for service: Any) -> ServiceStorage
6 | }
7 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Screens/MoreExampleList/MoreExampleListModel.swift:
--------------------------------------------------------------------------------
1 | struct MoreExampleListModel {
2 | let title: String
3 | let didSelectHandler: () -> Void
4 | }
5 |
--------------------------------------------------------------------------------
/Sources/Tools/Nullable.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | internal protocol Nullable {
4 |
5 | static var none: Self { get }
6 | }
7 |
8 | extension Optional: Nullable { }
9 |
--------------------------------------------------------------------------------
/Sources/Screen/Container/Storage/ScreenContainerStorage.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | internal protocol ScreenContainerStorage {
4 |
5 | var value: ScreenContainer? { get }
6 | }
7 |
--------------------------------------------------------------------------------
/Sources/Addons/Sharing/Activity/SharingActivityCategory.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit) && os(iOS)
2 | import UIKit
3 |
4 | public typealias SharingActivityCategory = UIActivity.Category
5 | #endif
6 |
--------------------------------------------------------------------------------
/Sources/Screen/Payload/Extensions/UIViewController+ScreenPayloadContainer.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | extension UIViewController: ScreenPayloadedContainer { }
5 | #endif
6 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Images/User.imageset/User.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hhru/Nivelir/HEAD/Example/NivelirExample/Resources/Assets.xcassets/Images/User.imageset/User.png
--------------------------------------------------------------------------------
/Sources/Deeplink/DeeplinkResult.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | internal enum DeeplinkResult {
4 |
5 | case success(DeeplinkStorage)
6 | case failure(Error)
7 | case warning(Error)
8 | }
9 |
--------------------------------------------------------------------------------
/Sources/Screen/Errors/ScreenError.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// A protocol representing an error that occurs during navigation.
4 | public protocol ScreenError: Error, CustomStringConvertible { }
5 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Brand Assets.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Brand Assets.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Brand Assets.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Images/Browser.imageset/Browser.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hhru/Nivelir/HEAD/Example/NivelirExample/Resources/Assets.xcassets/Images/Browser.imageset/Browser.pdf
--------------------------------------------------------------------------------
/Example/NivelirExample/Routing/Authorization/ScreenAuthorizeActionServices.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | protocol ScreenAuthorizeActionServices {
4 |
5 | func authorizationService() -> AuthorizationService
6 | }
7 |
--------------------------------------------------------------------------------
/Tests/.swiftlint.yml:
--------------------------------------------------------------------------------
1 | disabled_rules:
2 | - explicit_acl
3 | - explicit_top_level_acl
4 | - file_length
5 | - function_body_length
6 | - implicitly_unwrapped_optional
7 | - nesting
8 | - type_body_length
9 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Brand Assets.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Brand Assets.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Brand Assets.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Images/Turtlerock.imageset/turtlerock@2x.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hhru/Nivelir/HEAD/Example/NivelirExample/Resources/Assets.xcassets/Images/Turtlerock.imageset/turtlerock@2x.jpg
--------------------------------------------------------------------------------
/Sources/Tools/Extensions/UIKit/UIScreen+Extensions.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | extension UIScreen {
5 |
6 | internal var pixelSize: CGFloat {
7 | 1.0 / scale
8 | }
9 | }
10 | #endif
11 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | gem 'xcode-install'
4 |
5 | gem 'cocoapods'
6 | gem 'xcpretty'
7 | gem 'xcpretty-json-formatter'
8 |
9 | gem "danger"
10 | gem 'danger-xcode_summary'
11 | gem 'danger-swiftlint'
12 |
--------------------------------------------------------------------------------
/Nivelir.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Sources/Addons/DocumentPreview/Extensions/UIDocumentInteractionController+ScreenPayloadContainer.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit) && os(iOS)
2 | import UIKit
3 |
4 | extension UIDocumentInteractionController: ScreenPayloadedContainer { }
5 | #endif
6 |
--------------------------------------------------------------------------------
/Sources/Screen/Actions/Generic/Refresh/ScreenRefreshableContainer.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | @MainActor
4 | public protocol ScreenRefreshableContainer: ScreenContainer {
5 |
6 | func refresh(completion: @escaping () -> Void)
7 | }
8 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Routing/Authorization/ScreenAuthorizeActionScreens.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import Nivelir
3 |
4 | @MainActor
5 | protocol ScreenAuthorizeActionScreens {
6 |
7 | func showAuthorizationRoute() -> ScreenWindowRoute
8 | }
9 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Screens/Authorization/AuthorizationObserver.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import Nivelir
3 |
4 | @MainActor
5 | public protocol AuthorizationObserver: ScreenObserver {
6 |
7 | func authorizationFinished(isAuthorized: Bool)
8 | }
9 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Helpers/DI/Storage/ServiceSharedStorage.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | struct ServiceSharedStorage: ServiceStorage {
4 |
5 | let service: Any?
6 |
7 | init(service: Any) {
8 | self.service = service
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/Sources/Screen/Observation/Storage/ScreenObserverStorage.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | @MainActor
4 | internal protocol ScreenObserverStorage: AnyObject {
5 |
6 | var predicate: ScreenObserverPredicate { get }
7 | var value: ScreenObserver? { get }
8 | }
9 |
--------------------------------------------------------------------------------
/Scripts/swiftlint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | if [[ "${SKIP_SWIFTLINT}" == "YES" ]]; then
4 | exit 0
5 | fi
6 |
7 | readonly helpers_path="$( cd "$( dirname "$0" )" && pwd )/Helpers"
8 |
9 | source "${helpers_path}/script-run.sh"
10 | run swiftlint --quiet || true
11 |
--------------------------------------------------------------------------------
/Scripts/Helpers/script-paths.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | if [ -z "${helpers_path}" ]; then
4 | readonly helpers_path="$( cd "$( dirname "$0" )" && pwd )"
5 | fi
6 |
7 | readonly tools_path="$( cd "${helpers_path}/../" && pwd )"
8 | readonly root_path="$( cd "${tools_path}/../" && pwd )"
9 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Helpers/Reusable/Reusable.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | protocol Reusable {
4 | static var reuseIdentifier: String { get }
5 | }
6 |
7 | extension Reusable {
8 | static var reuseIdentifier: String {
9 | "\(Self.self)"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Screens/Landmark/LandmarkScreen.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 | import Nivelir
3 |
4 | struct LandmarkScreen: Screen {
5 |
6 | func build(navigator: ScreenNavigator) -> UIViewController {
7 | UIHostingController(rootView: LandmarkView())
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Images/Share.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Share.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Sources/Addons/HUD/Progress/Footer/ProgressFooter.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import Foundation
3 |
4 | /// A type that contains the data to be displayed on the footer of progress.
5 | ///
6 | /// - SeeAlso: ``ProgressContent``
7 | public protocol ProgressFooter: ProgressContent { }
8 | #endif
9 |
--------------------------------------------------------------------------------
/Sources/Addons/HUD/Progress/Header/ProgressHeader.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import Foundation
3 |
4 | /// A type that contains the data to be displayed on the header of progress.
5 | ///
6 | /// - SeeAlso: ``ProgressContent``
7 | public protocol ProgressHeader: ProgressContent { }
8 | #endif
9 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Images/Browser.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Browser.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Example/NivelirExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Sources/Addons/HUD/Progress/Indicator/ProgressIndicator.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import Foundation
3 |
4 | /// A type that contains the data to be displayed on the indicator of progress.
5 | ///
6 | /// - SeeAlso: ``ProgressContent``
7 | public protocol ProgressIndicator: ProgressContent { }
8 | #endif
9 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Brand Assets.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Brand Assets.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Brand Assets.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Scripts/Bootstrap/congratulations.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | readonly script_path="$( cd "$( dirname "$0" )" && pwd )"
4 |
5 | source "${script_path}/common.sh"
6 |
7 | echo ""
8 | echo "${congratulations_style}Congratulations!${default_style} Setting up the development environment successfully completed 🥳"
9 | echo ""
10 |
--------------------------------------------------------------------------------
/Sources/Screen/Route/Aliases/ScreenWindowRoute.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | /// Alias for the root route whose container type is `UIWindow`.
5 | ///
6 | /// - SeeAlso: `ScreenRoute`
7 | /// - SeeAlso: `ScreenRootRoute`
8 | public typealias ScreenWindowRoute = ScreenRootRoute
9 | #endif
10 |
--------------------------------------------------------------------------------
/Example/NivelirExample.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Example/NivelirExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Routing/Deeplinks/ChatDeeplinkPayload.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | struct ChatDeeplinkPayload: Decodable {
4 |
5 | enum CodingKeys: String, CodingKey {
6 | case roomID = "room_id"
7 | case chatID = "chat_id"
8 | }
9 |
10 | let roomID: Int
11 | let chatID: Int
12 | }
13 |
--------------------------------------------------------------------------------
/Sources/Tools/Extensions/Character+Extensions.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | extension Character {
4 |
5 | internal static let leftSquareBracket = Self("[")
6 | internal static let rightSquareBracket = Self("]")
7 | internal static let ampersand = Self("&")
8 | internal static let equals = Self("=")
9 | }
10 |
--------------------------------------------------------------------------------
/Nivelir.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Scripts/Bootstrap/welcome.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | readonly script_path="$( cd "$( dirname "$0" )" && pwd )"
4 |
5 | source "${script_path}/common.sh"
6 |
7 | echo "${default_style}"
8 | echo "This script will set up your development environment."
9 | echo "This might take a few minutes. Please don't interrupt the script."
10 | echo ""
--------------------------------------------------------------------------------
/Sources/Screen/Route/Aliases/ScreenModalRoute.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | /// Alias for the root route whose container type is `UIViewController`.
5 | ///
6 | /// - SeeAlso: `ScreenRoute`
7 | /// - SeeAlso: `ScreenRootRoute`
8 | public typealias ScreenModalRoute = ScreenRootRoute
9 | #endif
10 |
--------------------------------------------------------------------------------
/Sources/Tools/Extensions/Dictionary+Extensions.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | extension Dictionary {
4 |
5 | internal func updatingValue(_ value: Value, forKey key: Key) -> Self {
6 | var dictionary = self
7 |
8 | dictionary.updateValue(value, forKey: key)
9 |
10 | return dictionary
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Sources/Screen/Route/Aliases/ScreenTabsRoute.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | /// Alias for the root route whose container type is `UITabBarController`.
5 | ///
6 | /// - SeeAlso: `ScreenRoute`
7 | /// - SeeAlso: `ScreenRootRoute`
8 | public typealias ScreenTabsRoute = ScreenRootRoute
9 | #endif
10 |
--------------------------------------------------------------------------------
/Sources/Tools/Extensions/Collection+Extensions.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | extension Collection {
4 |
5 | internal var nonEmpty: Self? {
6 | isEmpty ? nil : self
7 | }
8 |
9 | internal subscript(safe index: Index) -> Iterator.Element? {
10 | indices.contains(index) ? self[index] : nil
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Sources/Tools/Extensions/Optional+Extensions.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | extension Optional {
4 |
5 | internal var isNil: Bool {
6 | self == nil
7 | }
8 | }
9 |
10 | extension Optional where Wrapped: Collection {
11 |
12 | internal var isEmptyOrNil: Bool {
13 | self?.isEmpty ?? true
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Helpers/DI/Scope/ServiceSharedScope.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | struct ServiceSharedScope: ServiceScope {
4 |
5 | static let `default` = Self()
6 |
7 | private init() { }
8 |
9 | func storage(for service: Any) -> ServiceStorage {
10 | ServiceSharedStorage(service: service)
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Example/Tools/ChatDeeplink (roomID 1, chatID 1).apns:
--------------------------------------------------------------------------------
1 | {
2 | "Simulator Target Bundle": "ru.hh.Nivelir-Example",
3 | "aps": {
4 | "alert": {
5 | "body": "User sent a new message in Room #1 – Chat 1",
6 | "title": "New message"
7 | }
8 | },
9 | "room_id": 1,
10 | "chat_id": 1
11 | }
12 |
--------------------------------------------------------------------------------
/Example/Tools/ChatDeeplink (roomID 1, chatID 2).apns:
--------------------------------------------------------------------------------
1 | {
2 | "Simulator Target Bundle": "ru.hh.Nivelir-Example",
3 | "aps": {
4 | "alert": {
5 | "body": "User sent a new message in Room #1 – Chat 2",
6 | "title": "New message"
7 | }
8 | },
9 | "room_id": 1,
10 | "chat_id": 2
11 | }
12 |
--------------------------------------------------------------------------------
/Example/Tools/ChatDeeplink (roomID 2, chatID 2).apns:
--------------------------------------------------------------------------------
1 | {
2 | "Simulator Target Bundle": "ru.hh.Nivelir-Example",
3 | "aps": {
4 | "alert": {
5 | "body": "User sent a new message in Room #2 – Chat 2",
6 | "title": "New message"
7 | }
8 | },
9 | "room_id": 2,
10 | "chat_id": 2
11 | }
12 |
--------------------------------------------------------------------------------
/Example/NivelirExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Sources/Screen/Route/Aliases/ScreenStackRoute.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | /// Alias for the root route whose container type is `UINavigationController`.
5 | ///
6 | /// - SeeAlso: `ScreenRoute`
7 | /// - SeeAlso: `ScreenRootRoute`
8 | public typealias ScreenStackRoute = ScreenRootRoute
9 | #endif
10 |
--------------------------------------------------------------------------------
/Sources/Addons/Sharing/Activity/SharingVisualActivity.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit) && os(iOS)
2 | import UIKit
3 |
4 | public protocol SharingVisualActivity: SharingCustomActivity {
5 |
6 | func prepare(
7 | for items: [SharingItem],
8 | completion: @escaping (_ completed: Bool) -> Void
9 | ) -> AnyModalScreen
10 | }
11 | #endif
12 |
--------------------------------------------------------------------------------
/Example/Podfile:
--------------------------------------------------------------------------------
1 | inhibit_all_warnings!
2 |
3 | target 'NivelirExample' do
4 | platform :ios, '15.0'
5 | use_frameworks!
6 |
7 | pod 'Nivelir', :path => ".."
8 | pod 'SnapKit'
9 | end
10 |
11 | target 'NivelirExample-tvOS' do
12 | platform :tvos, '15.0'
13 | use_frameworks!
14 |
15 | pod 'Nivelir', :path => ".."
16 | pod 'SnapKit'
17 | end
18 |
--------------------------------------------------------------------------------
/Sources/Addons/BottomSheet/Detention/BottomSheetDetentionDelegate.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import Foundation
3 |
4 | @MainActor
5 | internal protocol BottomSheetDetentionDelegate: AnyObject {
6 |
7 | func bottomSheetCanEndEditing() -> Bool
8 | func bottomSheetDidChangeSelectedDetentKey(to detentKey: BottomSheetDetentKey?)
9 | }
10 | #endif
11 |
--------------------------------------------------------------------------------
/Sources/Screen/Actions/Stack/SetStack/Modifiers/ScreenStackModifier.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | @MainActor
5 | public protocol ScreenStackModifier: CustomStringConvertible {
6 |
7 | func perform(
8 | stack: [UIViewController],
9 | navigator: ScreenNavigator
10 | ) throws -> [UIViewController]
11 | }
12 | #endif
13 |
--------------------------------------------------------------------------------
/Sources/Tools/Extensions/Result+Extensions.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | extension Result {
4 |
5 | internal func ignoringValue() -> Result {
6 | map { _ in Void() }
7 | }
8 | }
9 |
10 | extension Result where Success == Void {
11 |
12 | internal static var success: Self {
13 | .success(Void())
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Images/User.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "User.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | },
12 | "properties" : {
13 | "preserves-vector-representation" : true
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Screens/WhatsNewMore/WhatsNewMoreScreen.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Nivelir
3 |
4 | struct WhatsNewMoreScreen: Screen {
5 |
6 | func build(navigator: ScreenNavigator) -> UIViewController {
7 | WhatsNewMoreViewController(
8 | screenKey: key,
9 | screenNavigator: navigator
10 | )
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Brand Assets.brandassets/Top Shelf Image.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "tv",
9 | "scale" : "2x"
10 | }
11 | ],
12 | "info" : {
13 | "author" : "xcode",
14 | "version" : 1
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Images/MoreTab.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "MoreTab.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | },
12 | "properties" : {
13 | "preserves-vector-representation" : true
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Brand Assets.brandassets/Top Shelf Image Wide.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "tv",
9 | "scale" : "2x"
10 | }
11 | ],
12 | "info" : {
13 | "author" : "xcode",
14 | "version" : 1
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Sources/Addons/BottomSheet/Detention/Detent/BottomSheetDetentContext.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | @MainActor
5 | public protocol BottomSheetDetentContext {
6 |
7 | var presentedViewController: UIViewController { get }
8 | var containerTraitCollection: UITraitCollection { get }
9 | var maximumDetentValue: CGFloat { get }
10 | }
11 | #endif
12 |
--------------------------------------------------------------------------------
/Sources/Deeplink/Errors/DeeplinkError.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// A protocol representing an error that occurs when processing deeplinks.
4 | public protocol DeeplinkError: Error, CustomStringConvertible {
5 |
6 | var isWarning: Bool { get }
7 | }
8 |
9 | extension DeeplinkError {
10 |
11 | public var isWarning: Bool {
12 | false
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Helpers/Colors.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | enum Colors {
4 |
5 | static let background = UIColor(named: "Background")!
6 | static let unimportant = UIColor(named: "Unimportant")!
7 | static let important = UIColor(named: "Important")!
8 | static let title = UIColor(named: "Title")!
9 | static let icon = UIColor(named: "Title")!
10 | }
11 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Services/Authorization/AuthorizationService.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | protocol AuthorizationService: Sendable {
4 |
5 | var isAuthorized: Bool { get }
6 |
7 | @MainActor
8 | func login(
9 | phoneNumber: String,
10 | completion: @escaping (_ result: Result) -> Void
11 | )
12 |
13 | func logout()
14 | }
15 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Services/Profile/ProfileService.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | protocol ProfileService {
4 |
5 | @MainActor
6 | func uploadPhoto(
7 | image: UIImage,
8 | progress: @MainActor @Sendable @escaping (_ ratio: CGFloat) -> Void,
9 | completion: @MainActor @Sendable @escaping (_ result: Result) -> Void
10 | )
11 | }
12 |
--------------------------------------------------------------------------------
/Sources/Deeplink/DeeplinkType.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// Different types of deep links.
4 | @frozen
5 | public enum DeeplinkType {
6 |
7 | /// A ``Deeplink`` handled from a URL.
8 | case url
9 |
10 | /// A ``Deeplink`` handled from a Notification.
11 | case notification
12 |
13 | /// A ``Deeplink`` handled from a Shortcut.
14 | case shortcut
15 | }
16 |
--------------------------------------------------------------------------------
/Sources/Addons/HUD/Progress/Footer/Empty/ProgressEmptyFooter.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import Foundation
3 |
4 | /// Empty footer of progress.
5 | public struct ProgressEmptyFooter: ProgressFooter {
6 |
7 | public typealias View = ProgressEmptyFooterView
8 |
9 | /// Default instance
10 | public static let `default` = Self()
11 |
12 | private init() { }
13 | }
14 | #endif
15 |
--------------------------------------------------------------------------------
/Sources/Addons/HUD/Progress/Header/Empty/ProgressEmptyHeader.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import Foundation
3 |
4 | /// Empty header of progress.
5 | public struct ProgressEmptyHeader: ProgressHeader {
6 |
7 | public typealias View = ProgressEmptyHeaderView
8 |
9 | /// Default instance
10 | public static let `default` = Self()
11 |
12 | private init() { }
13 | }
14 | #endif
15 |
--------------------------------------------------------------------------------
/Sources/Screen/Navigator/WindowProvider/ScreenWindowProvider.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | /// A type that provides an instance of `UIWindow` for navigating and searching containers.
5 | @MainActor
6 | public protocol ScreenWindowProvider {
7 |
8 | /// The `UIWindow` for navigating and searching for containers.
9 | var window: UIWindow? { get }
10 | }
11 | #endif
12 |
--------------------------------------------------------------------------------
/Sources/Screen/Actions/ScreenActionStorage.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | public final class ScreenActionStorage {
4 |
5 | public private(set) var state: [State] = []
6 |
7 | internal init() { }
8 |
9 | public func storeState(_ state: State) {
10 | self.state.append(state)
11 | }
12 |
13 | public func clear() {
14 | state.removeAll()
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Sources/Screen/Container/Extensions/UIWindow+ScreenVisibleContainer.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | extension UIWindow: ScreenVisibleContainer {
5 |
6 | /// A Boolean value indicating whether the container is visible.
7 | ///
8 | /// Returns `true` if the window is not hidden.
9 | public var isVisible: Bool {
10 | !isHidden
11 | }
12 | }
13 | #endif
14 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Brand Assets.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "tv",
9 | "scale" : "2x"
10 | }
11 | ],
12 | "info" : {
13 | "author" : "xcode",
14 | "version" : 1
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Brand Assets.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "tv",
9 | "scale" : "2x"
10 | }
11 | ],
12 | "info" : {
13 | "author" : "xcode",
14 | "version" : 1
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Brand Assets.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "tv",
9 | "scale" : "2x"
10 | }
11 | ],
12 | "info" : {
13 | "author" : "xcode",
14 | "version" : 1
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Sources/Addons/Sharing/Activity/SharingSilentActivity.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit) && os(iOS)
2 | import UIKit
3 |
4 | public protocol SharingSilentActivity: SharingCustomActivity, Sendable {
5 |
6 | @MainActor
7 | func perform(
8 | for items: [SharingItem],
9 | navigator: ScreenNavigator,
10 | completion: @escaping (_ completed: Bool) -> Void
11 | )
12 | }
13 | #endif
14 |
--------------------------------------------------------------------------------
/Sources/Screen/Actions/Window/SetRoot/Animations/ScreenRootCustomAnimation.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | @MainActor
5 | public protocol ScreenRootCustomAnimation {
6 |
7 | func animate(
8 | container: UIWindow,
9 | from root: UIViewController?,
10 | to newRoot: UIViewController,
11 | completion: @escaping () -> Void
12 | )
13 | }
14 | #endif
15 |
--------------------------------------------------------------------------------
/Sources/Tools/Extensions/UIKit/UIApplication+Extensions.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | extension UIApplication {
5 |
6 | internal var firstKeyWindow: UIWindow? {
7 | connectedScenes
8 | .lazy
9 | .compactMap { $0 as? UIWindowScene }
10 | .flatMap { $0.windows }
11 | .first { $0.isKeyWindow }
12 | }
13 | }
14 | #endif
15 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Images/Chat.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Chat.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | },
12 | "properties" : {
13 | "preserves-vector-representation" : true,
14 | "template-rendering-intent" : "template"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Images/Room.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Room.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | },
12 | "properties" : {
13 | "preserves-vector-representation" : true,
14 | "template-rendering-intent" : "template"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Images/Close.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Close.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | },
12 | "properties" : {
13 | "preserves-vector-representation" : true,
14 | "template-rendering-intent" : "template"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Screens/WhatsNew/WhatsNewScreen.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Nivelir
3 |
4 | struct WhatsNewScreen: Screen {
5 |
6 | let screens: Screens
7 |
8 | func build(navigator: ScreenNavigator) -> UIViewController {
9 | WhatsNewViewController(
10 | screens: screens,
11 | screenKey: key,
12 | screenNavigator: navigator
13 | )
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Sources/Screen/Container/Storage/ScreenContainerSharedStorage.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | internal struct ScreenContainerSharedStorage: ScreenContainerStorage {
4 |
5 | private let container: ScreenContainer
6 |
7 | internal var value: ScreenContainer? {
8 | container
9 | }
10 |
11 | internal init(_ container: ScreenContainer) {
12 | self.container = container
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Sources/Screen/Observation/ScreenObserver.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// Protocol-Mark for Observers.
4 | ///
5 | /// The protocol is inherited by other protocols that the observers will implement.
6 | ///
7 | /// ```swift
8 | /// protocol EmployerReviewObserver: ScreenObserver {
9 | /// func employerFeedbackRead(employerReviewID: Int)
10 | /// }
11 | /// ```
12 | public protocol ScreenObserver: AnyObject { }
13 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Brand Assets.brandassets/App Icon.imagestack/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | },
6 | "layers" : [
7 | {
8 | "filename" : "Front.imagestacklayer"
9 | },
10 | {
11 | "filename" : "Middle.imagestacklayer"
12 | },
13 | {
14 | "filename" : "Back.imagestacklayer"
15 | }
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Images/Chevron.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Chevron.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | },
12 | "properties" : {
13 | "preserves-vector-representation" : true,
14 | "template-rendering-intent" : "template"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Images/RoomsTab.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "RoomsTab.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | },
12 | "properties" : {
13 | "preserves-vector-representation" : true,
14 | "template-rendering-intent" : "template"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Sources/Screen/Actions/Tabs/SelectTab/Animations/ScreenTabCustomAnimation.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | @MainActor
5 | public protocol ScreenTabCustomAnimation {
6 |
7 | func animate(
8 | container: UITabBarController,
9 | from selectedTab: UIViewController,
10 | to newSelectedTab: UIViewController,
11 | completion: @escaping () -> Void
12 | )
13 | }
14 | #endif
15 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Images/ProfileTab.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "ProfileTab.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | },
12 | "properties" : {
13 | "preserves-vector-representation" : true,
14 | "template-rendering-intent" : "template"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Screens/MoreExampleList/MoreExampleListScreen.swift:
--------------------------------------------------------------------------------
1 | import Nivelir
2 |
3 | struct MoreExampleListScreen: Screen {
4 |
5 | let screens: Screens
6 |
7 | func build(navigator: ScreenNavigator) -> UIViewController {
8 | MoreExampleListViewController(
9 | screens: screens,
10 | screenKey: key,
11 | screenNavigator: navigator
12 | )
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Sources/Screen/Container/Extensions/UINavigationController+ScreenIterableContainer.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | extension UINavigationController: ScreenIterableContainer {
5 |
6 | /// Returns nested containers from the navigation stack.
7 | ///
8 | /// - SeeAlso: `viewControllers`
9 | public var nestedContainers: [ScreenContainer] {
10 | viewControllers
11 | }
12 | }
13 | #endif
14 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Images/Unauthorized.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Unauthorized.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | },
12 | "properties" : {
13 | "preserves-vector-representation" : true,
14 | "template-rendering-intent" : "template"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Brand Assets.brandassets/App Icon - App Store.imagestack/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | },
6 | "layers" : [
7 | {
8 | "filename" : "Front.imagestacklayer"
9 | },
10 | {
11 | "filename" : "Middle.imagestacklayer"
12 | },
13 | {
14 | "filename" : "Back.imagestacklayer"
15 | }
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/Sources/Screen/Navigator/Logger/ScreenLogger.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// Screen navigation logger.
4 | public protocol ScreenLogger {
5 |
6 | /// Logs a message.
7 | ///
8 | /// - Parameter info: Info message to be logged.
9 | func info(_ info: @autoclosure () -> String)
10 |
11 | /// Logs an error.
12 | ///
13 | /// - Parameter error: Error to be logged.
14 | func error(_ error: @autoclosure () -> Error)
15 | }
16 |
--------------------------------------------------------------------------------
/Sources/Tools/Extensions/UIKit/UIEdgeInsets+Extensions.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | extension UIEdgeInsets {
5 |
6 | internal var horizontal: CGFloat {
7 | left + right
8 | }
9 |
10 | internal var vertical: CGFloat {
11 | top + bottom
12 | }
13 |
14 | internal init(equilateral side: CGFloat) {
15 | self.init(top: side, left: side, bottom: side, right: side)
16 | }
17 | }
18 | #endif
19 |
--------------------------------------------------------------------------------
/Sources/Tools/URLQueryDecoder/Options/URLQueryDecodingOptions.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | internal struct URLQueryDecodingOptions {
4 |
5 | internal let dateDecodingStrategy: URLQueryDateDecodingStrategy
6 | internal let dataDecodingStrategy: URLQueryDataDecodingStrategy
7 | internal let nonConformingFloatDecodingStrategy: URLQueryNonConformingFloatDecodingStrategy
8 | internal let keyDecodingStrategy: URLQueryKeyDecodingStrategy
9 | }
10 |
--------------------------------------------------------------------------------
/Sources/Tools/Extensions/UIKit/CGSize+Extensions.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | extension CGSize {
5 |
6 | internal init(equilateral side: CGFloat) {
7 | self.init(width: side, height: side)
8 | }
9 |
10 | internal func outset(by insets: UIEdgeInsets) -> Self {
11 | Self(
12 | width: width + insets.horizontal,
13 | height: height + insets.vertical
14 | )
15 | }
16 | }
17 | #endif
18 |
--------------------------------------------------------------------------------
/Scripts/Bootstrap/mintfile.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | readonly script_path="$( cd "$( dirname "$0" )" && pwd )"
4 |
5 | source "${script_path}/common.sh"
6 | source "${helpers_path}/script-run.sh"
7 |
8 | echo "Installing ${swift_style}Swift tools${default_style} specified in Mintfile..."
9 |
10 | if [[ "$(uname -m)" == "arm64" ]]; then
11 | eval "$(/opt/homebrew/bin/brew shellenv)"
12 | fi
13 |
14 | assert_failure '(cd "${root_path}" && mint bootstrap)'
15 |
16 | echo ""
17 |
--------------------------------------------------------------------------------
/Sources/Tools/DictionaryDecoder/Options/DictionaryDecodingOptions.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | internal struct DictionaryDecodingOptions {
4 |
5 | internal let dateDecodingStrategy: DictionaryDateDecodingStrategy
6 | internal let dataDecodingStrategy: DictionaryDataDecodingStrategy
7 | internal let nonConformingFloatDecodingStrategy: DictionaryNonConformingFloatDecodingStrategy
8 | internal let keyDecodingStrategy: DictionaryKeyDecodingStrategy
9 | }
10 |
--------------------------------------------------------------------------------
/Sources/Deeplink/DeeplinkStorage.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | internal final class DeeplinkStorage {
4 |
5 | internal let value: AnyDeeplink
6 | internal let type: DeeplinkType
7 | internal let scope: DeeplinkScope
8 |
9 | internal init(
10 | value: AnyDeeplink,
11 | type: DeeplinkType,
12 | scope: DeeplinkScope
13 | ) {
14 | self.value = value
15 | self.type = type
16 | self.scope = scope
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Sources/Screen/Container/Storage/ScreenContainerWeakStorage.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | internal struct ScreenContainerWeakStorage: ScreenContainerStorage {
4 |
5 | internal typealias Container = AnyObject & ScreenContainer
6 |
7 | private weak var container: Container?
8 |
9 | internal var value: ScreenContainer? {
10 | container
11 | }
12 |
13 | internal init(_ container: Container) {
14 | self.container = container
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Sources/Addons/BottomSheet/BottomSheetBorder.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | public struct BottomSheetBorder: Equatable, Sendable {
5 |
6 | public static let `default` = Self()
7 |
8 | public let width: CGFloat
9 | public let color: UIColor?
10 |
11 | public init(
12 | width: CGFloat = .zero,
13 | color: UIColor = .black
14 | ) {
15 | self.width = width
16 | self.color = color
17 | }
18 | }
19 | #endif
20 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Images/Turtlerock.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "filename" : "turtlerock@2x.jpg",
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Sources/Screen/Navigator/WindowProvider/ScreenKeyWindowProvider.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | /// An implementation of `ScreenWindowProvider` providing the first found key `UIWindow`.
5 | public struct ScreenKeyWindowProvider: ScreenWindowProvider {
6 |
7 | /// The `UIWindow` for navigating and searching for containers.
8 | public var window: UIWindow? {
9 | UIApplication.shared.firstKeyWindow
10 | }
11 |
12 | public init() { }
13 | }
14 | #endif
15 |
--------------------------------------------------------------------------------
/Sources/Tools/Extensions/UIKit/UIView+Extensions.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | extension UIView {
5 |
6 | internal var firstResponder: UIView? {
7 | if isFirstResponder {
8 | return self
9 | }
10 |
11 | for subview in subviews {
12 | if let firstResponder = subview.firstResponder {
13 | return firstResponder
14 | }
15 | }
16 |
17 | return nil
18 | }
19 | }
20 | #endif
21 |
--------------------------------------------------------------------------------
/Example/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - Nivelir (1.9.10)
3 | - SnapKit (5.6.0)
4 |
5 | DEPENDENCIES:
6 | - Nivelir (from `..`)
7 | - SnapKit
8 |
9 | SPEC REPOS:
10 | trunk:
11 | - SnapKit
12 |
13 | EXTERNAL SOURCES:
14 | Nivelir:
15 | :path: ".."
16 |
17 | SPEC CHECKSUMS:
18 | Nivelir: 1f8365f236cdfc17b8300b841792f1fd11112ee1
19 | SnapKit: e01d52ebb8ddbc333eefe2132acf85c8227d9c25
20 |
21 | PODFILE CHECKSUM: da8b281ef18accce1d0505caaeb1d708354daf4e
22 |
23 | COCOAPODS: 1.15.2
24 |
--------------------------------------------------------------------------------
/Sources/Addons/MediaPicker/MediaPickerImageExportPreset.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit) && os(iOS)
2 | import UIKit
3 |
4 | /// A type that specifies how to export images to the client application.
5 | @frozen
6 | public enum MediaPickerImageExportPreset: Sendable {
7 |
8 | @available(iOS 11, *)
9 | /// A preset for passing image data as-is to the client.
10 | case current
11 |
12 | /// A preset for converting HEIF formatted images to JPEG.
13 | case compatible
14 | }
15 | #endif
16 |
--------------------------------------------------------------------------------
/Sources/Tools/URLQueryDecoder/Options/URLQueryKeyDecodingStrategy.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// The values that determine how to decode a type’s coding keys from URL query keys.
4 | @frozen
5 | public enum URLQueryKeyDecodingStrategy {
6 |
7 | /// A key decoding strategy that doesn’t change key names during decoding.
8 | case useDefaultKeys
9 |
10 | /// A key decoding strategy defined by the closure you supply.
11 | case custom((_ codingPath: [CodingKey]) -> CodingKey)
12 | }
13 |
--------------------------------------------------------------------------------
/Sources/Deeplink/Errors/DeeplinkDecodingError.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | internal struct DeeplinkDecodingError: DeeplinkError {
4 |
5 | internal let description: String
6 |
7 | internal var isWarning: Bool {
8 | true
9 | }
10 |
11 | internal init(
12 | underlyingError: Error,
13 | trigger: Any
14 | ) {
15 | description = """
16 | Failed to decode data for \(trigger) with error:
17 | \(underlyingError)
18 | """
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Example/NivelirExample/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | @main
4 | class AppDelegate: UIResponder, UIApplicationDelegate {
5 |
6 | func application(
7 | _ application: UIApplication,
8 | configurationForConnecting connectingSceneSession: UISceneSession,
9 | options: UIScene.ConnectionOptions
10 | ) -> UISceneConfiguration {
11 | UISceneConfiguration(
12 | name: "Main",
13 | sessionRole: connectingSceneSession.role
14 | )
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Sources/Tools/DictionaryDecoder/Options/DictionaryKeyDecodingStrategy.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// The values that determine how to decode a type’s coding keys from Dictionary keys.
4 | @frozen
5 | public enum DictionaryKeyDecodingStrategy {
6 |
7 | /// A key decoding strategy that doesn’t change key names during decoding.
8 | case useDefaultKeys
9 |
10 | /// A key decoding strategy defined by the closure you supply.
11 | case custom(@Sendable (_ codingPath: [CodingKey]) -> CodingKey)
12 | }
13 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Screens/RoomList/RoomListScreen.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Nivelir
3 |
4 | struct RoomListScreen: Screen {
5 |
6 | let services: Services
7 | let screens: Screens
8 |
9 | func build(navigator: ScreenNavigator) -> UIViewController {
10 | RoomListViewController(
11 | authorizationService: services.authorizationService(),
12 | screens: screens,
13 | screenKey: key,
14 | screenNavigator: navigator
15 | )
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Sources/Addons/BottomSheet/Interaction/BottomSheetInteractionState.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import Foundation
3 |
4 | internal enum BottomSheetInteractionState {
5 |
6 | case starting
7 | case updating
8 | case finished
9 | case cancelled
10 |
11 | internal var isActive: Bool {
12 | switch self {
13 | case .starting, .updating:
14 | return true
15 |
16 | case .finished, .cancelled:
17 | return false
18 | }
19 | }
20 | }
21 | #endif
22 |
--------------------------------------------------------------------------------
/Sources/Addons/Sharing/Activity/SharingActivityType.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit) && os(iOS)
2 | import UIKit
3 |
4 | public typealias SharingActivityType = UIActivity.ActivityType
5 |
6 | extension SharingActivityType {
7 |
8 | public static func fromPropertyName(_ name: String = #function) -> Self {
9 | Bundle.main.bundleIdentifier.map { bundleIdentifier in
10 | Self(rawValue: "\(bundleIdentifier).activity.\(name)")
11 | } ?? Self(rawValue: "activity.\(name)")
12 | }
13 | }
14 | #endif
15 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Screens/Chat/ChatScreen.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Nivelir
3 |
4 | struct ChatScreen: Screen {
5 |
6 | let roomID: Int
7 | let chatID: Int
8 |
9 | var traits: Set {
10 | [roomID, chatID]
11 | }
12 |
13 | func build(navigator: ScreenNavigator) -> UIViewController {
14 | ChatViewController(
15 | roomID: roomID,
16 | chatID: chatID,
17 | screenKey: key,
18 | screenNavigator: navigator
19 | )
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Sources/Addons/BottomSheet/Presentation/BottomSheetPresentationState.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import Foundation
3 |
4 | internal enum BottomSheetPresentationState {
5 |
6 | case presenting
7 | case presented
8 | case dismissing
9 | case dismissed
10 |
11 | internal var canAnimateChanges: Bool {
12 | switch self {
13 | case .presenting, .presented, .dismissing:
14 | return true
15 |
16 | case .dismissed:
17 | return false
18 | }
19 | }
20 | }
21 | #endif
22 |
--------------------------------------------------------------------------------
/Sources/Screen/Navigator/Iterator/ScreenIterationResult.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// A value that represents either a continuation or a stop iteration,
4 | /// including the associated container value in each case.
5 | @frozen
6 | public enum ScreenIterationResult {
7 |
8 | /// Continue iterating if possible, starting from `suitableContainer`.
9 | case shouldContinue(suitableContainer: ScreenContainer?)
10 |
11 | /// Stop iterating on `suitableContainer`.
12 | case shouldStop(suitableContainer: ScreenContainer)
13 | }
14 |
--------------------------------------------------------------------------------
/Sources/Screen/Navigator/WindowProvider/ScreenCustomWindowProvider.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | /// An implementation of `ScreenWindowProvider` that provides a `UIWindow` explicitly passed through the initializer.
5 | public struct ScreenCustomWindowProvider: ScreenWindowProvider {
6 |
7 | /// The `UIWindow` for navigating and searching for containers.
8 | public private(set) weak var window: UIWindow?
9 |
10 | public init(window: UIWindow) {
11 | self.window = window
12 | }
13 | }
14 | #endif
15 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Screens/ChatList/ChatListScreen.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Nivelir
3 |
4 | struct ChatListScreen: Screen {
5 |
6 | let roomID: Int
7 | let screens: Screens
8 |
9 | var traits: Set {
10 | [roomID]
11 | }
12 |
13 | func build(navigator: ScreenNavigator) -> UIViewController {
14 | ChatListViewController(
15 | roomID: roomID,
16 | screens: screens,
17 | screenKey: key,
18 | screenNavigator: navigator
19 | )
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Sources/Screen/Errors/ScreenCanceledError.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// Action canceled.
4 | ///
5 | /// This error occurs whenever an action is canceled.
6 | public struct ScreenCanceledError: ScreenError {
7 |
8 | public let description: String
9 |
10 | /// Creates an error.
11 | ///
12 | /// - Parameters:
13 | /// - trigger: The action that caused the error.
14 | public init(for trigger: Any) {
15 | description = """
16 | Action canceled:
17 | \(trigger)
18 | """
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Dangerfile:
--------------------------------------------------------------------------------
1 | def report_xcode_summary(platform:)
2 | path = "xcodebuild-#{platform.downcase}.xcresult"
3 |
4 | xcode_summary.ignores_warnings = false
5 | xcode_summary.inline_mode = true
6 |
7 | xcode_summary.report(path)
8 | end
9 |
10 | warn('This pull request is marked as Work in Progress. DO NOT MERGE!') if github.pr_title.include? "[WIP]"
11 |
12 | swiftlint.lint_all_files = true
13 | swiftlint.lint_files(fail_on_error: true, inline_mode: true)
14 |
15 | report_xcode_summary(platform: "iOS")
16 | report_xcode_summary(platform: "tvOS")
17 |
--------------------------------------------------------------------------------
/Sources/Tools/URLQueryDecoder/Options/URLQueryDataDecodingStrategy.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// The strategies for decoding raw data.
4 | @frozen
5 | public enum URLQueryDataDecodingStrategy {
6 |
7 | /// The strategy that encodes data using the encoding specified by the data instance itself.
8 | case deferredToData
9 |
10 | /// The strategy that decodes data using Base 64 decoding.
11 | case base64
12 |
13 | /// The strategy that decodes data using a user-defined function.
14 | case custom((_ decoder: Decoder) throws -> Data)
15 | }
16 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Routing/Authorization/ScreenAuthorizeActionObserver.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | final class ScreenAuthorizeActionObserver: AuthorizationObserver {
4 |
5 | private var authorizationFinishedHandler: ((_ isAuthorized: Bool) -> Void)
6 |
7 | init(authorizationFinishedHandler: @escaping (_ isAuthorized: Bool) -> Void) {
8 | self.authorizationFinishedHandler = authorizationFinishedHandler
9 | }
10 |
11 | func authorizationFinished(isAuthorized: Bool) {
12 | authorizationFinishedHandler(isAuthorized)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Screens/Profile/ProfileScreen.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Nivelir
3 |
4 | struct ProfileScreen: Screen {
5 |
6 | let services: Services
7 | let screens: Screens
8 |
9 | func build(navigator: ScreenNavigator) -> UIViewController {
10 | ProfileViewController(
11 | authorizationService: services.authorizationService(),
12 | profileService: services.profileService(),
13 | screens: screens,
14 | screenKey: key,
15 | screenNavigator: navigator
16 | )
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Sources/Screen/Observation/Storage/ScreenObserverSharedStorage.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | internal final class ScreenObserverSharedStorage: ScreenObserverStorage {
4 |
5 | private let observer: ScreenObserver
6 |
7 | internal let predicate: ScreenObserverPredicate
8 |
9 | internal var value: ScreenObserver? {
10 | observer
11 | }
12 |
13 | internal init(
14 | observer: ScreenObserver,
15 | predicate: ScreenObserverPredicate
16 | ) {
17 | self.observer = observer
18 | self.predicate = predicate
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Sources/Tools/DictionaryDecoder/Options/DictionaryDataDecodingStrategy.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// The strategies for decoding raw data.
4 | @frozen
5 | public enum DictionaryDataDecodingStrategy {
6 |
7 | /// The strategy that encodes data using the encoding specified by the data instance itself.
8 | case deferredToData
9 |
10 | /// The strategy that decodes data using Base 64 decoding.
11 | case base64
12 |
13 | /// The strategy that decodes data using a user-defined function.
14 | case custom(@Sendable (_ decoder: Decoder) throws -> Data)
15 | }
16 |
--------------------------------------------------------------------------------
/Sources/Addons/Safari/InvalidSafariURLError.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import Foundation
3 |
4 | public struct InvalidSafariURLError: ScreenError {
5 |
6 | public let description: String
7 |
8 | public init(for trigger: Any) {
9 | description = """
10 | Safari does not support the url scheme of:
11 | \(trigger)
12 | """
13 | }
14 | }
15 |
16 | extension Result where Failure == Error {
17 |
18 | internal static func invalidSafariURL(for trigger: Any) -> Self {
19 | .failure(InvalidSafariURLError(for: trigger))
20 | }
21 | }
22 | #endif
23 |
--------------------------------------------------------------------------------
/docs/img/deprecated-icon.015b4f17.svg:
--------------------------------------------------------------------------------
1 |
10 |
11 |
--------------------------------------------------------------------------------
/Sources/Addons/URL/StoreApp/InvalidStoreAppIDError.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS) || os(tvOS)
2 | import Foundation
3 |
4 | public struct InvalidStoreAppIDError: ScreenError {
5 |
6 | public let description: String
7 |
8 | public init(for trigger: Any) {
9 | description = """
10 | Invalid store app ID for:
11 | \(trigger)
12 | """
13 | }
14 | }
15 |
16 | extension Result where Failure == Error {
17 |
18 | internal static func invalidStoreAppID(for trigger: Any) -> Self {
19 | .failure(InvalidStoreAppIDError(for: trigger))
20 | }
21 | }
22 | #endif
23 |
--------------------------------------------------------------------------------
/Sources/Screen/Payload/Extensions/ScreenPayloadedContainer+NSObject.swift:
--------------------------------------------------------------------------------
1 | #if canImport(ObjectiveC)
2 | import ObjectiveC
3 |
4 | private let screenPayloadAssociation = ObjectAssociation()
5 |
6 | extension ScreenPayloadedContainer where Self: NSObject {
7 |
8 | public var screenPayload: ScreenPayload {
9 | if let payload = screenPayloadAssociation[self] {
10 | return payload
11 | }
12 |
13 | let payload = ScreenPayload()
14 |
15 | screenPayloadAssociation[self] = payload
16 |
17 | return payload
18 | }
19 | }
20 | #endif
21 |
--------------------------------------------------------------------------------
/Sources/Addons/URL/Call/InvalidCallParametersError.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import Foundation
3 |
4 | public struct InvalidCallParametersError: ScreenError {
5 |
6 | public let description: String
7 |
8 | public init(for trigger: Any) {
9 | description = """
10 | Invalid call parameters for:
11 | \(trigger)
12 | """
13 | }
14 | }
15 |
16 | extension Result where Failure == Error {
17 |
18 | internal static func invalidCallParameters(for trigger: Any) -> Self {
19 | .failure(InvalidCallParametersError(for: trigger))
20 | }
21 | }
22 | #endif
23 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Screens/Landmark/LandmarkMapView.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 | import MapKit
3 |
4 | struct LandmarkMapView: View {
5 |
6 | @State private var region = MKCoordinateRegion(
7 | center: CLLocationCoordinate2D(latitude: 34.011_286, longitude: -116.166_868),
8 | span: MKCoordinateSpan(latitudeDelta: 0.2, longitudeDelta: 0.2)
9 | )
10 |
11 | var body: some View {
12 | Map(coordinateRegion: $region)
13 | }
14 | }
15 |
16 | struct MapView_Previews: PreviewProvider {
17 | static var previews: some View {
18 | LandmarkMapView()
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Sources/Addons/StoreProduct/InvalidStoreProductIDError.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import Foundation
3 |
4 | public struct InvalidStoreProductIDError: ScreenError {
5 |
6 | public let description: String
7 |
8 | public init(for trigger: Any) {
9 | description = """
10 | Invalid store product ID for:
11 | \(trigger)
12 | """
13 | }
14 | }
15 |
16 | extension Result where Failure == Error {
17 |
18 | internal static func invalidStoreProductID(for trigger: Any) -> Self {
19 | .failure(InvalidStoreProductIDError(for: trigger))
20 | }
21 | }
22 | #endif
23 |
--------------------------------------------------------------------------------
/Sources/Tools/Extensions/String+Extensions.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | extension String {
4 |
5 | internal static let urlPercentEscapedSpace = "%20"
6 | internal static let urlPlusReplacedSpace = "+"
7 | internal static let urlPathSeparator = "/"
8 |
9 | internal static let newLine = "\n"
10 |
11 | internal func indented(spaces: Int) -> String {
12 | let spaces = String(repeating: " ", count: spaces)
13 |
14 | return components(separatedBy: .newlines)
15 | .joined(separator: "\n\(spaces)")
16 | .prepending(contentsOf: spaces)
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Screens/Authorization/AuthorizationScreen.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Nivelir
3 |
4 | struct AuthorizationScreen: Screen {
5 |
6 | let services: Services
7 |
8 | func build(
9 | navigator: ScreenNavigator,
10 | observation: ScreenObservation
11 | ) -> UIViewController {
12 | AuthorizationViewController(
13 | authorizationService: services.authorizationService(),
14 | screenObservation: observation,
15 | screenKey: key,
16 | screenNavigator: navigator
17 | )
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Sources/Addons/HUD/Progress/Footer/Empty/ProgressEmptyFooterView.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | /// Empty view for the progress footer.
5 | public final class ProgressEmptyFooterView: UIView, ProgressContentView {
6 |
7 | public let content: ProgressEmptyFooter
8 |
9 | public init(content: ProgressEmptyFooter) {
10 | self.content = content
11 |
12 | super.init(frame: .zero)
13 | }
14 |
15 | @available(*, unavailable)
16 | public required init?(coder: NSCoder) {
17 | fatalError("init(coder:) has not been implemented")
18 | }
19 | }
20 | #endif
21 |
--------------------------------------------------------------------------------
/Sources/Addons/HUD/Progress/Header/Empty/ProgressEmptyHeaderView.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | /// Empty view for the progress header.
5 | public final class ProgressEmptyHeaderView: UIView, ProgressContentView {
6 |
7 | public var content: ProgressEmptyHeader
8 |
9 | public init(content: ProgressEmptyHeader) {
10 | self.content = content
11 |
12 | super.init(frame: .zero)
13 | }
14 |
15 | @available(*, unavailable)
16 | public required init?(coder: NSCoder) {
17 | fatalError("init(coder:) has not been implemented")
18 | }
19 | }
20 | #endif
21 |
--------------------------------------------------------------------------------
/Sources/Addons/URL/Mail/InvalidMailParametersError.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS) || os(tvOS)
2 | import Foundation
3 |
4 | public struct InvalidMailParametersError: ScreenError {
5 |
6 | public let description: String
7 |
8 | public init(for trigger: Any) {
9 | description = """
10 | Invalid mail parameters for:
11 | \(trigger)
12 | """
13 | }
14 | }
15 |
16 | extension Result where Failure == Error {
17 |
18 | internal static func invalidMailParameters(for trigger: Any) -> Self {
19 | .failure(InvalidMailParametersError(for: trigger))
20 | }
21 | }
22 | #endif
23 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Screens/Landmark/LandmarkCircleImage.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | struct LandmarkCircleImage: View {
4 | var body: some View {
5 | Image("Turtlerock")
6 | .accessibilityLabel("Turtle Rock")
7 | .clipShape(/*@START_MENU_TOKEN@*/Circle()/*@END_MENU_TOKEN@*/)
8 | .overlay {
9 | Circle().stroke(.white, lineWidth: 4)
10 | }
11 | .shadow(radius: 7)
12 | }
13 | }
14 |
15 | struct CircleImage_Previews: PreviewProvider {
16 | static var previews: some View {
17 | LandmarkCircleImage()
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Sources/Addons/BottomSheet/Animations/BottomSheetAnimationOptions.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | public struct BottomSheetAnimationOptions: Equatable, Sendable {
5 |
6 | public static let transition = Self()
7 | public static let changes = Self(duration: 0.3)
8 |
9 | public let duration: TimeInterval
10 | public let curve: UIView.AnimationCurve
11 |
12 | public init(
13 | duration: TimeInterval = 0.4,
14 | curve: UIView.AnimationCurve = .easeInOut
15 | ) {
16 | self.duration = duration
17 | self.curve = curve
18 | }
19 | }
20 | #endif
21 |
--------------------------------------------------------------------------------
/Sources/Addons/URL/Settings/InvalidOpenSettingsURLError.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS) || os(tvOS)
2 | import Foundation
3 |
4 | public struct InvalidOpenSettingsURLError: ScreenError {
5 |
6 | public let description: String
7 |
8 | public init(for trigger: Any) {
9 | description = """
10 | Invalid settings URL for:
11 | \(trigger)
12 | """
13 | }
14 | }
15 |
16 | extension Result where Failure == Error {
17 |
18 | internal static func invalidOpenSettingsURL(for trigger: Any) -> Self {
19 | .failure(InvalidOpenSettingsURLError(for: trigger))
20 | }
21 | }
22 | #endif
23 |
--------------------------------------------------------------------------------
/Sources/Addons/StoreProduct/StoreProductManager.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit) && canImport(StoreKit) && os(iOS)
2 | import UIKit
3 | import StoreKit
4 |
5 | internal final class StoreProductManager:
6 | NSObject,
7 | SKStoreProductViewControllerDelegate {
8 |
9 | private let storeProduct: StoreProduct
10 |
11 | internal init(storeProduct: StoreProduct) {
12 | self.storeProduct = storeProduct
13 | }
14 |
15 | internal func productViewControllerDidFinish(
16 | _ viewController: SKStoreProductViewController
17 | ) {
18 | storeProduct.didFinish?()
19 | }
20 | }
21 |
22 | #endif
23 |
--------------------------------------------------------------------------------
/Sources/Screen/Container/Extensions/UIViewController+ScreenVisibleContainer.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | extension UIViewController: ScreenVisibleContainer {
5 |
6 | /// A Boolean value indicating whether the container is visible.
7 | ///
8 | /// Returns `true` if controller's view is loaded and is not hidden.
9 | public var isVisible: Bool {
10 | viewIfLoaded.map { $0.window != nil && !$0.isHidden } ?? false
11 | }
12 |
13 | @available(iOS 13.0, tvOS 13.0, *)
14 | public var windowScene: UIWindowScene? {
15 | window?.windowScene
16 | }
17 | }
18 | #endif
19 |
--------------------------------------------------------------------------------
/Scripts/Helpers/script-run.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | export MINT_PATH="$HOME/.mint"
4 | export MINT_LINK_PATH="$MINT_PATH/bin"
5 |
6 | if [[ -f "/opt/homebrew/bin/brew" ]]; then
7 | eval "$(/opt/homebrew/bin/brew shellenv)"
8 | fi
9 |
10 | run() {
11 | if [ -z "${root_path}" ]; then
12 | if [ -z "${helpers_path}" ]; then
13 | readonly helpers_path="$( cd "$( dirname "$0" )" && pwd )"
14 | fi
15 |
16 | source "${helpers_path}/script-paths.sh"
17 | fi
18 |
19 | if which mint >/dev/null; then
20 | (cd "${root_path}" && mint run "$@")
21 | else
22 | echo "warning: Mint does not exist"
23 | fi
24 | }
25 |
--------------------------------------------------------------------------------
/Sources/Addons/BottomSheet/BottomSheetGrabber.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | public struct BottomSheetGrabber: Equatable, Sendable {
5 |
6 | public static let `default` = Self()
7 |
8 | public let size: CGSize
9 | public let color: UIColor
10 | public let inset: CGFloat
11 |
12 | public init(
13 | size: CGSize = CGSize(width: 36.0, height: 5.0),
14 | color: UIColor = UIColor.darkGray.withAlphaComponent(0.4),
15 | inset: CGFloat = 5.0
16 | ) {
17 | self.size = size
18 | self.color = color
19 | self.inset = inset
20 | }
21 | }
22 | #endif
23 |
--------------------------------------------------------------------------------
/Sources/Addons/BottomSheet/Extensions/UIViewController+BottomSheet.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | extension UIViewController {
5 |
6 | public var bottomSheet: BottomSheetController? {
7 | let viewController = presenting?.presented ?? self
8 |
9 | guard viewController.modalPresentationStyle == .custom else {
10 | return nil
11 | }
12 |
13 | return viewController.transitioningDelegate as? BottomSheetController
14 | }
15 |
16 | public var isPresentedAsBottomSheet: Bool {
17 | (bottomSheet != nil) && (presenting != nil)
18 | }
19 | }
20 | #endif
21 |
--------------------------------------------------------------------------------
/Sources/Deeplink/Extensions/UNNotificationResponse+Extensions.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UserNotifications) && os(iOS)
2 | import Foundation
3 | import UserNotifications
4 |
5 | extension UNNotificationResponse {
6 |
7 | internal var logDescription: String? {
8 | let userInfo = notification.request.content.userInfo
9 |
10 | let jsonData = try? JSONSerialization.data(
11 | withJSONObject: userInfo,
12 | options: .prettyPrinted
13 | )
14 |
15 | return jsonData.flatMap { jsonData in
16 | String(data: jsonData, encoding: .utf8)
17 | }
18 | }
19 | }
20 | #endif
21 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Helpers/DI/ServiceKey.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | struct ServiceKey: Hashable {
4 |
5 | let type: Any.Type
6 | let name: String
7 | let traits: [AnyHashable]
8 |
9 | func hash(into hasher: inout Hasher) {
10 | ObjectIdentifier(type).hash(into: &hasher)
11 | name.hash(into: &hasher)
12 | traits.hash(into: &hasher)
13 | }
14 | }
15 |
16 | extension ServiceKey: Equatable {
17 |
18 | static func == (lhs: Self, rhs: Self) -> Bool {
19 | (lhs.type == rhs.type)
20 | && (lhs.name == rhs.name)
21 | && (lhs.traits == rhs.traits)
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Sources/Addons/MediaPicker/Errors/UnavailableMediaPickerSourceError.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import Foundation
3 |
4 | public struct UnavailableMediaPickerSourceError: ScreenError {
5 |
6 | public let description: String
7 |
8 | public init(for trigger: Any) {
9 | description = """
10 | Media source is not available for:
11 | \(trigger)
12 | """
13 | }
14 | }
15 |
16 | extension Result where Failure == Error {
17 |
18 | internal static func unavailableMediaPickerSource(for trigger: Any) -> Self {
19 | .failure(UnavailableMediaPickerSourceError(for: trigger))
20 | }
21 | }
22 | #endif
23 |
--------------------------------------------------------------------------------
/Sources/Addons/MediaPicker/Errors/UnavailableMediaPickerTypesError.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import Foundation
3 |
4 | public struct UnavailableMediaPickerTypesError: ScreenError {
5 |
6 | public let description: String
7 |
8 | public init(for trigger: Any) {
9 | description = """
10 | Media types are not available for:
11 | \(trigger)
12 | """
13 | }
14 | }
15 |
16 | extension Result where Failure == Error {
17 |
18 | internal static func unavailableMediaPickerTypes(for trigger: Any) -> Self {
19 | .failure(UnavailableMediaPickerTypesError(for: trigger))
20 | }
21 | }
22 | #endif
23 |
--------------------------------------------------------------------------------
/Sources/Screen/Observation/Storage/ScreenObserverWeakStorage.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | internal final class ScreenObserverWeakStorage: ScreenObserverStorage {
4 |
5 | internal typealias Observer = ScreenObserver & AnyObject
6 |
7 | private weak var observer: Observer?
8 |
9 | internal let predicate: ScreenObserverPredicate
10 |
11 | internal var value: ScreenObserver? {
12 | observer
13 | }
14 |
15 | internal init(
16 | observer: Observer,
17 | predicate: ScreenObserverPredicate
18 | ) {
19 | self.observer = observer
20 | self.predicate = predicate
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Sources/Addons/Sharing/Activity/SharingCustomActivity.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit) && os(iOS)
2 | import UIKit
3 |
4 | public protocol SharingCustomActivity {
5 |
6 | static var category: SharingActivityCategory { get }
7 |
8 | var type: SharingActivityType? { get }
9 | var title: String { get }
10 | var image: UIImage { get }
11 |
12 | func isApplicable(for items: [SharingItem]) -> Bool
13 | }
14 |
15 | extension SharingCustomActivity {
16 |
17 | public static var category: SharingActivityCategory {
18 | .action
19 | }
20 |
21 | public var type: SharingActivityType? {
22 | nil
23 | }
24 | }
25 | #endif
26 |
--------------------------------------------------------------------------------
/Sources/Addons/URL/FailedToOpenURLError.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS) || os(tvOS)
2 | import Foundation
3 |
4 | public struct FailedToOpenURLError: ScreenError {
5 |
6 | public let description: String
7 |
8 | public init(
9 | url: URL,
10 | for trigger: Any
11 | ) {
12 | description = """
13 | Failed to open URL ("\(url)") for:
14 | \(trigger)
15 | """
16 | }
17 | }
18 |
19 | extension Result where Failure == Error {
20 |
21 | internal static func failedToOpenURL(_ url: URL, for trigger: Any) -> Self {
22 | .failure(FailedToOpenURLError(url: url, for: trigger))
23 | }
24 | }
25 | #endif
26 |
--------------------------------------------------------------------------------
/Sources/Addons/MediaPicker/Errors/MediaPickerSourceAccessDeniedError.swift:
--------------------------------------------------------------------------------
1 | #if os(iOS)
2 | import Foundation
3 |
4 | public struct MediaPickerSourceAccessDeniedError: ScreenError {
5 |
6 | public let description: String
7 |
8 | public init(for trigger: Any) {
9 | description = """
10 | User does not allow the app to access the media source for:
11 | \(trigger)
12 | """
13 | }
14 | }
15 |
16 | extension Result where Failure == Error {
17 |
18 | internal static func mediaPickerSourceAccessDenied(for trigger: Any) -> Self {
19 | .failure(MediaPickerSourceAccessDeniedError(for: trigger))
20 | }
21 | }
22 | #endif
23 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.7
2 | import PackageDescription
3 |
4 | let package = Package(
5 | name: "Nivelir",
6 | platforms: [
7 | .iOS(.v13),
8 | .tvOS(.v13)
9 | ],
10 | products: [
11 | .library(
12 | name: "Nivelir",
13 | targets: ["Nivelir"]
14 | )
15 | ],
16 | targets: [
17 | .target(
18 | name: "Nivelir",
19 | path: "Sources"
20 | ),
21 | .testTarget(
22 | name: "NivelirTests",
23 | dependencies: ["Nivelir"],
24 | path: "Tests"
25 | )
26 | ],
27 | swiftLanguageVersions: [.v5]
28 | )
29 |
--------------------------------------------------------------------------------
/Sources/Screen/Payload/ScreenPayload.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// Payload associated with the screen container.
4 | ///
5 | /// This is a helper class that is used to store navigation data
6 | /// that should be in memory until the screen container itself is released.
7 | ///
8 | /// - SeeAlso: `ScreenPayloadedContainer`
9 | public final class ScreenPayload {
10 |
11 | private var storage: [Any] = []
12 |
13 | /// Creates an empty payload.
14 | public init() { }
15 |
16 | /// Stores navigation data.
17 | ///
18 | /// - Parameter data: Navigation data.
19 | public func store(_ data: Any) {
20 | storage.append(data)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Sources/Addons/BottomSheet/BottomSheetRubberBandEffect.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | public struct BottomSheetRubberBandEffect: Sendable {
5 |
6 | public let handler: @Sendable (_ delta: CGFloat) -> CGFloat
7 |
8 | public init(handler: @escaping @Sendable (_ delta: CGFloat) -> CGFloat) {
9 | self.handler = handler
10 | }
11 |
12 | public func callAsFunction(value: CGFloat, limit: CGFloat) -> CGFloat {
13 | handler(abs(limit - value))
14 | }
15 | }
16 |
17 | extension BottomSheetRubberBandEffect {
18 |
19 | public static let `default` = Self { delta in
20 | 2.0 * delta.squareRoot()
21 | }
22 | }
23 | #endif
24 |
--------------------------------------------------------------------------------
/Sources/Deeplink/URL/Errors/URLDeeplinkInvalidComponentsError.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// Failed to extract components from deeplink URL.
4 | public struct URLDeeplinkInvalidComponentsError: DeeplinkError {
5 |
6 | public let description: String
7 |
8 | /// Creates an error.
9 | ///
10 | /// - Parameters:
11 | /// - url: A URL that caused the error.
12 | /// - trigger: The deeplink that caused the error.
13 | public init(
14 | url: URL,
15 | for trigger: Any
16 | ) {
17 | description = """
18 | Failed to extract components from URL "\(url)" for:
19 | \(trigger)
20 | """
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Sources/Addons/BottomSheet/Detention/Detent/BottomSheetDetentKey.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import Foundation
3 |
4 | public struct BottomSheetDetentKey: Hashable, RawRepresentable, Sendable {
5 |
6 | public let rawValue: String
7 |
8 | public init(rawValue: String) {
9 | self.rawValue = rawValue
10 | }
11 | }
12 |
13 | extension BottomSheetDetentKey {
14 |
15 | public static var content: Self {
16 | Self(rawValue: #function)
17 | }
18 |
19 | public static var large: Self {
20 | Self(rawValue: #function)
21 | }
22 |
23 | public static var medium: Self {
24 | Self(rawValue: #function)
25 | }
26 | }
27 | #endif
28 |
--------------------------------------------------------------------------------
/Sources/Tools/URLQueryDecoder/Options/URLQueryNonConformingFloatDecodingStrategy.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// The strategies for encoding nonconforming floating-point numbers,
4 | /// also known as IEEE 754 exceptional values.
5 | @frozen
6 | public enum URLQueryNonConformingFloatDecodingStrategy {
7 |
8 | /// The strategy that throws an error upon decoding an exceptional floating-point value.
9 | case `throw`
10 |
11 | /// The strategy that decodes exceptional floating-point values from a specified string representation.
12 | case convertFromString(
13 | positiveInfinity: String,
14 | negativeInfinity: String,
15 | nan: String
16 | )
17 | }
18 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Helpers/Images.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | enum Images {
4 |
5 | static let close = UIImage(named: "Close")!
6 | static let roomsTab = UIImage(named: "RoomsTab")!
7 | static let room = UIImage(named: "RoomsTab")!
8 | static let profileTab = UIImage(named: "ProfileTab")!
9 | static let chat = UIImage(named: "Chat")!
10 | static let user = UIImage(named: "User")!
11 | static let unauthorized = UIImage(named: "Unauthorized")!
12 | static let chevron = UIImage(named: "Chevron")!
13 | static let moreTab = UIImage(named: "MoreTab")!
14 | static let share = UIImage(named: "Share")!
15 | static let browser = UIImage(named: "Browser")!
16 | }
17 |
--------------------------------------------------------------------------------
/Sources/Screen/Decorators/Modal/ModalStyle/ScreenModalStyle.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | /// Modal screen presentation style.
5 | /// Changes the presentation animation when the screen is shown modally.
6 | @frozen
7 | public enum ScreenModalStyle {
8 |
9 | /// Default animation using `UIModalPresentationStyle` and `UIModalTransitionStyle`.
10 | case `default`(
11 | presentation: UIModalPresentationStyle? = nil,
12 | transition: UIModalTransitionStyle? = nil
13 | )
14 |
15 | /// Custom animation using `UIViewControllerTransitioningDelegate` implementation.
16 | case custom(delegate: UIViewControllerTransitioningDelegate)
17 | }
18 | #endif
19 |
--------------------------------------------------------------------------------
/Sources/Tools/DictionaryDecoder/Options/DictionaryNonConformingFloatDecodingStrategy.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// The strategies for encoding nonconforming floating-point numbers,
4 | /// also known as IEEE 754 exceptional values.
5 | @frozen
6 | public enum DictionaryNonConformingFloatDecodingStrategy {
7 |
8 | /// The strategy that throws an error upon decoding an exceptional floating-point value.
9 | case `throw`
10 |
11 | /// The strategy that decodes exceptional floating-point values from a specified string representation.
12 | case convertFromString(
13 | positiveInfinity: String,
14 | negativeInfinity: String,
15 | nan: String
16 | )
17 | }
18 |
--------------------------------------------------------------------------------
/Sources/Deeplink/Extensions/UIApplicationShortcutItem+Extensions.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit) && os(iOS)
2 | import UIKit
3 |
4 | extension UIApplicationShortcutItem {
5 |
6 | internal var logDescription: String? {
7 | let description: [String: Any?] = [
8 | "type": type,
9 | "title": localizedTitle,
10 | "userInfo": userInfo
11 | ]
12 |
13 | let jsonData = try? JSONSerialization.data(
14 | withJSONObject: description,
15 | options: .prettyPrinted
16 | )
17 |
18 | return jsonData.flatMap { jsonData in
19 | String(data: jsonData, encoding: .utf8)
20 | }
21 | }
22 | }
23 | #endif
24 |
--------------------------------------------------------------------------------
/Scripts/Bootstrap/gemfile.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | readonly arguments=$@
4 | readonly script_path="$( cd "$( dirname "$0" )" && pwd )"
5 |
6 | source "${script_path}/common.sh"
7 |
8 | if [[ "$(uname -m)" == "arm64" ]]; then
9 | eval "$(/opt/homebrew/bin/brew shellenv)"
10 | fi
11 |
12 | eval "$(rbenv init -)"
13 |
14 | if [[ " ${arguments[*]} " == *" ${update_flag} "* ]]; then
15 | echo "Updating ${ruby_style}Ruby gems${default_style} specified in Gemfile..."
16 | assert_failure '(cd "${root_path}" && bundle update)'
17 | else
18 | echo "Installing ${ruby_style}Ruby gems${default_style} specified in Gemfile..."
19 | assert_failure '(cd "${root_path}" && bundle install)'
20 | fi
21 |
22 | echo ""
23 |
--------------------------------------------------------------------------------
/Sources/Screen/Actions/Generic/ScreenFailAction.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | public struct ScreenFailAction: ScreenAction {
4 |
5 | public typealias Output = Void
6 |
7 | public let error: Error
8 |
9 | public init(error: Error) {
10 | self.error = error
11 | }
12 |
13 | public func perform(
14 | container: Container,
15 | navigator: ScreenNavigator,
16 | completion: @escaping Completion
17 | ) {
18 | completion(.failure(error))
19 | }
20 | }
21 |
22 | extension ScreenThenable {
23 |
24 | public func fail(with error: Error) -> Self {
25 | then(ScreenFailAction(error: error))
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/docs/img/added-icon.d6f7e47d.svg:
--------------------------------------------------------------------------------
1 |
10 |
11 |
--------------------------------------------------------------------------------
/Sources/Tools/AnyCodingKey.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | internal struct AnyCodingKey: CodingKey {
4 |
5 | internal static let `super` = Self("super")
6 |
7 | internal let stringValue: String
8 | internal let intValue: Int?
9 |
10 | internal init(_ stringValue: String) {
11 | self.stringValue = stringValue
12 | self.intValue = nil
13 | }
14 |
15 | internal init(_ intValue: Int) {
16 | self.stringValue = "\(intValue)"
17 | self.intValue = intValue
18 | }
19 |
20 | internal init?(stringValue: String) {
21 | self.init(stringValue)
22 | }
23 |
24 | internal init?(intValue: Int) {
25 | self.init(intValue)
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/docs/js/highlight-js-shell.dd7f411f.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * This source file is part of the Swift.org open source project
3 | *
4 | * Copyright (c) 2021 Apple Inc. and the Swift project authors
5 | * Licensed under Apache License v2.0 with Runtime Library Exception
6 | *
7 | * See https://swift.org/LICENSE.txt for license information
8 | * See https://swift.org/CONTRIBUTORS.txt for Swift project authors
9 | */
10 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["highlight-js-shell"],{b65b:function(s,n){function e(s){return{name:"Shell Session",aliases:["console","shellsession"],contains:[{className:"meta",begin:/^\s{0,3}[/~\w\d[\]()@-]*[>%$#][ ]?/,starts:{end:/[^\\](?=\s*$)/,subLanguage:"bash"}}]}}s.exports=e}}]);
--------------------------------------------------------------------------------
/Sources/Screen/Any/AnyScreenBaseBox.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | internal class AnyScreenBaseBox: Screen {
4 |
5 | internal var name: String {
6 | fatalError("\(#function) has not been implemented")
7 | }
8 |
9 | internal var traits: Set {
10 | fatalError("\(#function) has not been implemented")
11 | }
12 |
13 | nonisolated internal var description: String {
14 | fatalError("\(#function) has not been implemented")
15 | }
16 |
17 | // swiftlint:disable:next unavailable_function
18 | internal func build(navigator: ScreenNavigator) -> Container {
19 | fatalError("\(#function) has not been implemented")
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Scripts/Bootstrap/spm.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | readonly arguments=$@
4 | readonly script_path="$( cd "$( dirname "$0" )" && pwd )"
5 |
6 | source "${script_path}/common.sh"
7 |
8 | if [[ "$(uname -m)" == "arm64" ]]; then
9 | eval "$(/opt/homebrew/bin/brew shellenv)"
10 | fi
11 |
12 | eval "$(swiftenv init -)"
13 |
14 | if [[ " ${arguments[*]} " == *" ${update_flag} "* ]]; then
15 | echo "Updating ${spm_style}Swift packages${default_style} specified in Package.swift..."
16 | assert_failure '(cd "${root_path}" && swift package update)'
17 | else
18 | echo "Resolving ${spm_style}Swift packages${default_style} specified in Package.swift..."
19 | assert_failure '(cd "${root_path}" && swift package resolve)'
20 | fi
21 |
22 | echo ""
23 |
--------------------------------------------------------------------------------
/Sources/Deeplink/AnyDeeplink.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// Erased type of ``Deeplink`` protocol.
4 | ///
5 | /// - SeeAlso: ``Deeplink``
6 | @MainActor
7 | public protocol AnyDeeplink {
8 |
9 | /// The default implementation casts `screens` to the ``Deeplink/Screens`` type
10 | /// and performs ``Deeplink/navigate(screens:navigator:handler:)`` navigation.
11 | /// - Parameters:
12 | /// - screens: Screen Factory.
13 | /// - navigator: Navigator for performing navigation actions.
14 | /// - handler: Handler for processing a new ``Deeplink``.
15 | func navigateIfPossible(
16 | screens: Any?,
17 | navigator: ScreenNavigator,
18 | handler: DeeplinkHandler
19 | ) throws
20 | }
21 |
--------------------------------------------------------------------------------
/Sources/Addons/BottomSheet/Dimming/BottomSheetDimming.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | public struct BottomSheetDimming: Equatable, Sendable {
5 |
6 | public static let `default` = Self()
7 |
8 | public let color: UIColor
9 | public let blurStyle: UIBlurEffect.Style?
10 | public let largestUndimmedDetentKey: BottomSheetDetentKey?
11 |
12 | public init(
13 | color: UIColor = UIColor.black.withAlphaComponent(0.6),
14 | blurStyle: UIBlurEffect.Style? = nil,
15 | largestUndimmedDetentKey: BottomSheetDetentKey? = nil
16 | ) {
17 | self.color = color
18 | self.blurStyle = blurStyle
19 | self.largestUndimmedDetentKey = largestUndimmedDetentKey
20 | }
21 | }
22 | #endif
23 |
--------------------------------------------------------------------------------
/Sources/Screen/Actions/Stack/SetStack/Animations/ScreenStackCustomAnimation.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | public protocol ScreenStackCustomAnimation {
5 |
6 | func isEqual(to other: ScreenStackCustomAnimation) -> Bool
7 |
8 | @MainActor
9 | func animate(
10 | container: UINavigationController,
11 | stack: [UIViewController],
12 | completion: @escaping () -> Void
13 | )
14 | }
15 |
16 | extension ScreenStackCustomAnimation where Self: Equatable {
17 |
18 | public func isEqual(to other: ScreenStackCustomAnimation) -> Bool {
19 | guard let other = other as? Self else {
20 | return false
21 | }
22 |
23 | return self == other
24 | }
25 | }
26 | #endif
27 |
--------------------------------------------------------------------------------
/Sources/Screen/Actions/Window/SetRoot/Animations/ScreenRootAnimation.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | @MainActor
5 | @frozen
6 | public enum ScreenRootAnimation {
7 |
8 | case custom(ScreenRootCustomAnimation)
9 |
10 | public func animate(
11 | container: UIWindow,
12 | from root: UIViewController?,
13 | to newRoot: UIViewController,
14 | completion: @escaping () -> Void
15 | ) {
16 | switch self {
17 | case let .custom(animation):
18 | animation.animate(
19 | container: container,
20 | from: root,
21 | to: newRoot,
22 | completion: completion
23 | )
24 | }
25 | }
26 | }
27 | #endif
28 |
--------------------------------------------------------------------------------
/Scripts/bootstrap.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | readonly script_path="$( cd "$( dirname "$0" )" && pwd )"
6 | readonly bootstrap_path="${script_path}/Bootstrap"
7 |
8 | "${bootstrap_path}/welcome.sh"
9 | "${bootstrap_path}/macos.sh"
10 |
11 | "${bootstrap_path}/homebrew.sh" --update --verify
12 | "${bootstrap_path}/rbenv.sh" --update --verify
13 | "${bootstrap_path}/ruby.sh"
14 |
15 | "${bootstrap_path}/bundler.sh" --update
16 | "${bootstrap_path}/gemfile.sh"
17 |
18 | "${bootstrap_path}/xcode.sh"
19 |
20 | "${bootstrap_path}/swiftenv.sh" --update
21 | "${bootstrap_path}/swift.sh" --update
22 |
23 | "${bootstrap_path}/spm.sh"
24 |
25 | "${bootstrap_path}/mint.sh" --update
26 | "${bootstrap_path}/mintfile.sh"
27 |
28 | "${bootstrap_path}/congratulations.sh"
29 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Routing/Extensions/HUD+Extensions.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Nivelir
3 |
4 | extension HUD {
5 |
6 | static var spinner: Self {
7 | spinner(
8 | ProgressSpinnerIndicator(color: Colors.important),
9 | message: ProgressMessageFooter(text: "Please wait...", color: Colors.title)
10 | )
11 | }
12 |
13 | static var success: Self {
14 | success(ProgressSuccessIndicator(color: Colors.important))
15 | }
16 |
17 | static var failure: Self {
18 | failure(ProgressFailureIndicator(color: Colors.important))
19 | }
20 |
21 | static func percentage(ratio: CGFloat) -> Self {
22 | percentage(ProgressPercentageIndicator(ratio: ratio, color: Colors.important))
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Sources/Screen/Container/ScreenIterableContainer.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | /// A screen container that can be iterated over.
5 | ///
6 | /// This type of container is implemented by containers that can have nested containers.
7 | /// For example, `UINavigationController` has nested containers that are stored in a stack.
8 | /// The `UITabBarController` has nested containers that are stored in the tab.
9 | ///
10 | /// This protocol is used to find the specific container in nested containers.
11 | ///
12 | /// - SeeAlso: `ScreenContainer`
13 | @MainActor
14 | public protocol ScreenIterableContainer: ScreenContainer {
15 |
16 | /// Returns the nested containers of a container.
17 | var nestedContainers: [ScreenContainer] { get }
18 | }
19 | #endif
20 |
--------------------------------------------------------------------------------
/Sources/Screen/Container/Extensions/UITabBarController+ScreenIterableContainer.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | extension UITabBarController: ScreenIterableContainer {
5 |
6 | /// Returns nested containers in tabs, with the `selectedViewController` container being the last.
7 | ///
8 | /// - SeeAlso: `viewControllers`
9 | /// - SeeAlso: `selectedViewController`
10 | public var nestedContainers: [ScreenContainer] {
11 | let tabContainers = viewControllers ?? []
12 |
13 | return selectedViewController.map { selectedContainer in
14 | tabContainers
15 | .removingAll { $0 === selectedContainer }
16 | .appending(selectedContainer)
17 | } ?? tabContainers
18 | }
19 | }
20 | #endif
21 |
--------------------------------------------------------------------------------
/Sources/Deeplink/Errors/DeeplinkInvalidContextError.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// The `Context` instance is not supported by the deeplink type.
4 | public struct DeeplinkInvalidContextError: DeeplinkError {
5 |
6 | public let description: String
7 |
8 | /// Creates an error.
9 | ///
10 | /// - Parameters:
11 | /// - context: Context instance.
12 | /// - type: Expected context type.
13 | /// - trigger: The deeplink that caused the error.
14 | public init(
15 | context: Any?,
16 | type: Any.Type,
17 | for trigger: Any
18 | ) {
19 | description = """
20 | The type of the context \(context ?? "nil") does not match the expected type \(type) for:
21 | \(trigger)
22 | """
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Sources/Deeplink/Errors/DeeplinkInvalidScreensError.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// The `Screens` instance is not supported by the deeplink type.
4 | public struct DeeplinkInvalidScreensError: DeeplinkError {
5 |
6 | public let description: String
7 |
8 | /// Creates an error.
9 | ///
10 | /// - Parameters:
11 | /// - screens: Screens instance.
12 | /// - type: Expected screens type.
13 | /// - trigger: The deeplink that caused the error.
14 | public init(
15 | screens: Any?,
16 | type: Any.Type,
17 | for trigger: Any
18 | ) {
19 | description = """
20 | The type of the screens \(screens ?? "nil") does not match the expected type \(type) for:
21 | \(trigger)
22 | """
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Sources/Screen/Actions/Tabs/SelectTab/Animations/ScreenTabAnimation.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | @MainActor
5 | @frozen
6 | public enum ScreenTabAnimation {
7 |
8 | case custom(ScreenTabCustomAnimation)
9 |
10 | public func animate(
11 | container: UITabBarController,
12 | from selectedTab: UIViewController,
13 | to newSelectedTab: UIViewController,
14 | completion: @escaping () -> Void
15 | ) {
16 | switch self {
17 | case let .custom(animation):
18 | animation.animate(
19 | container: container,
20 | from: selectedTab,
21 | to: newSelectedTab,
22 | completion: completion
23 | )
24 | }
25 | }
26 | }
27 | #endif
28 |
--------------------------------------------------------------------------------
/Scripts/Bootstrap/bundler.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | readonly arguments=$@
4 | readonly script_path="$( cd "$( dirname "$0" )" && pwd )"
5 |
6 | source "${script_path}/common.sh"
7 |
8 | echo "Checking ${bundler_style}Bundler${default_style} installation:"
9 |
10 | if [[ "$(uname -m)" == "arm64" ]]; then
11 | eval "$(/opt/homebrew/bin/brew shellenv)"
12 | fi
13 |
14 | eval "$(rbenv init -)"
15 |
16 | if rbenv which bundler &> /dev/null; then
17 | if [[ " ${arguments[*]} " == *" ${update_flag} "* ]]; then
18 | echo " Bundler already installed. Updating..."
19 | assert_failure 'gem update bundler'
20 | else
21 | echo " Bundler already installed."
22 | fi
23 | else
24 | echo " Bundler not found. Installing..."
25 | assert_failure 'gem install bundler'
26 | fi
27 |
28 | echo ""
29 |
--------------------------------------------------------------------------------
/Scripts/Bootstrap/macos.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | readonly script_path="$( cd "$( dirname "$0" )" && pwd )"
4 |
5 | source "${script_path}/common.sh"
6 |
7 | plain_version() {
8 | echo "$@" | awk -F. '{ printf("%d%03d%03d%03d", $1,$2,$3,$4); }'
9 | }
10 |
11 | echo "Checking ${macos_style}macOS${default_style} version:"
12 |
13 | readonly macos_required_version='11.3.0'
14 | readonly macos_version=$(/usr/bin/sw_vers -productVersion 2>&1)
15 |
16 | if [ "$(plain_version ${macos_version})" -lt "$(plain_version ${macos_required_version})" ]; then
17 | echo " ${error_style}Your macOS version (${macos_version}) is older then required version (${macos_required_version}). Exiting...${default_style}"
18 | exit 1
19 | else
20 | echo " Your macOS version: ${macos_version}"
21 | fi
22 |
23 | echo ""
24 |
--------------------------------------------------------------------------------
/Sources/Screen/Actions/Stack/SetStack/Modifiers/ScreenStackClearModifier.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | public struct ScreenStackClearModifier: ScreenStackModifier {
5 |
6 | public let description: String
7 |
8 | public init() {
9 | description = "Clear"
10 | }
11 |
12 | public func perform(
13 | stack: [UIViewController],
14 | navigator: ScreenNavigator
15 | ) -> [UIViewController] {
16 | []
17 | }
18 | }
19 |
20 | extension ScreenThenable where Current: UINavigationController {
21 |
22 | public func clear(animation: ScreenStackAnimation? = .default) -> Self {
23 | setStack(
24 | modifier: ScreenStackClearModifier(),
25 | animation: animation
26 | )
27 | }
28 | }
29 | #endif
30 |
--------------------------------------------------------------------------------
/Sources/Screen/Observation/ScreenObserverToken.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// Token associated with the subscription lifecycle for the observer.
4 | ///
5 | /// Once the token is removed from memory (deinit is called),
6 | /// the subscription will be canceled and new notifications will not be received by the observer.
7 | /// The subscription can also be canceled manually using the ``cancel()`` method.
8 | public final class ScreenObserverToken {
9 |
10 | private let cancellation: () -> Void
11 |
12 | internal init(cancellation: @escaping () -> Void) {
13 | self.cancellation = cancellation
14 | }
15 |
16 | deinit {
17 | cancellation()
18 | }
19 |
20 | /// Unsubscribe the observer.
21 | public func cancel() {
22 | cancellation()
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Sources/Screen/Route/ScreenRouteConvertible.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// A type with a root route.
4 | ///
5 | /// Can be used to erase the `ScreenRoute` type.
6 | ///
7 | /// - SeeAlso: `ScreenRoute`
8 | /// - SeeAlso: `ScreenRootRoute`
9 | @MainActor
10 | public protocol ScreenRouteConvertible {
11 |
12 | /// Returns the root route with the actions of the current instance.
13 | ///
14 | /// - Returns: An instance containing all the actions.
15 | func route() -> ScreenRootRoute
16 | }
17 |
18 | extension ScreenRoute: ScreenRouteConvertible {
19 |
20 | public func route() -> ScreenRootRoute {
21 | ScreenRootRoute(actions: actions as? [AnyScreenAction] ?? [])
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Sources/Addons/HUD/Progress/Animation/ProgressCustomAnimation.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | /// A type describing the animation of updating progress view of``HUD``.
5 | ///
6 | /// See ``ProgressDefaultAnimation`` for example.
7 | @MainActor
8 | public protocol ProgressCustomAnimation {
9 |
10 | /// Animates updates of parts of the progress view.
11 | /// - Parameters:
12 | /// - view: View of the `header`, `indicator` or `footer` part.
13 | /// - previousView: Previous view of the `header`, `indicator` or `footer` part.
14 | /// - containerView: Container view of the `header`, `indicator` or `footer` part.
15 | func animateView(
16 | _ view: UIView,
17 | previousView: UIView,
18 | containerView: UIView
19 | )
20 | }
21 | #endif
22 |
--------------------------------------------------------------------------------
/Nivelir.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |spec|
2 | spec.name = "Nivelir"
3 | spec.version = "1.9.10"
4 | spec.summary = "A Swift DSL for navigation in iOS and tvOS apps with a simplified, chainable, and compile time safe syntax."
5 |
6 | spec.homepage = "https://github.com/hhru/Nivelir"
7 | spec.license = { :type => 'MIT', :file => 'LICENSE' }
8 | spec.author = { "Almaz Ibragimov" => "almazrafi@gmail.com" }
9 | spec.source = { :git => "https://github.com/hhru/Nivelir.git", :tag => "#{spec.version}" }
10 |
11 | spec.swift_version = '6.0'
12 | spec.requires_arc = true
13 | spec.source_files = 'Sources/**/*.swift'
14 |
15 | spec.ios.frameworks = 'Foundation'
16 | spec.ios.deployment_target = "13.0"
17 |
18 | spec.tvos.frameworks = 'Foundation'
19 | spec.tvos.deployment_target = "13.0"
20 | end
21 |
--------------------------------------------------------------------------------
/Sources/Screen/Container/ScreenVisibleContainer.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | #if canImport(UIKit)
4 | import UIKit
5 | #endif
6 |
7 | /// A screen container that can be visible.
8 | ///
9 | /// This is a simple protocol that is used to determine the visibility of a container.
10 | /// The `UIWindow` and `UIViewController` classes and all their subclasses already implement this protocol
11 | ///
12 | /// - SeeAlso: `ScreenContainer`
13 | @MainActor
14 | public protocol ScreenVisibleContainer: ScreenContainer {
15 |
16 | /// A Boolean value indicating whether the container is visible.
17 | var isVisible: Bool { get }
18 |
19 | #if canImport(UIKit)
20 | /// The scene containing the container.
21 | @available(iOS 13.0, tvOS 13.0, *)
22 | var windowScene: UIWindowScene? { get }
23 | #endif
24 | }
25 |
--------------------------------------------------------------------------------
/Sources/Tools/ObjectAssociation.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | internal final class ObjectAssociation: Sendable {
4 |
5 | private let policy: objc_AssociationPolicy
6 |
7 | internal init(policy: objc_AssociationPolicy = .OBJC_ASSOCIATION_RETAIN_NONATOMIC) {
8 | self.policy = policy
9 | }
10 |
11 | internal subscript(object: AnyObject) -> T? {
12 | get {
13 | objc_getAssociatedObject(
14 | object,
15 | Unmanaged.passRetained(self).toOpaque()
16 | ) as? T
17 | }
18 |
19 | set {
20 | objc_setAssociatedObject(
21 | object,
22 | Unmanaged.passRetained(self).toOpaque(),
23 | newValue,
24 | policy
25 | )
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Sources/Addons/HUD/Progress/Content/AnyProgressContent.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | /// Erased protocol type ``ProgressContent``.
5 | ///
6 | /// - SeeAlso: ``ProgressContent``
7 | @MainActor
8 | public protocol AnyProgressContent {
9 |
10 | /// A console log representation of `self`.
11 | var logDescription: String? { get }
12 |
13 | /// Updates or creates a new instance of the content view using this content
14 | /// if the `contentView` type matches the ``ProgressContent/View`` type.
15 | /// - Parameter contentView: The current view associated with the content.
16 | func updateContentViewIfPossible(_ contentView: UIView?) -> UIView
17 | }
18 |
19 | extension AnyProgressContent {
20 |
21 | public var logDescription: String? {
22 | nil
23 | }
24 | }
25 | #endif
26 |
--------------------------------------------------------------------------------
/Sources/Screen/Container/ScreenContainer.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// A screen container on which the navigation is performed.
4 | ///
5 | /// Any navigation is performed on containers, for UIKit they are divided into several types:
6 | /// - Window container : instances of `UIWindow`
7 | /// - Tabs container: instances of `UITabBarController`
8 | /// - Stack container : instances of `UINavigationController`
9 | /// - Modal container : instances of `UIViewController`
10 | ///
11 | /// Since `UITabBarController` and `UINavigationController` are subclasses of `UIViewController`,
12 | /// they are also modal containers.
13 | ///
14 | /// This protocol is already implemented by the `UIWindow`, `UIViewController` classes and all their subclasses.
15 | ///
16 | /// - SeeAlso: `Screen`
17 | public protocol ScreenContainer { }
18 |
--------------------------------------------------------------------------------
/Tests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Sources/Screen/Navigator/Logger/DefaultScreenLogger.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// Default Logger implementation.
4 | public final class DefaultScreenLogger: ScreenLogger {
5 |
6 | public let isInfoEnabled: Bool
7 | public let isErrorsEnabled: Bool
8 |
9 | public init(
10 | isInfoEnabled: Bool = true,
11 | isErrorsEnabled: Bool = true
12 | ) {
13 | self.isInfoEnabled = isInfoEnabled
14 | self.isErrorsEnabled = isErrorsEnabled
15 | }
16 |
17 | public func info(_ info: @autoclosure () -> String) {
18 | if isInfoEnabled {
19 | print("Info: \(info())")
20 | }
21 | }
22 |
23 | public func error(_ error: @autoclosure () -> Error) {
24 | if isErrorsEnabled {
25 | print("Error: \(error())")
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Sources/Addons/BottomSheet/Action/InvalidBottomSheetContainerError.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | public struct InvalidBottomSheetContainerError: ScreenError {
4 |
5 | public let description: String
6 |
7 | public init(container: ScreenContainer, for trigger: Any) {
8 | description = """
9 | The container \(container) is not presented as a bottom sheet for:
10 | \(trigger)
11 | """
12 | }
13 | }
14 |
15 | extension Result where Failure == Error {
16 |
17 | internal static func invalidBottomSheetContainer(
18 | _ container: ScreenContainer,
19 | for trigger: Any
20 | ) -> Self {
21 | .failure(
22 | InvalidBottomSheetContainerError(
23 | container: container,
24 | for: trigger
25 | )
26 | )
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/docs/js/highlight-js-json.471128d2.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * This source file is part of the Swift.org open source project
3 | *
4 | * Copyright (c) 2021 Apple Inc. and the Swift project authors
5 | * Licensed under Apache License v2.0 with Runtime Library Exception
6 | *
7 | * See https://swift.org/LICENSE.txt for license information
8 | * See https://swift.org/CONTRIBUTORS.txt for Swift project authors
9 | */
10 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["highlight-js-json"],{"5ad2":function(n,e){function a(n){const e={className:"attr",begin:/"(\\.|[^\\"\r\n])*"(?=\s*:)/,relevance:1.01},a={match:/[{}[\],:]/,className:"punctuation",relevance:0},s={beginKeywords:["true","false","null"].join(" ")};return{name:"JSON",contains:[e,a,n.QUOTE_STRING_MODE,s,n.C_NUMBER_MODE,n.C_LINE_COMMENT_MODE,n.C_BLOCK_COMMENT_MODE],illegal:"\\S"}}n.exports=a}}]);
--------------------------------------------------------------------------------
/Sources/Addons/BottomSheet/BottomSheetShadow.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | public struct BottomSheetShadow: Equatable, Sendable {
5 |
6 | public static let `default` = Self()
7 |
8 | public let offset: CGSize
9 | public let radius: CGFloat
10 | public let color: UIColor?
11 | public let opacity: Float
12 | public let shouldRasterize: Bool
13 |
14 | public init(
15 | offset: CGSize = CGSize(width: .zero, height: -3),
16 | radius: CGFloat = 3.0,
17 | color: UIColor? = .black,
18 | opacity: Float = .zero,
19 | shouldRasterize: Bool = false
20 | ) {
21 | self.offset = offset
22 | self.radius = radius
23 | self.color = color
24 | self.opacity = opacity
25 | self.shouldRasterize = shouldRasterize
26 | }
27 | }
28 | #endif
29 |
--------------------------------------------------------------------------------
/Sources/Deeplink/Errors/DeeplinkWarningsError.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | internal struct DeeplinkWarningsError: DeeplinkError {
4 |
5 | internal var description: String {
6 | let warnings = warnings
7 | .map { "\($0)" }
8 | .joined(separator: .newLine)
9 | .indented(spaces: 2)
10 |
11 | return """
12 | Failed to handle deeplink of \(deeplinkType) type with warnings:
13 | \(warnings)
14 | """
15 | }
16 |
17 | internal var isWarning: Bool {
18 | true
19 | }
20 |
21 | internal let deeplinkType: Any.Type
22 | internal let warnings: [Error]
23 |
24 | internal init(
25 | deeplinkType: Any.Type,
26 | warnings: [Error]
27 | ) {
28 | self.deeplinkType = deeplinkType
29 | self.warnings = warnings
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Sources/Screen/Actions/Generic/Refresh/ScreenRefreshAction.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | public struct ScreenRefreshAction: ScreenAction {
4 |
5 | public typealias Output = Void
6 |
7 | public init() { }
8 |
9 | public func perform(
10 | container: Container,
11 | navigator: ScreenNavigator,
12 | completion: @escaping Completion
13 | ) {
14 | guard let refreshableContainer = container as? ScreenRefreshableContainer else {
15 | return completion(.containerTypeMismatch(container, type: ScreenRefreshableContainer.self, for: self))
16 | }
17 |
18 | refreshableContainer.refresh { completion(.success) }
19 | }
20 | }
21 |
22 | extension ScreenThenable {
23 |
24 | public func refresh() -> Self {
25 | then(ScreenRefreshAction())
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Sources/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | $(MARKETING_VERSION)
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Sources/Deeplink/Notification/Errors/NotificationDeeplinkInvalidUserInfoError.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UserNotifications) && os(iOS)
2 | import Foundation
3 | import UserNotifications
4 |
5 | /// Failed to extract userInfo from response to the notification.
6 | public struct NotificationDeeplinkInvalidUserInfoError: DeeplinkError {
7 |
8 | public let description: String
9 |
10 | /// Creates an error.
11 | ///
12 | /// - Parameters:
13 | /// - response: Response to the notification that caused the error.
14 | /// - trigger: The deeplink that caused the error.
15 | public init(
16 | response: UNNotificationResponse,
17 | for trigger: Any
18 | ) {
19 | description = """
20 | Failed to extract userInfo from response to the notification for:
21 | \(trigger)
22 | """
23 | }
24 | }
25 | #endif
26 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Brand Assets.brandassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "assets" : [
3 | {
4 | "filename" : "App Icon - App Store.imagestack",
5 | "idiom" : "tv",
6 | "role" : "primary-app-icon",
7 | "size" : "1280x768"
8 | },
9 | {
10 | "filename" : "App Icon.imagestack",
11 | "idiom" : "tv",
12 | "role" : "primary-app-icon",
13 | "size" : "400x240"
14 | },
15 | {
16 | "filename" : "Top Shelf Image Wide.imageset",
17 | "idiom" : "tv",
18 | "role" : "top-shelf-image-wide",
19 | "size" : "2320x720"
20 | },
21 | {
22 | "filename" : "Top Shelf Image.imageset",
23 | "idiom" : "tv",
24 | "role" : "top-shelf-image",
25 | "size" : "1920x720"
26 | }
27 | ],
28 | "info" : {
29 | "author" : "xcode",
30 | "version" : 1
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Scripts/generate-docs.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | readonly helpers_path="$( cd "$( dirname "$0" )" && pwd )/Helpers"
4 | readonly target_name="Nivelir iOS"
5 | readonly derived_data_path="DerivedData"
6 | readonly docs_path="docs"
7 | readonly docarchive_file_path="${derived_data_path}/Build/Products/Debug-iphonesimulator/Nivelir.doccarchive"
8 | readonly url_base_path="Nivelir"
9 |
10 | source "${helpers_path}/script-paths.sh"
11 |
12 | cd $root_path
13 |
14 | xcodebuild docbuild \
15 | -scheme "${target_name}" \
16 | -derivedDataPath "${derived_data_path}" \
17 | -destination 'platform=iOS Simulator,name=iPhone 13'
18 |
19 | rm -rf "${docs_path}"
20 |
21 | $(xcrun --find docc) process-archive \
22 | transform-for-static-hosting "${docarchive_file_path}" \
23 | --output-path "${docs_path}" \
24 | --hosting-base-path "${url_base_path}"
25 |
26 | rm -rf "${derived_data_path}"
27 |
--------------------------------------------------------------------------------
/Sources/Addons/MediaPicker/MediaPickerManager.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit) && os(iOS)
2 | import UIKit
3 |
4 | internal final class MediaPickerManager:
5 | NSObject,
6 | UINavigationControllerDelegate,
7 | UIImagePickerControllerDelegate {
8 |
9 | nonisolated private let proxy: MediaPickerProxy
10 |
11 | internal init(mediaPicker: MediaPicker) {
12 | self.proxy = MediaPickerProxy(mediaPicker: mediaPicker)
13 | }
14 |
15 | internal override func responds(to aSelector: Selector?) -> Bool {
16 | super.responds(to: aSelector) || proxy.responds(to: aSelector) == true
17 | }
18 |
19 | internal override func forwardingTarget(for aSelector: Selector?) -> Any? {
20 | if proxy.responds(to: aSelector) == true {
21 | return proxy
22 | }
23 |
24 | return super.forwardingTarget(for: aSelector)
25 | }
26 | }
27 | #endif
28 |
--------------------------------------------------------------------------------
/Sources/Tools/Extensions/UIKit/UIWindow+Extensions.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | extension UIWindow {
5 |
6 | /// The root container for the window.
7 | ///
8 | /// The root container provides the content view of the window.
9 | /// Assigning a container to this property (either programmatically or using Interface Builder)
10 | /// installs the container’s view as the content view of the window.
11 | /// The new content view is configured to track the window size, changing as the window size changes.
12 | /// If the window has an existing view hierarchy, the old views are removed before the new ones are installed.
13 | ///
14 | /// The default value of this property is `nil`.
15 | ///
16 | /// - SeeAlso: `rootViewController`
17 | public var root: UIViewController? {
18 | rootViewController
19 | }
20 | }
21 | #endif
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## OS X files
2 | .DS_Store
3 | .DS_Store?
4 | .Trashes
5 | .Spotlight-V100
6 | *.swp
7 |
8 | ## Xcode build files
9 | DerivedData/
10 | build/
11 |
12 | ## Xcode private settings
13 | *.pbxuser
14 | !default.pbxuser
15 | *.mode1v3
16 | !default.mode1v3
17 | *.mode2v3
18 | !default.mode2v3
19 | *.perspectivev3
20 | !default.perspectivev3
21 | xcuserdata/
22 |
23 | ## Other
24 | *.xccheckout
25 | *.moved-aside
26 | *.xcuserstate
27 | *.xcscmblueprint
28 |
29 | ## Obj-C/Swift specific
30 | *.hmap
31 | *.ipa
32 | *.dSYM.zip
33 | *.dSYM
34 |
35 | ## Playgrounds
36 | timeline.xctimeline
37 | playground.xcworkspace
38 |
39 | # Swift Packages Manager
40 | Packages
41 | .build
42 | .swiftpm
43 |
44 | # CocoaPods
45 | Pods/
46 |
47 | # Carthage
48 | Carthage/Build
49 |
50 | # fastlane
51 | fastlane/report.xml
52 | fastlane/Preview.html
53 | fastlane/screenshots/**/*.png
54 | fastlane/test_output
55 |
--------------------------------------------------------------------------------
/Sources/Addons/Alert/AlertTextField.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | /// Types of text fields added to the alert.
5 | @frozen
6 | public enum AlertTextField: Sendable {
7 |
8 | /// A text field, with text and placeholder customization.
9 | case standard(
10 | text: String,
11 | placeholder: String? = nil
12 | )
13 |
14 | /// A text field, customizable via block for configuring the text field prior to displaying the alert.
15 | /// This block has no return value and takes a single parameter corresponding to the text field object.
16 | /// Use that parameter to change the text field properties.
17 | case custom(configuration: @Sendable (UITextField) -> Void)
18 | }
19 |
20 | extension AlertTextField {
21 |
22 | /// A text field, with empty text and a placeholder.
23 | public static let standard = Self.standard(text: "")
24 | }
25 | #endif
26 |
--------------------------------------------------------------------------------
/Sources/Screen/Payload/ScreenPayloadedContainer.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// A screen container that can store navigation data.
4 | ///
5 | /// This protocol is used in screen decorators, which can generate data
6 | /// that needs to be stored in memory until the container itself is released.
7 | ///
8 | /// This protocol is already implemented by the `UIViewController` class and all its subclasses.
9 | /// The default implementation for `NSObject` subclasses uses the `objc_setAssociatedObject` method
10 | /// to associate the payload with the container.
11 | ///
12 | /// - SeeAlso: `ScreenContainer`
13 | @MainActor
14 | public protocol ScreenPayloadedContainer: ScreenContainer {
15 |
16 | /// Screen payload.
17 | ///
18 | /// This object stores the data generated by navigation.
19 | ///
20 | /// - SeeAlso: `ScreenPayload`
21 | var screenPayload: ScreenPayload { get }
22 | }
23 |
--------------------------------------------------------------------------------
/Sources/Tools/URLQueryDecoder/URLQueryComponent.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | internal indirect enum URLQueryComponent {
4 |
5 | case string(String)
6 | case array([Int: Self])
7 | case dictionary([String: Self])
8 |
9 | internal var string: String? {
10 | switch self {
11 | case let .string(string):
12 | return string
13 |
14 | default:
15 | return nil
16 | }
17 | }
18 |
19 | internal var array: [Int: Self]? {
20 | switch self {
21 | case let .array(array):
22 | return array
23 |
24 | default:
25 | return nil
26 | }
27 | }
28 |
29 | internal var dictionary: [String: Self]? {
30 | switch self {
31 | case let .dictionary(dictionary):
32 | return dictionary
33 |
34 | default:
35 | return nil
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Scripts/Bootstrap/swift.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | readonly script_path="$( cd "$( dirname "$0" )" && pwd )"
4 |
5 | source "${script_path}/common.sh"
6 |
7 | echo "Checking ${swift_style}Swift${default_style} version:"
8 |
9 | if [[ "$(uname -m)" == "arm64" ]]; then
10 | eval "$(/opt/homebrew/bin/brew shellenv)"
11 | fi
12 |
13 | eval "$(swiftenv init -)"
14 |
15 | readonly swift_required_version=$(cat "${root_path}"/.swift-version)
16 | readonly swift_versions=($(swiftenv versions 2>&1))
17 |
18 | if [[ " ${swift_versions[@]} " =~ " ${swift_required_version} " ]]; then
19 | echo " Required Swift version ($swift_required_version) already installed."
20 | else
21 | echo " Required Swift version ($swift_required_version) not found. Installing..."
22 | assert_failure '(cd "${root_path}" && swiftenv install $swift_required_version)'
23 | assert_warning '(cd "${root_path}" && swiftenv rehash)'
24 | fi
25 |
26 | echo ""
27 |
--------------------------------------------------------------------------------
/Sources/Addons/BottomSheet/BottomSheetCard.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | public struct BottomSheetCard: Equatable, Sendable {
5 |
6 | public static let `default` = Self()
7 |
8 | public let backgroundColor: UIColor?
9 | public let cornerRadius: CGFloat
10 | public let border: BottomSheetBorder?
11 | public let shadow: BottomSheetShadow?
12 | public let contentInsets: UIEdgeInsets
13 |
14 | public init(
15 | backgroundColor: UIColor? = nil,
16 | cornerRadius: CGFloat = 16.0,
17 | border: BottomSheetBorder? = nil,
18 | shadow: BottomSheetShadow? = nil,
19 | contentInsets: UIEdgeInsets = .zero
20 | ) {
21 | self.backgroundColor = backgroundColor
22 | self.cornerRadius = cornerRadius
23 | self.border = border
24 | self.shadow = shadow
25 | self.contentInsets = contentInsets
26 | }
27 | }
28 | #endif
29 |
--------------------------------------------------------------------------------
/Scripts/Bootstrap/swiftenv.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | readonly arguments=$@
4 | readonly script_path="$( cd "$( dirname "$0" )" && pwd )"
5 |
6 | source "${script_path}/common.sh"
7 |
8 | readonly shell_init_line='if which swiftenv > /dev/null; then eval "$(swiftenv init -)"; fi'
9 |
10 | setup_shell() {
11 | local shell_profile_path=$1
12 |
13 | if [[ ! -f "${shell_profile_path}" ]]; then
14 | > "${shell_profile_path}"
15 | fi
16 |
17 | if [[ $(grep -L "${shell_init_line}" "${shell_profile_path}") ]]; then
18 | echo "${shell_init_line}" >> "${shell_profile_path}"
19 | fi
20 | }
21 |
22 | echo "Checking ${swiftenv_style}swiftenv${default_style} installation:"
23 |
24 | brew_install_if_needed kylef/formulae/swiftenv "$arguments"
25 | setup_shell "${HOME}/.zshrc"
26 |
27 | if [[ -f "${HOME}/.bash_profile" ]]; then
28 | setup_shell "${HOME}/.bash_profile"
29 | fi
30 |
31 | eval "$(swiftenv init -)"
32 |
33 | echo ""
34 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Colors/Icon.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "display-p3",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.200",
9 | "green" : "0.200",
10 | "red" : "0.200"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "srgb",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "1.000",
27 | "green" : "1.000",
28 | "red" : "1.000"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Colors/Title.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "display-p3",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.200",
9 | "green" : "0.200",
10 | "red" : "0.200"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "srgb",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "1.000",
27 | "green" : "1.000",
28 | "red" : "1.000"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Colors/Unimportant.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.557",
9 | "green" : "0.557",
10 | "red" : "0.557"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "srgb",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "0.643",
27 | "green" : "0.643",
28 | "red" : "0.643"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Sources/Addons/HUD/Progress/Content/ProgressContentView.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | /// The requirements for a content view that you create using a content.
5 | ///
6 | /// This protocol provides a blueprint for a content view object that renders the content that you define.
7 | /// The content view’s content encapsulates all of the supported properties
8 | /// and behaviors for content view customization.
9 | public protocol ProgressContentView: UIView {
10 |
11 | /// The content type associated with the view.
12 | /// The content must be associated with this view type, creating a one-to-one relationship.
13 | associatedtype Content: ProgressContent
14 |
15 | /// The current content of the view.
16 | var content: Content { get }
17 |
18 | /// Creating a view with content.
19 | /// - Parameter content: Content for the initial setup of the view.
20 | init(content: Content)
21 | }
22 | #endif
23 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "display-p3",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.314",
9 | "green" : "0.196",
10 | "red" : "0.843"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "display-p3",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "0.314",
27 | "green" : "0.196",
28 | "red" : "0.843"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Colors/Background.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "1.000",
9 | "green" : "1.000",
10 | "red" : "1.000"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "display-p3",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "0.129",
27 | "green" : "0.129",
28 | "red" : "0.129"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Resources/Assets.xcassets/Colors/Important.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "display-p3",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.314",
9 | "green" : "0.196",
10 | "red" : "0.843"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "display-p3",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "0.314",
27 | "green" : "0.196",
28 | "red" : "0.843"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Services/Authorization/DefaultAuthorizationService.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | final class DefaultAuthorizationService: AuthorizationService {
4 |
5 | var isAuthorized: Bool {
6 | get { UserDefaults.standard.bool(forKey: #function) }
7 | set { UserDefaults.standard.set(newValue, forKey: #function) }
8 | }
9 |
10 | func login(
11 | phoneNumber: String,
12 | completion: @escaping (_ result: Result) -> Void
13 | ) {
14 | DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
15 | let random = Int.random(in: 1...10)
16 |
17 | guard random < 8 else {
18 | return completion(.failure(AuthorizationError.unavailable))
19 | }
20 |
21 | self.isAuthorized = true
22 |
23 | completion(.success(Void()))
24 | }
25 | }
26 |
27 | func logout() {
28 | self.isAuthorized = false
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Sources/Tools/Extensions/UIKit/CACornerMask+Extensions.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | extension CACornerMask {
5 |
6 | internal static var allCorners: CACornerMask {
7 | [
8 | .layerMinXMinYCorner,
9 | .layerMinXMaxYCorner,
10 | .layerMaxXMinYCorner,
11 | .layerMaxXMaxYCorner
12 | ]
13 | }
14 |
15 | internal var rectCorners: UIRectCorner {
16 | let cornerMaskMap: KeyValuePairs = [
17 | .layerMinXMinYCorner: .topLeft,
18 | .layerMinXMaxYCorner: .bottomLeft,
19 | .layerMaxXMinYCorner: .topRight,
20 | .layerMaxXMaxYCorner: .bottomRight
21 | ]
22 |
23 | return cornerMaskMap
24 | .lazy
25 | .filter { contains($0.key) }
26 | .reduce(into: UIRectCorner()) { result, corner in
27 | result.insert(corner.value)
28 | }
29 | }
30 | }
31 | #endif
32 |
--------------------------------------------------------------------------------
/docs/js/highlight-js-diff.62d66733.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * This source file is part of the Swift.org open source project
3 | *
4 | * Copyright (c) 2021 Apple Inc. and the Swift project authors
5 | * Licensed under Apache License v2.0 with Runtime Library Exception
6 | *
7 | * See https://swift.org/LICENSE.txt for license information
8 | * See https://swift.org/CONTRIBUTORS.txt for Swift project authors
9 | */
10 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["highlight-js-diff"],{"48b8":function(e,n){function a(e){const n=e.regex;return{name:"Diff",aliases:["patch"],contains:[{className:"meta",relevance:10,match:n.either(/^@@ +-\d+,\d+ +\+\d+,\d+ +@@/,/^\*\*\* +\d+,\d+ +\*\*\*\*$/,/^--- +\d+,\d+ +----$/)},{className:"comment",variants:[{begin:n.either(/Index: /,/^index/,/={3,}/,/^-{3}/,/^\*{3} /,/^\+{3}/,/^diff --git/),end:/$/},{match:/^\*{15}$/}]},{className:"addition",begin:/^\+/,end:/$/},{className:"deletion",begin:/^-/,end:/$/},{className:"addition",begin:/^!/,end:/$/}]}}e.exports=a}}]);
--------------------------------------------------------------------------------
/Sources/Addons/MediaPicker/MediaPickerProxy.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit) && os(iOS)
2 | import UIKit
3 |
4 | internal final class MediaPickerProxy: NSObject, Sendable {
5 |
6 | private let mediaPicker: MediaPicker
7 |
8 | internal init(mediaPicker: MediaPicker) {
9 | self.mediaPicker = mediaPicker
10 | }
11 |
12 | @MainActor
13 | @objc internal func imagePickerController(
14 | _ picker: UIImagePickerController,
15 | didFinishPickingMediaWithInfo rawInfo: [String: Any]
16 | ) {
17 | var info: [UIImagePickerController.InfoKey: Any] = [:]
18 |
19 | for (key, value) in rawInfo {
20 | info[UIImagePickerController.InfoKey(rawValue: key)] = value
21 | }
22 |
23 | mediaPicker.didFinish(MediaPickerResult(info: info))
24 | }
25 |
26 | @MainActor
27 | @objc internal func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
28 | mediaPicker.didFinish(nil)
29 | }
30 | }
31 | #endif
32 |
--------------------------------------------------------------------------------
/Scripts/Bootstrap/ruby.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | readonly script_path="$( cd "$( dirname "$0" )" && pwd )"
4 |
5 | source "${script_path}/common.sh"
6 |
7 | echo "Checking ${ruby_style}Ruby${default_style} version:"
8 |
9 | if [[ "$(uname -m)" == "arm64" ]]; then
10 | eval "$(/opt/homebrew/bin/brew shellenv)"
11 | fi
12 |
13 | eval "$(rbenv init -)"
14 |
15 | readonly ruby_required_version=$(cat "${root_path}"/.ruby-version)
16 | readonly ruby_versions=($(rbenv versions 2>&1))
17 | readonly ruby_install_flags="-Wno-error=implicit-function-declaration"
18 |
19 | if [[ " ${ruby_versions[@]} " =~ " ${ruby_required_version} " ]]; then
20 | echo " Required Ruby version ($ruby_required_version) already installed."
21 | else
22 | echo " Required Ruby version ($ruby_required_version) not found. Installing..."
23 | assert_failure '(cd "${root_path}" && RUBY_CFLAGS="${ruby_install_flags}" rbenv install $ruby_required_version)'
24 | assert_warning '(cd "${root_path}" && rbenv rehash)'
25 | fi
26 |
27 | echo ""
28 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Routing/Sharing/SharingNivelirLinkItem.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import Nivelir
3 |
4 | struct SharingNivelirLinkItem: SharingCustomItem {
5 |
6 | let url = URL(string: "https://github.com/hhru/Nivelir")!
7 |
8 | var placeholder: Any {
9 | url
10 | }
11 |
12 | func value(for activityType: SharingActivityType?) -> Any? {
13 | guard let activityType else {
14 | return url
15 | }
16 |
17 | switch activityType {
18 | case .copyToPasteboard, .openInBrowser:
19 | return url
20 |
21 | default:
22 | return """
23 | Nivelir: \(url)
24 |
25 | Send from iOS device.
26 | """
27 | }
28 | }
29 |
30 | func subject(for activityType: SharingActivityType?) -> String? {
31 | "Nivelir: \(url)"
32 | }
33 | }
34 |
35 | extension SharingItem {
36 |
37 | static var nivelirLink: Self {
38 | .custom(SharingNivelirLinkItem())
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Sources/Addons/Alert/AlertTextFieldsManager.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | @MainActor
5 | internal final class AlertTextFieldsManager: NSObject {
6 |
7 | private let textFields: [UITextField?]
8 | private let textsChanged: (_ texts: [String]) -> Void
9 |
10 | internal init(
11 | textFields: [UITextField?],
12 | textsChanged: @escaping (_ texts: [String]) -> Void
13 | ) {
14 | self.textFields = textFields
15 | self.textsChanged = textsChanged
16 |
17 | super.init()
18 |
19 | textFields.forEach { textField in
20 | textField?.addTarget(
21 | self,
22 | action: #selector(handleTextFieldEditingChanged),
23 | for: .editingChanged
24 | )
25 | }
26 |
27 | handleTextFieldEditingChanged()
28 | }
29 |
30 | @objc private func handleTextFieldEditingChanged() {
31 | textsChanged(textFields.map { $0?.text ?? "" })
32 | }
33 | }
34 | #endif
35 |
--------------------------------------------------------------------------------
/Sources/Screen/Errors/ScreenContainerNotFoundError.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// No container found.
4 | ///
5 | /// This error occurs whenever an action fails to find the container.
6 | public struct ScreenContainerNotFoundError: ScreenError {
7 |
8 | public let description: String
9 |
10 | /// Creates an error.
11 | ///
12 | /// - Parameters:
13 | /// - type: The type of container that could not be found.
14 | /// - trigger: The action that caused the error.
15 | public init(type: Any.Type, for trigger: Any) {
16 | description = """
17 | No container of \(type) type found for:
18 | \(trigger)
19 | """
20 | }
21 | }
22 |
23 | extension Result where Failure == Error {
24 |
25 | internal static func containerNotFound(type: Any.Type, for trigger: Any) -> Self {
26 | .failure(
27 | ScreenContainerNotFoundError(
28 | type: type,
29 | for: trigger
30 | )
31 | )
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Sources/Tools/Extensions/UIKit/UINavigationController+Extensions.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | extension UINavigationController {
5 |
6 | /// The root container of the stack.
7 | ///
8 | /// - SeeAlso: `viewControllers`
9 | public var stackRoot: UIViewController? {
10 | viewControllers.first
11 | }
12 |
13 | /// The container at the top of the stack.
14 | ///
15 | /// - SeeAlso: `topViewController`
16 | public var stackTop: UIViewController? {
17 | topViewController
18 | }
19 |
20 | /// The container associated with the currently visible view in the navigation interface.
21 | ///
22 | /// The currently visible container can belong either to the container at the top of the stack
23 | /// or to a container that was presented modally on top of the stack container itself.
24 | ///
25 | /// - SeeAlso: `visibleViewController`
26 | public var stackVisible: UIViewController? {
27 | visibleViewController
28 | }
29 | }
30 | #endif
31 |
--------------------------------------------------------------------------------
/Sources/Tools/NotificationObserver.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | internal class NotificationObserver {
4 |
5 | private var token: NSObjectProtocol?
6 |
7 | internal let center: NotificationCenter
8 | internal let name: Notification.Name
9 | internal let queue: OperationQueue?
10 | internal let handler: (@Sendable (_ notification: Notification) -> Void)?
11 |
12 | internal init(
13 | center: NotificationCenter = .default,
14 | name: Notification.Name,
15 | queue: OperationQueue? = nil,
16 | handler: (@Sendable (_ notification: Notification) -> Void)?
17 | ) {
18 | self.center = center
19 | self.name = name
20 | self.queue = queue
21 | self.handler = handler
22 |
23 | self.token = center.addObserver(forName: name, object: nil, queue: queue) { notification in
24 | handler?(notification)
25 | }
26 | }
27 |
28 | deinit {
29 | if let token {
30 | center.removeObserver(token)
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Sources/Screen/Any/AnyScreenBox.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | internal final class AnyScreenBox<
4 | Wrapped: Screen,
5 | Container: ScreenContainer
6 | >: AnyScreenBaseBox {
7 |
8 | internal typealias Builder = (
9 | _ screen: Wrapped,
10 | _ navigator: ScreenNavigator
11 | ) -> Container
12 |
13 | private let wrapped: Wrapped
14 | private let builder: Builder
15 |
16 | internal override var name: String {
17 | wrapped.name
18 | }
19 |
20 | internal override var traits: Set {
21 | wrapped.traits
22 | }
23 |
24 | private let _description: String
25 | internal override var description: String {
26 | _description
27 | }
28 |
29 | internal init(_ wrapped: Wrapped, builder: @escaping Builder) {
30 | self.wrapped = wrapped
31 | self.builder = builder
32 | _description = wrapped.description
33 | }
34 |
35 | internal override func build(navigator: ScreenNavigator) -> Container {
36 | builder(wrapped, navigator)
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Sources/Tools/URLQueryDecoder/Options/URLQueryDateDecodingStrategy.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// The strategies available for formatting dates when decoding them from URL.
4 | @frozen
5 | public enum URLQueryDateDecodingStrategy {
6 |
7 | /// The strategy that uses formatting from the Date structure.
8 | case deferredToDate
9 |
10 | /// The strategy that decodes dates in terms of seconds since midnight UTC on January 1st, 1970.
11 | case secondsSince1970
12 |
13 | /// The strategy that decodes dates in terms of milliseconds since midnight UTC on January 1st, 1970.
14 | case millisecondsSince1970
15 |
16 | /// The strategy that formats dates according to the ISO 8601 standard.
17 | @available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *)
18 | case iso8601
19 |
20 | /// The strategy that defers formatting settings to a supplied date formatter.
21 | case formatted(DateFormatter)
22 |
23 | /// The strategy that formats custom dates by calling a user-defined function.
24 | case custom((_ decoder: Decoder) throws -> Date)
25 | }
26 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Helpers/Reusable/UITableView+Reusable.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | extension UITableView {
4 |
5 | func registerReusableHeaderFooterView(of type: T.Type) {
6 | register(type, forHeaderFooterViewReuseIdentifier: T.reuseIdentifier)
7 | }
8 |
9 | func dequeueReusableHeaderFooterView(of type: T.Type) -> T {
10 | dequeueReusableHeaderFooterView(withIdentifier: T.reuseIdentifier) as! T
11 | }
12 |
13 | func registerReusableCell(of type: T.Type) {
14 | register(type, forCellReuseIdentifier: T.reuseIdentifier)
15 | }
16 |
17 | func dequeueReusableCell(of type: T.Type) -> T {
18 | dequeueReusableCell(withIdentifier: T.reuseIdentifier) as! T
19 | }
20 |
21 | func dequeueReusableCell(of type: T.Type, for indexPath: IndexPath) -> T {
22 | dequeueReusableCell(withIdentifier: T.reuseIdentifier, for: indexPath) as! T
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Scripts/Bootstrap/mint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | readonly arguments=$@
4 | readonly script_path="$( cd "$( dirname "$0" )" && pwd )"
5 |
6 | source "${script_path}/common.sh"
7 |
8 | readonly shell_mint_path_line="export MINT_PATH=\"\$HOME/.mint\""
9 | readonly shell_mint_link_path_line="export MINT_LINK_PATH=\"\$MINT_PATH/bin\""
10 |
11 | setup_shell() {
12 | local shell_profile_path=$1
13 |
14 | if [[ ! -f "${shell_profile_path}" ]]; then
15 | > "${shell_profile_path}"
16 | fi
17 |
18 | if [[ $(grep -L "${shell_mint_path_line}" "${shell_profile_path}") ]]; then
19 | echo "${shell_mint_path_line}" >> "${shell_profile_path}"
20 | fi
21 |
22 | if [[ $(grep -L "${shell_mint_link_path_line}" "${shell_profile_path}") ]]; then
23 | echo "${shell_mint_link_path_line}" >> "${shell_profile_path}"
24 | fi
25 | }
26 |
27 | echo "Checking ${mint_style}Mint${default_style} installation:"
28 |
29 | brew_install_if_needed mint "$arguments"
30 | setup_shell "${HOME}/.zshrc"
31 |
32 | if [[ -f "${HOME}/.bash_profile" ]]; then
33 | setup_shell "${HOME}/.bash_profile"
34 | fi
35 |
36 | echo ""
37 |
--------------------------------------------------------------------------------
/Sources/Screen/Navigator/Iterator/ScreenIterationPredicate.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// A closure box that takes a container as its argument and returns a `ScreenIterationResult` value,
4 | /// indicating whether to continue or stop iterating.
5 | public struct ScreenIterationPredicate {
6 |
7 | private let box: (_ container: ScreenContainer) -> ScreenIterationResult
8 |
9 | /// - Parameter box: A closure that takes a container as its argument and returns a `ScreenIterationResult` value,
10 | /// indicating whether to continue or stop iterating.
11 | public init(_ box: @escaping (_ container: ScreenContainer) -> ScreenIterationResult) {
12 | self.box = box
13 | }
14 |
15 | /// Returns a ScreenIterationResult value that indicates whether to continue or stop iterating.
16 | /// - Parameter container: The container against which to evaluate the predicate.
17 | /// - Returns: `ScreenIterationResult` with the iteration result.
18 | public func checkContainer(_ container: ScreenContainer) -> ScreenIterationResult {
19 | box(container)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Sources/Tools/DictionaryDecoder/Options/DictionaryDateDecodingStrategy.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// The strategies available for formatting dates when decoding them from Dictionary.
4 | @frozen
5 | public enum DictionaryDateDecodingStrategy {
6 |
7 | /// The strategy that uses formatting from the Date structure.
8 | case deferredToDate
9 |
10 | /// The strategy that decodes dates in terms of seconds since midnight UTC on January 1st, 1970.
11 | case secondsSince1970
12 |
13 | /// The strategy that decodes dates in terms of milliseconds since midnight UTC on January 1st, 1970.
14 | case millisecondsSince1970
15 |
16 | /// The strategy that formats dates according to the ISO 8601 standard.
17 | @available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *)
18 | case iso8601
19 |
20 | /// The strategy that defers formatting settings to a supplied date formatter.
21 | case formatted(DateFormatter)
22 |
23 | /// The strategy that formats custom dates by calling a user-defined function.
24 | case custom(@Sendable (_ decoder: Decoder) throws -> Date)
25 | }
26 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Dependencies/Services.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Nivelir
3 |
4 | struct Services {
5 |
6 | private let container = ServiceContainer()
7 |
8 | let window: UIWindow
9 |
10 | @MainActor
11 | func screenNavigator() -> ScreenNavigator {
12 | container.shared {
13 | ScreenNavigator(window: window)
14 | }
15 | }
16 |
17 | @MainActor
18 | func deeplinkManager() -> DeeplinkManager {
19 | container.shared {
20 | DeeplinkManager(
21 | deeplinkTypes: [
22 | ChatDeeplink.self
23 | ],
24 | navigator: screenNavigator()
25 | )
26 | }
27 | }
28 |
29 | func authorizationService() -> AuthorizationService {
30 | container.shared {
31 | DefaultAuthorizationService()
32 | }
33 | }
34 |
35 | @MainActor
36 | func profileService() -> ProfileService {
37 | DefaultProfileService(authorizationService: authorizationService())
38 | }
39 | }
40 |
41 | extension Services: ScreenAuthorizeActionServices { }
42 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 HeadHunter
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Sources/Addons/Sharing/Item/SharingItem.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit) && os(iOS)
2 | import UIKit
3 |
4 | @frozen
5 | public enum SharingItem: CustomStringConvertible, @unchecked Sendable {
6 |
7 | case regular(Any)
8 | case custom(SharingCustomItem)
9 |
10 | public var description: String {
11 | switch self {
12 | case let .regular(value):
13 | return "\(value)"
14 |
15 | case let .custom(value):
16 | return "\(value)"
17 | }
18 | }
19 |
20 | internal var activityItem: Any {
21 | switch self {
22 | case let .regular(value):
23 | return value
24 |
25 | case let .custom(value):
26 | return SharingItemManager(item: value)
27 | }
28 | }
29 |
30 | internal init(activityItem: Any) {
31 | switch activityItem {
32 | case let item as SharingCustomItem:
33 | self = .custom(item)
34 |
35 | case let manager as SharingItemManager:
36 | self = .custom(manager.item)
37 |
38 | default:
39 | self = .regular(activityItem)
40 | }
41 | }
42 | }
43 | #endif
44 |
--------------------------------------------------------------------------------
/docs/img/modified-icon.f496e73d.svg:
--------------------------------------------------------------------------------
1 |
10 |
11 |
--------------------------------------------------------------------------------
/Sources/Screen/Errors/ScreenContainerNotSupportedError.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// The container type is not supported.
4 | ///
5 | /// This error occurs whenever an action handles an unsupported container type.
6 | public struct ScreenContainerNotSupportedError: ScreenError {
7 |
8 | public let description: String
9 |
10 | /// Creates an error.
11 | ///
12 | /// - Parameters:
13 | /// - container: Container that does not match the expected type.
14 | /// - trigger: The action that caused the error.
15 | public init(container: ScreenContainer, for trigger: Any) {
16 | description = """
17 | The type of the container \(container) is not supported for:
18 | \(trigger)
19 | """
20 | }
21 | }
22 |
23 | extension Result where Failure == Error {
24 |
25 | internal static func containerNotSupported(
26 | _ container: ScreenContainer,
27 | for trigger: Any
28 | ) -> Self {
29 | .failure(
30 | ScreenContainerNotSupportedError(
31 | container: container,
32 | for: trigger
33 | )
34 | )
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Sources/Screen/Actions/Any/AnyScreenActionBaseBox.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | internal class AnyScreenActionBaseBox:
4 | ScreenAction,
5 | CustomStringConvertible {
6 |
7 | internal typealias Output = Output
8 |
9 | nonisolated internal var description: String {
10 | fatalError("\(#function) has not been implemented")
11 | }
12 |
13 | // swiftlint:disable:next unavailable_function
14 | internal func cast(to type: Action.Type) -> Action? {
15 | fatalError("\(#function) has not been implemented")
16 | }
17 |
18 | // swiftlint:disable:next unavailable_function
19 | internal func combine(
20 | with other: Action
21 | ) -> AnyScreenAction? {
22 | fatalError("\(#function) has not been implemented")
23 | }
24 |
25 | // swiftlint:disable:next unavailable_function
26 | internal func perform(
27 | container: Container,
28 | navigator: ScreenNavigator,
29 | completion: @escaping Completion
30 | ) {
31 | fatalError("\(#function) has not been implemented")
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Sources/Addons/URL/Settings/ScreenOpenSettingsAction.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | /// Action to open the system settings of the application.
5 | public struct ScreenOpenSettingsAction: ScreenAction {
6 |
7 | public typealias Output = Void
8 |
9 | public init() { }
10 |
11 | public func perform(
12 | container: Container,
13 | navigator: ScreenNavigator,
14 | completion: @escaping Completion
15 | ) {
16 | guard let url = URL(string: UIApplication.openSettingsURLString) else {
17 | return completion(.invalidOpenSettingsURL(for: self))
18 | }
19 |
20 | ScreenOpenURLAction(url: url).perform(
21 | container: container,
22 | navigator: navigator,
23 | completion: completion
24 | )
25 | }
26 | }
27 |
28 | extension ScreenThenable {
29 |
30 | /// The system launches the Settings app and displays the app’s custom settings, if it has any.
31 | /// - Returns: An instance containing the new action.
32 | public func openAppSettings() -> Self {
33 | then(ScreenOpenSettingsAction())
34 | }
35 | }
36 | #endif
37 |
--------------------------------------------------------------------------------
/Sources/Screen/ScreenKey.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// А screen key that can be used to search for a container in the container hierarchy.
4 | ///
5 | /// Usually you don't need to create an instance of this type.
6 | /// It can be obtained from the `key` property of the `Screen` protocol.
7 | ///
8 | /// - SeeAlso: `Screen`.
9 | /// - SeeAlso: `ScreenKeyedContainer`.
10 | public struct ScreenKey: Hashable, CustomStringConvertible {
11 |
12 | /// Screen name.
13 | public let name: String
14 |
15 | /// Screen traits that are used to distinguish screens with the same name.
16 | public let traits: Set
17 |
18 | public let description: String
19 |
20 | /// Creates a screen key.
21 | ///
22 | /// - Parameters:
23 | /// - name: Screen name.
24 | /// - traits: Screen traits that are used to distinguish screens with the same name.
25 | public init(name: String, traits: Set = []) {
26 | self.name = name
27 | self.traits = traits
28 | let traits = self.traits.map { $0.base }
29 | description = traits.isEmpty
30 | ? name
31 | : "\(name)(\(traits))"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Sources/Addons/BottomSheet/Interaction/Interactions/BottomSheetInteraction.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | @MainActor
5 | internal protocol BottomSheetInteraction {
6 |
7 | var gestureInitialValue: CGFloat { get }
8 | var currentDetentDelta: CGFloat { get }
9 |
10 | var canRecognizeGestures: Bool { get }
11 |
12 | func start(
13 | presentationController: BottomSheetPresentationController,
14 | gestureRecognizer: UIPanGestureRecognizer,
15 | simultaneousScrollView: UIScrollView?
16 | )
17 |
18 | func update(
19 | presentationController: BottomSheetPresentationController,
20 | gestureRecognizer: UIPanGestureRecognizer,
21 | simultaneousScrollView: UIScrollView?
22 | )
23 |
24 | func finish(
25 | presentationController: BottomSheetPresentationController,
26 | gestureRecognizer: UIPanGestureRecognizer,
27 | simultaneousScrollView: UIScrollView?
28 | )
29 |
30 | func cancel(
31 | presentationController: BottomSheetPresentationController,
32 | gestureRecognizer: UIPanGestureRecognizer,
33 | simultaneousScrollView: UIScrollView?
34 | )
35 | }
36 | #endif
37 |
--------------------------------------------------------------------------------
/Sources/Screen/Actions/Window/ScreenMakeKeyAction.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | /// Makes the receiver the key window.
5 | public struct ScreenMakeKeyAction: ScreenAction {
6 |
7 | public typealias Output = Void
8 |
9 | public init() { }
10 |
11 | public func perform(
12 | container: Container,
13 | navigator: ScreenNavigator,
14 | completion: @escaping Completion
15 | ) {
16 | container.makeKey()
17 |
18 | completion(.success)
19 | }
20 | }
21 |
22 | extension ScreenThenable where Current: UIWindow {
23 |
24 | /// Makes the receiver the key window.
25 | ///
26 | /// Usage examples
27 | /// ==============
28 | ///
29 | /// - Makes the window container of the current container the key window:
30 | ///
31 | /// ``` swift
32 | /// navigator.navigate(from: container) { route in
33 | /// route
34 | /// .window
35 | /// .makeKey()
36 | /// }
37 | /// ```
38 | ///
39 | /// - Returns: An instance containing the new action.
40 | public func makeKey() -> Self {
41 | then(ScreenMakeKeyAction())
42 | }
43 | }
44 | #endif
45 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Screens/Landmark/LandmarkView.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | struct LandmarkView: View {
4 | var body: some View {
5 | VStack {
6 | LandmarkMapView()
7 | .ignoresSafeArea(edges: .top)
8 | .frame(height: 300)
9 |
10 | LandmarkCircleImage()
11 | .offset(y: -130)
12 | .padding(.bottom, -130)
13 |
14 | VStack(alignment: .leading) {
15 | Text("Turtle Rock")
16 | .font(.title)
17 |
18 | HStack {
19 | Text("Joshua Tree National Park")
20 | Spacer()
21 | Text("California")
22 | }
23 | .font(.subheadline)
24 | .foregroundColor(.secondary)
25 |
26 | Divider()
27 |
28 | Text("About Turtle Rock").font(.title2)
29 |
30 | Text("Descriptive text goes here.")
31 | }
32 | .padding()
33 |
34 | Spacer()
35 | }
36 | }
37 | }
38 |
39 | struct ContentView_Previews: PreviewProvider {
40 | static var previews: some View {
41 | LandmarkView()
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Sources/Addons/HUD/ScreenHideHUDAction.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | /// Action to hide the currently displayed ``HUD``.
5 | public struct ScreenHideHUDAction: ScreenAction {
6 |
7 | public typealias Output = Void
8 |
9 | public init() { }
10 |
11 | public func perform(
12 | container: Container,
13 | navigator: ScreenNavigator,
14 | completion: @escaping Completion
15 | ) {
16 | navigator.logInfo("Dismissing HUD presented by \(type(of: container))")
17 |
18 | guard let window = navigator.window else {
19 | return completion(.containerNotFound(type: UIWindow.self, for: self))
20 | }
21 |
22 | HUD.hide(in: window) {
23 | completion(.success)
24 | }
25 | }
26 | }
27 |
28 | extension ScreenThenable {
29 |
30 | /// Finds the top-most HUD subview in window of navigator that hasn’t finished and hides it.
31 | /// The counterpart to this method is ``ScreenThenable/showHUD(_:animation:duration:)``.
32 | /// - Returns: An instance containing the new action.
33 | public func hideHUD() -> Self {
34 | then(ScreenHideHUDAction())
35 | }
36 | }
37 | #endif
38 |
--------------------------------------------------------------------------------
/Sources/Screen/Actions/Generic/ScreenGetAction.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | public struct ScreenGetAction: ScreenAction {
4 |
5 | public typealias Output = Void
6 |
7 | public typealias Body = (
8 | _ container: Container,
9 | _ completion: @escaping Completion
10 | ) -> Void
11 |
12 | public let body: Body
13 |
14 | public init(body: @escaping Body) {
15 | self.body = body
16 | }
17 |
18 | public init(body: @escaping (_ container: Container) -> Void) {
19 | self.init { container, completion in
20 | body(container)
21 | completion(.success)
22 | }
23 | }
24 |
25 | public func perform(
26 | container: Container,
27 | navigator: ScreenNavigator,
28 | completion: @escaping Completion
29 | ) {
30 | body(container, completion)
31 | }
32 | }
33 |
34 | extension ScreenThenable {
35 |
36 | public func get(body: @escaping ScreenGetAction.Body) -> Self {
37 | then(ScreenGetAction(body: body))
38 | }
39 |
40 | public func get(body: @escaping (_ container: Current) -> Void) -> Self {
41 | then(ScreenGetAction(body: body))
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Sources/Screen/Errors/ScreenContainerAlreadyPresentingError.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// The container is already presenting another container.
4 | ///
5 | /// This error occurs whenever an action fails to present a modal container on a container
6 | /// which is already presenting another container.
7 | public struct ScreenContainerAlreadyPresentingError: ScreenError {
8 |
9 | public let description: String
10 |
11 | /// Creates an error.
12 | ///
13 | /// - Parameters:
14 | /// - container: Container that is already presenting another screen.
15 | /// - trigger: The action that caused the error.
16 | public init(container: ScreenContainer, for trigger: Any) {
17 | description = """
18 | The container \(container) is already presenting another container for:
19 | \(trigger)
20 | """
21 | }
22 | }
23 |
24 | extension Result where Failure == Error {
25 |
26 | internal static func containerAlreadyPresenting(
27 | _ container: ScreenContainer,
28 | for trigger: Any
29 | ) -> Self {
30 | .failure(
31 | ScreenContainerAlreadyPresentingError(
32 | container: container,
33 | for: trigger
34 | )
35 | )
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/docs/js/highlight-js-http.163e45b6.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * This source file is part of the Swift.org open source project
3 | *
4 | * Copyright (c) 2021 Apple Inc. and the Swift project authors
5 | * Licensed under Apache License v2.0 with Runtime Library Exception
6 | *
7 | * See https://swift.org/LICENSE.txt for license information
8 | * See https://swift.org/CONTRIBUTORS.txt for Swift project authors
9 | */
10 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["highlight-js-http"],{c01d:function(e,n){function a(e){const n=e.regex,a="HTTP/(2|1\\.[01])",s=/[A-Za-z][A-Za-z0-9-]*/,t={className:"attribute",begin:n.concat("^",s,"(?=\\:\\s)"),starts:{contains:[{className:"punctuation",begin:/: /,relevance:0,starts:{end:"$",relevance:0}}]}},i=[t,{begin:"\\n\\n",starts:{subLanguage:[],endsWithParent:!0}}];return{name:"HTTP",aliases:["https"],illegal:/\S/,contains:[{begin:"^(?="+a+" \\d{3})",end:/$/,contains:[{className:"meta",begin:a},{className:"number",begin:"\\b\\d{3}\\b"}],starts:{end:/\b\B/,illegal:/\S/,contains:i}},{begin:"(?=^[A-Z]+ (.*?) "+a+"$)",end:/$/,contains:[{className:"string",begin:" ",end:" ",excludeBegin:!0,excludeEnd:!0},{className:"meta",begin:a},{className:"keyword",begin:"[A-Z]+"}],starts:{end:/\b\B/,illegal:/\S/,contains:i}},e.inherit(t,{relevance:0})]}}e.exports=a}}]);
--------------------------------------------------------------------------------
/Sources/Addons/Sharing/Activity/SharingActivity.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit) && os(iOS)
2 | import UIKit
3 |
4 | public struct SharingActivity {
5 |
6 | private typealias Body = (_ navigator: ScreenNavigator) -> UIActivity
7 |
8 | private let body: Body
9 |
10 | private init(body: @escaping Body) {
11 | self.body = body
12 | }
13 |
14 | internal func activity(navigator: ScreenNavigator) -> UIActivity {
15 | body(navigator)
16 | }
17 | }
18 |
19 | extension SharingActivity {
20 |
21 | public static func regular(_ value: UIActivity) -> Self {
22 | Self { _ in
23 | value
24 | }
25 | }
26 |
27 | @MainActor
28 | public static func custom(_ value: Value) -> Self {
29 | Self { navigator in
30 | SharingActivityManager(
31 | navigator: navigator,
32 | activity: value
33 | )
34 | }
35 | }
36 |
37 | @MainActor
38 | public static func custom(_ value: Value) -> Self {
39 | Self { navigator in
40 | SharingActivityManager(
41 | navigator: navigator,
42 | activity: value
43 | )
44 | }
45 | }
46 | }
47 | #endif
48 |
--------------------------------------------------------------------------------
/Sources/Addons/HUD/Progress/Indicator/Failure/ProgressFailureIndicator.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | /// Progress indicator displaying an error with an animated cross.
5 | ///
6 | /// Use this object to customize the display of the cross in the view.
7 | public struct ProgressFailureIndicator: ProgressIndicator {
8 |
9 | public typealias View = ProgressFailureIndicatorView
10 |
11 | /// The default configuration.
12 | public static let `default` = Self()
13 |
14 | /// The stroke color for the shape path.
15 | public let color: UIColor
16 |
17 | /// Insets relative to the superview.
18 | public let insets: UIEdgeInsets
19 |
20 | public var logDescription: String? {
21 | ".failure"
22 | }
23 |
24 | /// Creates new indicator content.
25 | /// - Parameters:
26 | /// - color: The color used to stroke the shape’s path.
27 | /// - insets: Insets relative to the superview.
28 | public init(
29 | color: UIColor = .lightGray,
30 | insets: UIEdgeInsets = UIEdgeInsets(
31 | top: 24.0,
32 | left: 24.0,
33 | bottom: 24.0,
34 | right: 24.0
35 | )
36 | ) {
37 | self.color = color
38 | self.insets = insets
39 | }
40 | }
41 | #endif
42 |
--------------------------------------------------------------------------------
/Sources/Addons/HUD/Progress/Indicator/Spinner/ProgressSpinnerIndicator.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | /// Progress indicator showing the spinner.
5 | ///
6 | /// Use this object to customize the display of the spinner in the view.
7 | public struct ProgressSpinnerIndicator: ProgressIndicator {
8 |
9 | public typealias View = ProgressSpinnerIndicatorView
10 |
11 | /// The default configuration.
12 | public static let `default` = Self()
13 |
14 | /// The color used to stroke the shape’s path.
15 | public let color: UIColor
16 |
17 | /// Additional indents to expand the container.
18 | public let insets: UIEdgeInsets
19 |
20 | public var logDescription: String? {
21 | ".spinner"
22 | }
23 |
24 | /// Creates new indicator content.
25 | /// - Parameters:
26 | /// - color: The color used to stroke the shape’s path.
27 | /// - insets: Additional indents to expand the container.
28 | public init(
29 | color: UIColor = .lightGray,
30 | insets: UIEdgeInsets = UIEdgeInsets(
31 | top: 20.0,
32 | left: 20.0,
33 | bottom: 20.0,
34 | right: 20.0
35 | )
36 | ) {
37 | self.color = color
38 | self.insets = insets
39 | }
40 | }
41 | #endif
42 |
--------------------------------------------------------------------------------
/Sources/Addons/HUD/Progress/Indicator/Success/ProgressSuccessIndicator.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | /// A progress indicator displaying an animated checkmark.
5 | ///
6 | /// Use this object to customize the display of the checkmark in the view.
7 | public struct ProgressSuccessIndicator: ProgressIndicator {
8 |
9 | public typealias View = ProgressSuccessIndicatorView
10 |
11 | /// The default configuration.
12 | public static let `default` = Self()
13 |
14 | /// The color used to stroke the shape’s path.
15 | public let color: UIColor
16 |
17 | /// Additional indents to expand the container.
18 | public let insets: UIEdgeInsets
19 |
20 | public var logDescription: String? {
21 | ".success"
22 | }
23 |
24 | /// Creates new indicator content.
25 | /// - Parameters:
26 | /// - color: The color used to stroke the shape’s path.
27 | /// - insets: Additional indents to expand the container.
28 | public init(
29 | color: UIColor = .lightGray,
30 | insets: UIEdgeInsets = UIEdgeInsets(
31 | top: 24.0,
32 | left: 24.0,
33 | bottom: 24.0,
34 | right: 24.0
35 | )
36 | ) {
37 | self.color = color
38 | self.insets = insets
39 | }
40 | }
41 | #endif
42 |
--------------------------------------------------------------------------------
/Sources/Addons/MediaPicker/MediaPickerCameraSettings.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit) && os(iOS)
2 | import UIKit
3 |
4 | /// The object that defines the settings for the camera.
5 | public struct MediaPickerCameraSettings: Sendable {
6 |
7 | /// Default settings
8 | public static let `default` = Self()
9 |
10 | /// The camera used by the image picker controller.
11 | public let device: UIImagePickerController.CameraDevice
12 |
13 | /// The capture mode used by the camera.
14 | public let captureMode: UIImagePickerController.CameraCaptureMode
15 |
16 | /// The flash mode used by the active camera.
17 | public let flashMode: UIImagePickerController.CameraFlashMode
18 |
19 | /// - Parameters:
20 | /// - device: The camera used by the image picker controller.
21 | /// - captureMode: The capture mode used by the camera.
22 | /// - flashMode: The flash mode used by the active camera.
23 | public init(
24 | device: UIImagePickerController.CameraDevice = .rear,
25 | captureMode: UIImagePickerController.CameraCaptureMode = .photo,
26 | flashMode: UIImagePickerController.CameraFlashMode = .auto
27 | ) {
28 | self.device = device
29 | self.captureMode = captureMode
30 | self.flashMode = flashMode
31 | }
32 | }
33 | #endif
34 |
--------------------------------------------------------------------------------
/Sources/Addons/HUD/Progress/Indicator/Activity/ProgressActivityIndicator.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | /// A progress indicator that displays an activity indicator.
5 | ///
6 | /// Use this object to customize the display of the activity indicator in the view.
7 | public struct ProgressActivityIndicator: ProgressIndicator {
8 |
9 | public typealias View = ProgressActivityIndicatorView
10 |
11 | /// The default configuration.
12 | public static let `default` = Self()
13 |
14 | /// The color of the activity indicator.
15 | public let color: UIColor
16 |
17 | /// Outset (or expanded) by the specified amount.
18 | public let insets: UIEdgeInsets
19 |
20 | public var logDescription: String? {
21 | ".activity"
22 | }
23 |
24 | /// Creates new indicator content.
25 | /// - Parameters:
26 | /// - color: The color of the activity indicator.
27 | /// - insets: Outset (or expanded) by the specified amount.
28 | public init(
29 | color: UIColor = .lightGray,
30 | insets: UIEdgeInsets = UIEdgeInsets(
31 | top: 25.5,
32 | left: 25.5,
33 | bottom: 25.5,
34 | right: 25.5
35 | )
36 | ) {
37 | self.color = color
38 | self.insets = insets
39 | }
40 | }
41 | #endif
42 |
--------------------------------------------------------------------------------
/Sources/Screen/Actions/Window/ScreenMakeKeyAndVisibleAction.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | /// Shows the window container and makes it the key window.
5 | public struct ScreenMakeKeyAndVisibleAction: ScreenAction {
6 |
7 | public typealias Output = Void
8 |
9 | public init() { }
10 |
11 | public func perform(
12 | container: Container,
13 | navigator: ScreenNavigator,
14 | completion: @escaping Completion
15 | ) {
16 | container.makeKeyAndVisible()
17 |
18 | completion(.success)
19 | }
20 | }
21 |
22 | extension ScreenThenable where Current: UIWindow {
23 |
24 | /// Shows the window container and makes it the key window.
25 | ///
26 | /// Usage examples
27 | /// ==============
28 | ///
29 | /// - Shows the window container of the current container and makes it the key window:
30 | ///
31 | /// ``` swift
32 | /// navigator.navigate(from: container) { route in
33 | /// route
34 | /// .window
35 | /// .makeKeyAndVisible()
36 | /// }
37 | /// ```
38 | ///
39 | /// - Returns: An instance containing the new action.
40 | public func makeKeyAndVisible() -> Self {
41 | then(ScreenMakeKeyAndVisibleAction())
42 | }
43 | }
44 | #endif
45 |
--------------------------------------------------------------------------------
/Sources/Screen/Errors/ScreenContainerTypeMismatchError.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// The type of the container does not match the expected type.
4 | ///
5 | /// This error occurs whenever an action fails to cast the container to the expected type.
6 | public struct ScreenContainerTypeMismatchError: ScreenError {
7 |
8 | public let description: String
9 |
10 | /// Creates an error.
11 | ///
12 | /// - Parameters:
13 | /// - container: Container that does not match the expected type.
14 | /// - type: Expected container type.
15 | /// - trigger: The action that caused the error.
16 | public init(container: ScreenContainer, type: Any.Type, for trigger: Any) {
17 | description = """
18 | The type of the container \(container) does not match the expected type \(type) for:
19 | \(trigger)
20 | """
21 | }
22 | }
23 |
24 | extension Result where Failure == Error {
25 |
26 | internal static func containerTypeMismatch(
27 | _ container: ScreenContainer,
28 | type: Any.Type,
29 | for trigger: Any
30 | ) -> Self {
31 | .failure(
32 | ScreenContainerTypeMismatchError(
33 | container: container,
34 | type: type,
35 | for: trigger
36 | )
37 | )
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Sources/Tools/Extensions/UIKit/UIScrollView+Extenions.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | extension UIScrollView {
5 |
6 | internal var isScrolledVertically: Bool {
7 | contentOffset.y >= -adjustedContentInset.top + UIScreen.main.pixelSize
8 | }
9 |
10 | internal var canScrollVertically: Bool {
11 | if alwaysBounceVertical {
12 | return true
13 | }
14 |
15 | return contentSize.height >= frame.height
16 | - adjustedContentInset.vertical
17 | + UIScreen.main.pixelSize
18 | }
19 |
20 | internal var canScrollHorizontally: Bool {
21 | if alwaysBounceHorizontal {
22 | return true
23 | }
24 |
25 | return contentSize.width >= frame.width
26 | - adjustedContentInset.horizontal
27 | + UIScreen.main.pixelSize
28 | }
29 |
30 | internal func resetVerticalScroll(finishing: Bool) {
31 | setContentOffset(
32 | CGPoint(
33 | x: contentOffset.x,
34 | y: -adjustedContentInset.top
35 | ),
36 | animated: false
37 | )
38 |
39 | if finishing, panGestureRecognizer.isEnabled {
40 | panGestureRecognizer.isEnabled = false
41 | panGestureRecognizer.isEnabled = true
42 | }
43 | }
44 | }
45 | #endif
46 |
--------------------------------------------------------------------------------
/docs/theme-settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "meta": {},
3 | "theme": {
4 | "colors": {
5 | "text": "",
6 | "text-background": "",
7 | "grid": "",
8 | "article-background": "",
9 | "generic-modal-background": "",
10 | "secondary-label": "",
11 | "header-text": "",
12 | "not-found": {
13 | "input-border": ""
14 | },
15 | "runtime-preview": {
16 | "text": ""
17 | },
18 | "tabnav-item": {
19 | "border-color": ""
20 | },
21 | "svg-icon": {
22 | "fill-light": "",
23 | "fill-dark": ""
24 | },
25 | "loading-placeholder": {
26 | "background": ""
27 | },
28 | "button": {
29 | "text": "",
30 | "light": {
31 | "background": "",
32 | "backgroundHover": "",
33 | "backgroundActive": ""
34 | },
35 | "dark": {
36 | "background": "",
37 | "backgroundHover": "",
38 | "backgroundActive": ""
39 | }
40 | },
41 | "link": null
42 | },
43 | "style": {
44 | "button": {
45 | "borderRadius": null
46 | }
47 | },
48 | "typography": {
49 | "html-font": ""
50 | }
51 | },
52 | "features": {
53 | "docs": {
54 | "summary": {
55 | "hide": false
56 | }
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/Sources/Screen/Actions/Tabs/SelectTab/Animations/ScreenTabTransitionAnimation.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | public struct ScreenTabTransitionAnimation: ScreenTabCustomAnimation {
5 |
6 | public let duration: TimeInterval
7 | public let options: UIView.AnimationOptions
8 |
9 | public init(
10 | duration: TimeInterval,
11 | options: UIView.AnimationOptions
12 | ) {
13 | self.duration = duration
14 | self.options = options
15 | }
16 |
17 | public func animate(
18 | container: UITabBarController,
19 | from selectedTab: UIViewController,
20 | to newSelectedTab: UIViewController,
21 | completion: @escaping () -> Void
22 | ) {
23 | UIView.transition(
24 | from: selectedTab.view,
25 | to: newSelectedTab.view,
26 | duration: duration,
27 | options: options
28 | ) { _ in
29 | completion()
30 | }
31 | }
32 | }
33 |
34 | extension ScreenTabTransitionAnimation {
35 |
36 | public static let crossDissolve = Self(
37 | duration: 0.3,
38 | options: .transitionCrossDissolve
39 | )
40 | }
41 |
42 | extension ScreenTabAnimation {
43 |
44 | public static let crossDissolve = Self.custom(
45 | ScreenTabTransitionAnimation.crossDissolve
46 | )
47 | }
48 | #endif
49 |
--------------------------------------------------------------------------------
/Sources/Tools/Extensions/UIKit/UITabBarController+Extensions.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | extension UITabBarController {
5 |
6 | /// The container associated with the currently selected tab item.
7 | ///
8 | /// This container is the one whose custom view is currently displayed by the tab bar interface.
9 | /// The specified container must be in the `viewControllers` array.
10 | /// Assigning a new container to this property changes the currently displayed view
11 | /// and also selects an appropriate tab in the tab bar.
12 | /// Changing the container also updates the `selectedIndex` property accordingly.
13 | /// The default value of this property is `nil`.
14 | ///
15 | /// You can use this property to select any of the containers in the `viewControllers` property.
16 | /// This includes containers that are managed by the More navigation controller
17 | /// and whose tab bar items are not visible in the tab bar.
18 | /// You can also use it to select the More navigation controller itself,
19 | /// which is available from the `moreNavigationController` property.
20 | ///
21 | /// - SeeAlso: `selectedViewController`
22 | public var selectedTab: UIViewController? {
23 | get { selectedViewController }
24 | set { selectedViewController = newValue }
25 | }
26 | }
27 | #endif
28 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Screens/Chat/ChatViewController.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Nivelir
3 |
4 | final class ChatViewController: UIViewController, ScreenKeyedContainer {
5 |
6 | let roomID: Int
7 | let chatID: Int
8 |
9 | let screenKey: ScreenKey
10 | let screenNavigator: ScreenNavigator
11 |
12 | init(
13 | roomID: Int,
14 | chatID: Int,
15 | screenKey: ScreenKey,
16 | screenNavigator: ScreenNavigator
17 | ) {
18 | self.roomID = roomID
19 | self.chatID = chatID
20 |
21 | self.screenKey = screenKey
22 | self.screenNavigator = screenNavigator
23 |
24 | super.init(nibName: nil, bundle: nil)
25 |
26 | #if os(iOS)
27 | hidesBottomBarWhenPushed = true
28 | #endif
29 | }
30 |
31 | @available(*, unavailable)
32 | required init?(coder: NSCoder) {
33 | fatalError("init(coder:) has not been implemented")
34 | }
35 |
36 | override func loadView() {
37 | let chatEmptyView = ChatEmptyView()
38 |
39 | chatEmptyView.title = "Chat \(chatID) in room #\(roomID)"
40 |
41 | view = chatEmptyView
42 | }
43 | }
44 |
45 | extension ChatViewController: ScreenRefreshableContainer {
46 |
47 | func refresh(completion: @escaping () -> Void) {
48 | print("Refreshed")
49 |
50 | completion()
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Sources/Screen/Actions/Stack/SetStack/Animations/ScreenStackTransitionAnimation.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | public struct ScreenStackTransitionAnimation: ScreenStackCustomAnimation, Equatable, Sendable {
5 |
6 | public let duration: TimeInterval
7 | public let options: UIView.AnimationOptions
8 |
9 | public init(
10 | duration: TimeInterval,
11 | options: UIView.AnimationOptions
12 | ) {
13 | self.duration = duration
14 | self.options = options
15 | }
16 |
17 | public func animate(
18 | container: UINavigationController,
19 | stack: [UIViewController],
20 | completion: @escaping () -> Void
21 | ) {
22 | UIView.transition(
23 | with: container.view,
24 | duration: duration,
25 | options: options,
26 | animations: { },
27 | completion: { _ in
28 | completion()
29 | }
30 | )
31 | }
32 | }
33 |
34 | extension ScreenStackTransitionAnimation {
35 |
36 | public static let crossDissolve = Self(
37 | duration: 0.3,
38 | options: .transitionCrossDissolve
39 | )
40 | }
41 |
42 | extension ScreenStackAnimation {
43 |
44 | public static let crossDissolve = Self.custom(
45 | ScreenStackTransitionAnimation.crossDissolve
46 | )
47 | }
48 | #endif
49 |
--------------------------------------------------------------------------------
/Sources/Screen/Actions/Window/SetRoot/Animations/ScreenRootTransitionAnimation.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | public struct ScreenRootTransitionAnimation: ScreenRootCustomAnimation {
5 |
6 | public let duration: TimeInterval
7 | public let options: UIView.AnimationOptions
8 |
9 | public init(
10 | duration: TimeInterval,
11 | options: UIView.AnimationOptions
12 | ) {
13 | self.duration = duration
14 | self.options = options
15 | }
16 |
17 | public func animate(
18 | container: UIWindow,
19 | from root: UIViewController?,
20 | to newRoot: UIViewController,
21 | completion: @escaping () -> Void
22 | ) {
23 | UIView.transition(
24 | with: container,
25 | duration: duration,
26 | options: options,
27 | animations: { },
28 | completion: { _ in
29 | completion()
30 | }
31 | )
32 | }
33 | }
34 |
35 | extension ScreenRootTransitionAnimation {
36 |
37 | public static let crossDissolve = Self(
38 | duration: 0.3,
39 | options: .transitionCrossDissolve
40 | )
41 | }
42 |
43 | extension ScreenRootAnimation {
44 |
45 | public static let crossDissolve = Self.custom(
46 | ScreenRootTransitionAnimation.crossDissolve
47 | )
48 | }
49 | #endif
50 |
--------------------------------------------------------------------------------
/Sources/Screen/Decorators/Modal/ScreenTabBarItemDecorator.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | /// Sets the `tabBarItem` property for a container with type `UIViewController`.
5 | public struct ScreenTabBarItemDecorator: ScreenDecorator {
6 |
7 | public let item: UITabBarItem
8 |
9 | public var payload: Any? {
10 | nil
11 | }
12 |
13 | public let description: String
14 |
15 | public init(item: UITabBarItem) {
16 | self.item = item
17 | description = "TabBarItemDecorator"
18 | }
19 |
20 | public func build(
21 | screen: Wrapped,
22 | navigator: ScreenNavigator
23 | ) -> Container where Wrapped.Container == Container {
24 | let container = screen.build(navigator: navigator)
25 |
26 | container.tabBarItem = item
27 |
28 | return container
29 | }
30 | }
31 |
32 | extension Screen where Container: UIViewController {
33 |
34 | /// Sets the `tabBarItem` property for a container with type `UIViewController`.
35 | /// - Parameter item: `item` to set.
36 | /// - Returns: New `Screen` with `UITabBarItem` set to `tabBarItem`.
37 | @MainActor
38 | public func withTabBarItem(_ item: UITabBarItem) -> AnyScreen {
39 | decorated(by: ScreenTabBarItemDecorator(item: item))
40 | }
41 | }
42 | #endif
43 |
--------------------------------------------------------------------------------
/Sources/Addons/BottomSheet/Interaction/Interactions/BottomSheetDismissedInteraction.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | internal final class BottomSheetDismissedInteraction: BottomSheetInteraction {
5 |
6 | internal var gestureInitialValue: CGFloat {
7 | .zero
8 | }
9 |
10 | internal var currentDetentDelta: CGFloat {
11 | .zero
12 | }
13 |
14 | internal var canRecognizeGestures: Bool {
15 | false
16 | }
17 |
18 | internal func start(
19 | presentationController: BottomSheetPresentationController,
20 | gestureRecognizer: UIPanGestureRecognizer,
21 | simultaneousScrollView: UIScrollView?
22 | ) { }
23 |
24 | internal func update(
25 | presentationController: BottomSheetPresentationController,
26 | gestureRecognizer: UIPanGestureRecognizer,
27 | simultaneousScrollView: UIScrollView?
28 | ) { }
29 |
30 | internal func finish(
31 | presentationController: BottomSheetPresentationController,
32 | gestureRecognizer: UIPanGestureRecognizer,
33 | simultaneousScrollView: UIScrollView?
34 | ) { }
35 |
36 | internal func cancel(
37 | presentationController: BottomSheetPresentationController,
38 | gestureRecognizer: UIPanGestureRecognizer,
39 | simultaneousScrollView: UIScrollView?
40 | ) { }
41 | }
42 | #endif
43 |
--------------------------------------------------------------------------------
/Example/NivelirExample/Helpers/DI/ServiceContainer.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | final class ServiceContainer {
4 |
5 | private var services: [ServiceKey: ServiceStorage] = [:]
6 | private let lock = NSRecursiveLock()
7 |
8 | func resolve(
9 | name: String = #function,
10 | traits: [AnyHashable] = [],
11 | using factory: () -> Service,
12 | at scope: ServiceScope
13 | ) -> Service {
14 | lock.lock()
15 |
16 | defer {
17 | lock.unlock()
18 | }
19 |
20 | let serviceKey = ServiceKey(
21 | type: Service.self,
22 | name: name,
23 | traits: traits
24 | )
25 |
26 | if let service = services[serviceKey]?.service.flatMap({ $0 as? Service }) {
27 | return service
28 | }
29 |
30 | let service = factory()
31 | let storage = scope.storage(for: service)
32 |
33 | services[serviceKey] = storage
34 |
35 | return service
36 | }
37 |
38 | func shared(
39 | name: String = #function,
40 | traits: [AnyHashable] = [],
41 | using factory: () -> Service
42 | ) -> Service {
43 | resolve(
44 | name: name,
45 | traits: traits,
46 | using: factory,
47 | at: ServiceSharedScope.default
48 | )
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Sources/Addons/HUD/Progress/Animation/ProgressAnimation.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | /// The type of progress view animation of the ``HUD``.
5 | ///
6 | /// An animation is used to update and show the `header`, `indicator` and `footer` parts of the progress view.
7 | @MainActor
8 | @frozen
9 | public enum ProgressAnimation {
10 |
11 | /// Custom animation for showing progress view of the HUD.
12 | ///
13 | /// Implement ``ProgressCustomAnimation`` to configure a custom animation
14 | /// and pass an instance to the associated value.
15 | case custom(ProgressCustomAnimation)
16 |
17 | /// Animates updates of parts of the progress view.
18 | /// - Parameters:
19 | /// - view: View of the `header`, `indicator` or `footer` part.
20 | /// - previousView: Previous view of the `header`, `indicator` or `footer` part.
21 | /// - containerView: Container view of the `header`, `indicator` or `footer` part.
22 | public func animateView(
23 | _ view: UIView,
24 | previousView: UIView,
25 | containerView: UIView
26 | ) {
27 | switch self {
28 | case let .custom(animation):
29 | animation.animateView(
30 | view,
31 | previousView: previousView,
32 | containerView: containerView
33 | )
34 | }
35 | }
36 | }
37 | #endif
38 |
--------------------------------------------------------------------------------
/Sources/Addons/BottomSheet/Decorators/ScreenBottomSheetDecorator.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | public struct ScreenBottomSheetDecorator: ScreenDecorator {
5 |
6 | private let bottomSheetController: BottomSheetController
7 |
8 | public let bottomSheet: BottomSheet
9 |
10 | public var payload: Any? {
11 | bottomSheetController
12 | }
13 |
14 | public let description: String
15 |
16 | public init(bottomSheet: BottomSheet) {
17 | self.bottomSheet = bottomSheet
18 | self.bottomSheetController = BottomSheetController(bottomSheet: bottomSheet)
19 | description = "BottomSheetDecorator"
20 | }
21 |
22 | public func build(
23 | screen: Wrapped,
24 | navigator: ScreenNavigator
25 | ) -> Container where Wrapped.Container == Container {
26 | let container = screen.build(navigator: navigator)
27 |
28 | container.modalPresentationStyle = .custom
29 | container.transitioningDelegate = bottomSheetController
30 |
31 | return container
32 | }
33 | }
34 |
35 | extension Screen where Container: UIViewController {
36 |
37 | @MainActor
38 | public func withBottomSheet(_ bottomSheet: BottomSheet) -> AnyScreen {
39 | decorated(by: ScreenBottomSheetDecorator(bottomSheet: bottomSheet))
40 | }
41 | }
42 | #endif
43 |
--------------------------------------------------------------------------------
/Sources/Addons/HUD/Progress/Indicator/Image/ProgressImageIndicator.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 |
4 | /// A progress indicator displaying the image.
5 | ///
6 | /// Use this object to configure how the image is displayed in the view.
7 | public struct ProgressImageIndicator: ProgressIndicator {
8 |
9 | public typealias View = ProgressImageIndicatorView
10 |
11 | /// The default configuration.
12 | /// - Parameter image: The image displayed in the image view.
13 | public static func `default`(image: UIImage) -> Self {
14 | Self(image: image)
15 | }
16 |
17 | /// The image displayed in the image view.
18 | public let image: UIImage
19 |
20 | /// Additional indentation to expand the container.
21 | public let insets: UIEdgeInsets
22 |
23 | public var logDescription: String? {
24 | ".image"
25 | }
26 |
27 | /// Creates new indicator content.
28 | /// - Parameters:
29 | /// - image: The image displayed in the image view.
30 | /// - insets: Additional indents to expand the container.
31 | public init(
32 | image: UIImage,
33 | insets: UIEdgeInsets = UIEdgeInsets(
34 | top: 20.0,
35 | left: 20.0,
36 | bottom: 20.0,
37 | right: 20.0
38 | )
39 | ) {
40 | self.image = image
41 | self.insets = insets
42 | }
43 | }
44 | #endif
45 |
--------------------------------------------------------------------------------
/Scripts/Bootstrap/xcode.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | while [[ "$#" -gt 0 ]]; do
6 | case $1 in
7 | --sudo-password) sudo_password="${2}"; shift ;;
8 | *) echo "Unknown parameter passed: $1"; exit 1 ;;
9 | esac
10 | shift
11 | done
12 |
13 | if [ -n "${sudo_password}" ]; then
14 | echo "${sudo_password}" | sudo -S -E "$0" "$@"
15 | exit $?
16 | fi
17 |
18 | readonly script_path="$( cd "$( dirname "$0" )" && pwd )"
19 |
20 | source "${script_path}/common.sh"
21 |
22 | echo "Checking ${xcode_style}Xcode${default_style} installation:"
23 |
24 | if [[ "$(uname -m)" == "arm64" ]]; then
25 | eval "$(/opt/homebrew/bin/brew shellenv)"
26 | fi
27 |
28 | eval "$(rbenv init -)"
29 |
30 | readonly xcode_required_version=$(cat "${root_path}"/.xcode-version)
31 | readonly xcode_version=($(bundle exec xcversion selected 2> /dev/null | sed -n 's/Xcode \(.*\)/\1/p'))
32 |
33 | if [[ "$xcode_version" == "$xcode_required_version" ]]; then
34 | echo " Required Xcode version ($xcode_required_version) already installed."
35 | else
36 | echo " Required Xcode version ($xcode_required_version) not found. Installing..."
37 |
38 | bundle exec xcversion update
39 | bundle exec xcversion install ${xcode_required_version}
40 |
41 | echo " Selecting Xcode version..."
42 |
43 | bundle exec xcversion select "${xcode_required_version}"
44 | sudo xcodebuild -license accept
45 | fi
46 |
47 | echo ""
48 |
--------------------------------------------------------------------------------
/Sources/Screen/Actions/Any/AnyScreenActionBox.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | internal final class AnyScreenActionBox<
4 | Wrapped: ScreenAction,
5 | Output
6 | >: AnyScreenActionBaseBox {
7 |
8 | internal typealias Mapper = (_ result: Result) -> Result