From 798991c0540480eebad9d270e3f03a783f2e1b45 Mon Sep 17 00:00:00 2001 From: Jakub Olejnik Date: Thu, 28 Dec 2023 13:59:03 +0100 Subject: [PATCH 01/12] =?UTF-8?q?=E2=9C=A8=20Add=20UI=20extensions=20from?= =?UTF-8?q?=20ProjectTemplate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ACKategories.xcodeproj/project.pbxproj | 76 ++++++--- .../ACKategories/{ => UI}/GradientView.swift | 0 .../UI/Popup/PopupModalAnimation.swift | 147 ++++++++++++++++++ .../UI/Popup/PopupPresenting.swift | 18 +++ Sources/ACKategories/{ => UI}/Reusable.swift | 0 .../ACKategories/{ => UI}/ReusableView.swift | 0 .../SelfSizingTableHeaderFooterView.swift | 0 .../ACKategories/{ => UI}/TagListView.swift | 0 .../UI/Theme/ThemeExtensions.swift | 9 ++ .../ACKategories/UI/Theme/ThemeProvider.swift | 11 ++ .../{ => UI}/UIApplicationExtensions.swift | 0 .../{ => UI}/UILabelExtensions.swift | 0 .../UINavigationControllerExtensions.swift | 0 .../{ => UI}/UISearchBarExtensions.swift | 0 .../{ => UI}/UIStackViewExtensions.swift | 0 .../ACKategories/{ => UI}/UIView+Spacer.swift | 0 .../{ => UI}/UIViewController+Children.swift | 0 .../{ => UI}/UIViewController+FrontMost.swift | 0 .../{ => UI}/UIViewExtensions.swift | 0 19 files changed, 243 insertions(+), 18 deletions(-) rename Sources/ACKategories/{ => UI}/GradientView.swift (100%) create mode 100644 Sources/ACKategories/UI/Popup/PopupModalAnimation.swift create mode 100644 Sources/ACKategories/UI/Popup/PopupPresenting.swift rename Sources/ACKategories/{ => UI}/Reusable.swift (100%) rename Sources/ACKategories/{ => UI}/ReusableView.swift (100%) rename Sources/ACKategories/{ => UI}/SelfSizingTableHeaderFooterView.swift (100%) rename Sources/ACKategories/{ => UI}/TagListView.swift (100%) create mode 100644 Sources/ACKategories/UI/Theme/ThemeExtensions.swift create mode 100644 Sources/ACKategories/UI/Theme/ThemeProvider.swift rename Sources/ACKategories/{ => UI}/UIApplicationExtensions.swift (100%) rename Sources/ACKategories/{ => UI}/UILabelExtensions.swift (100%) rename Sources/ACKategories/{ => UI}/UINavigationControllerExtensions.swift (100%) rename Sources/ACKategories/{ => UI}/UISearchBarExtensions.swift (100%) rename Sources/ACKategories/{ => UI}/UIStackViewExtensions.swift (100%) rename Sources/ACKategories/{ => UI}/UIView+Spacer.swift (100%) rename Sources/ACKategories/{ => UI}/UIViewController+Children.swift (100%) rename Sources/ACKategories/{ => UI}/UIViewController+FrontMost.swift (100%) rename Sources/ACKategories/{ => UI}/UIViewExtensions.swift (100%) diff --git a/ACKategories.xcodeproj/project.pbxproj b/ACKategories.xcodeproj/project.pbxproj index 3eff084e..2f7321bf 100644 --- a/ACKategories.xcodeproj/project.pbxproj +++ b/ACKategories.xcodeproj/project.pbxproj @@ -15,6 +15,11 @@ 6971A98A2AFD1AF5000FC317 /* ACKategories.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 69ACD6C22AFD130C0021127B /* ACKategories.framework */; }; 6971A9902AFD1B0F000FC317 /* ControlBlocksTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69E0A6B62AFD114600C8E8D9 /* ControlBlocksTests.swift */; }; 697D61D42B0BC41900020664 /* EdgeInsetsExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 697D61D32B0BC41900020664 /* EdgeInsetsExtensions.swift */; }; + 698376CF2B3DA81200CD9E89 /* PopupPresenting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 698376C92B3DA81200CD9E89 /* PopupPresenting.swift */; }; + 698376D02B3DA81200CD9E89 /* PopupModalAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 698376CA2B3DA81200CD9E89 /* PopupModalAnimation.swift */; }; + 698376D12B3DA81200CD9E89 /* ThemeExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 698376CC2B3DA81200CD9E89 /* ThemeExtensions.swift */; }; + 698376D22B3DA81200CD9E89 /* ThemeProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 698376CD2B3DA81200CD9E89 /* ThemeProvider.swift */; }; + 698376D32B3DA81200CD9E89 /* GradientView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 698376CE2B3DA81200CD9E89 /* GradientView.swift */; }; 69ACD6CA2AFD130D0021127B /* ACKategories.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 69ACD6C22AFD130C0021127B /* ACKategories.framework */; }; 69ACD6D72AFD133A0021127B /* ACKategories.h in Sources */ = {isa = PBXBuildFile; fileRef = 69E0A5F92AFD10BE00C8E8D9 /* ACKategories.h */; }; 69ACD6D82AFD133A0021127B /* ArrayExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69E0A5F82AFD10BE00C8E8D9 /* ArrayExtensions.swift */; }; @@ -27,7 +32,6 @@ 69ACD6DF2AFD133A0021127B /* DateFormatting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69E0A6082AFD10BE00C8E8D9 /* DateFormatting.swift */; }; 69ACD6E02AFD133A0021127B /* DictionaryExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69E0A6052AFD10BE00C8E8D9 /* DictionaryExtensions.swift */; }; 69ACD6E12AFD133A0021127B /* ErrorHandlers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69E0A6102AFD10BE00C8E8D9 /* ErrorHandlers.swift */; }; - 69ACD6E22AFD133A0021127B /* GradientView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69E0A61A2AFD10BE00C8E8D9 /* GradientView.swift */; }; 69ACD6E32AFD133A0021127B /* IntExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69E0A6172AFD10BE00C8E8D9 /* IntExtensions.swift */; }; 69ACD6E42AFD133A0021127B /* NSAttributedStringExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69E0A6212AFD10BE00C8E8D9 /* NSAttributedStringExtensions.swift */; }; 69ACD6E52AFD133A0021127B /* NSMutableParagraphStyleExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69E0A60F2AFD10BE00C8E8D9 /* NSMutableParagraphStyleExtensions.swift */; }; @@ -156,6 +160,11 @@ 6971A9862AFD1AF5000FC317 /* ACKategoriesResponderTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ACKategoriesResponderTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 697B023227DB65B50082F4AC /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = ""; }; 697D61D32B0BC41900020664 /* EdgeInsetsExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EdgeInsetsExtensions.swift; sourceTree = ""; }; + 698376C92B3DA81200CD9E89 /* PopupPresenting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PopupPresenting.swift; sourceTree = ""; }; + 698376CA2B3DA81200CD9E89 /* PopupModalAnimation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PopupModalAnimation.swift; sourceTree = ""; }; + 698376CC2B3DA81200CD9E89 /* ThemeExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThemeExtensions.swift; sourceTree = ""; }; + 698376CD2B3DA81200CD9E89 /* ThemeProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThemeProvider.swift; sourceTree = ""; }; + 698376CE2B3DA81200CD9E89 /* GradientView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GradientView.swift; sourceTree = ""; }; 69ACD6C22AFD130C0021127B /* ACKategories.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ACKategories.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 69ACD6C92AFD130D0021127B /* ACKategoriesTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ACKategoriesTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 69E0A5F52AFD10BE00C8E8D9 /* UISearchBarExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISearchBarExtensions.swift; sourceTree = ""; }; @@ -192,7 +201,6 @@ 69E0A6172AFD10BE00C8E8D9 /* IntExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IntExtensions.swift; sourceTree = ""; }; 69E0A6182AFD10BE00C8E8D9 /* DateExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateExtensions.swift; sourceTree = ""; }; 69E0A6192AFD10BE00C8E8D9 /* UILabelExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UILabelExtensions.swift; sourceTree = ""; }; - 69E0A61A2AFD10BE00C8E8D9 /* GradientView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GradientView.swift; sourceTree = ""; }; 69E0A61B2AFD10BE00C8E8D9 /* CollectionExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionExtensions.swift; sourceTree = ""; }; 69E0A61C2AFD10BE00C8E8D9 /* UIButtonExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIButtonExtensions.swift; sourceTree = ""; }; 69E0A61D2AFD10BE00C8E8D9 /* BetterURL.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BetterURL.swift; sourceTree = ""; }; @@ -318,6 +326,47 @@ path = ACKategoriesResponderTests; sourceTree = ""; }; + 698376C72B3DA81200CD9E89 /* UI */ = { + isa = PBXGroup; + children = ( + 698376CE2B3DA81200CD9E89 /* GradientView.swift */, + 69E0A6062AFD10BE00C8E8D9 /* Reusable.swift */, + 69E0A61E2AFD10BE00C8E8D9 /* ReusableView.swift */, + 69E0A5FA2AFD10BE00C8E8D9 /* SelfSizingTableHeaderFooterView.swift */, + 69E0A6112AFD10BE00C8E8D9 /* TagListView.swift */, + 69E0A61F2AFD10BE00C8E8D9 /* UIApplicationExtensions.swift */, + 69E0A6192AFD10BE00C8E8D9 /* UILabelExtensions.swift */, + 69E0A6132AFD10BE00C8E8D9 /* UINavigationControllerExtensions.swift */, + 69E0A5F52AFD10BE00C8E8D9 /* UISearchBarExtensions.swift */, + 69E0A5FB2AFD10BE00C8E8D9 /* UIStackViewExtensions.swift */, + 69E0A6092AFD10BE00C8E8D9 /* UIView+Spacer.swift */, + 69E0A60E2AFD10BE00C8E8D9 /* UIViewController+Children.swift */, + 69E0A5FC2AFD10BE00C8E8D9 /* UIViewController+FrontMost.swift */, + 69E0A6142AFD10BE00C8E8D9 /* UIViewExtensions.swift */, + 698376C82B3DA81200CD9E89 /* Popup */, + 698376CB2B3DA81200CD9E89 /* Theme */, + ); + path = UI; + sourceTree = ""; + }; + 698376C82B3DA81200CD9E89 /* Popup */ = { + isa = PBXGroup; + children = ( + 698376C92B3DA81200CD9E89 /* PopupPresenting.swift */, + 698376CA2B3DA81200CD9E89 /* PopupModalAnimation.swift */, + ); + path = Popup; + sourceTree = ""; + }; + 698376CB2B3DA81200CD9E89 /* Theme */ = { + isa = PBXGroup; + children = ( + 698376CC2B3DA81200CD9E89 /* ThemeExtensions.swift */, + 698376CD2B3DA81200CD9E89 /* ThemeProvider.swift */, + ); + path = Theme; + sourceTree = ""; + }; 69E0A5F32AFD10BE00C8E8D9 /* Sources */ = { isa = PBXGroup; children = ( @@ -331,6 +380,7 @@ children = ( 69E0A5F92AFD10BE00C8E8D9 /* ACKategories.h */, 69E0A5F82AFD10BE00C8E8D9 /* ArrayExtensions.swift */, + 6A72B2212B1A15AC00A59EDD /* BackGesture.swift */, 69E0A61D2AFD10BE00C8E8D9 /* BetterURL.swift */, 69E0A6152AFD10BE00C8E8D9 /* BundleExtensions.swift */, 69E0A61B2AFD10BE00C8E8D9 /* CollectionExtensions.swift */, @@ -341,38 +391,24 @@ 69E0A6052AFD10BE00C8E8D9 /* DictionaryExtensions.swift */, 697D61D32B0BC41900020664 /* EdgeInsetsExtensions.swift */, 69E0A6102AFD10BE00C8E8D9 /* ErrorHandlers.swift */, - 69E0A61A2AFD10BE00C8E8D9 /* GradientView.swift */, 69E0A6172AFD10BE00C8E8D9 /* IntExtensions.swift */, 69E0A6212AFD10BE00C8E8D9 /* NSAttributedStringExtensions.swift */, 69E0A60F2AFD10BE00C8E8D9 /* NSMutableParagraphStyleExtensions.swift */, 69E0A60D2AFD10BE00C8E8D9 /* NumberFormatterExtensions.swift */, 69E0A6282AFD10BE00C8E8D9 /* PublisherExtensions.swift */, - 69E0A6062AFD10BE00C8E8D9 /* Reusable.swift */, - 69E0A61E2AFD10BE00C8E8D9 /* ReusableView.swift */, - 69E0A5FA2AFD10BE00C8E8D9 /* SelfSizingTableHeaderFooterView.swift */, 69E0A6072AFD10BE00C8E8D9 /* StringExtensions.swift */, - 69E0A6112AFD10BE00C8E8D9 /* TagListView.swift */, - 69E0A61F2AFD10BE00C8E8D9 /* UIApplicationExtensions.swift */, 69E0A61C2AFD10BE00C8E8D9 /* UIButtonExtensions.swift */, 69E0A6292AFD10BE00C8E8D9 /* UIColorExtensions.swift */, 69E0A60A2AFD10BE00C8E8D9 /* UIControl+Blocks.swift */, 69E0A60C2AFD10BE00C8E8D9 /* UIControlEvents.swift */, 69E0A6202AFD10BE00C8E8D9 /* UIDeviceExtensions.swift */, 69E0A6122AFD10BE00C8E8D9 /* UIImageExtensions.swift */, - 69E0A6192AFD10BE00C8E8D9 /* UILabelExtensions.swift */, - 69E0A6132AFD10BE00C8E8D9 /* UINavigationControllerExtensions.swift */, - 69E0A5F52AFD10BE00C8E8D9 /* UISearchBarExtensions.swift */, - 69E0A5FB2AFD10BE00C8E8D9 /* UIStackViewExtensions.swift */, - 69E0A6092AFD10BE00C8E8D9 /* UIView+Spacer.swift */, - 69E0A60E2AFD10BE00C8E8D9 /* UIViewController+Children.swift */, - 69E0A5FC2AFD10BE00C8E8D9 /* UIViewController+FrontMost.swift */, - 69E0A6142AFD10BE00C8E8D9 /* UIViewExtensions.swift */, 69E0A5FD2AFD10BE00C8E8D9 /* UserDefaultsExtensions.swift */, 69E0A6222AFD10BE00C8E8D9 /* Base */, 69E0A5F62AFD10BE00C8E8D9 /* PropertyWrappers */, 69E0A5FE2AFD10BE00C8E8D9 /* RandomExtensions */, 69E0A6032AFD10BE00C8E8D9 /* SwiftUIExtensions */, - 6A72B2212B1A15AC00A59EDD /* BackGesture.swift */, + 698376C72B3DA81200CD9E89 /* UI */, ); path = ACKategories; sourceTree = ""; @@ -815,6 +851,8 @@ buildActionMask = 2147483647; files = ( 69ACD6D72AFD133A0021127B /* ACKategories.h in Sources */, + 698376D02B3DA81200CD9E89 /* PopupModalAnimation.swift in Sources */, + 698376D32B3DA81200CD9E89 /* GradientView.swift in Sources */, 69ACD6D82AFD133A0021127B /* ArrayExtensions.swift in Sources */, 69ACD6D92AFD133A0021127B /* BetterURL.swift in Sources */, 69ACD6DA2AFD133A0021127B /* BundleExtensions.swift in Sources */, @@ -825,11 +863,11 @@ 69ACD6DF2AFD133A0021127B /* DateFormatting.swift in Sources */, 69ACD6E02AFD133A0021127B /* DictionaryExtensions.swift in Sources */, 69ACD6E12AFD133A0021127B /* ErrorHandlers.swift in Sources */, - 69ACD6E22AFD133A0021127B /* GradientView.swift in Sources */, 69ACD6E32AFD133A0021127B /* IntExtensions.swift in Sources */, 69ACD6E42AFD133A0021127B /* NSAttributedStringExtensions.swift in Sources */, 69ACD6E52AFD133A0021127B /* NSMutableParagraphStyleExtensions.swift in Sources */, 69ACD6E62AFD133A0021127B /* NumberFormatterExtensions.swift in Sources */, + 698376D22B3DA81200CD9E89 /* ThemeProvider.swift in Sources */, 697D61D42B0BC41900020664 /* EdgeInsetsExtensions.swift in Sources */, 69ACD6E72AFD133A0021127B /* PublisherExtensions.swift in Sources */, 69ACD6E82AFD133A0021127B /* Reusable.swift in Sources */, @@ -855,7 +893,9 @@ 69ACD6FB2AFD133A0021127B /* UIViewExtensions.swift in Sources */, 69ACD6FC2AFD133A0021127B /* UserDefaultsExtensions.swift in Sources */, 69ACD6FD2AFD133A0021127B /* ViewController.swift in Sources */, + 698376D12B3DA81200CD9E89 /* ThemeExtensions.swift in Sources */, 69ACD6FE2AFD133A0021127B /* FlowCoordinator.swift in Sources */, + 698376CF2B3DA81200CD9E89 /* PopupPresenting.swift in Sources */, 69ACD6FF2AFD133A0021127B /* Logger.swift in Sources */, 69ACD7002AFD133A0021127B /* ViewModel.swift in Sources */, 69ACD7012AFD133A0021127B /* Base.swift in Sources */, diff --git a/Sources/ACKategories/GradientView.swift b/Sources/ACKategories/UI/GradientView.swift similarity index 100% rename from Sources/ACKategories/GradientView.swift rename to Sources/ACKategories/UI/GradientView.swift diff --git a/Sources/ACKategories/UI/Popup/PopupModalAnimation.swift b/Sources/ACKategories/UI/Popup/PopupModalAnimation.swift new file mode 100644 index 00000000..cb5c2b3e --- /dev/null +++ b/Sources/ACKategories/UI/Popup/PopupModalAnimation.swift @@ -0,0 +1,147 @@ +#if canImport(UIKit) && !os(watchOS) +import Foundation +import UIKit + +/// Animation for presenting popups +public final class PopupModalAnimation: NSObject, UIViewControllerAnimatedTransitioning { + + /// Popup horizontal inset + public var popupXInset: CGFloat = 19 + + /// Background color for popup view + public var popupBackgroundColor: UIColor = .clear + + /// Background color for + public var backgroundColor: UIColor = .clear + + /// Visual effect for background view + public var visualEffect: UIVisualEffect? = UIBlurEffect(style: .light) + + /// Final alpha for background view + public var finalAlpha: CGFloat = 1.0 + + /// Duration of presenting modal + public var presentDuration: TimeInterval = 1.0 + + /// Duration of dismissing modal + public var dismissDuration: TimeInterval = 0.3 + + /// Shadow offset + public var shadowOffset: CGSize = CGSize.zero + + /// Shadow color + public var shadowColor: CGColor = UIColor.black.cgColor + + /// Shadow radius + public var shadowRadius: CGFloat = 5.0 + + /// Shadow opacity + public var shadowOpacity: Float = 0.5 + + /// Popup animation damping + public var animationDamping: CGFloat = 0.8 + + /// Initial animation spring velocity + public var animationInitialSpringVelocity: CGFloat = 1 + + public enum AnimationType { + case present + case dismiss + } + + public var animationType: AnimationType = .present + + private lazy var coverView: UIVisualEffectView = { + let view = UIVisualEffectView() + view.effect = visualEffect + view.backgroundColor = popupBackgroundColor + return view + }() + + public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { + + //The view controller's view that is presenting the modal view + let containerView = transitionContext.containerView + containerView.translatesAutoresizingMaskIntoConstraints = false + + if animationType == .present { + //The modal view itself + guard let modalView = transitionContext.viewController(forKey: .to)?.view, let containerParent = containerView.superview else { return } + + modalView.translatesAutoresizingMaskIntoConstraints = false + + coverView.alpha = 0 + containerView.addSubview(coverView) + NSLayoutConstraint.activate([ + containerView.leadingAnchor.constraint(equalTo: containerParent.leadingAnchor), + containerView.trailingAnchor.constraint(equalTo: containerParent.trailingAnchor), + containerView.topAnchor.constraint(equalTo: containerParent.topAnchor), + containerView.bottomAnchor.constraint(equalTo: containerParent.bottomAnchor), + ]) + containerView.backgroundColor = backgroundColor + coverView.frame = containerView.frame + + containerView.addSubview(modalView) + NSLayoutConstraint.activate([ + modalView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: popupXInset), + modalView.heightAnchor.constraint(lessThanOrEqualTo: containerView.heightAnchor, multiplier: 0.85), + modalView.centerXAnchor.constraint(equalTo: containerView.centerXAnchor), + modalView.centerYAnchor.constraint(equalTo: containerView.centerYAnchor), + ]) + modalView.layer.contentsScale = UIScreen.main.scale + modalView.layer.shadowColor = shadowColor + modalView.layer.shadowOffset = shadowOffset + modalView.layer.shadowRadius = shadowRadius + modalView.layer.shadowOpacity = shadowOpacity + modalView.layer.masksToBounds = false + modalView.clipsToBounds = false + + let endFrame = modalView.frame + modalView.frame = CGRect(x: endFrame.origin.x, y: containerView.frame.size.height, width: endFrame.size.width, height: endFrame.size.height) + containerView.bringSubviewToFront(modalView) + + //Move off of the screen so we can slide it up + UIView.animate(withDuration: transitionDuration(using: transitionContext), delay: 0, usingSpringWithDamping: animationDamping, initialSpringVelocity: animationInitialSpringVelocity, options: .curveLinear, animations: { + modalView.frame = endFrame + self.coverView.alpha = self.finalAlpha + self.coverView.effect = self.visualEffect + }, completion: { _ in + transitionContext.completeTransition(true) + }) + + } else if animationType == .dismiss { + guard let modalView = transitionContext.viewController(forKey: .from)?.view else { return } + //The modal view itself + UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: { + var frame = modalView.frame + frame.origin.y = containerView.frame.height + modalView.layer.transform = CATransform3DRotate(modalView.layer.transform, (3.14/180) * 35, 1, 0.0, 0.0) + self.coverView.alpha = 0 + self.coverView.effect = nil + + modalView.frame = frame + }, completion: { _ in + self.coverView.removeFromSuperview() + transitionContext.completeTransition(true) + }) + + } + } + + public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { + animationType == .present ? presentDuration : dismissDuration + } +} + +extension PopupModalAnimation: UIViewControllerTransitioningDelegate { + public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { + animationType = .present + return self + } + + public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { + animationType = .dismiss + return self + } +} +#endif diff --git a/Sources/ACKategories/UI/Popup/PopupPresenting.swift b/Sources/ACKategories/UI/Popup/PopupPresenting.swift new file mode 100644 index 00000000..5f8bb969 --- /dev/null +++ b/Sources/ACKategories/UI/Popup/PopupPresenting.swift @@ -0,0 +1,18 @@ +#if canImport(UIKit) && !os(watchOS) +import UIKit + +public protocol PopupPresenting { + var popupAnimation: PopupModalAnimation { get } + + func present(popup: UIViewController) +} + +public extension PopupPresenting where Self: UIViewController { + /// Presents popup with animation + func present(popup: UIViewController, animated: Bool) { + popup.transitioningDelegate = popupAnimation + popup.modalPresentationStyle = .custom + present(popup, animated: animated) + } +} +#endif diff --git a/Sources/ACKategories/Reusable.swift b/Sources/ACKategories/UI/Reusable.swift similarity index 100% rename from Sources/ACKategories/Reusable.swift rename to Sources/ACKategories/UI/Reusable.swift diff --git a/Sources/ACKategories/ReusableView.swift b/Sources/ACKategories/UI/ReusableView.swift similarity index 100% rename from Sources/ACKategories/ReusableView.swift rename to Sources/ACKategories/UI/ReusableView.swift diff --git a/Sources/ACKategories/SelfSizingTableHeaderFooterView.swift b/Sources/ACKategories/UI/SelfSizingTableHeaderFooterView.swift similarity index 100% rename from Sources/ACKategories/SelfSizingTableHeaderFooterView.swift rename to Sources/ACKategories/UI/SelfSizingTableHeaderFooterView.swift diff --git a/Sources/ACKategories/TagListView.swift b/Sources/ACKategories/UI/TagListView.swift similarity index 100% rename from Sources/ACKategories/TagListView.swift rename to Sources/ACKategories/UI/TagListView.swift diff --git a/Sources/ACKategories/UI/Theme/ThemeExtensions.swift b/Sources/ACKategories/UI/Theme/ThemeExtensions.swift new file mode 100644 index 00000000..fa2094ca --- /dev/null +++ b/Sources/ACKategories/UI/Theme/ThemeExtensions.swift @@ -0,0 +1,9 @@ +#if canImport(UIKit) +import UIKit + +extension UIColor: ThemeProvider { } +#endif + +#if canImport(UIKit) && !os(watchOS) +extension UIView: ThemeProvider { } +#endif diff --git a/Sources/ACKategories/UI/Theme/ThemeProvider.swift b/Sources/ACKategories/UI/Theme/ThemeProvider.swift new file mode 100644 index 00000000..32db7713 --- /dev/null +++ b/Sources/ACKategories/UI/Theme/ThemeProvider.swift @@ -0,0 +1,11 @@ +import Foundation + +public struct Theme { } + +public protocol ThemeProvider { } + +public extension ThemeProvider { + static var theme: Theme.Type { Theme.self } + + var theme: Theme { Theme() } // theoretically unneccessary allocation overhead every call, but SnapKit uses the same pattern so... +} diff --git a/Sources/ACKategories/UIApplicationExtensions.swift b/Sources/ACKategories/UI/UIApplicationExtensions.swift similarity index 100% rename from Sources/ACKategories/UIApplicationExtensions.swift rename to Sources/ACKategories/UI/UIApplicationExtensions.swift diff --git a/Sources/ACKategories/UILabelExtensions.swift b/Sources/ACKategories/UI/UILabelExtensions.swift similarity index 100% rename from Sources/ACKategories/UILabelExtensions.swift rename to Sources/ACKategories/UI/UILabelExtensions.swift diff --git a/Sources/ACKategories/UINavigationControllerExtensions.swift b/Sources/ACKategories/UI/UINavigationControllerExtensions.swift similarity index 100% rename from Sources/ACKategories/UINavigationControllerExtensions.swift rename to Sources/ACKategories/UI/UINavigationControllerExtensions.swift diff --git a/Sources/ACKategories/UISearchBarExtensions.swift b/Sources/ACKategories/UI/UISearchBarExtensions.swift similarity index 100% rename from Sources/ACKategories/UISearchBarExtensions.swift rename to Sources/ACKategories/UI/UISearchBarExtensions.swift diff --git a/Sources/ACKategories/UIStackViewExtensions.swift b/Sources/ACKategories/UI/UIStackViewExtensions.swift similarity index 100% rename from Sources/ACKategories/UIStackViewExtensions.swift rename to Sources/ACKategories/UI/UIStackViewExtensions.swift diff --git a/Sources/ACKategories/UIView+Spacer.swift b/Sources/ACKategories/UI/UIView+Spacer.swift similarity index 100% rename from Sources/ACKategories/UIView+Spacer.swift rename to Sources/ACKategories/UI/UIView+Spacer.swift diff --git a/Sources/ACKategories/UIViewController+Children.swift b/Sources/ACKategories/UI/UIViewController+Children.swift similarity index 100% rename from Sources/ACKategories/UIViewController+Children.swift rename to Sources/ACKategories/UI/UIViewController+Children.swift diff --git a/Sources/ACKategories/UIViewController+FrontMost.swift b/Sources/ACKategories/UI/UIViewController+FrontMost.swift similarity index 100% rename from Sources/ACKategories/UIViewController+FrontMost.swift rename to Sources/ACKategories/UI/UIViewController+FrontMost.swift diff --git a/Sources/ACKategories/UIViewExtensions.swift b/Sources/ACKategories/UI/UIViewExtensions.swift similarity index 100% rename from Sources/ACKategories/UIViewExtensions.swift rename to Sources/ACKategories/UI/UIViewExtensions.swift From a3efc92cf63390f612834f3ab77033202c4b9eda Mon Sep 17 00:00:00 2001 From: Jakub Olejnik Date: Thu, 28 Dec 2023 14:58:35 +0100 Subject: [PATCH 02/12] =?UTF-8?q?=E2=9C=A8=20Add=20Networking=20from=20tem?= =?UTF-8?q?plate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ACKategories.xcodeproj/project.pbxproj | 669 +++++++++++++++++- ....xcscheme => ACKategoriesTesting.xcscheme} | 40 +- .../xcschemes/Networking.xcscheme | 85 +++ Package.swift | 14 + .../Networking/APIServiceMock.swift | 41 ++ .../Networking/HTTPResponse+TestData.swift | 16 + .../Networking/HTTPURLResponse+TestData.swift | 17 + .../Networking/NetworkMock.swift | 15 + .../Networking/URL+TestData.swift | 6 + .../Networking/URLRequest+TestData.swift | 12 + Sources/Networking/APIService.swift | 118 +++ Sources/Networking/APIServicing.swift | 52 ++ Sources/Networking/HTTPMethod.swift | 41 ++ Sources/Networking/HTTPResponse.swift | 28 + Sources/Networking/Networking.swift | 23 + Sources/Networking/OAuthInterceptor.swift | 68 ++ Sources/Networking/RequestAddress.swift | 22 + Sources/Networking/RequestBody.swift | 99 +++ Tests/ACKategoriesTests/EdgeInsetsTests.swift | 1 + .../APIService+OAuthInterceptor_Tests.swift | 176 +++++ Tests/NetworkingTests/APIService_Tests.swift | 81 +++ Tests/NetworkingTests/Networking.xctestplan | 24 + .../OAuthInterceptor_Tests.swift | 247 +++++++ .../RequestAddress_Tests.swift | 17 + 24 files changed, 1897 insertions(+), 15 deletions(-) rename ACKategories.xcodeproj/xcshareddata/xcschemes/{ACKategoriesResponderTests.xcscheme => ACKategoriesTesting.xcscheme} (63%) create mode 100644 ACKategories.xcodeproj/xcshareddata/xcschemes/Networking.xcscheme create mode 100644 Sources/ACKategoriesTesting/Networking/APIServiceMock.swift create mode 100644 Sources/ACKategoriesTesting/Networking/HTTPResponse+TestData.swift create mode 100644 Sources/ACKategoriesTesting/Networking/HTTPURLResponse+TestData.swift create mode 100644 Sources/ACKategoriesTesting/Networking/NetworkMock.swift create mode 100644 Sources/ACKategoriesTesting/Networking/URL+TestData.swift create mode 100644 Sources/ACKategoriesTesting/Networking/URLRequest+TestData.swift create mode 100644 Sources/Networking/APIService.swift create mode 100644 Sources/Networking/APIServicing.swift create mode 100644 Sources/Networking/HTTPMethod.swift create mode 100644 Sources/Networking/HTTPResponse.swift create mode 100644 Sources/Networking/Networking.swift create mode 100644 Sources/Networking/OAuthInterceptor.swift create mode 100644 Sources/Networking/RequestAddress.swift create mode 100644 Sources/Networking/RequestBody.swift create mode 100644 Tests/NetworkingTests/APIService+OAuthInterceptor_Tests.swift create mode 100644 Tests/NetworkingTests/APIService_Tests.swift create mode 100644 Tests/NetworkingTests/Networking.xctestplan create mode 100644 Tests/NetworkingTests/OAuthInterceptor_Tests.swift create mode 100644 Tests/NetworkingTests/RequestAddress_Tests.swift diff --git a/ACKategories.xcodeproj/project.pbxproj b/ACKategories.xcodeproj/project.pbxproj index 2f7321bf..c2502bcf 100644 --- a/ACKategories.xcodeproj/project.pbxproj +++ b/ACKategories.xcodeproj/project.pbxproj @@ -7,11 +7,32 @@ objects = { /* Begin PBXBuildFile section */ + 690BCB8D2B3DB62400EDA6F8 /* Networking.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 693A925D2B3DB290008B3DC3 /* Networking.framework */; platformFilters = (ios, macos, tvos, watchos, ); }; 691B9AD92AFD1E5C008AE7BD /* ACKategories.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 69ACD6C22AFD130C0021127B /* ACKategories.framework */; }; 691B9ADA2AFD1E5C008AE7BD /* ACKategories.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 69ACD6C22AFD130C0021127B /* ACKategories.framework */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 6922C77D2AFD1C1A00519CDF /* UINavigationControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6922C77C2AFD1C1A00519CDF /* UINavigationControllerTests.swift */; }; 6922C7802AFD1C8B00519CDF /* Dummies.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69E0A6A02AFD114600C8E8D9 /* Dummies.swift */; }; 6922C7812AFD1C8B00519CDF /* FlowCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69E0A6A12AFD114600C8E8D9 /* FlowCoordinatorTests.swift */; }; + 693A92652B3DB290008B3DC3 /* Networking.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 693A925D2B3DB290008B3DC3 /* Networking.framework */; }; + 693A927B2B3DB2AA008B3DC3 /* RequestAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 693A92732B3DB2AA008B3DC3 /* RequestAddress.swift */; }; + 693A927C2B3DB2AA008B3DC3 /* APIService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 693A92742B3DB2AA008B3DC3 /* APIService.swift */; }; + 693A927D2B3DB2AA008B3DC3 /* RequestBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = 693A92752B3DB2AA008B3DC3 /* RequestBody.swift */; }; + 693A927E2B3DB2AA008B3DC3 /* Networking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 693A92762B3DB2AA008B3DC3 /* Networking.swift */; }; + 693A927F2B3DB2AA008B3DC3 /* HTTPResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 693A92772B3DB2AA008B3DC3 /* HTTPResponse.swift */; }; + 693A92802B3DB2AA008B3DC3 /* HTTPMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 693A92782B3DB2AA008B3DC3 /* HTTPMethod.swift */; }; + 693A92812B3DB2AA008B3DC3 /* APIServicing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 693A92792B3DB2AA008B3DC3 /* APIServicing.swift */; }; + 693A92822B3DB2AA008B3DC3 /* OAuthInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 693A927A2B3DB2AA008B3DC3 /* OAuthInterceptor.swift */; }; + 693A92882B3DB353008B3DC3 /* APIService_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 693A92842B3DB353008B3DC3 /* APIService_Tests.swift */; }; + 693A92892B3DB353008B3DC3 /* OAuthInterceptor_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 693A92852B3DB353008B3DC3 /* OAuthInterceptor_Tests.swift */; }; + 693A928A2B3DB353008B3DC3 /* APIService+OAuthInterceptor_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 693A92862B3DB353008B3DC3 /* APIService+OAuthInterceptor_Tests.swift */; }; + 693A928B2B3DB353008B3DC3 /* RequestAddress_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 693A92872B3DB353008B3DC3 /* RequestAddress_Tests.swift */; }; + 693A92A02B3DB394008B3DC3 /* NetworkMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 693A929A2B3DB394008B3DC3 /* NetworkMock.swift */; }; + 693A92A12B3DB394008B3DC3 /* APIServiceMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 693A929B2B3DB394008B3DC3 /* APIServiceMock.swift */; }; + 693A92A22B3DB394008B3DC3 /* URLRequest+TestData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 693A929C2B3DB394008B3DC3 /* URLRequest+TestData.swift */; }; + 693A92A32B3DB394008B3DC3 /* HTTPURLResponse+TestData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 693A929D2B3DB394008B3DC3 /* HTTPURLResponse+TestData.swift */; }; + 693A92A42B3DB394008B3DC3 /* HTTPResponse+TestData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 693A929E2B3DB394008B3DC3 /* HTTPResponse+TestData.swift */; }; + 693A92A52B3DB394008B3DC3 /* URL+TestData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 693A929F2B3DB394008B3DC3 /* URL+TestData.swift */; }; + 693A92A62B3DB39F008B3DC3 /* ACKategoriesTesting.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 693A92912B3DB388008B3DC3 /* ACKategoriesTesting.framework */; }; 6971A98A2AFD1AF5000FC317 /* ACKategories.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 69ACD6C22AFD130C0021127B /* ACKategories.framework */; }; 6971A9902AFD1B0F000FC317 /* ControlBlocksTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69E0A6B62AFD114600C8E8D9 /* ControlBlocksTests.swift */; }; 697D61D42B0BC41900020664 /* EdgeInsetsExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 697D61D32B0BC41900020664 /* EdgeInsetsExtensions.swift */; }; @@ -108,6 +129,13 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + 690BCB8F2B3DB62400EDA6F8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 69E819DF23C773010054687B /* Project object */; + proxyType = 1; + remoteGlobalIDString = 693A925C2B3DB290008B3DC3; + remoteInfo = Networking; + }; 691B9ADB2AFD1E5C008AE7BD /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 69E819DF23C773010054687B /* Project object */; @@ -115,6 +143,20 @@ remoteGlobalIDString = 69ACD6C12AFD130C0021127B; remoteInfo = ACKategories; }; + 693A92662B3DB290008B3DC3 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 69E819DF23C773010054687B /* Project object */; + proxyType = 1; + remoteGlobalIDString = 693A925C2B3DB290008B3DC3; + remoteInfo = Networking; + }; + 693A92A82B3DB39F008B3DC3 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 69E819DF23C773010054687B /* Project object */; + proxyType = 1; + remoteGlobalIDString = 693A92902B3DB388008B3DC3; + remoteInfo = ACKategoriesTesting; + }; 6971A98B2AFD1AF5000FC317 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 69E819DF23C773010054687B /* Project object */; @@ -153,9 +195,31 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 690BCB8C2B3DB5DE00EDA6F8 /* Networking.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = file; name = Networking.xctestplan; path = Tests/NetworkingTests/Networking.xctestplan; sourceTree = SOURCE_ROOT; }; 6922C77C2AFD1C1A00519CDF /* UINavigationControllerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UINavigationControllerTests.swift; sourceTree = ""; }; 6922C77E2AFD1C3300519CDF /* ACKategoriesResponder.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = ACKategoriesResponder.xctestplan; sourceTree = ""; }; 6922C77F2AFD1C5000519CDF /* ACKategories.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = ACKategories.xctestplan; sourceTree = ""; }; + 693A925D2B3DB290008B3DC3 /* Networking.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Networking.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 693A92642B3DB290008B3DC3 /* NetworkingTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NetworkingTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 693A92732B3DB2AA008B3DC3 /* RequestAddress.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestAddress.swift; sourceTree = ""; }; + 693A92742B3DB2AA008B3DC3 /* APIService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = APIService.swift; sourceTree = ""; }; + 693A92752B3DB2AA008B3DC3 /* RequestBody.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestBody.swift; sourceTree = ""; }; + 693A92762B3DB2AA008B3DC3 /* Networking.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Networking.swift; sourceTree = ""; }; + 693A92772B3DB2AA008B3DC3 /* HTTPResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPResponse.swift; sourceTree = ""; }; + 693A92782B3DB2AA008B3DC3 /* HTTPMethod.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPMethod.swift; sourceTree = ""; }; + 693A92792B3DB2AA008B3DC3 /* APIServicing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = APIServicing.swift; sourceTree = ""; }; + 693A927A2B3DB2AA008B3DC3 /* OAuthInterceptor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OAuthInterceptor.swift; sourceTree = ""; }; + 693A92842B3DB353008B3DC3 /* APIService_Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = APIService_Tests.swift; sourceTree = ""; }; + 693A92852B3DB353008B3DC3 /* OAuthInterceptor_Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OAuthInterceptor_Tests.swift; sourceTree = ""; }; + 693A92862B3DB353008B3DC3 /* APIService+OAuthInterceptor_Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "APIService+OAuthInterceptor_Tests.swift"; sourceTree = ""; }; + 693A92872B3DB353008B3DC3 /* RequestAddress_Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestAddress_Tests.swift; sourceTree = ""; }; + 693A92912B3DB388008B3DC3 /* ACKategoriesTesting.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ACKategoriesTesting.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 693A929A2B3DB394008B3DC3 /* NetworkMock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkMock.swift; sourceTree = ""; }; + 693A929B2B3DB394008B3DC3 /* APIServiceMock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = APIServiceMock.swift; sourceTree = ""; }; + 693A929C2B3DB394008B3DC3 /* URLRequest+TestData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "URLRequest+TestData.swift"; sourceTree = ""; }; + 693A929D2B3DB394008B3DC3 /* HTTPURLResponse+TestData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HTTPURLResponse+TestData.swift"; sourceTree = ""; }; + 693A929E2B3DB394008B3DC3 /* HTTPResponse+TestData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HTTPResponse+TestData.swift"; sourceTree = ""; }; + 693A929F2B3DB394008B3DC3 /* URL+TestData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "URL+TestData.swift"; sourceTree = ""; }; 695096D823C7908B00E8F457 /* ACKategoriesExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ACKategoriesExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 6971A9862AFD1AF5000FC317 /* ACKategoriesResponderTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ACKategoriesResponderTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 697B023227DB65B50082F4AC /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = ""; }; @@ -258,6 +322,30 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 693A925A2B3DB290008B3DC3 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 693A92612B3DB290008B3DC3 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 693A92652B3DB290008B3DC3 /* Networking.framework in Frameworks */, + 693A92A62B3DB39F008B3DC3 /* ACKategoriesTesting.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 693A928E2B3DB388008B3DC3 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 690BCB8D2B3DB62400EDA6F8 /* Networking.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 695096D523C7908B00E8F457 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -299,6 +387,54 @@ name = Frameworks; sourceTree = ""; }; + 693A92722B3DB2AA008B3DC3 /* Networking */ = { + isa = PBXGroup; + children = ( + 693A92732B3DB2AA008B3DC3 /* RequestAddress.swift */, + 693A92742B3DB2AA008B3DC3 /* APIService.swift */, + 693A92752B3DB2AA008B3DC3 /* RequestBody.swift */, + 693A92762B3DB2AA008B3DC3 /* Networking.swift */, + 693A92772B3DB2AA008B3DC3 /* HTTPResponse.swift */, + 693A92782B3DB2AA008B3DC3 /* HTTPMethod.swift */, + 693A92792B3DB2AA008B3DC3 /* APIServicing.swift */, + 693A927A2B3DB2AA008B3DC3 /* OAuthInterceptor.swift */, + ); + path = Networking; + sourceTree = ""; + }; + 693A92832B3DB353008B3DC3 /* NetworkingTests */ = { + isa = PBXGroup; + children = ( + 690BCB8C2B3DB5DE00EDA6F8 /* Networking.xctestplan */, + 693A92842B3DB353008B3DC3 /* APIService_Tests.swift */, + 693A92852B3DB353008B3DC3 /* OAuthInterceptor_Tests.swift */, + 693A92862B3DB353008B3DC3 /* APIService+OAuthInterceptor_Tests.swift */, + 693A92872B3DB353008B3DC3 /* RequestAddress_Tests.swift */, + ); + path = NetworkingTests; + sourceTree = ""; + }; + 693A92982B3DB394008B3DC3 /* ACKategoriesTesting */ = { + isa = PBXGroup; + children = ( + 693A92992B3DB394008B3DC3 /* Networking */, + ); + path = ACKategoriesTesting; + sourceTree = ""; + }; + 693A92992B3DB394008B3DC3 /* Networking */ = { + isa = PBXGroup; + children = ( + 693A929A2B3DB394008B3DC3 /* NetworkMock.swift */, + 693A929B2B3DB394008B3DC3 /* APIServiceMock.swift */, + 693A929C2B3DB394008B3DC3 /* URLRequest+TestData.swift */, + 693A929D2B3DB394008B3DC3 /* HTTPURLResponse+TestData.swift */, + 693A929E2B3DB394008B3DC3 /* HTTPResponse+TestData.swift */, + 693A929F2B3DB394008B3DC3 /* URL+TestData.swift */, + ); + path = Networking; + sourceTree = ""; + }; 695096D923C7908B00E8F457 /* ACKategoriesExample */ = { isa = PBXGroup; children = ( @@ -371,6 +507,8 @@ isa = PBXGroup; children = ( 69E0A5F42AFD10BE00C8E8D9 /* ACKategories */, + 693A92982B3DB394008B3DC3 /* ACKategoriesTesting */, + 693A92722B3DB2AA008B3DC3 /* Networking */, ); path = Sources; sourceTree = ""; @@ -457,6 +595,7 @@ children = ( 6971A9872AFD1AF5000FC317 /* ACKategoriesResponderTests */, 69E0A69C2AFD114600C8E8D9 /* ACKategoriesTests */, + 693A92832B3DB353008B3DC3 /* NetworkingTests */, ); path = Tests; sourceTree = ""; @@ -538,6 +677,9 @@ 69ACD6C22AFD130C0021127B /* ACKategories.framework */, 69ACD6C92AFD130D0021127B /* ACKategoriesTests.xctest */, 6971A9862AFD1AF5000FC317 /* ACKategoriesResponderTests.xctest */, + 693A925D2B3DB290008B3DC3 /* Networking.framework */, + 693A92642B3DB290008B3DC3 /* NetworkingTests.xctest */, + 693A92912B3DB388008B3DC3 /* ACKategoriesTesting.framework */, ); name = Products; sourceTree = ""; @@ -643,6 +785,20 @@ /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ + 693A92582B3DB290008B3DC3 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 693A928C2B3DB388008B3DC3 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 69ACD6BD2AFD130C0021127B /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -653,6 +809,62 @@ /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ + 693A925C2B3DB290008B3DC3 /* Networking */ = { + isa = PBXNativeTarget; + buildConfigurationList = 693A92702B3DB290008B3DC3 /* Build configuration list for PBXNativeTarget "Networking" */; + buildPhases = ( + 693A92582B3DB290008B3DC3 /* Headers */, + 693A92592B3DB290008B3DC3 /* Sources */, + 693A925A2B3DB290008B3DC3 /* Frameworks */, + 693A925B2B3DB290008B3DC3 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Networking; + productName = Networking; + productReference = 693A925D2B3DB290008B3DC3 /* Networking.framework */; + productType = "com.apple.product-type.framework"; + }; + 693A92632B3DB290008B3DC3 /* NetworkingTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 693A92712B3DB290008B3DC3 /* Build configuration list for PBXNativeTarget "NetworkingTests" */; + buildPhases = ( + 693A92602B3DB290008B3DC3 /* Sources */, + 693A92612B3DB290008B3DC3 /* Frameworks */, + 693A92622B3DB290008B3DC3 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 693A92672B3DB290008B3DC3 /* PBXTargetDependency */, + 693A92A92B3DB39F008B3DC3 /* PBXTargetDependency */, + ); + name = NetworkingTests; + productName = NetworkingTests; + productReference = 693A92642B3DB290008B3DC3 /* NetworkingTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 693A92902B3DB388008B3DC3 /* ACKategoriesTesting */ = { + isa = PBXNativeTarget; + buildConfigurationList = 693A92952B3DB388008B3DC3 /* Build configuration list for PBXNativeTarget "ACKategoriesTesting" */; + buildPhases = ( + 693A928C2B3DB388008B3DC3 /* Headers */, + 693A928D2B3DB388008B3DC3 /* Sources */, + 693A928E2B3DB388008B3DC3 /* Frameworks */, + 693A928F2B3DB388008B3DC3 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 690BCB902B3DB62400EDA6F8 /* PBXTargetDependency */, + ); + name = ACKategoriesTesting; + productName = ACKategoriesTesting; + productReference = 693A92912B3DB388008B3DC3 /* ACKategoriesTesting.framework */; + productType = "com.apple.product-type.framework"; + }; 695096D723C7908B00E8F457 /* ACKategoriesExample */ = { isa = PBXNativeTarget; buildConfigurationList = 695096E923C7908D00E8F457 /* Build configuration list for PBXNativeTarget "ACKategoriesExample" */; @@ -736,9 +948,18 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = YES; - LastSwiftUpdateCheck = 1500; + LastSwiftUpdateCheck = 1510; LastUpgradeCheck = 1500; TargetAttributes = { + 693A925C2B3DB290008B3DC3 = { + CreatedOnToolsVersion = 15.1; + }; + 693A92632B3DB290008B3DC3 = { + CreatedOnToolsVersion = 15.1; + }; + 693A92902B3DB388008B3DC3 = { + CreatedOnToolsVersion = 15.1; + }; 695096D723C7908B00E8F457 = { CreatedOnToolsVersion = 11.3; LastSwiftMigration = 1130; @@ -774,11 +995,35 @@ 69ACD6C12AFD130C0021127B /* ACKategories */, 69ACD6C82AFD130D0021127B /* ACKategoriesTests */, 6971A9852AFD1AF5000FC317 /* ACKategoriesResponderTests */, + 693A925C2B3DB290008B3DC3 /* Networking */, + 693A92632B3DB290008B3DC3 /* NetworkingTests */, + 693A92902B3DB388008B3DC3 /* ACKategoriesTesting */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 693A925B2B3DB290008B3DC3 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 693A92622B3DB290008B3DC3 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 693A928F2B3DB388008B3DC3 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 695096D623C7908B00E8F457 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -812,6 +1057,45 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 693A92592B3DB290008B3DC3 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 693A927C2B3DB2AA008B3DC3 /* APIService.swift in Sources */, + 693A92822B3DB2AA008B3DC3 /* OAuthInterceptor.swift in Sources */, + 693A927B2B3DB2AA008B3DC3 /* RequestAddress.swift in Sources */, + 693A92802B3DB2AA008B3DC3 /* HTTPMethod.swift in Sources */, + 693A927F2B3DB2AA008B3DC3 /* HTTPResponse.swift in Sources */, + 693A927D2B3DB2AA008B3DC3 /* RequestBody.swift in Sources */, + 693A92812B3DB2AA008B3DC3 /* APIServicing.swift in Sources */, + 693A927E2B3DB2AA008B3DC3 /* Networking.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 693A92602B3DB290008B3DC3 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 693A92882B3DB353008B3DC3 /* APIService_Tests.swift in Sources */, + 693A928A2B3DB353008B3DC3 /* APIService+OAuthInterceptor_Tests.swift in Sources */, + 693A928B2B3DB353008B3DC3 /* RequestAddress_Tests.swift in Sources */, + 693A92892B3DB353008B3DC3 /* OAuthInterceptor_Tests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 693A928D2B3DB388008B3DC3 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 693A92A32B3DB394008B3DC3 /* HTTPURLResponse+TestData.swift in Sources */, + 693A92A52B3DB394008B3DC3 /* URL+TestData.swift in Sources */, + 693A92A12B3DB394008B3DC3 /* APIServiceMock.swift in Sources */, + 693A92A42B3DB394008B3DC3 /* HTTPResponse+TestData.swift in Sources */, + 693A92A02B3DB394008B3DC3 /* NetworkMock.swift in Sources */, + 693A92A22B3DB394008B3DC3 /* URLRequest+TestData.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 695096D423C7908B00E8F457 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -935,11 +1219,32 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 690BCB902B3DB62400EDA6F8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + platformFilters = ( + ios, + macos, + tvos, + watchos, + ); + target = 693A925C2B3DB290008B3DC3 /* Networking */; + targetProxy = 690BCB8F2B3DB62400EDA6F8 /* PBXContainerItemProxy */; + }; 691B9ADC2AFD1E5C008AE7BD /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 69ACD6C12AFD130C0021127B /* ACKategories */; targetProxy = 691B9ADB2AFD1E5C008AE7BD /* PBXContainerItemProxy */; }; + 693A92672B3DB290008B3DC3 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 693A925C2B3DB290008B3DC3 /* Networking */; + targetProxy = 693A92662B3DB290008B3DC3 /* PBXContainerItemProxy */; + }; + 693A92A92B3DB39F008B3DC3 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 693A92902B3DB388008B3DC3 /* ACKategoriesTesting */; + targetProxy = 693A92A82B3DB39F008B3DC3 /* PBXContainerItemProxy */; + }; 6971A98C2AFD1AF5000FC317 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 69ACD6C12AFD130C0021127B /* ACKategories */; @@ -958,6 +1263,337 @@ /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ + 693A926C2B3DB290008B3DC3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 10.15; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = cz.ackee.Networking; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx watchos watchsimulator"; + SUPPORTS_MACCATALYST = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TVOS_DEPLOYMENT_TARGET = 13.0; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + WATCHOS_DEPLOYMENT_TARGET = 6.0; + }; + name = Debug; + }; + 693A926D2B3DB290008B3DC3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 10.15; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = cz.ackee.Networking; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx watchos watchsimulator"; + SUPPORTS_MACCATALYST = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TVOS_DEPLOYMENT_TARGET = 13.0; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + WATCHOS_DEPLOYMENT_TARGET = 6.0; + }; + name = Release; + }; + 693A926E2B3DB290008B3DC3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.2; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = cz.ackee.NetworkingTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 693A926F2B3DB290008B3DC3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.2; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = cz.ackee.NetworkingTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 693A92962B3DB388008B3DC3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 17.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = cz.ackee.ACKategoriesTesting; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TVOS_DEPLOYMENT_TARGET = 17.2; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + WATCHOS_DEPLOYMENT_TARGET = 10.2; + }; + name = Debug; + }; + 693A92972B3DB388008B3DC3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 17.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = cz.ackee.ACKategoriesTesting; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TVOS_DEPLOYMENT_TARGET = 17.2; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + WATCHOS_DEPLOYMENT_TARGET = 10.2; + }; + name = Release; + }; 695096EA23C7908D00E8F457 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -1238,12 +1874,14 @@ PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx watchos watchsimulator"; SUPPORTS_MACCATALYST = YES; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -1296,10 +1934,12 @@ PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx watchos watchsimulator"; SUPPORTS_MACCATALYST = YES; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -1482,6 +2122,33 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 693A92702B3DB290008B3DC3 /* Build configuration list for PBXNativeTarget "Networking" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 693A926C2B3DB290008B3DC3 /* Debug */, + 693A926D2B3DB290008B3DC3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 693A92712B3DB290008B3DC3 /* Build configuration list for PBXNativeTarget "NetworkingTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 693A926E2B3DB290008B3DC3 /* Debug */, + 693A926F2B3DB290008B3DC3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 693A92952B3DB388008B3DC3 /* Build configuration list for PBXNativeTarget "ACKategoriesTesting" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 693A92962B3DB388008B3DC3 /* Debug */, + 693A92972B3DB388008B3DC3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 695096E923C7908D00E8F457 /* Build configuration list for PBXNativeTarget "ACKategoriesExample" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/ACKategories.xcodeproj/xcshareddata/xcschemes/ACKategoriesResponderTests.xcscheme b/ACKategories.xcodeproj/xcshareddata/xcschemes/ACKategoriesTesting.xcscheme similarity index 63% rename from ACKategories.xcodeproj/xcshareddata/xcschemes/ACKategoriesResponderTests.xcscheme rename to ACKategories.xcodeproj/xcshareddata/xcschemes/ACKategoriesTesting.xcscheme index c9cf2943..e83e29d9 100644 --- a/ACKategories.xcodeproj/xcshareddata/xcschemes/ACKategoriesResponderTests.xcscheme +++ b/ACKategories.xcodeproj/xcshareddata/xcschemes/ACKategoriesTesting.xcscheme @@ -1,10 +1,26 @@ + + + + + + - - - - - - + + + + diff --git a/ACKategories.xcodeproj/xcshareddata/xcschemes/Networking.xcscheme b/ACKategories.xcodeproj/xcshareddata/xcschemes/Networking.xcscheme new file mode 100644 index 00000000..f2bbae9e --- /dev/null +++ b/ACKategories.xcodeproj/xcshareddata/xcschemes/Networking.xcscheme @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Package.swift b/Package.swift index bd15de2e..e1fe3c7e 100644 --- a/Package.swift +++ b/Package.swift @@ -11,6 +11,8 @@ let package = Package( ], products: [ .library(name: "ACKategories", targets: ["ACKategories"]), + .library(name: "ACKategoriesTesting", targets: ["ACKategoriesTesting"]), + .library(name: "Networking", targets: ["Networking"]), ], targets: [ .target(name: "ACKategories"), @@ -18,5 +20,17 @@ let package = Package( name: "ACKategoriesTests", dependencies: ["ACKategories"] ), + .target( + name: "ACKategoriesTesting", + dependencies: ["Networking"] + ), + .target(name: "Networking"), + .testTarget( + name: "NetworkingTests", + dependencies: [ + "ACKategoriesTesting", + "Networking", + ] + ), ] ) diff --git a/Sources/ACKategoriesTesting/Networking/APIServiceMock.swift b/Sources/ACKategoriesTesting/Networking/APIServiceMock.swift new file mode 100644 index 00000000..d1417e2e --- /dev/null +++ b/Sources/ACKategoriesTesting/Networking/APIServiceMock.swift @@ -0,0 +1,41 @@ +import Foundation +import Networking + +@available(tvOS 13.0, iOS 13.0, watchOS 6.0, macOS 10.15, *) +public final class APIService_Mock: APIServicing { + public init() { + + } + + public var requestBody: (URLRequest) async throws -> HTTPResponse = { + .init(request: $0, response: nil, data: nil) + } + + public func request(_ request: URLRequest) async throws -> HTTPResponse { + try await requestBody(request) + } + + public func request( + _ address: RequestAddress, + method: HTTPMethod, + query: [String: String]?, + headers: [String: String]?, + body: RequestBody? + ) async throws -> HTTPResponse { + let url: URL = { + switch address { + case .url(let url): return url + case .path(let path): + return .ackeeCZ.appendingPathComponent(path) + } + }() + + return try await request(.init( + url: url, + method: method, + query: query, + headers: headers, + body: body + )) + } +} diff --git a/Sources/ACKategoriesTesting/Networking/HTTPResponse+TestData.swift b/Sources/ACKategoriesTesting/Networking/HTTPResponse+TestData.swift new file mode 100644 index 00000000..cd7e033d --- /dev/null +++ b/Sources/ACKategoriesTesting/Networking/HTTPResponse+TestData.swift @@ -0,0 +1,16 @@ +import Foundation +import Networking + +public extension HTTPResponse { + static func test( + request: URLRequest = .test(), + response: HTTPURLResponse? = nil, + data: Data? = nil + ) -> HTTPResponse { + .init( + request: request, + response: response, + data: data + ) + } +} diff --git a/Sources/ACKategoriesTesting/Networking/HTTPURLResponse+TestData.swift b/Sources/ACKategoriesTesting/Networking/HTTPURLResponse+TestData.swift new file mode 100644 index 00000000..a806b0ac --- /dev/null +++ b/Sources/ACKategoriesTesting/Networking/HTTPURLResponse+TestData.swift @@ -0,0 +1,17 @@ +import Foundation + +public extension HTTPURLResponse { + static func test( + url: URL = .ackeeCZ, + statusCode: Int = 200, + httpVersion: String? = nil, + headerFields: [String: String]? = nil + ) -> HTTPURLResponse? { + .init( + url: url, + statusCode: statusCode, + httpVersion: httpVersion, + headerFields: headerFields + ) + } +} diff --git a/Sources/ACKategoriesTesting/Networking/NetworkMock.swift b/Sources/ACKategoriesTesting/Networking/NetworkMock.swift new file mode 100644 index 00000000..785c8c5a --- /dev/null +++ b/Sources/ACKategoriesTesting/Networking/NetworkMock.swift @@ -0,0 +1,15 @@ +import Foundation +import Networking + +@available(tvOS 13.0, iOS 13.0, watchOS 6.0, macOS 10.15, *) +public final class Network_Mock: Networking { + public var requestBody: (URLRequest) async throws -> HTTPResponse = { _ in .test() } + + public init() { + + } + + public func request(_ request: URLRequest) async throws -> HTTPResponse { + try await requestBody(request) + } +} diff --git a/Sources/ACKategoriesTesting/Networking/URL+TestData.swift b/Sources/ACKategoriesTesting/Networking/URL+TestData.swift new file mode 100644 index 00000000..dfc5c417 --- /dev/null +++ b/Sources/ACKategoriesTesting/Networking/URL+TestData.swift @@ -0,0 +1,6 @@ +import Foundation + +public extension URL { + static var ackeeCZ = URL(string: "https://ackee.cz")! + static var ackeeDE = URL(string: "https://ackee.de")! +} diff --git a/Sources/ACKategoriesTesting/Networking/URLRequest+TestData.swift b/Sources/ACKategoriesTesting/Networking/URLRequest+TestData.swift new file mode 100644 index 00000000..23bc53fc --- /dev/null +++ b/Sources/ACKategoriesTesting/Networking/URLRequest+TestData.swift @@ -0,0 +1,12 @@ +import Foundation + +public extension URLRequest { + static func test( + url: URL = .ackeeCZ, + headers: [String: String]? = nil + ) -> URLRequest { + var request = URLRequest(url: url) + request.allHTTPHeaderFields = headers + return request + } +} diff --git a/Sources/Networking/APIService.swift b/Sources/Networking/APIService.swift new file mode 100644 index 00000000..da83df68 --- /dev/null +++ b/Sources/Networking/APIService.swift @@ -0,0 +1,118 @@ +import Foundation + +@available(tvOS 13.0, iOS 13.0, watchOS 6.0, macOS 10.15, *) +public final class APIService: APIServicing { + /// Error thrown when response status code is unexpected + public struct UnexpectedStatusCodeError: Error { + /// Received response with unexpected status code + public let response: HTTPResponse + + /// - Parameter response: Received response with unexpected status code + public init(response: HTTPResponse) { + self.response = response + } + } + + private let baseURLFactory: () -> URL + private let network: Networking + private let requestInterceptors: [RequestInterceptor] + private let responseInterceptors: [ResponseInterceptor] + + /// Create new `JSONAPIService` + /// - Parameters: + /// - baseURL: Base URL for requests that use path instead of full URL/request + /// - network: Network object performing API calls, `URLSession` basically + /// - requestInterceptors: List of request interceptors, useful for logging or header injections + /// - responseInterceptors: List of response interceptors, useful for logging or token refresh + public init( + baseURL: @autoclosure @escaping () -> URL, + network: Networking, + requestInterceptors: [RequestInterceptor] = [], + responseInterceptors: [ResponseInterceptor] = [] + ) { + self.baseURLFactory = baseURL + self.network = network + self.requestInterceptors = requestInterceptors + self.responseInterceptors = responseInterceptors + } + + public func request(_ originalRequest: URLRequest) async throws -> HTTPResponse { + var request = originalRequest + + for interceptor in requestInterceptors { + try await interceptor.intercept( + service: self, + request: &request + ) + } + + var response = try await network.request(request) + + for interceptor in responseInterceptors { + try await interceptor.intercept( + service: self, + response: &response + ) + } + + guard response.isAccepted() else { + throw UnexpectedStatusCodeError(response: response) + } + + return response + } + + public func request( + _ address: RequestAddress, + method: HTTPMethod = .get, + query: [String: String]? = nil, + headers: [String: String]? = nil, + body: RequestBody? = nil + ) async throws -> HTTPResponse { + let url: URL = { + switch address { + case .url(let url): return url + case .path(let path): + return baseURLFactory().appendingPathComponent(path) + } + }() + + return try await self.request(.init( + url: url, + method: method, + query: query, + headers: headers, + body: body + )) + } +} + +public extension URLRequest { + init( + url: URL, + method: HTTPMethod, + query: [String: String]?, + headers: [String: String]?, + body: RequestBody? + ) { + let url: URL = { + guard let query, !query.isEmpty else { return url } + + var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: true) + var queryItems = urlComponents?.queryItems ?? [] + query.forEach { key, value in + queryItems.append(.init(name: key, value: value)) + } + urlComponents?.queryItems = queryItems + + return urlComponents?.url ?? url + }() + + var request = URLRequest(url: url) + request.httpMethod = method.rawValue + request.httpBody = body?.data + request.allHTTPHeaderFields = headers + request.setValue(body?.contentType, forHTTPHeaderField: "Content-Type") + self = request + } +} diff --git a/Sources/Networking/APIServicing.swift b/Sources/Networking/APIServicing.swift new file mode 100644 index 00000000..ead9797f --- /dev/null +++ b/Sources/Networking/APIServicing.swift @@ -0,0 +1,52 @@ +import Foundation + +/// Protocol wrapping objects that perform network requests +@available(tvOS 13.0, iOS 13.0, watchOS 6.0, macOS 10.15, *) +public protocol APIServicing { + /// Send given `URLRequest` + /// - Parameter request: Request to be sent + /// - Returns: HTTPResponse to given request + func request(_ request: URLRequest) async throws -> HTTPResponse + + /// Construct and send request using given parameters + /// - Parameters: + /// - address: Request address + /// - method: Request method + /// - query: Query parameters + /// - headers: Custom headers + /// - body: Request body + /// - Returns: Received HTTP response + func request( + _ address: RequestAddress, + method: HTTPMethod, + query: [String: String]?, + headers: [String: String]?, + body: RequestBody? + ) async throws -> HTTPResponse +} + +/// Protocol wrapping interceptors that can modify requests before they are sent +@available(tvOS 13.0, iOS 13.0, watchOS 6.0, macOS 10.15, *) +public protocol RequestInterceptor { + /// Intercept request sent by service + /// - Parameters: + /// - service: Service that wants to send given request + /// - request: Request that should be sent and can be modified + func intercept( + service: APIServicing, + request: inout URLRequest + ) async throws +} + +/// Protocol wrapping interceptors that can modify responses before they are returned from API service +@available(tvOS 13.0, iOS 13.0, watchOS 6.0, macOS 10.15, *) +public protocol ResponseInterceptor { + /// Intercept response that was returned to service + /// - Parameters: + /// - service: Service that received given response + /// - response: Response that was received and can be modified + func intercept( + service: APIServicing, + response: inout HTTPResponse + ) async throws +} diff --git a/Sources/Networking/HTTPMethod.swift b/Sources/Networking/HTTPMethod.swift new file mode 100644 index 00000000..18cc2e05 --- /dev/null +++ b/Sources/Networking/HTTPMethod.swift @@ -0,0 +1,41 @@ +import Foundation + +public enum HTTPMethod { + case options + case get + case head + case post + case put + case patch + case delete + case trace + case connect + case custom(String) +} + +extension HTTPMethod: RawRepresentable { + public var rawValue: String { + switch self { + case .options: return "OPTIONS" + case .get: return "GET" + case .head: return "HEAD" + case .post: return "POST" + case .put: return "PUT" + case .patch: return "PATCH" + case .delete: return "DELETE" + case .trace: return "TRACE" + case .connect: return "CONNECT" + case .custom(let method): return method + } + } + + public init?(rawValue: String) { + self.init(stringLiteral: rawValue) + } +} + +extension HTTPMethod: ExpressibleByStringLiteral { + public init(stringLiteral value: String) { + self = .custom(value) + } +} diff --git a/Sources/Networking/HTTPResponse.swift b/Sources/Networking/HTTPResponse.swift new file mode 100644 index 00000000..cceed3ba --- /dev/null +++ b/Sources/Networking/HTTPResponse.swift @@ -0,0 +1,28 @@ +import Foundation + +public struct HTTPResponse { + /// Request that was sent + public let request: URLRequest + /// Received `HTTPURLResponse` + public let response: HTTPURLResponse? + /// Body of response + public let data: Data? + /// Shortcut to response status code + public var statusCode: Int? { response?.statusCode } + + public init( + request: URLRequest, + response: HTTPURLResponse?, + data: Data? + ) { + self.request = request + self.response = response + self.data = data + } + + /// Check that `statusCode` is in accepted range (200...299 by default) + /// - Parameter acceptedStatusCodes: List of accepte status codes + public func isAccepted(acceptedStatusCodes codes: [Int] = .init(200...299)) -> Bool { + statusCode.map { codes.contains($0) } ?? true + } +} diff --git a/Sources/Networking/Networking.swift b/Sources/Networking/Networking.swift new file mode 100644 index 00000000..f054abe1 --- /dev/null +++ b/Sources/Networking/Networking.swift @@ -0,0 +1,23 @@ +import Foundation + +/// Protocol wrapping raw network requests, basically URLSession +@available(tvOS 13.0, iOS 13.0, watchOS 6.0, macOS 10.15, *) +public protocol Networking { + /// Send given request + /// - Parameter request: Request to be sent + /// - Returns: Received response + func request(_ request: URLRequest) async throws -> HTTPResponse +} + +@available(tvOS 13.0, iOS 13.0, watchOS 6.0, macOS 12.0, *) +extension URLSession: Networking { + public func request(_ request: URLRequest) async throws -> HTTPResponse { + let (data, response) = try await data(for: request) + + return .init( + request: request, + response: response as? HTTPURLResponse, + data: data + ) + } +} diff --git a/Sources/Networking/OAuthInterceptor.swift b/Sources/Networking/OAuthInterceptor.swift new file mode 100644 index 00000000..1c4bae25 --- /dev/null +++ b/Sources/Networking/OAuthInterceptor.swift @@ -0,0 +1,68 @@ +import Foundation + +/// Interceptor that is ready to solve token refresh +@available(tvOS 13.0, iOS 13.0, watchOS 6.0, macOS 10.15, *) +public final actor OAuthInterceptor: ResponseInterceptor { + /// Enum for results when checking if request current auth data + public enum UsedCurrentAuthData { + /// Request used current auth data + case yes + /// Request did not use current auth data and should use + /// given URLRequest to retry + case no(URLRequest) + } + + /// Determines if given response is error due to expired auth data (auth token basically) + public let isExpiredAuthDataResponse: (HTTPResponse) async -> Bool + /// Determines if given response and its request used current auth data (auth token basically). + /// + /// Based on that `refeshAuthData` could be called. + public let requestUsedCurrentAuthData: (HTTPResponse) async -> OAuthInterceptor.UsedCurrentAuthData + + /// Closure that refreshes auth data (auth token basically) and returns a function that transforms received response (and its request) + /// to new request so we can retry that request again with new auth data + public let refreshAuthData: () async throws -> (HTTPResponse) -> (URLRequest) + + /// To prevent actor [re-entrancy issue](https://medium.com/@mark.moeykens/swift-actor-reentrancy-model-explained-11463d993c59) + /// we track call to `refreshAuthData()` closure as it should never occur multiple times at once + private var refreshTask: Task<(HTTPResponse) -> (URLRequest), Error>? + + /// Create new interceptor + /// - Parameters: + /// - isExpiredAuthDataResponse: Determines if given response is error due to expired auth data (auth token basically) + /// - requestUsedCurrentAuthData: Determines if given response and its request used current auth data (auth token basically). Based on that `refeshAuthData` could be called. + /// - refreshAuthData: Closure that refreshes auth data (auth token basically) and returns a function that transforms received response (and its request) to new request so we can retry that request again with new auth data + public init( + isExpiredAuthDataResponse: @escaping (HTTPResponse) async -> Bool, + requestUsedCurrentAuthData: @escaping (HTTPResponse) async -> UsedCurrentAuthData, + refreshAuthData: @escaping () async throws -> (HTTPResponse) -> (URLRequest) + ) { + self.isExpiredAuthDataResponse = isExpiredAuthDataResponse + self.requestUsedCurrentAuthData = requestUsedCurrentAuthData + self.refreshAuthData = refreshAuthData + } + + public func intercept( + service: APIServicing, + response originalResponse: inout HTTPResponse + ) async throws { + guard await isExpiredAuthDataResponse(originalResponse) else { + return + } + + switch await requestUsedCurrentAuthData(originalResponse) { + case .yes: + let task = refreshTask ?? .init { + defer { refreshTask = nil } + return try await refreshAuthData() + } + + refreshTask = task + + let newRequest = try await task.value(originalResponse) + originalResponse = try await service.request(newRequest) + case .no(let newRequest): + originalResponse = try await service.request(newRequest) + } + } +} diff --git a/Sources/Networking/RequestAddress.swift b/Sources/Networking/RequestAddress.swift new file mode 100644 index 00000000..eb5d4f25 --- /dev/null +++ b/Sources/Networking/RequestAddress.swift @@ -0,0 +1,22 @@ +import Foundation + +/// Represents request address that could be relative to service's base URL +/// +/// If initialized by string literal/interpolation, respective case is created, +/// if Foundation.URL can be created and scheme is not empty, it creates `.url` otherwise `.path` +public enum RequestAddress: Hashable { + case url(URL) + case path(String) +} + +extension RequestAddress: ExpressibleByStringInterpolation { + public init(stringLiteral value: String) { + if let url = URL(string: value), + let scheme = url.scheme, + !scheme.isEmpty { + self = .url(url) + } else { + self = .path(value) + } + } +} diff --git a/Sources/Networking/RequestBody.swift b/Sources/Networking/RequestBody.swift new file mode 100644 index 00000000..2161d798 --- /dev/null +++ b/Sources/Networking/RequestBody.swift @@ -0,0 +1,99 @@ +import Foundation + +public protocol Encoder: AnyObject { + /// Content type to which encoder encodes data + /// + /// Used for `Content-Type` header + static var contentType: String { get } + + func encode(_ encodable: T) throws -> Data +} + +extension JSONEncoder: Encoder { + public static var contentType: String { "application/json" } +} + +public struct RequestBody { + public let contentType: String + public let data: Data + + public init( + contentType: String, + data: Data + ) { + self.contentType = contentType + self.data = data + } + + public init(_ encodable: T, encoder: Encoder) throws { + contentType = type(of: encoder).contentType + data = try encoder.encode(encodable) + } + + public init( + jsonDictionary dict: [String: Any], + options: JSONSerialization.WritingOptions = [ + .prettyPrinted, + .sortedKeys, + ] + ) throws { + try self.init(jsonObject: dict, options: options) + } + + public init( + jsonArray array: [Any], + options: JSONSerialization.WritingOptions = [ + .prettyPrinted, + .sortedKeys, + ] + ) throws { + try self.init(jsonObject: array, options: options) + } + + public init(formURLEncoded dict: [String: String]) throws { + let data = dict.compactMap { key, value in + guard !key.isEmpty, !value.isEmpty, + let encodedKey = key.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed), + let encodedValue = key.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) + else { return nil } + + return encodedKey + "=" + encodedValue + } + .joined(separator: "&") + .data(using: .utf8) + + guard let data else { + struct CannotEncodeData: Error { } + throw CannotEncodeData() + } + + self.data = data + self.contentType = "application/x-www-form-urlencoded" + } + + private init( + jsonObject: Any, + options: JSONSerialization.WritingOptions = [ + .prettyPrinted, + .sortedKeys, + ] + ) throws { + data = try JSONSerialization.data( + withJSONObject: jsonObject, + options: options + ) + contentType = JSONEncoder.contentType + } +} + +extension RequestBody: ExpressibleByDictionaryLiteral { + public init(dictionaryLiteral elements: (String, Any)...) { + try! self.init(jsonDictionary: .init(elements) { $1 }) + } +} + +extension RequestBody: ExpressibleByArrayLiteral { + public init(arrayLiteral elements: Any...) { + try! self.init(jsonArray: elements) + } +} diff --git a/Tests/ACKategoriesTests/EdgeInsetsTests.swift b/Tests/ACKategoriesTests/EdgeInsetsTests.swift index 9516013b..554e710b 100644 --- a/Tests/ACKategoriesTests/EdgeInsetsTests.swift +++ b/Tests/ACKategoriesTests/EdgeInsetsTests.swift @@ -2,6 +2,7 @@ import ACKategories import SwiftUI import XCTest +@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *) final class EdgeInsetsTests: XCTestCase { func test_init_size() { XCTAssertEqual( diff --git a/Tests/NetworkingTests/APIService+OAuthInterceptor_Tests.swift b/Tests/NetworkingTests/APIService+OAuthInterceptor_Tests.swift new file mode 100644 index 00000000..e5a599cb --- /dev/null +++ b/Tests/NetworkingTests/APIService+OAuthInterceptor_Tests.swift @@ -0,0 +1,176 @@ +import ACKategoriesTesting +import Networking +import XCTest + +@MainActor +@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *) +final class APIService_OAuthInterceptor_IntegrationTests: XCTestCase { + private var network: Network_Mock! + + // MARK: - Setup + + override func setUp() { + network = .init() + super.setUp() + } + + // MARK: - Tests + + func test_expiredTokenRefresh() async throws { + let originalToken = "abc" + let newToken = "def" + let authHeaderName = "Authorization" + var currentToken = originalToken + + let refreshResponseRequest: (HTTPResponse) -> URLRequest = { response in + var request = response.request + request.setValue(currentToken, forHTTPHeaderField: authHeaderName) + return request + } + + let oauth = OAuthInterceptor( + isExpiredAuthDataResponse: { $0.statusCode == 401 }, + requestUsedCurrentAuthData: { + let isCurrent = $0.request.value(forHTTPHeaderField: authHeaderName) == currentToken + guard !isCurrent else { + return .yes + } + + return .no(refreshResponseRequest($0)) + }, + refreshAuthData: { + currentToken = newToken + + self.network.requestBody = { + .test( + request: $0, + response: .init( + url: try XCTUnwrap($0.url), + statusCode: 200, + httpVersion: nil, + headerFields: nil + ) + ) + } + + return refreshResponseRequest + } + ) + let apiService = APIService( + baseURL: .ackeeCZ, + network: network, + responseInterceptors: [oauth] + ) + + network.requestBody = { + .test( + request: $0, + response: .init( + url: try XCTUnwrap($0.url), + statusCode: 401, + httpVersion: nil, + headerFields: nil + ) + ) + } + + let response = try await apiService.request(.test( + url: .ackeeCZ, + headers: ["Authorization": originalToken] + )) + + XCTAssertEqual(newToken, response.request.value(forHTTPHeaderField: authHeaderName)) + } + + func test_expiredTokenRefresh_concurrent() async throws { + let originalToken = "abc" + let newToken = "def" + let authHeaderName = "Authorization" + let taskHeaderName = "Task" + var currentToken = originalToken + var refreshCount = 0 + var retriedRequests = [URLRequest]() + + let refreshResponseRequest: (HTTPResponse) -> URLRequest = { response in + var request = response.request + request.setValue(currentToken, forHTTPHeaderField: authHeaderName) + return request + } + + let oauth = OAuthInterceptor( + isExpiredAuthDataResponse: { $0.statusCode == 401 }, + requestUsedCurrentAuthData: { + let isCurrent = $0.request.value(forHTTPHeaderField: authHeaderName) == currentToken + guard !isCurrent else { + return .yes + } + + return .no(refreshResponseRequest($0)) + }, + refreshAuthData: { + refreshCount += 1 + currentToken = newToken + + self.network.requestBody = { + try await Task.sleep(nanoseconds: 1_000_000) + retriedRequests.append($0) + return .test( + request: $0, + response: .init( + url: try XCTUnwrap($0.url), + statusCode: 200, + httpVersion: nil, + headerFields: nil + ) + ) + } + + return refreshResponseRequest + } + ) + let apiService = APIService( + baseURL: .ackeeCZ, + network: network, + responseInterceptors: [oauth] + ) + + network.requestBody = { + .test( + request: $0, + response: .init( + url: try XCTUnwrap($0.url), + statusCode: 401, + httpVersion: nil, + headerFields: nil + ) + ) + } + + let taskCount = Int.random(in: 10...300) + let exp = expectation(description: "Refresh expectation") + exp.expectedFulfillmentCount = taskCount + + for i in 0.. URLRequest = { response in + var request = response.request + var headers = request.allHTTPHeaderFields! + headers["refreshed"] = "true" + request.allHTTPHeaderFields = headers + return request + } + + let subject = OAuthInterceptor( + isExpiredAuthDataResponse: { response in + let request = response.request + print("[IS_EXPIRED]", request.allHTTPHeaderFields!["index"]!, request.allHTTPHeaderFields!["refreshed"] ?? "false") + return true + }, + requestUsedCurrentAuthData: { response in + guard response.request.allHTTPHeaderFields?["index"] == "0" else { + return response.request.allHTTPHeaderFields?["refreshed"] == "true" ? .yes : .no(refreshResponseRequest(response)) + } + + return .yes + }, + refreshAuthData: { + refreshCount += 1 + return { response in + let request = response.request + print("[REFRESH]", request.allHTTPHeaderFields!["index"]!, request.allHTTPHeaderFields!["refreshed"] ?? "false") + return refreshResponseRequest(response) + } + } + ) + + service.requestBody = { request in + print("[REQUEST][RETRY]", request.allHTTPHeaderFields!["index"]!, request.allHTTPHeaderFields!["refreshed"] ?? "false") + try await Task.sleep(nanoseconds: 1_000_000_000) + return .test() + } + + let taskCount = Int.random(in: 10...300) + let exp = expectation(description: "Refresh expectation") + exp.expectedFulfillmentCount = taskCount + + for i in 0.. Date: Thu, 28 Dec 2023 17:21:50 +0100 Subject: [PATCH 03/12] =?UTF-8?q?=E2=9C=A8=20Add=20VersionUpdateManager=20?= =?UTF-8?q?from=20template?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ACKategories.xcodeproj/project.pbxproj | 94 ++++++++++++++----- Package.swift | 10 +- .../VersionUpdate/MinBuildNumberFetcher.swift | 6 ++ .../VersionUpdate/VersionUpdateManager.swift | 29 ++++++ .../VersionUpdateFetcher_Mock.swift | 10 ++ .../VersionUpdateManager_Tests.swift | 58 ++++++++++++ 6 files changed, 179 insertions(+), 28 deletions(-) create mode 100644 Sources/ACKategories/VersionUpdate/MinBuildNumberFetcher.swift create mode 100644 Sources/ACKategories/VersionUpdate/VersionUpdateManager.swift create mode 100644 Sources/ACKategoriesTesting/VersionUpdateFetcher_Mock.swift create mode 100644 Tests/ACKategoriesTests/VersionUpdate/VersionUpdateManager_Tests.swift diff --git a/ACKategories.xcodeproj/project.pbxproj b/ACKategories.xcodeproj/project.pbxproj index c2502bcf..0e98bd56 100644 --- a/ACKategories.xcodeproj/project.pbxproj +++ b/ACKategories.xcodeproj/project.pbxproj @@ -7,7 +7,7 @@ objects = { /* Begin PBXBuildFile section */ - 690BCB8D2B3DB62400EDA6F8 /* Networking.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 693A925D2B3DB290008B3DC3 /* Networking.framework */; platformFilters = (ios, macos, tvos, watchos, ); }; + 690BCB8D2B3DB62400EDA6F8 /* Networking.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 693A925D2B3DB290008B3DC3 /* Networking.framework */; }; 691B9AD92AFD1E5C008AE7BD /* ACKategories.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 69ACD6C22AFD130C0021127B /* ACKategories.framework */; }; 691B9ADA2AFD1E5C008AE7BD /* ACKategories.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 69ACD6C22AFD130C0021127B /* ACKategories.framework */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 6922C77D2AFD1C1A00519CDF /* UINavigationControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6922C77C2AFD1C1A00519CDF /* UINavigationControllerTests.swift */; }; @@ -33,6 +33,13 @@ 693A92A42B3DB394008B3DC3 /* HTTPResponse+TestData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 693A929E2B3DB394008B3DC3 /* HTTPResponse+TestData.swift */; }; 693A92A52B3DB394008B3DC3 /* URL+TestData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 693A929F2B3DB394008B3DC3 /* URL+TestData.swift */; }; 693A92A62B3DB39F008B3DC3 /* ACKategoriesTesting.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 693A92912B3DB388008B3DC3 /* ACKategoriesTesting.framework */; }; + 694D14EB2B3DD61A0083E614 /* VersionUpdateManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 694D14E92B3DD6190083E614 /* VersionUpdateManager.swift */; }; + 694D14EC2B3DD61A0083E614 /* MinBuildNumberFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 694D14EA2B3DD6190083E614 /* MinBuildNumberFetcher.swift */; }; + 694D14EE2B3DD64C0083E614 /* VersionUpdateFetcher_Mock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 694D14ED2B3DD64C0083E614 /* VersionUpdateFetcher_Mock.swift */; }; + 694D14F22B3DD66C0083E614 /* EdgeInsetsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 694D14EF2B3DD66C0083E614 /* EdgeInsetsTests.swift */; }; + 694D14F32B3DD66C0083E614 /* VersionUpdateManager_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 694D14F12B3DD66C0083E614 /* VersionUpdateManager_Tests.swift */; }; + 694D14F42B3DD71C0083E614 /* ACKategories.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 69ACD6C22AFD130C0021127B /* ACKategories.framework */; }; + 694D14F92B3DD7A40083E614 /* ACKategoriesTesting.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 693A92912B3DB388008B3DC3 /* ACKategoriesTesting.framework */; }; 6971A98A2AFD1AF5000FC317 /* ACKategories.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 69ACD6C22AFD130C0021127B /* ACKategories.framework */; }; 6971A9902AFD1B0F000FC317 /* ControlBlocksTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69E0A6B62AFD114600C8E8D9 /* ControlBlocksTests.swift */; }; 697D61D42B0BC41900020664 /* EdgeInsetsExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 697D61D32B0BC41900020664 /* EdgeInsetsExtensions.swift */; }; @@ -157,6 +164,20 @@ remoteGlobalIDString = 693A92902B3DB388008B3DC3; remoteInfo = ACKategoriesTesting; }; + 694D14F62B3DD71C0083E614 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 69E819DF23C773010054687B /* Project object */; + proxyType = 1; + remoteGlobalIDString = 69ACD6C12AFD130C0021127B; + remoteInfo = ACKategories; + }; + 694D14FB2B3DD7A40083E614 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 69E819DF23C773010054687B /* Project object */; + proxyType = 1; + remoteGlobalIDString = 693A92902B3DB388008B3DC3; + remoteInfo = ACKategoriesTesting; + }; 6971A98B2AFD1AF5000FC317 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 69E819DF23C773010054687B /* Project object */; @@ -195,7 +216,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 690BCB8C2B3DB5DE00EDA6F8 /* Networking.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = file; name = Networking.xctestplan; path = Tests/NetworkingTests/Networking.xctestplan; sourceTree = SOURCE_ROOT; }; + 690BCB8C2B3DB5DE00EDA6F8 /* Networking.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; name = Networking.xctestplan; path = Tests/NetworkingTests/Networking.xctestplan; sourceTree = SOURCE_ROOT; }; 6922C77C2AFD1C1A00519CDF /* UINavigationControllerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UINavigationControllerTests.swift; sourceTree = ""; }; 6922C77E2AFD1C3300519CDF /* ACKategoriesResponder.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = ACKategoriesResponder.xctestplan; sourceTree = ""; }; 6922C77F2AFD1C5000519CDF /* ACKategories.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = ACKategories.xctestplan; sourceTree = ""; }; @@ -220,6 +241,11 @@ 693A929D2B3DB394008B3DC3 /* HTTPURLResponse+TestData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HTTPURLResponse+TestData.swift"; sourceTree = ""; }; 693A929E2B3DB394008B3DC3 /* HTTPResponse+TestData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HTTPResponse+TestData.swift"; sourceTree = ""; }; 693A929F2B3DB394008B3DC3 /* URL+TestData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "URL+TestData.swift"; sourceTree = ""; }; + 694D14E92B3DD6190083E614 /* VersionUpdateManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VersionUpdateManager.swift; sourceTree = ""; }; + 694D14EA2B3DD6190083E614 /* MinBuildNumberFetcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MinBuildNumberFetcher.swift; sourceTree = ""; }; + 694D14ED2B3DD64C0083E614 /* VersionUpdateFetcher_Mock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VersionUpdateFetcher_Mock.swift; sourceTree = ""; }; + 694D14EF2B3DD66C0083E614 /* EdgeInsetsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EdgeInsetsTests.swift; sourceTree = ""; }; + 694D14F12B3DD66C0083E614 /* VersionUpdateManager_Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VersionUpdateManager_Tests.swift; sourceTree = ""; }; 695096D823C7908B00E8F457 /* ACKategoriesExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ACKategoriesExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 6971A9862AFD1AF5000FC317 /* ACKategoriesResponderTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ACKategoriesResponderTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 697B023227DB65B50082F4AC /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = ""; }; @@ -342,6 +368,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 694D14F42B3DD71C0083E614 /* ACKategories.framework in Frameworks */, 690BCB8D2B3DB62400EDA6F8 /* Networking.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -374,6 +401,7 @@ buildActionMask = 2147483647; files = ( 69ACD6CA2AFD130D0021127B /* ACKategories.framework in Frameworks */, + 694D14F92B3DD7A40083E614 /* ACKategoriesTesting.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -417,6 +445,7 @@ 693A92982B3DB394008B3DC3 /* ACKategoriesTesting */ = { isa = PBXGroup; children = ( + 694D14ED2B3DD64C0083E614 /* VersionUpdateFetcher_Mock.swift */, 693A92992B3DB394008B3DC3 /* Networking */, ); path = ACKategoriesTesting; @@ -435,6 +464,23 @@ path = Networking; sourceTree = ""; }; + 694D14E82B3DD6190083E614 /* VersionUpdate */ = { + isa = PBXGroup; + children = ( + 694D14E92B3DD6190083E614 /* VersionUpdateManager.swift */, + 694D14EA2B3DD6190083E614 /* MinBuildNumberFetcher.swift */, + ); + path = VersionUpdate; + sourceTree = ""; + }; + 694D14F02B3DD66C0083E614 /* VersionUpdate */ = { + isa = PBXGroup; + children = ( + 694D14F12B3DD66C0083E614 /* VersionUpdateManager_Tests.swift */, + ); + path = VersionUpdate; + sourceTree = ""; + }; 695096D923C7908B00E8F457 /* ACKategoriesExample */ = { isa = PBXGroup; children = ( @@ -547,6 +593,7 @@ 69E0A5FE2AFD10BE00C8E8D9 /* RandomExtensions */, 69E0A6032AFD10BE00C8E8D9 /* SwiftUIExtensions */, 698376C72B3DA81200CD9E89 /* UI */, + 694D14E82B3DD6190083E614 /* VersionUpdate */, ); path = ACKategories; sourceTree = ""; @@ -609,6 +656,7 @@ 69E0A6AC2AFD114600C8E8D9 /* ColorTests.swift */, 69E0A6AF2AFD114600C8E8D9 /* ConditionalAssignmentTests.swift */, 69E0A6B42AFD114600C8E8D9 /* DateFormattingTests.swift */, + 694D14EF2B3DD66C0083E614 /* EdgeInsetsTests.swift */, 69E0A6AE2AFD114600C8E8D9 /* FoundationTests.swift */, 69E0A6A22AFD114600C8E8D9 /* IntTests.swift */, 69E0A6AB2AFD114600C8E8D9 /* ReusableViewTests.swift */, @@ -619,6 +667,7 @@ 6922C77F2AFD1C5000519CDF /* ACKategories.xctestplan */, 69E0A69D2AFD114600C8E8D9 /* PropertyWrappers */, 69E0A6B02AFD114600C8E8D9 /* Random */, + 694D14F02B3DD66C0083E614 /* VersionUpdate */, ); path = ACKategoriesTests; sourceTree = ""; @@ -859,6 +908,7 @@ ); dependencies = ( 690BCB902B3DB62400EDA6F8 /* PBXTargetDependency */, + 694D14F72B3DD71C0083E614 /* PBXTargetDependency */, ); name = ACKategoriesTesting; productName = ACKategoriesTesting; @@ -935,6 +985,7 @@ ); dependencies = ( 69ACD6CC2AFD130D0021127B /* PBXTargetDependency */, + 694D14FC2B3DD7A40083E614 /* PBXTargetDependency */, ); name = ACKategoriesTests; productName = ACKategoriesTests; @@ -1093,6 +1144,7 @@ 693A92A42B3DB394008B3DC3 /* HTTPResponse+TestData.swift in Sources */, 693A92A02B3DB394008B3DC3 /* NetworkMock.swift in Sources */, 693A92A22B3DB394008B3DC3 /* URLRequest+TestData.swift in Sources */, + 694D14EE2B3DD64C0083E614 /* VersionUpdateFetcher_Mock.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1143,12 +1195,14 @@ 69ACD6DB2AFD133A0021127B /* CollectionExtensions.swift in Sources */, 69ACD6DC2AFD133A0021127B /* Combine+Concurrency.swift in Sources */, 69ACD6DD2AFD133A0021127B /* ConditionalAssignment.swift in Sources */, + 694D14EB2B3DD61A0083E614 /* VersionUpdateManager.swift in Sources */, 69ACD6DE2AFD133A0021127B /* DateExtensions.swift in Sources */, 69ACD6DF2AFD133A0021127B /* DateFormatting.swift in Sources */, 69ACD6E02AFD133A0021127B /* DictionaryExtensions.swift in Sources */, 69ACD6E12AFD133A0021127B /* ErrorHandlers.swift in Sources */, 69ACD6E32AFD133A0021127B /* IntExtensions.swift in Sources */, 69ACD6E42AFD133A0021127B /* NSAttributedStringExtensions.swift in Sources */, + 694D14EC2B3DD61A0083E614 /* MinBuildNumberFetcher.swift in Sources */, 69ACD6E52AFD133A0021127B /* NSMutableParagraphStyleExtensions.swift in Sources */, 69ACD6E62AFD133A0021127B /* NumberFormatterExtensions.swift in Sources */, 698376D22B3DA81200CD9E89 /* ThemeProvider.swift in Sources */, @@ -1200,7 +1254,9 @@ 69ACD7092AFD13480021127B /* BetterURL_Tests.swift in Sources */, 69ACD70A2AFD13480021127B /* CollectionTests.swift in Sources */, 69ACD70B2AFD13480021127B /* ColorTests.swift in Sources */, + 694D14F22B3DD66C0083E614 /* EdgeInsetsTests.swift in Sources */, 69ACD70C2AFD13480021127B /* ConditionalAssignmentTests.swift in Sources */, + 694D14F32B3DD66C0083E614 /* VersionUpdateManager_Tests.swift in Sources */, 69ACD70E2AFD13480021127B /* DateFormattingTests.swift in Sources */, 69ACD70F2AFD13480021127B /* FoundationTests.swift in Sources */, 69ACD7102AFD13480021127B /* IntTests.swift in Sources */, @@ -1221,12 +1277,6 @@ /* Begin PBXTargetDependency section */ 690BCB902B3DB62400EDA6F8 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - platformFilters = ( - ios, - macos, - tvos, - watchos, - ); target = 693A925C2B3DB290008B3DC3 /* Networking */; targetProxy = 690BCB8F2B3DB62400EDA6F8 /* PBXContainerItemProxy */; }; @@ -1245,6 +1295,16 @@ target = 693A92902B3DB388008B3DC3 /* ACKategoriesTesting */; targetProxy = 693A92A82B3DB39F008B3DC3 /* PBXContainerItemProxy */; }; + 694D14F72B3DD71C0083E614 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 69ACD6C12AFD130C0021127B /* ACKategories */; + targetProxy = 694D14F62B3DD71C0083E614 /* PBXContainerItemProxy */; + }; + 694D14FC2B3DD7A40083E614 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 693A92902B3DB388008B3DC3 /* ACKategoriesTesting */; + targetProxy = 694D14FB2B3DD7A40083E614 /* PBXContainerItemProxy */; + }; 6971A98C2AFD1AF5000FC317 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 69ACD6C12AFD130C0021127B /* ACKategories */; @@ -1299,14 +1359,12 @@ GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_NSHumanReadableCopyright = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", ); LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MACOSX_DEPLOYMENT_TARGET = 10.15; MARKETING_VERSION = 1.0; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; @@ -1323,10 +1381,8 @@ SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; - TVOS_DEPLOYMENT_TARGET = 13.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; - WATCHOS_DEPLOYMENT_TARGET = 6.0; }; name = Debug; }; @@ -1360,14 +1416,12 @@ GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_NSHumanReadableCopyright = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", ); LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MACOSX_DEPLOYMENT_TARGET = 10.15; MARKETING_VERSION = 1.0; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; @@ -1382,11 +1436,9 @@ SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; - TVOS_DEPLOYMENT_TARGET = 13.0; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; - WATCHOS_DEPLOYMENT_TARGET = 6.0; }; name = Release; }; @@ -1419,7 +1471,6 @@ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 17.2; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; @@ -1458,7 +1509,6 @@ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 17.2; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = NO; @@ -1509,7 +1559,6 @@ GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_NSHumanReadableCopyright = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 17.2; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1530,10 +1579,8 @@ SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; - TVOS_DEPLOYMENT_TARGET = 17.2; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; - WATCHOS_DEPLOYMENT_TARGET = 10.2; }; name = Debug; }; @@ -1567,7 +1614,6 @@ GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_NSHumanReadableCopyright = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 17.2; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1586,11 +1632,9 @@ SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; - TVOS_DEPLOYMENT_TARGET = 17.2; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; - WATCHOS_DEPLOYMENT_TARGET = 10.2; }; name = Release; }; @@ -1948,7 +1992,6 @@ 69ACD6D52AFD130D0021127B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; @@ -1995,7 +2038,6 @@ 69ACD6D62AFD130D0021127B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; diff --git a/Package.swift b/Package.swift index e1fe3c7e..530a77f1 100644 --- a/Package.swift +++ b/Package.swift @@ -18,11 +18,17 @@ let package = Package( .target(name: "ACKategories"), .testTarget( name: "ACKategoriesTests", - dependencies: ["ACKategories"] + dependencies: [ + "ACKategories", + "ACKategoriesTesting", + ] ), .target( name: "ACKategoriesTesting", - dependencies: ["Networking"] + dependencies: [ + "ACKategories", + "Networking", + ] ), .target(name: "Networking"), .testTarget( diff --git a/Sources/ACKategories/VersionUpdate/MinBuildNumberFetcher.swift b/Sources/ACKategories/VersionUpdate/MinBuildNumberFetcher.swift new file mode 100644 index 00000000..954e6582 --- /dev/null +++ b/Sources/ACKategories/VersionUpdate/MinBuildNumberFetcher.swift @@ -0,0 +1,6 @@ +import Foundation + +@available(tvOS 13.0, iOS 13.0, watchOS 6.0, macOS 10.15, *) +public protocol MinBuildNumberFetcher { + var minBuildNumber: Int { get async throws } +} diff --git a/Sources/ACKategories/VersionUpdate/VersionUpdateManager.swift b/Sources/ACKategories/VersionUpdate/VersionUpdateManager.swift new file mode 100644 index 00000000..4fbee4aa --- /dev/null +++ b/Sources/ACKategories/VersionUpdate/VersionUpdateManager.swift @@ -0,0 +1,29 @@ +import Foundation + +@available(tvOS 13.0, iOS 13.0, watchOS 6.0, macOS 10.15, *) +public protocol VersionUpdateManaging { + var updateRequired: Bool { get async } +} + +@available(tvOS 13.0, iOS 13.0, watchOS 6.0, macOS 10.15, *) +public final class VersionUpdateManager: VersionUpdateManaging { + public var updateRequired: Bool { + get async { + guard let min = try? await fetcher.minBuildNumber else { return false } + return min > buildNumberProvider() + } + } + + private let buildNumberProvider: () -> Int + private let fetcher: MinBuildNumberFetcher + + public init( + buildNumberProvider: @escaping @autoclosure () -> Int = Bundle.main.infoDictionary + .flatMap { $0["CFBundleVersion"] as? String } + .flatMap(Int.init) ?? Int.max, + fetcher: MinBuildNumberFetcher + ) { + self.buildNumberProvider = buildNumberProvider + self.fetcher = fetcher + } +} diff --git a/Sources/ACKategoriesTesting/VersionUpdateFetcher_Mock.swift b/Sources/ACKategoriesTesting/VersionUpdateFetcher_Mock.swift new file mode 100644 index 00000000..7892c4ca --- /dev/null +++ b/Sources/ACKategoriesTesting/VersionUpdateFetcher_Mock.swift @@ -0,0 +1,10 @@ +import ACKategories + +@available(tvOS 13.0, iOS 13.0, watchOS 6.0, macOS 10.15, *) +public final class VersionUpdateFetcher_Mock: MinBuildNumberFetcher { + public var minBuildNumber = 0 + + public init(minBuildNumber: Int = 0) { + self.minBuildNumber = minBuildNumber + } +} diff --git a/Tests/ACKategoriesTests/VersionUpdate/VersionUpdateManager_Tests.swift b/Tests/ACKategoriesTests/VersionUpdate/VersionUpdateManager_Tests.swift new file mode 100644 index 00000000..1a80f4c3 --- /dev/null +++ b/Tests/ACKategoriesTests/VersionUpdate/VersionUpdateManager_Tests.swift @@ -0,0 +1,58 @@ +import ACKategories +import ACKategoriesTesting +import XCTest + +@MainActor +@available(tvOS 13.0, iOS 13.0, watchOS 6.0, macOS 10.15, *) +final class VersionUpdateManager_Tests: XCTestCase { + private var fetcher: VersionUpdateFetcher_Mock! + + // MARK: - Setup + + override func setUp() { + fetcher = .init() + super.setUp() + } + + func test_minBuildNumber_lower() async throws { + let buildNumber = Int.random(in: 1..<10_000) + + fetcher.minBuildNumber = buildNumber - 1 + + let subject = VersionUpdateManager( + buildNumberProvider: buildNumber, + fetcher: fetcher + ) + let updateRequired = await subject.updateRequired + + XCTAssertFalse(updateRequired) + } + + func test_minBuildNumber_equal() async throws { + let buildNumber = Int.random(in: 1..<10_000) + + fetcher.minBuildNumber = buildNumber + + let subject = VersionUpdateManager( + buildNumberProvider: buildNumber, + fetcher: fetcher + ) + let updateRequired = await subject.updateRequired + + XCTAssertFalse(updateRequired) + } + + func test_minBuildNumber_higher() async throws { + let buildNumber = Int.random(in: 1..<10_000) + + fetcher.minBuildNumber = buildNumber + 1 + + let subject = VersionUpdateManager( + buildNumberProvider: buildNumber, + fetcher: fetcher + ) + let updateRequired = await subject.updateRequired + + XCTAssertTrue(updateRequired) + } +} From a85460ae37aa4041370813aa910c5052309b1241 Mon Sep 17 00:00:00 2001 From: Jakub Olejnik Date: Wed, 3 Jan 2024 14:20:55 +0100 Subject: [PATCH 04/12] =?UTF-8?q?=E2=9C=A8=20Add=20FirebaseFetcher?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ACKategories.xcodeproj/project.pbxproj | 261 ++++++++++++++++++ Cartfile | 2 + Cartfile.resolved | 2 + Package.resolved | 122 ++++++++ Package.swift | 14 + Sources/FirebaseFetcher/FirebaseFetcher.swift | 22 ++ 6 files changed, 423 insertions(+) create mode 100644 Cartfile create mode 100644 Cartfile.resolved create mode 100644 Package.resolved create mode 100644 Sources/FirebaseFetcher/FirebaseFetcher.swift diff --git a/ACKategories.xcodeproj/project.pbxproj b/ACKategories.xcodeproj/project.pbxproj index 0e98bd56..cedabe85 100644 --- a/ACKategories.xcodeproj/project.pbxproj +++ b/ACKategories.xcodeproj/project.pbxproj @@ -8,6 +8,20 @@ /* Begin PBXBuildFile section */ 690BCB8D2B3DB62400EDA6F8 /* Networking.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 693A925D2B3DB290008B3DC3 /* Networking.framework */; }; + 691A1FCF2B45931800B52E56 /* ACKategories.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 69ACD6C22AFD130C0021127B /* ACKategories.framework */; }; + 691A1FD52B45932A00B52E56 /* FirebaseFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 691A1FD42B45932A00B52E56 /* FirebaseFetcher.swift */; }; + 691A1FE42B4593EB00B52E56 /* GoogleAppMeasurement.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 691A1FD82B4593EB00B52E56 /* GoogleAppMeasurement.xcframework */; }; + 691A1FE62B4593EB00B52E56 /* FirebaseCoreInternal.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 691A1FD92B4593EB00B52E56 /* FirebaseCoreInternal.xcframework */; }; + 691A1FE82B4593EB00B52E56 /* FirebaseAnalytics.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 691A1FDA2B4593EB00B52E56 /* FirebaseAnalytics.xcframework */; }; + 691A1FEA2B4593EB00B52E56 /* FBLPromises.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 691A1FDB2B4593EB00B52E56 /* FBLPromises.xcframework */; }; + 691A1FEC2B4593EB00B52E56 /* FirebaseRemoteConfig.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 691A1FDC2B4593EB00B52E56 /* FirebaseRemoteConfig.xcframework */; }; + 691A1FEE2B4593EB00B52E56 /* GoogleUtilities.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 691A1FDD2B4593EB00B52E56 /* GoogleUtilities.xcframework */; }; + 691A1FF02B4593EB00B52E56 /* nanopb.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 691A1FDE2B4593EB00B52E56 /* nanopb.xcframework */; }; + 691A1FF22B4593EB00B52E56 /* FirebaseInstallations.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 691A1FDF2B4593EB00B52E56 /* FirebaseInstallations.xcframework */; }; + 691A1FF42B4593EB00B52E56 /* GoogleAppMeasurementIdentitySupport.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 691A1FE02B4593EB00B52E56 /* GoogleAppMeasurementIdentitySupport.xcframework */; }; + 691A1FF62B4593EC00B52E56 /* FirebaseABTesting.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 691A1FE12B4593EB00B52E56 /* FirebaseABTesting.xcframework */; }; + 691A1FF82B4593EC00B52E56 /* FirebaseCore.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 691A1FE22B4593EB00B52E56 /* FirebaseCore.xcframework */; }; + 691A1FFA2B4593EC00B52E56 /* FirebaseSharedSwift.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 691A1FE32B4593EB00B52E56 /* FirebaseSharedSwift.xcframework */; }; 691B9AD92AFD1E5C008AE7BD /* ACKategories.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 69ACD6C22AFD130C0021127B /* ACKategories.framework */; }; 691B9ADA2AFD1E5C008AE7BD /* ACKategories.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 69ACD6C22AFD130C0021127B /* ACKategories.framework */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 6922C77D2AFD1C1A00519CDF /* UINavigationControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6922C77C2AFD1C1A00519CDF /* UINavigationControllerTests.swift */; }; @@ -143,6 +157,13 @@ remoteGlobalIDString = 693A925C2B3DB290008B3DC3; remoteInfo = Networking; }; + 691A1FD12B45931800B52E56 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 69E819DF23C773010054687B /* Project object */; + proxyType = 1; + remoteGlobalIDString = 69ACD6C12AFD130C0021127B; + remoteInfo = ACKategories; + }; 691B9ADB2AFD1E5C008AE7BD /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 69E819DF23C773010054687B /* Project object */; @@ -217,6 +238,21 @@ /* Begin PBXFileReference section */ 690BCB8C2B3DB5DE00EDA6F8 /* Networking.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; name = Networking.xctestplan; path = Tests/NetworkingTests/Networking.xctestplan; sourceTree = SOURCE_ROOT; }; + 691A1FC72B4592BC00B52E56 /* FirebaseFetcher.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FirebaseFetcher.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 691A1FD42B45932A00B52E56 /* FirebaseFetcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FirebaseFetcher.swift; sourceTree = ""; }; + 691A1FD62B45935400B52E56 /* Cartfile */ = {isa = PBXFileReference; lastKnownFileType = text; path = Cartfile; sourceTree = ""; }; + 691A1FD82B4593EB00B52E56 /* GoogleAppMeasurement.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = GoogleAppMeasurement.xcframework; path = Carthage/Build/GoogleAppMeasurement.xcframework; sourceTree = ""; }; + 691A1FD92B4593EB00B52E56 /* FirebaseCoreInternal.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = FirebaseCoreInternal.xcframework; path = Carthage/Build/FirebaseCoreInternal.xcframework; sourceTree = ""; }; + 691A1FDA2B4593EB00B52E56 /* FirebaseAnalytics.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = FirebaseAnalytics.xcframework; path = Carthage/Build/FirebaseAnalytics.xcframework; sourceTree = ""; }; + 691A1FDB2B4593EB00B52E56 /* FBLPromises.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = FBLPromises.xcframework; path = Carthage/Build/FBLPromises.xcframework; sourceTree = ""; }; + 691A1FDC2B4593EB00B52E56 /* FirebaseRemoteConfig.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = FirebaseRemoteConfig.xcframework; path = Carthage/Build/FirebaseRemoteConfig.xcframework; sourceTree = ""; }; + 691A1FDD2B4593EB00B52E56 /* GoogleUtilities.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = GoogleUtilities.xcframework; path = Carthage/Build/GoogleUtilities.xcframework; sourceTree = ""; }; + 691A1FDE2B4593EB00B52E56 /* nanopb.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = nanopb.xcframework; path = Carthage/Build/nanopb.xcframework; sourceTree = ""; }; + 691A1FDF2B4593EB00B52E56 /* FirebaseInstallations.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = FirebaseInstallations.xcframework; path = Carthage/Build/FirebaseInstallations.xcframework; sourceTree = ""; }; + 691A1FE02B4593EB00B52E56 /* GoogleAppMeasurementIdentitySupport.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = GoogleAppMeasurementIdentitySupport.xcframework; path = Carthage/Build/GoogleAppMeasurementIdentitySupport.xcframework; sourceTree = ""; }; + 691A1FE12B4593EB00B52E56 /* FirebaseABTesting.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = FirebaseABTesting.xcframework; path = Carthage/Build/FirebaseABTesting.xcframework; sourceTree = ""; }; + 691A1FE22B4593EB00B52E56 /* FirebaseCore.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = FirebaseCore.xcframework; path = Carthage/Build/FirebaseCore.xcframework; sourceTree = ""; }; + 691A1FE32B4593EB00B52E56 /* FirebaseSharedSwift.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = FirebaseSharedSwift.xcframework; path = Carthage/Build/FirebaseSharedSwift.xcframework; sourceTree = ""; }; 6922C77C2AFD1C1A00519CDF /* UINavigationControllerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UINavigationControllerTests.swift; sourceTree = ""; }; 6922C77E2AFD1C3300519CDF /* ACKategoriesResponder.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = ACKategoriesResponder.xctestplan; sourceTree = ""; }; 6922C77F2AFD1C5000519CDF /* ACKategories.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = ACKategories.xctestplan; sourceTree = ""; }; @@ -348,6 +384,26 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 691A1FC42B4592BC00B52E56 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 691A1FFA2B4593EC00B52E56 /* FirebaseSharedSwift.xcframework in Frameworks */, + 691A1FE42B4593EB00B52E56 /* GoogleAppMeasurement.xcframework in Frameworks */, + 691A1FF82B4593EC00B52E56 /* FirebaseCore.xcframework in Frameworks */, + 691A1FF42B4593EB00B52E56 /* GoogleAppMeasurementIdentitySupport.xcframework in Frameworks */, + 691A1FE82B4593EB00B52E56 /* FirebaseAnalytics.xcframework in Frameworks */, + 691A1FEE2B4593EB00B52E56 /* GoogleUtilities.xcframework in Frameworks */, + 691A1FCF2B45931800B52E56 /* ACKategories.framework in Frameworks */, + 691A1FEA2B4593EB00B52E56 /* FBLPromises.xcframework in Frameworks */, + 691A1FEC2B4593EB00B52E56 /* FirebaseRemoteConfig.xcframework in Frameworks */, + 691A1FF62B4593EC00B52E56 /* FirebaseABTesting.xcframework in Frameworks */, + 691A1FE62B4593EB00B52E56 /* FirebaseCoreInternal.xcframework in Frameworks */, + 691A1FF02B4593EB00B52E56 /* nanopb.xcframework in Frameworks */, + 691A1FF22B4593EB00B52E56 /* FirebaseInstallations.xcframework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 693A925A2B3DB290008B3DC3 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -408,9 +464,29 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 691A1FCE2B45930400B52E56 /* FirebaseFetcher */ = { + isa = PBXGroup; + children = ( + 691A1FD42B45932A00B52E56 /* FirebaseFetcher.swift */, + ); + path = FirebaseFetcher; + sourceTree = ""; + }; 691B9AD82AFD1E5C008AE7BD /* Frameworks */ = { isa = PBXGroup; children = ( + 691A1FDB2B4593EB00B52E56 /* FBLPromises.xcframework */, + 691A1FE12B4593EB00B52E56 /* FirebaseABTesting.xcframework */, + 691A1FDA2B4593EB00B52E56 /* FirebaseAnalytics.xcframework */, + 691A1FE22B4593EB00B52E56 /* FirebaseCore.xcframework */, + 691A1FD92B4593EB00B52E56 /* FirebaseCoreInternal.xcframework */, + 691A1FDF2B4593EB00B52E56 /* FirebaseInstallations.xcframework */, + 691A1FDC2B4593EB00B52E56 /* FirebaseRemoteConfig.xcframework */, + 691A1FE32B4593EB00B52E56 /* FirebaseSharedSwift.xcframework */, + 691A1FD82B4593EB00B52E56 /* GoogleAppMeasurement.xcframework */, + 691A1FE02B4593EB00B52E56 /* GoogleAppMeasurementIdentitySupport.xcframework */, + 691A1FDD2B4593EB00B52E56 /* GoogleUtilities.xcframework */, + 691A1FDE2B4593EB00B52E56 /* nanopb.xcframework */, ); name = Frameworks; sourceTree = ""; @@ -552,6 +628,7 @@ 69E0A5F32AFD10BE00C8E8D9 /* Sources */ = { isa = PBXGroup; children = ( + 691A1FCE2B45930400B52E56 /* FirebaseFetcher */, 69E0A5F42AFD10BE00C8E8D9 /* ACKategories */, 693A92982B3DB394008B3DC3 /* ACKategoriesTesting */, 693A92722B3DB2AA008B3DC3 /* Networking */, @@ -710,6 +787,7 @@ 69E819DE23C773010054687B = { isa = PBXGroup; children = ( + 691A1FD62B45935400B52E56 /* Cartfile */, 697B023227DB65B50082F4AC /* CHANGELOG.md */, 695096D923C7908B00E8F457 /* ACKategoriesExample */, 69E819EB23C773240054687B /* Products */, @@ -729,6 +807,7 @@ 693A925D2B3DB290008B3DC3 /* Networking.framework */, 693A92642B3DB290008B3DC3 /* NetworkingTests.xctest */, 693A92912B3DB388008B3DC3 /* ACKategoriesTesting.framework */, + 691A1FC72B4592BC00B52E56 /* FirebaseFetcher.framework */, ); name = Products; sourceTree = ""; @@ -834,6 +913,13 @@ /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ + 691A1FC22B4592BC00B52E56 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 693A92582B3DB290008B3DC3 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -858,6 +944,25 @@ /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ + 691A1FC62B4592BC00B52E56 /* FirebaseFetcher */ = { + isa = PBXNativeTarget; + buildConfigurationList = 691A1FCD2B4592BC00B52E56 /* Build configuration list for PBXNativeTarget "FirebaseFetcher" */; + buildPhases = ( + 691A1FC22B4592BC00B52E56 /* Headers */, + 691A1FC32B4592BC00B52E56 /* Sources */, + 691A1FC42B4592BC00B52E56 /* Frameworks */, + 691A1FC52B4592BC00B52E56 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 691A1FD22B45931800B52E56 /* PBXTargetDependency */, + ); + name = FirebaseFetcher; + productName = FirebaseFetcher; + productReference = 691A1FC72B4592BC00B52E56 /* FirebaseFetcher.framework */; + productType = "com.apple.product-type.framework"; + }; 693A925C2B3DB290008B3DC3 /* Networking */ = { isa = PBXNativeTarget; buildConfigurationList = 693A92702B3DB290008B3DC3 /* Build configuration list for PBXNativeTarget "Networking" */; @@ -1002,6 +1107,10 @@ LastSwiftUpdateCheck = 1510; LastUpgradeCheck = 1500; TargetAttributes = { + 691A1FC62B4592BC00B52E56 = { + CreatedOnToolsVersion = 15.1; + LastSwiftMigration = 1510; + }; 693A925C2B3DB290008B3DC3 = { CreatedOnToolsVersion = 15.1; }; @@ -1049,11 +1158,19 @@ 693A925C2B3DB290008B3DC3 /* Networking */, 693A92632B3DB290008B3DC3 /* NetworkingTests */, 693A92902B3DB388008B3DC3 /* ACKategoriesTesting */, + 691A1FC62B4592BC00B52E56 /* FirebaseFetcher */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 691A1FC52B4592BC00B52E56 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 693A925B2B3DB290008B3DC3 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -1108,6 +1225,14 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 691A1FC32B4592BC00B52E56 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 691A1FD52B45932A00B52E56 /* FirebaseFetcher.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 693A92592B3DB290008B3DC3 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -1280,6 +1405,11 @@ target = 693A925C2B3DB290008B3DC3 /* Networking */; targetProxy = 690BCB8F2B3DB62400EDA6F8 /* PBXContainerItemProxy */; }; + 691A1FD22B45931800B52E56 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 69ACD6C12AFD130C0021127B /* ACKategories */; + targetProxy = 691A1FD12B45931800B52E56 /* PBXContainerItemProxy */; + }; 691B9ADC2AFD1E5C008AE7BD /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 69ACD6C12AFD130C0021127B /* ACKategories */; @@ -1323,6 +1453,128 @@ /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ + 691A1FCB2B4592BC00B52E56 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = cz.ackee.FirebaseFetcher; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = macosx; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx watchos watchsimulator"; + SUPPORTS_MACCATALYST = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 691A1FCC2B4592BC00B52E56 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = cz.ackee.FirebaseFetcher; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = macosx; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx watchos watchsimulator"; + SUPPORTS_MACCATALYST = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; 693A926C2B3DB290008B3DC3 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -2164,6 +2416,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 691A1FCD2B4592BC00B52E56 /* Build configuration list for PBXNativeTarget "FirebaseFetcher" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 691A1FCB2B4592BC00B52E56 /* Debug */, + 691A1FCC2B4592BC00B52E56 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 693A92702B3DB290008B3DC3 /* Build configuration list for PBXNativeTarget "Networking" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/Cartfile b/Cartfile new file mode 100644 index 00000000..f24f2cef --- /dev/null +++ b/Cartfile @@ -0,0 +1,2 @@ +binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseAnalyticsBinary.json" ~> 10.19 +binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseRemoteConfigBinary.json" ~> 10.19 diff --git a/Cartfile.resolved b/Cartfile.resolved new file mode 100644 index 00000000..c18a4459 --- /dev/null +++ b/Cartfile.resolved @@ -0,0 +1,2 @@ +binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseAnalyticsBinary.json" "10.19.0" +binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseRemoteConfigBinary.json" "10.19.0" diff --git a/Package.resolved b/Package.resolved new file mode 100644 index 00000000..782e7f22 --- /dev/null +++ b/Package.resolved @@ -0,0 +1,122 @@ +{ + "pins" : [ + { + "identity" : "abseil-cpp-binary", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/abseil-cpp-binary.git", + "state" : { + "revision" : "bfc0b6f81adc06ce5121eb23f628473638d67c5c", + "version" : "1.2022062300.0" + } + }, + { + "identity" : "app-check", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/app-check.git", + "state" : { + "revision" : "5746b2d35c91c50581590ed97abe4c06b5037274", + "version" : "10.18.0" + } + }, + { + "identity" : "firebase-ios-sdk", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/firebase-ios-sdk", + "state" : { + "revision" : "c60c958e707c50a9cf8bcb7cfd7d51c566d726c5", + "version" : "10.19.1" + } + }, + { + "identity" : "googleappmeasurement", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleAppMeasurement.git", + "state" : { + "revision" : "6b332152355c372ace9966d8ee76ed191f97025e", + "version" : "10.17.0" + } + }, + { + "identity" : "googledatatransport", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleDataTransport.git", + "state" : { + "revision" : "a732a4b47f59e4f725a2ea10f0c77e93a7131117", + "version" : "9.3.0" + } + }, + { + "identity" : "googleutilities", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleUtilities.git", + "state" : { + "revision" : "bc27fad73504f3d4af235de451f02ee22586ebd3", + "version" : "7.12.1" + } + }, + { + "identity" : "grpc-binary", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/grpc-binary.git", + "state" : { + "revision" : "a673bc2937fbe886dd1f99c401b01b6d977a9c98", + "version" : "1.49.1" + } + }, + { + "identity" : "gtm-session-fetcher", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/gtm-session-fetcher.git", + "state" : { + "revision" : "115f75e43851774934d695449a4836123c3246e1", + "version" : "3.2.0" + } + }, + { + "identity" : "interop-ios-for-google-sdks", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/interop-ios-for-google-sdks.git", + "state" : { + "revision" : "2d12673670417654f08f5f90fdd62926dc3a2648", + "version" : "100.0.0" + } + }, + { + "identity" : "leveldb", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/leveldb.git", + "state" : { + "revision" : "9d108e9112aa1d65ce508facf804674546116d9c", + "version" : "1.22.3" + } + }, + { + "identity" : "nanopb", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/nanopb.git", + "state" : { + "revision" : "819d0a2173aff699fb8c364b6fb906f7cdb1a692", + "version" : "2.30909.0" + } + }, + { + "identity" : "promises", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/promises.git", + "state" : { + "revision" : "e70e889c0196c76d22759eb50d6a0270ca9f1d9e", + "version" : "2.3.1" + } + }, + { + "identity" : "swift-protobuf", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-protobuf.git", + "state" : { + "revision" : "65e8f29b2d63c4e38e736b25c27b83e012159be8", + "version" : "1.25.2" + } + } + ], + "version" : 2 +} diff --git a/Package.swift b/Package.swift index 530a77f1..2b601258 100644 --- a/Package.swift +++ b/Package.swift @@ -13,6 +13,13 @@ let package = Package( .library(name: "ACKategories", targets: ["ACKategories"]), .library(name: "ACKategoriesTesting", targets: ["ACKategoriesTesting"]), .library(name: "Networking", targets: ["Networking"]), + .library(name: "FirebaseFetcher", targets: ["FirebaseFetcher"]), + ], + dependencies: [ + .package( + url: "https://github.com/firebase/firebase-ios-sdk", + from: "10.19.0" + ), ], targets: [ .target(name: "ACKategories"), @@ -38,5 +45,12 @@ let package = Package( "Networking", ] ), + .target( + name: "FirebaseFetcher", + dependencies: [ + "ACKategories", + .product(name: "FirebaseRemoteConfig", package: "firebase-ios-sdk"), + ] + ), ] ) diff --git a/Sources/FirebaseFetcher/FirebaseFetcher.swift b/Sources/FirebaseFetcher/FirebaseFetcher.swift new file mode 100644 index 00000000..88245ae9 --- /dev/null +++ b/Sources/FirebaseFetcher/FirebaseFetcher.swift @@ -0,0 +1,22 @@ +import ACKategories +import FirebaseRemoteConfig + +public final class FirebaseFetcher: MinBuildNumberFetcher { + public var minBuildNumber: Int { + get async throws { + try await remoteConfig.fetchAndActivate() + return remoteConfig[decodedValue: key] ?? Int.max + } + } + + private let key: String + private let remoteConfig: RemoteConfig + + public init( + key: String, + remoteConfig: RemoteConfig = RemoteConfig.remoteConfig() + ) { + self.key = key + self.remoteConfig = remoteConfig + } +} From ade796c2c6c40338a28a543e88b3c8b947a71982 Mon Sep 17 00:00:00 2001 From: Jakub Olejnik Date: Wed, 3 Jan 2024 14:33:50 +0100 Subject: [PATCH 05/12] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Use=20Xcode=2015.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/xcode-version | 2 +- Sources/FirebaseFetcher/FirebaseFetcher.swift | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/xcode-version b/.github/xcode-version index 9dc738e6..ccc2f3b8 100644 --- a/.github/xcode-version +++ b/.github/xcode-version @@ -1 +1 @@ -15.0.1 \ No newline at end of file +15.1 \ No newline at end of file diff --git a/Sources/FirebaseFetcher/FirebaseFetcher.swift b/Sources/FirebaseFetcher/FirebaseFetcher.swift index 88245ae9..1997f119 100644 --- a/Sources/FirebaseFetcher/FirebaseFetcher.swift +++ b/Sources/FirebaseFetcher/FirebaseFetcher.swift @@ -1,6 +1,7 @@ import ACKategories import FirebaseRemoteConfig +@available(macOS 10.15.0, *) public final class FirebaseFetcher: MinBuildNumberFetcher { public var minBuildNumber: Int { get async throws { From 2096e69269d0b7dbc7437a106d49d44bbb715a7c Mon Sep 17 00:00:00 2001 From: Jakub Olejnik Date: Wed, 3 Jan 2024 14:45:14 +0100 Subject: [PATCH 06/12] =?UTF-8?q?=F0=9F=94=A7=20Build=20FirebaseFetcher=20?= =?UTF-8?q?on=20CI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/tests.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 55722fef..835b87ad 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -20,6 +20,8 @@ jobs: path: Tests-iOS.xcresult - name: iOS responder tests run: set -o pipefail && xcodebuild test -scheme ACKategories -resultBundlePath Tests-iOS-Responder.xcresult -sdk iphonesimulator -destination "platform=iOS Simulator,name=$IOS_DEVICE,OS=latest" ONLY_ACTIVE_ARCH=YES | xcpretty + - name: Build FirebaseFetcher + run: set -o pipefail && xcodebuild clean -scheme FirebaseFetcher -resultBundlePath Tests-iOS-Responder.xcresult -sdk iphonesimulator -destination "platform=iOS Simulator,name=$IOS_DEVICE,OS=latest" ONLY_ACTIVE_ARCH=YES | xcpretty - uses: actions/upload-artifact@v3 if: failure() with: @@ -52,5 +54,7 @@ jobs: steps: - uses: actions/checkout@v4 - uses: AckeeCZ/load-xcode-version@1.1.0 + - name: SPM build + run: swift build - name: SPM tests run: swift test \ No newline at end of file From 07eb3c95b91dee2687d0bdc59d1481f7491f7039 Mon Sep 17 00:00:00 2001 From: Jakub Olejnik Date: Fri, 5 Jan 2024 11:43:26 +0100 Subject: [PATCH 07/12] =?UTF-8?q?=F0=9F=94=A5=20Get=20rid=20of=20FirebaseF?= =?UTF-8?q?etcher?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/tests.yml | 2 - Cartfile | 2 - Package.resolved | 122 ------------------ Package.swift | 11 -- Sources/FirebaseFetcher/FirebaseFetcher.swift | 23 ---- 5 files changed, 160 deletions(-) delete mode 100644 Cartfile delete mode 100644 Package.resolved delete mode 100644 Sources/FirebaseFetcher/FirebaseFetcher.swift diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 835b87ad..1a43b9cb 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -20,8 +20,6 @@ jobs: path: Tests-iOS.xcresult - name: iOS responder tests run: set -o pipefail && xcodebuild test -scheme ACKategories -resultBundlePath Tests-iOS-Responder.xcresult -sdk iphonesimulator -destination "platform=iOS Simulator,name=$IOS_DEVICE,OS=latest" ONLY_ACTIVE_ARCH=YES | xcpretty - - name: Build FirebaseFetcher - run: set -o pipefail && xcodebuild clean -scheme FirebaseFetcher -resultBundlePath Tests-iOS-Responder.xcresult -sdk iphonesimulator -destination "platform=iOS Simulator,name=$IOS_DEVICE,OS=latest" ONLY_ACTIVE_ARCH=YES | xcpretty - uses: actions/upload-artifact@v3 if: failure() with: diff --git a/Cartfile b/Cartfile deleted file mode 100644 index f24f2cef..00000000 --- a/Cartfile +++ /dev/null @@ -1,2 +0,0 @@ -binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseAnalyticsBinary.json" ~> 10.19 -binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseRemoteConfigBinary.json" ~> 10.19 diff --git a/Package.resolved b/Package.resolved deleted file mode 100644 index 782e7f22..00000000 --- a/Package.resolved +++ /dev/null @@ -1,122 +0,0 @@ -{ - "pins" : [ - { - "identity" : "abseil-cpp-binary", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/abseil-cpp-binary.git", - "state" : { - "revision" : "bfc0b6f81adc06ce5121eb23f628473638d67c5c", - "version" : "1.2022062300.0" - } - }, - { - "identity" : "app-check", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/app-check.git", - "state" : { - "revision" : "5746b2d35c91c50581590ed97abe4c06b5037274", - "version" : "10.18.0" - } - }, - { - "identity" : "firebase-ios-sdk", - "kind" : "remoteSourceControl", - "location" : "https://github.com/firebase/firebase-ios-sdk", - "state" : { - "revision" : "c60c958e707c50a9cf8bcb7cfd7d51c566d726c5", - "version" : "10.19.1" - } - }, - { - "identity" : "googleappmeasurement", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/GoogleAppMeasurement.git", - "state" : { - "revision" : "6b332152355c372ace9966d8ee76ed191f97025e", - "version" : "10.17.0" - } - }, - { - "identity" : "googledatatransport", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/GoogleDataTransport.git", - "state" : { - "revision" : "a732a4b47f59e4f725a2ea10f0c77e93a7131117", - "version" : "9.3.0" - } - }, - { - "identity" : "googleutilities", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/GoogleUtilities.git", - "state" : { - "revision" : "bc27fad73504f3d4af235de451f02ee22586ebd3", - "version" : "7.12.1" - } - }, - { - "identity" : "grpc-binary", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/grpc-binary.git", - "state" : { - "revision" : "a673bc2937fbe886dd1f99c401b01b6d977a9c98", - "version" : "1.49.1" - } - }, - { - "identity" : "gtm-session-fetcher", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/gtm-session-fetcher.git", - "state" : { - "revision" : "115f75e43851774934d695449a4836123c3246e1", - "version" : "3.2.0" - } - }, - { - "identity" : "interop-ios-for-google-sdks", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/interop-ios-for-google-sdks.git", - "state" : { - "revision" : "2d12673670417654f08f5f90fdd62926dc3a2648", - "version" : "100.0.0" - } - }, - { - "identity" : "leveldb", - "kind" : "remoteSourceControl", - "location" : "https://github.com/firebase/leveldb.git", - "state" : { - "revision" : "9d108e9112aa1d65ce508facf804674546116d9c", - "version" : "1.22.3" - } - }, - { - "identity" : "nanopb", - "kind" : "remoteSourceControl", - "location" : "https://github.com/firebase/nanopb.git", - "state" : { - "revision" : "819d0a2173aff699fb8c364b6fb906f7cdb1a692", - "version" : "2.30909.0" - } - }, - { - "identity" : "promises", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/promises.git", - "state" : { - "revision" : "e70e889c0196c76d22759eb50d6a0270ca9f1d9e", - "version" : "2.3.1" - } - }, - { - "identity" : "swift-protobuf", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-protobuf.git", - "state" : { - "revision" : "65e8f29b2d63c4e38e736b25c27b83e012159be8", - "version" : "1.25.2" - } - } - ], - "version" : 2 -} diff --git a/Package.swift b/Package.swift index 2b601258..affb0c91 100644 --- a/Package.swift +++ b/Package.swift @@ -13,13 +13,6 @@ let package = Package( .library(name: "ACKategories", targets: ["ACKategories"]), .library(name: "ACKategoriesTesting", targets: ["ACKategoriesTesting"]), .library(name: "Networking", targets: ["Networking"]), - .library(name: "FirebaseFetcher", targets: ["FirebaseFetcher"]), - ], - dependencies: [ - .package( - url: "https://github.com/firebase/firebase-ios-sdk", - from: "10.19.0" - ), ], targets: [ .target(name: "ACKategories"), @@ -45,11 +38,7 @@ let package = Package( "Networking", ] ), - .target( - name: "FirebaseFetcher", dependencies: [ - "ACKategories", - .product(name: "FirebaseRemoteConfig", package: "firebase-ios-sdk"), ] ), ] diff --git a/Sources/FirebaseFetcher/FirebaseFetcher.swift b/Sources/FirebaseFetcher/FirebaseFetcher.swift deleted file mode 100644 index 1997f119..00000000 --- a/Sources/FirebaseFetcher/FirebaseFetcher.swift +++ /dev/null @@ -1,23 +0,0 @@ -import ACKategories -import FirebaseRemoteConfig - -@available(macOS 10.15.0, *) -public final class FirebaseFetcher: MinBuildNumberFetcher { - public var minBuildNumber: Int { - get async throws { - try await remoteConfig.fetchAndActivate() - return remoteConfig[decodedValue: key] ?? Int.max - } - } - - private let key: String - private let remoteConfig: RemoteConfig - - public init( - key: String, - remoteConfig: RemoteConfig = RemoteConfig.remoteConfig() - ) { - self.key = key - self.remoteConfig = remoteConfig - } -} From 2e6d2dfd17021c1d16d7eb5d6b5cc77f8dd1ca9d Mon Sep 17 00:00:00 2001 From: Jakub Olejnik Date: Fri, 5 Jan 2024 12:19:13 +0100 Subject: [PATCH 08/12] =?UTF-8?q?=E2=9C=A8=20Add=20PushNotifications=20tar?= =?UTF-8?q?get?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/pr.yml | 2 +- .github/workflows/tests.yml | 10 +- ACKategories.xcodeproj/project.pbxproj | 451 ++++++++---------- .../xcschemes/PushNotifications.xcscheme | 79 +++ Package.swift | 5 +- Sources/PushNotifications/PushManager.swift | 142 ++++++ .../UNNotificationSettingsExtensions.swift | 22 + 7 files changed, 438 insertions(+), 273 deletions(-) create mode 100644 ACKategories.xcodeproj/xcshareddata/xcschemes/PushNotifications.xcscheme create mode 100644 Sources/PushNotifications/PushManager.swift create mode 100644 Sources/PushNotifications/UNNotificationSettingsExtensions.swift diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 355144b7..6fba3542 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -20,7 +20,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Changelog Reminder - uses: peterjgrainger/action-changelog-reminder@v1.2.0 + uses: peterjgrainger/action-changelog-reminder@v1.3.0 with: changelog_regex: 'CHANGELOG.md' env: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1a43b9cb..8f735cbf 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,35 +13,35 @@ jobs: - uses: AckeeCZ/load-xcode-version@1.1.0 - name: iOS tests run: set -o pipefail && xcodebuild test -scheme ACKategories -resultBundlePath Tests-iOS.xcresult -sdk iphonesimulator -destination "platform=iOS Simulator,name=$IOS_DEVICE,OS=latest" ONLY_ACTIVE_ARCH=YES | xcpretty - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: failure() with: name: Tests-iOS.xcresult path: Tests-iOS.xcresult - name: iOS responder tests run: set -o pipefail && xcodebuild test -scheme ACKategories -resultBundlePath Tests-iOS-Responder.xcresult -sdk iphonesimulator -destination "platform=iOS Simulator,name=$IOS_DEVICE,OS=latest" ONLY_ACTIVE_ARCH=YES | xcpretty - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: failure() with: name: Tests-iOS-Responder.xcresult path: Tests-iOS-Responder.xcresult - name: macOS tests run: set -o pipefail && xcodebuild test -scheme ACKategories -resultBundlePath Tests-macOS.xcresult -destination 'platform=OS X,arch=x86_64' | xcpretty - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: failure() with: name: Tests-macOS.xcresult path: Tests-macOS.xcresult - name: watchOS tests run: set -o pipefail && xcodebuild test -scheme ACKategories -resultBundlePath Tests-watchOS.xcresult -sdk watchsimulator -destination "platform=watchOS Simulator,name=Apple Watch Ultra 2 (49mm),OS=latest" ONLY_ACTIVE_ARCH=YES | xcpretty - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: failure() with: name: Tests-watchOS.xcresult path: Tests-watchOS.xcresult - name: tvOS tests run: set -o pipefail && xcodebuild test -scheme ACKategories -resultBundlePath Tests-tvOS.xcresult -sdk appletvsimulator -destination "platform=tvOS Simulator,name=Apple TV 4K (3rd generation),OS=latest" ONLY_ACTIVE_ARCH=YES | xcpretty - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: failure() with: name: Tests-tvOS.xcresult diff --git a/ACKategories.xcodeproj/project.pbxproj b/ACKategories.xcodeproj/project.pbxproj index cedabe85..07c6e7c5 100644 --- a/ACKategories.xcodeproj/project.pbxproj +++ b/ACKategories.xcodeproj/project.pbxproj @@ -8,20 +8,6 @@ /* Begin PBXBuildFile section */ 690BCB8D2B3DB62400EDA6F8 /* Networking.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 693A925D2B3DB290008B3DC3 /* Networking.framework */; }; - 691A1FCF2B45931800B52E56 /* ACKategories.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 69ACD6C22AFD130C0021127B /* ACKategories.framework */; }; - 691A1FD52B45932A00B52E56 /* FirebaseFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 691A1FD42B45932A00B52E56 /* FirebaseFetcher.swift */; }; - 691A1FE42B4593EB00B52E56 /* GoogleAppMeasurement.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 691A1FD82B4593EB00B52E56 /* GoogleAppMeasurement.xcframework */; }; - 691A1FE62B4593EB00B52E56 /* FirebaseCoreInternal.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 691A1FD92B4593EB00B52E56 /* FirebaseCoreInternal.xcframework */; }; - 691A1FE82B4593EB00B52E56 /* FirebaseAnalytics.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 691A1FDA2B4593EB00B52E56 /* FirebaseAnalytics.xcframework */; }; - 691A1FEA2B4593EB00B52E56 /* FBLPromises.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 691A1FDB2B4593EB00B52E56 /* FBLPromises.xcframework */; }; - 691A1FEC2B4593EB00B52E56 /* FirebaseRemoteConfig.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 691A1FDC2B4593EB00B52E56 /* FirebaseRemoteConfig.xcframework */; }; - 691A1FEE2B4593EB00B52E56 /* GoogleUtilities.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 691A1FDD2B4593EB00B52E56 /* GoogleUtilities.xcframework */; }; - 691A1FF02B4593EB00B52E56 /* nanopb.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 691A1FDE2B4593EB00B52E56 /* nanopb.xcframework */; }; - 691A1FF22B4593EB00B52E56 /* FirebaseInstallations.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 691A1FDF2B4593EB00B52E56 /* FirebaseInstallations.xcframework */; }; - 691A1FF42B4593EB00B52E56 /* GoogleAppMeasurementIdentitySupport.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 691A1FE02B4593EB00B52E56 /* GoogleAppMeasurementIdentitySupport.xcframework */; }; - 691A1FF62B4593EC00B52E56 /* FirebaseABTesting.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 691A1FE12B4593EB00B52E56 /* FirebaseABTesting.xcframework */; }; - 691A1FF82B4593EC00B52E56 /* FirebaseCore.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 691A1FE22B4593EB00B52E56 /* FirebaseCore.xcframework */; }; - 691A1FFA2B4593EC00B52E56 /* FirebaseSharedSwift.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 691A1FE32B4593EB00B52E56 /* FirebaseSharedSwift.xcframework */; }; 691B9AD92AFD1E5C008AE7BD /* ACKategories.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 69ACD6C22AFD130C0021127B /* ACKategories.framework */; }; 691B9ADA2AFD1E5C008AE7BD /* ACKategories.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 69ACD6C22AFD130C0021127B /* ACKategories.framework */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 6922C77D2AFD1C1A00519CDF /* UINavigationControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6922C77C2AFD1C1A00519CDF /* UINavigationControllerTests.swift */; }; @@ -129,6 +115,8 @@ 69ACD71C2AFD13480021127B /* IntRandomTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69E0A6B22AFD114600C8E8D9 /* IntRandomTests.swift */; }; 69ACD71D2AFD13480021127B /* StringRandomTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69E0A6B32AFD114600C8E8D9 /* StringRandomTests.swift */; }; 69C6BE902B0BC80E008A4ECF /* UIWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69E0A6A82AFD114600C8E8D9 /* UIWindow.swift */; }; + 69DF227B2B459D0D0025C555 /* PushManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69DF22792B459D0D0025C555 /* PushManager.swift */; }; + 69DF227C2B459D0D0025C555 /* UNNotificationSettingsExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69DF227A2B459D0D0025C555 /* UNNotificationSettingsExtensions.swift */; }; 69FA5FAD23C868A900B44BCD /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 69FA5F9023C868A900B44BCD /* Assets.xcassets */; }; 69FA5FAE23C868A900B44BCD /* UIControlBlocksViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69FA5F9323C868A900B44BCD /* UIControlBlocksViewController.swift */; }; 69FA5FAF23C868A900B44BCD /* TitleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69FA5F9523C868A900B44BCD /* TitleViewController.swift */; }; @@ -157,13 +145,6 @@ remoteGlobalIDString = 693A925C2B3DB290008B3DC3; remoteInfo = Networking; }; - 691A1FD12B45931800B52E56 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 69E819DF23C773010054687B /* Project object */; - proxyType = 1; - remoteGlobalIDString = 69ACD6C12AFD130C0021127B; - remoteInfo = ACKategories; - }; 691B9ADB2AFD1E5C008AE7BD /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 69E819DF23C773010054687B /* Project object */; @@ -238,21 +219,6 @@ /* Begin PBXFileReference section */ 690BCB8C2B3DB5DE00EDA6F8 /* Networking.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; name = Networking.xctestplan; path = Tests/NetworkingTests/Networking.xctestplan; sourceTree = SOURCE_ROOT; }; - 691A1FC72B4592BC00B52E56 /* FirebaseFetcher.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FirebaseFetcher.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 691A1FD42B45932A00B52E56 /* FirebaseFetcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FirebaseFetcher.swift; sourceTree = ""; }; - 691A1FD62B45935400B52E56 /* Cartfile */ = {isa = PBXFileReference; lastKnownFileType = text; path = Cartfile; sourceTree = ""; }; - 691A1FD82B4593EB00B52E56 /* GoogleAppMeasurement.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = GoogleAppMeasurement.xcframework; path = Carthage/Build/GoogleAppMeasurement.xcframework; sourceTree = ""; }; - 691A1FD92B4593EB00B52E56 /* FirebaseCoreInternal.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = FirebaseCoreInternal.xcframework; path = Carthage/Build/FirebaseCoreInternal.xcframework; sourceTree = ""; }; - 691A1FDA2B4593EB00B52E56 /* FirebaseAnalytics.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = FirebaseAnalytics.xcframework; path = Carthage/Build/FirebaseAnalytics.xcframework; sourceTree = ""; }; - 691A1FDB2B4593EB00B52E56 /* FBLPromises.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = FBLPromises.xcframework; path = Carthage/Build/FBLPromises.xcframework; sourceTree = ""; }; - 691A1FDC2B4593EB00B52E56 /* FirebaseRemoteConfig.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = FirebaseRemoteConfig.xcframework; path = Carthage/Build/FirebaseRemoteConfig.xcframework; sourceTree = ""; }; - 691A1FDD2B4593EB00B52E56 /* GoogleUtilities.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = GoogleUtilities.xcframework; path = Carthage/Build/GoogleUtilities.xcframework; sourceTree = ""; }; - 691A1FDE2B4593EB00B52E56 /* nanopb.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = nanopb.xcframework; path = Carthage/Build/nanopb.xcframework; sourceTree = ""; }; - 691A1FDF2B4593EB00B52E56 /* FirebaseInstallations.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = FirebaseInstallations.xcframework; path = Carthage/Build/FirebaseInstallations.xcframework; sourceTree = ""; }; - 691A1FE02B4593EB00B52E56 /* GoogleAppMeasurementIdentitySupport.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = GoogleAppMeasurementIdentitySupport.xcframework; path = Carthage/Build/GoogleAppMeasurementIdentitySupport.xcframework; sourceTree = ""; }; - 691A1FE12B4593EB00B52E56 /* FirebaseABTesting.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = FirebaseABTesting.xcframework; path = Carthage/Build/FirebaseABTesting.xcframework; sourceTree = ""; }; - 691A1FE22B4593EB00B52E56 /* FirebaseCore.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = FirebaseCore.xcframework; path = Carthage/Build/FirebaseCore.xcframework; sourceTree = ""; }; - 691A1FE32B4593EB00B52E56 /* FirebaseSharedSwift.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = FirebaseSharedSwift.xcframework; path = Carthage/Build/FirebaseSharedSwift.xcframework; sourceTree = ""; }; 6922C77C2AFD1C1A00519CDF /* UINavigationControllerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UINavigationControllerTests.swift; sourceTree = ""; }; 6922C77E2AFD1C3300519CDF /* ACKategoriesResponder.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = ACKategoriesResponder.xctestplan; sourceTree = ""; }; 6922C77F2AFD1C5000519CDF /* ACKategories.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = ACKategories.xctestplan; sourceTree = ""; }; @@ -293,6 +259,9 @@ 698376CE2B3DA81200CD9E89 /* GradientView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GradientView.swift; sourceTree = ""; }; 69ACD6C22AFD130C0021127B /* ACKategories.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ACKategories.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 69ACD6C92AFD130D0021127B /* ACKategoriesTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ACKategoriesTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 69DF225A2B459CC50025C555 /* PushNotifications.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PushNotifications.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 69DF22792B459D0D0025C555 /* PushManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PushManager.swift; sourceTree = ""; }; + 69DF227A2B459D0D0025C555 /* UNNotificationSettingsExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UNNotificationSettingsExtensions.swift; sourceTree = ""; }; 69E0A5F52AFD10BE00C8E8D9 /* UISearchBarExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UISearchBarExtensions.swift; sourceTree = ""; }; 69E0A5F72AFD10BE00C8E8D9 /* UserDefault.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserDefault.swift; sourceTree = ""; }; 69E0A5F82AFD10BE00C8E8D9 /* ArrayExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArrayExtensions.swift; sourceTree = ""; }; @@ -384,26 +353,6 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 691A1FC42B4592BC00B52E56 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 691A1FFA2B4593EC00B52E56 /* FirebaseSharedSwift.xcframework in Frameworks */, - 691A1FE42B4593EB00B52E56 /* GoogleAppMeasurement.xcframework in Frameworks */, - 691A1FF82B4593EC00B52E56 /* FirebaseCore.xcframework in Frameworks */, - 691A1FF42B4593EB00B52E56 /* GoogleAppMeasurementIdentitySupport.xcframework in Frameworks */, - 691A1FE82B4593EB00B52E56 /* FirebaseAnalytics.xcframework in Frameworks */, - 691A1FEE2B4593EB00B52E56 /* GoogleUtilities.xcframework in Frameworks */, - 691A1FCF2B45931800B52E56 /* ACKategories.framework in Frameworks */, - 691A1FEA2B4593EB00B52E56 /* FBLPromises.xcframework in Frameworks */, - 691A1FEC2B4593EB00B52E56 /* FirebaseRemoteConfig.xcframework in Frameworks */, - 691A1FF62B4593EC00B52E56 /* FirebaseABTesting.xcframework in Frameworks */, - 691A1FE62B4593EB00B52E56 /* FirebaseCoreInternal.xcframework in Frameworks */, - 691A1FF02B4593EB00B52E56 /* nanopb.xcframework in Frameworks */, - 691A1FF22B4593EB00B52E56 /* FirebaseInstallations.xcframework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 693A925A2B3DB290008B3DC3 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -461,36 +410,16 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 69DF22572B459CC50025C555 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 691A1FCE2B45930400B52E56 /* FirebaseFetcher */ = { - isa = PBXGroup; - children = ( - 691A1FD42B45932A00B52E56 /* FirebaseFetcher.swift */, - ); - path = FirebaseFetcher; - sourceTree = ""; - }; - 691B9AD82AFD1E5C008AE7BD /* Frameworks */ = { - isa = PBXGroup; - children = ( - 691A1FDB2B4593EB00B52E56 /* FBLPromises.xcframework */, - 691A1FE12B4593EB00B52E56 /* FirebaseABTesting.xcframework */, - 691A1FDA2B4593EB00B52E56 /* FirebaseAnalytics.xcframework */, - 691A1FE22B4593EB00B52E56 /* FirebaseCore.xcframework */, - 691A1FD92B4593EB00B52E56 /* FirebaseCoreInternal.xcframework */, - 691A1FDF2B4593EB00B52E56 /* FirebaseInstallations.xcframework */, - 691A1FDC2B4593EB00B52E56 /* FirebaseRemoteConfig.xcframework */, - 691A1FE32B4593EB00B52E56 /* FirebaseSharedSwift.xcframework */, - 691A1FD82B4593EB00B52E56 /* GoogleAppMeasurement.xcframework */, - 691A1FE02B4593EB00B52E56 /* GoogleAppMeasurementIdentitySupport.xcframework */, - 691A1FDD2B4593EB00B52E56 /* GoogleUtilities.xcframework */, - 691A1FDE2B4593EB00B52E56 /* nanopb.xcframework */, - ); - name = Frameworks; - sourceTree = ""; - }; 693A92722B3DB2AA008B3DC3 /* Networking */ = { isa = PBXGroup; children = ( @@ -625,13 +554,22 @@ path = Theme; sourceTree = ""; }; + 69DF22782B459D0D0025C555 /* PushNotifications */ = { + isa = PBXGroup; + children = ( + 69DF22792B459D0D0025C555 /* PushManager.swift */, + 69DF227A2B459D0D0025C555 /* UNNotificationSettingsExtensions.swift */, + ); + path = PushNotifications; + sourceTree = ""; + }; 69E0A5F32AFD10BE00C8E8D9 /* Sources */ = { isa = PBXGroup; children = ( - 691A1FCE2B45930400B52E56 /* FirebaseFetcher */, 69E0A5F42AFD10BE00C8E8D9 /* ACKategories */, 693A92982B3DB394008B3DC3 /* ACKategoriesTesting */, 693A92722B3DB2AA008B3DC3 /* Networking */, + 69DF22782B459D0D0025C555 /* PushNotifications */, ); path = Sources; sourceTree = ""; @@ -787,13 +725,11 @@ 69E819DE23C773010054687B = { isa = PBXGroup; children = ( - 691A1FD62B45935400B52E56 /* Cartfile */, 697B023227DB65B50082F4AC /* CHANGELOG.md */, 695096D923C7908B00E8F457 /* ACKategoriesExample */, 69E819EB23C773240054687B /* Products */, 69E0A5F32AFD10BE00C8E8D9 /* Sources */, 69E0A69B2AFD114600C8E8D9 /* Tests */, - 691B9AD82AFD1E5C008AE7BD /* Frameworks */, ); sourceTree = ""; }; @@ -807,7 +743,7 @@ 693A925D2B3DB290008B3DC3 /* Networking.framework */, 693A92642B3DB290008B3DC3 /* NetworkingTests.xctest */, 693A92912B3DB388008B3DC3 /* ACKategoriesTesting.framework */, - 691A1FC72B4592BC00B52E56 /* FirebaseFetcher.framework */, + 69DF225A2B459CC50025C555 /* PushNotifications.framework */, ); name = Products; sourceTree = ""; @@ -913,28 +849,28 @@ /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ - 691A1FC22B4592BC00B52E56 /* Headers */ = { + 693A92582B3DB290008B3DC3 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; - 693A92582B3DB290008B3DC3 /* Headers */ = { + 693A928C2B3DB388008B3DC3 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; - 693A928C2B3DB388008B3DC3 /* Headers */ = { + 69ACD6BD2AFD130C0021127B /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; - 69ACD6BD2AFD130C0021127B /* Headers */ = { + 69DF22552B459CC50025C555 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( @@ -944,25 +880,6 @@ /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ - 691A1FC62B4592BC00B52E56 /* FirebaseFetcher */ = { - isa = PBXNativeTarget; - buildConfigurationList = 691A1FCD2B4592BC00B52E56 /* Build configuration list for PBXNativeTarget "FirebaseFetcher" */; - buildPhases = ( - 691A1FC22B4592BC00B52E56 /* Headers */, - 691A1FC32B4592BC00B52E56 /* Sources */, - 691A1FC42B4592BC00B52E56 /* Frameworks */, - 691A1FC52B4592BC00B52E56 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 691A1FD22B45931800B52E56 /* PBXTargetDependency */, - ); - name = FirebaseFetcher; - productName = FirebaseFetcher; - productReference = 691A1FC72B4592BC00B52E56 /* FirebaseFetcher.framework */; - productType = "com.apple.product-type.framework"; - }; 693A925C2B3DB290008B3DC3 /* Networking */ = { isa = PBXNativeTarget; buildConfigurationList = 693A92702B3DB290008B3DC3 /* Build configuration list for PBXNativeTarget "Networking" */; @@ -1097,6 +1014,24 @@ productReference = 69ACD6C92AFD130D0021127B /* ACKategoriesTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; + 69DF22592B459CC50025C555 /* PushNotifications */ = { + isa = PBXNativeTarget; + buildConfigurationList = 69DF226D2B459CC50025C555 /* Build configuration list for PBXNativeTarget "PushNotifications" */; + buildPhases = ( + 69DF22552B459CC50025C555 /* Headers */, + 69DF22562B459CC50025C555 /* Sources */, + 69DF22572B459CC50025C555 /* Frameworks */, + 69DF22582B459CC50025C555 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = PushNotifications; + productName = PushNotifications; + productReference = 69DF225A2B459CC50025C555 /* PushNotifications.framework */; + productType = "com.apple.product-type.framework"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -1107,10 +1042,6 @@ LastSwiftUpdateCheck = 1510; LastUpgradeCheck = 1500; TargetAttributes = { - 691A1FC62B4592BC00B52E56 = { - CreatedOnToolsVersion = 15.1; - LastSwiftMigration = 1510; - }; 693A925C2B3DB290008B3DC3 = { CreatedOnToolsVersion = 15.1; }; @@ -1134,6 +1065,9 @@ 69ACD6C82AFD130D0021127B = { CreatedOnToolsVersion = 15.0.1; }; + 69DF22592B459CC50025C555 = { + CreatedOnToolsVersion = 15.1; + }; }; }; buildConfigurationList = 69E819E223C773010054687B /* Build configuration list for PBXProject "ACKategories" */; @@ -1158,19 +1092,12 @@ 693A925C2B3DB290008B3DC3 /* Networking */, 693A92632B3DB290008B3DC3 /* NetworkingTests */, 693A92902B3DB388008B3DC3 /* ACKategoriesTesting */, - 691A1FC62B4592BC00B52E56 /* FirebaseFetcher */, + 69DF22592B459CC50025C555 /* PushNotifications */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ - 691A1FC52B4592BC00B52E56 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; 693A925B2B3DB290008B3DC3 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -1222,17 +1149,16 @@ ); runOnlyForDeploymentPostprocessing = 0; }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 691A1FC32B4592BC00B52E56 /* Sources */ = { - isa = PBXSourcesBuildPhase; + 69DF22582B459CC50025C555 /* Resources */ = { + isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 691A1FD52B45932A00B52E56 /* FirebaseFetcher.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ 693A92592B3DB290008B3DC3 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -1397,6 +1323,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 69DF22562B459CC50025C555 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 69DF227C2B459D0D0025C555 /* UNNotificationSettingsExtensions.swift in Sources */, + 69DF227B2B459D0D0025C555 /* PushManager.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -1405,11 +1340,6 @@ target = 693A925C2B3DB290008B3DC3 /* Networking */; targetProxy = 690BCB8F2B3DB62400EDA6F8 /* PBXContainerItemProxy */; }; - 691A1FD22B45931800B52E56 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 69ACD6C12AFD130C0021127B /* ACKategories */; - targetProxy = 691A1FD12B45931800B52E56 /* PBXContainerItemProxy */; - }; 691B9ADC2AFD1E5C008AE7BD /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 69ACD6C12AFD130C0021127B /* ACKategories */; @@ -1453,128 +1383,6 @@ /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ - 691A1FCB2B4592BC00B52E56 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = dwarf; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_DYNAMIC_NO_PIC = NO; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - "@loader_path/Frameworks", - ); - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MARKETING_VERSION = 1.0; - MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; - MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = cz.ackee.FirebaseFetcher; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SDKROOT = macosx; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx watchos watchsimulator"; - SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - 691A1FCC2B4592BC00B52E56 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_MODULE_VERIFIER = YES; - ENABLE_NS_ASSERTIONS = NO; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - "@loader_path/Frameworks", - ); - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MARKETING_VERSION = 1.0; - MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; - MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = cz.ackee.FirebaseFetcher; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SDKROOT = macosx; - SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx watchos watchsimulator"; - SUPPORTS_MACCATALYST = YES; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; 693A926C2B3DB290008B3DC3 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -2326,6 +2134,121 @@ }; name = Release; }; + 69DF22692B459CC50025C555 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = cz.ackee.PushNotifications; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 69DF226A2B459CC50025C555 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = cz.ackee.PushNotifications; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; 69E819E323C773010054687B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -2416,15 +2339,6 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 691A1FCD2B4592BC00B52E56 /* Build configuration list for PBXNativeTarget "FirebaseFetcher" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 691A1FCB2B4592BC00B52E56 /* Debug */, - 691A1FCC2B4592BC00B52E56 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; 693A92702B3DB290008B3DC3 /* Build configuration list for PBXNativeTarget "Networking" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -2488,6 +2402,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 69DF226D2B459CC50025C555 /* Build configuration list for PBXNativeTarget "PushNotifications" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 69DF22692B459CC50025C555 /* Debug */, + 69DF226A2B459CC50025C555 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 69E819E223C773010054687B /* Build configuration list for PBXProject "ACKategories" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/ACKategories.xcodeproj/xcshareddata/xcschemes/PushNotifications.xcscheme b/ACKategories.xcodeproj/xcshareddata/xcschemes/PushNotifications.xcscheme new file mode 100644 index 00000000..124585d1 --- /dev/null +++ b/ACKategories.xcodeproj/xcshareddata/xcschemes/PushNotifications.xcscheme @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Package.swift b/Package.swift index affb0c91..d03ecffc 100644 --- a/Package.swift +++ b/Package.swift @@ -13,6 +13,7 @@ let package = Package( .library(name: "ACKategories", targets: ["ACKategories"]), .library(name: "ACKategoriesTesting", targets: ["ACKategoriesTesting"]), .library(name: "Networking", targets: ["Networking"]), + .library(name: "PushNotifications", targets: ["PushNotifications"]), ], targets: [ .target(name: "ACKategories"), @@ -38,8 +39,6 @@ let package = Package( "Networking", ] ), - dependencies: [ - ] - ), + .target(name: "PushNotifications"), ] ) diff --git a/Sources/PushNotifications/PushManager.swift b/Sources/PushNotifications/PushManager.swift new file mode 100644 index 00000000..93452514 --- /dev/null +++ b/Sources/PushNotifications/PushManager.swift @@ -0,0 +1,142 @@ +import Foundation +import UserNotifications + +@available(iOS 13.0, macOS 10.15, watchOS 6.0, tvOS 13.0, *) +public protocol PushManaging { + var actions: PushManagingActions { get } + + var notificationSettings: AsyncStream { get } + var currentNotificationSettings: UNNotificationSettings? { get } +} + +@available(iOS 13.0, macOS 10.15, watchOS 6.0, tvOS 13.0, *) +public protocol PushManagingActions { + func start() + func requestPermission(options: UNAuthorizationOptions) async + + func addNotificationReceivedHandler(_ handler: @escaping (UNNotification) async -> ()) -> String + func removeNotificationReceivedHandler(id: String) + + #if !os(tvOS) + func addNotificationOpenedHandler(_ handler: @escaping (UNNotificationResponse) async -> ()) -> String + func removeNotificationOpenedHandler(id: String) + #endif +} + +@available(iOS 13.0, macOS 10.15, watchOS 6.0, tvOS 13.0, *) +public extension PushManaging where Self: PushManagingActions { + var actions: PushManagingActions { self } +} + +@available(iOS 13.0, macOS 10.15, watchOS 6.0, tvOS 13.0, *) +public final class PushManager: NSObject, PushManaging, PushManagingActions { + public private(set) lazy var notificationSettings = AsyncStream { continuation in + notificationSettingsContinuation = continuation + + if let currentNotificationSettings { + continuation.yield(currentNotificationSettings) + } + } + public private(set) var currentNotificationSettings: UNNotificationSettings? + + public lazy var presentationOptions: (UNNotification) async -> UNNotificationPresentationOptions = { [weak self] notification in + guard let self else { return [] } + return await notificationCenter.notificationSettings().allowedPresentationOptions + } + + public var openSettings: (UNNotification?) -> () = { _ in } + + private let notificationCenter: UNUserNotificationCenter + private var notificationSettingsContinuation: AsyncStream.Continuation? + + private var notificationReceivedHandlers = [String: (UNNotification) async -> ()]() + + #if !os(tvOS) + private var notificationOpenedHandlers = [String: (UNNotificationResponse) async -> ()]() + #endif + + public init( + notificationCenter: UNUserNotificationCenter = .current() + ) { + self.notificationCenter = notificationCenter + super.init() + } + + deinit { + notificationSettingsContinuation?.finish() + } + + public func start() { + notificationCenter.delegate = self + } + + public func requestPermission(options: UNAuthorizationOptions) async { + guard let granted = try? await notificationCenter.requestAuthorization(options: options), + granted + else { return } + + let settings = await notificationCenter.notificationSettings() + currentNotificationSettings = settings + notificationSettingsContinuation?.yield(settings) + } + + public func addNotificationReceivedHandler( + _ handler: @escaping (UNNotification) async -> () + ) -> String { + let id = UUID().uuidString + notificationReceivedHandlers[id] = handler + return id + } + + public func removeNotificationReceivedHandler(id: String) { + notificationReceivedHandlers[id] = nil + } + + #if !os(tvOS) + public func addNotificationOpenedHandler( + _ handler: @escaping (UNNotificationResponse) async -> () + ) -> String { + let id = UUID().uuidString + notificationOpenedHandlers[id] = handler + return id + } + + public func removeNotificationOpenedHandler(id: String) { + notificationOpenedHandlers[id] = nil + } + #endif +} + +@available(iOS 13.0, macOS 10.15, watchOS 6.0, tvOS 13.0, *) +extension PushManager: UNUserNotificationCenterDelegate { + public func userNotificationCenter( + _ center: UNUserNotificationCenter, + willPresent notification: UNNotification + ) async -> UNNotificationPresentationOptions { + for handler in notificationReceivedHandlers.values { + await handler(notification) + } + + return await presentationOptions(notification) + } + + #if !os(tvOS) + public func userNotificationCenter( + _ center: UNUserNotificationCenter, + didReceive response: UNNotificationResponse + ) async { + for handler in notificationOpenedHandlers.values { + await handler(response) + } + } + #endif + + #if !os(watchOS) && !os(tvOS) + public func userNotificationCenter( + _ center: UNUserNotificationCenter, + openSettingsFor notification: UNNotification? + ) { + openSettings(notification) + } + #endif +} diff --git a/Sources/PushNotifications/UNNotificationSettingsExtensions.swift b/Sources/PushNotifications/UNNotificationSettingsExtensions.swift new file mode 100644 index 00000000..07492ae8 --- /dev/null +++ b/Sources/PushNotifications/UNNotificationSettingsExtensions.swift @@ -0,0 +1,22 @@ +import UserNotifications + +@available(iOS 13.0, macOS 10.15, *) +public extension UNNotificationSettings { + var allowedPresentationOptions: UNNotificationPresentationOptions { + var options = [(UNNotificationSetting, UNNotificationPresentationOptions)]() + + #if !os(tvOS) + options.append((soundSetting, UNNotificationPresentationOptions.sound)) + options.append((alertSetting, .alert)) + #endif + + #if !os(watchOS) + options.append((badgeSetting, .badge)) + #endif + + return .init(options.compactMap { setting, option in + if setting == .enabled { return option } + return nil + }) + } +} From 580f8e09d75210963bc1392ba8ebecb756bce769 Mon Sep 17 00:00:00 2001 From: Jakub Olejnik Date: Fri, 5 Jan 2024 14:05:49 +0100 Subject: [PATCH 09/12] =?UTF-8?q?=F0=9F=93=9D=20Update=20changelog?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bf72a4d..27775b10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ ``` ## Next +- Merge [iOS template lib](https://github.com/AckeeCZ/iOS-MVVM-ProjectTemplate) ([#143](https://github.com/AckeeCZ/ACKategories/pull/143), kudos to @olejnjak) + - add Networking module + - add VersionUpdate module + - add PushNotifications module - Add `isTV` and `isMac` for `UIDevice` ([#142](https://github.com/AckeeCZ/ACKategories/pull/142), kudos to @leinhauplk) - Add helper function for easier back gesture setup ([#141](https://github.com/AckeeCZ/ACKategories/pull/141), kudos to @leinhauplk) - Support tvOS & watchOS, use single multiplatform target for Carthage ([#140](https://github.com/AckeeCZ/ACKategories/pull/140), kudos to @olejnjak) From 8d0372c42e4d5c1631e08433231165434ddad8fd Mon Sep 17 00:00:00 2001 From: Jakub Olejnik Date: Sun, 28 Jan 2024 17:55:12 +0100 Subject: [PATCH 10/12] =?UTF-8?q?=F0=9F=94=A5=20Remove=20deploy=20workflow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy.yml | 31 ------------------------------- 1 file changed, 31 deletions(-) delete mode 100644 .github/workflows/deploy.yml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml deleted file mode 100644 index 3121c1de..00000000 --- a/.github/workflows/deploy.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: Deploy - -on: - push: - tags: - - '*' - -jobs: - carthage: - name: Upload Carthage binary - runs-on: macos-13 - steps: - - uses: actions/checkout@v4 - - uses: AckeeCZ/load-xcode-version@1.1.0 - - name: Build - run: carthage build --no-skip-current --cache-builds --use-xcframeworks - - name: Archive - run: | - DST=$PWD - mkdir -p /tmp/ACKategories - mv Carthage/Build/*.xcframework /tmp/ACKategories - cd /tmp - zip -r "$DST/"ACKategories.xcframework.zip ACKategories - - uses: xresloader/upload-to-github-release@v1.3.12 - if: startsWith(github.ref, 'refs/tags/') - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - file: ACKategories.xcframework.zip - tags: true - draft: false From 8e45168a2deeaad1d1abc7a059a955626f0f3726 Mon Sep 17 00:00:00 2001 From: Jakub Olejnik Date: Sun, 28 Jan 2024 17:56:32 +0100 Subject: [PATCH 11/12] =?UTF-8?q?=F0=9F=94=A7=20Use=20Xcode=2015.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/xcode-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/xcode-version b/.github/xcode-version index ccc2f3b8..0d57595e 100644 --- a/.github/xcode-version +++ b/.github/xcode-version @@ -1 +1 @@ -15.1 \ No newline at end of file +15.2 \ No newline at end of file From ca4d70371592e81a3c70b439aa2bdd346fe18c82 Mon Sep 17 00:00:00 2001 From: Jakub Olejnik Date: Sun, 28 Jan 2024 19:26:20 +0100 Subject: [PATCH 12/12] =?UTF-8?q?=F0=9F=94=A5=20Cleanup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 ++- .../xcshareddata/IDEWorkspaceChecks.plist | 8 ++++++++ Cartfile.resolved | 2 -- 3 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 ACKategories.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 Cartfile.resolved diff --git a/.gitignore b/.gitignore index 3c2246c7..952c54d9 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ Carthage /.build .DS_Store DerivedData -.swiftpm/ \ No newline at end of file +.swiftpm/ +xcuserdata diff --git a/ACKategories.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ACKategories.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/ACKategories.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Cartfile.resolved b/Cartfile.resolved deleted file mode 100644 index c18a4459..00000000 --- a/Cartfile.resolved +++ /dev/null @@ -1,2 +0,0 @@ -binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseAnalyticsBinary.json" "10.19.0" -binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseRemoteConfigBinary.json" "10.19.0"