diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 313b3db..6e4d785 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -5,10 +5,15 @@ on: branches: - master +# See this page for available Xcode versions: +# https://github.com/actions/runner-images/blob/main/images/macos/macos-13-Readme.md +env: + DEVELOPER_DIR: /Applications/Xcode_15.0.app/Contents/Developer + jobs: build: - runs-on: macOS-latest + runs-on: macos-13 steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3139421..6b02e7e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,10 +2,15 @@ name: CI on: [pull_request] +# See this page for available Xcode versions: +# https://github.com/actions/runner-images/blob/main/images/macos/macos-13-Readme.md +env: + DEVELOPER_DIR: /Applications/Xcode_15.0.app/Contents/Developer + jobs: build: - runs-on: macos-12 + runs-on: macos-13 steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/prepare_release.yml b/.github/workflows/prepare_release.yml index 96812af..5691caa 100644 --- a/.github/workflows/prepare_release.yml +++ b/.github/workflows/prepare_release.yml @@ -3,10 +3,15 @@ name: Create release PRs on: create +# See this page for available Xcode versions: +# https://github.com/actions/runner-images/blob/main/images/macos/macos-13-Readme.md +env: + DEVELOPER_DIR: /Applications/Xcode_15.0.app/Contents/Developer + jobs: build: - runs-on: macOS-latest + runs-on: macos-13 if: contains(github.ref, 'release') # allow to filter on release branches only steps: diff --git a/ADUtils.podspec b/ADUtils.podspec index 4e403cc..0f12dc4 100644 --- a/ADUtils.podspec +++ b/ADUtils.podspec @@ -1,17 +1,17 @@ Pod::Spec.new do |spec| spec.name = 'ADUtils' - spec.version = '11.5.0' + spec.version = '12.0.0' spec.authors = 'Fabernovel' spec.homepage = 'https://github.com/faberNovel/ADUtils' spec.summary = 'Fabernovel\'s toolbox for iOS' - spec.ios.deployment_target = '10.0' - spec.tvos.deployment_target = '10.0' + spec.ios.deployment_target = '13.0' + spec.tvos.deployment_target = '13.0' spec.license = { :type => 'MIT', :text => 'Created and licensed by Fabernovel Technologies. Copyright 2014-2018 Fabernovel Technologies. All rights reserved.' } spec.source = { :git => 'https://github.com/faberNovel/ADUtils.git', :tag => "v#{spec.version}" } spec.framework = 'Foundation', 'UIKit' spec.requires_arc = true spec.default_subspec = 'objc' - spec.swift_versions = ['5.0', '5.1'] + spec.swift_versions = ['5.7'] spec.subspec 'Swift' do |subspec| # Subspec compliant with App extensions diff --git a/ADUtils.xcodeproj/project.pbxproj b/ADUtils.xcodeproj/project.pbxproj index 56da6fd..732706c 100644 --- a/ADUtils.xcodeproj/project.pbxproj +++ b/ADUtils.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 52; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -452,9 +452,10 @@ 7A0EC6461535D8ED00E5F54A /* Project object */ = { isa = PBXProject; attributes = { + BuildIndependentTargetsInParallel = YES; CLASSPREFIX = AD; LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 1120; + LastUpgradeCheck = 1500; TargetAttributes = { 49B1F77D1CE4B3DB00A29BFE = { CreatedOnToolsVersion = 7.3; @@ -471,11 +472,11 @@ }; buildConfigurationList = 7A0EC6491535D8ED00E5F54A /* Build configuration list for PBXProject "ADUtils" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( - English, en, + Base, ); mainGroup = 7A0EC6441535D8ED00E5F54A; packageReferences = ( @@ -520,6 +521,7 @@ /* Begin PBXShellScriptBuildPhase section */ 490133B11C1EFC11000CC04B /* Turn TODO into Warning */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -530,10 +532,12 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "KEYWORDS=\"TODO:|FIXME:|\\?\\?\\?:|\\!\\!\\!:\"\nfind \"${SRCROOT}/Classes\" -name \"*.h\" -or -name \"*.m\" -print0 | xargs -0 egrep --with-filename --line-number --only-matching \"($KEYWORDS).*\\$\" | perl -p -e \"s/($KEYWORDS)/ warning: \\$1/\""; + shellScript = "KEYWORDS=\"TODO:|FIXME:|\\?\\?\\?:|\\!\\!\\!:\"\nfind \"${SRCROOT}/Classes\" -name \"*.h\" -or -name \"*.m\" -print0 | xargs -0 egrep --with-filename --line-number --only-matching \"($KEYWORDS).*\\$\" | perl -p -e \"s/($KEYWORDS)/ warning: \\$1/\"\n"; + showEnvVarsInLog = 0; }; 490133B21C1EFC25000CC04B /* Warning if file > 250 lines */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -544,10 +548,12 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "find \"${SRCROOT}/Classes\" \\( -name \"*.h\" -or -name \"*.m\" \\) -and \\( -path \"${SRCROOT}/Pods/*\" -prune -o -print0 \\) | xargs -0 wc -l | awk '$1 > 250 && $2 != \"total\" {for(i=2;i 250 && $2 != \"total\" {for(i=2;i/dev/null 2>&1\n/usr/libexec/PlistBuddy -c \"Delete :CFBundleGitBranch\" \"${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\" >/dev/null 2>&1\n/usr/libexec/PlistBuddy -c \"Delete :CFBundleGitStatus\" \"${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\" >/dev/null 2>&1\nif [ -n \"$GIT_COMMIT\" ]; then\n /usr/libexec/PlistBuddy -c \"Add :CFBundleGitCommit string \\\"${GIT_COMMIT}\\\"\" \"${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\"\nfi\nif [ -n \"$GIT_BRANCH\" ]; then\n /usr/libexec/PlistBuddy -c \"Add :CFBundleGitBranch string \\\"${GIT_BRANCH}\\\"\" \"${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\"\nfi\nif [ -n \"$GIT_STATUS\" ]; then\n /usr/libexec/PlistBuddy -c \"Add :CFBundleGitStatus string \\\"${GIT_STATUS}\\\"\" \"${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\"\nfi"; + shellScript = "GIT_COMMIT=`git rev-parse --short HEAD`\nGIT_BRANCH=`git symbolic-ref HEAD`\nGIT_STATUS=`git status --porcelain -z`\n/usr/libexec/PlistBuddy -c \"Delete :CFBundleGitCommit\" \"${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\" >/dev/null 2>&1\n/usr/libexec/PlistBuddy -c \"Delete :CFBundleGitBranch\" \"${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\" >/dev/null 2>&1\n/usr/libexec/PlistBuddy -c \"Delete :CFBundleGitStatus\" \"${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\" >/dev/null 2>&1\nif [ -n \"$GIT_COMMIT\" ]; then\n /usr/libexec/PlistBuddy -c \"Add :CFBundleGitCommit string \\\"${GIT_COMMIT}\\\"\" \"${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\"\nfi\nif [ -n \"$GIT_BRANCH\" ]; then\n /usr/libexec/PlistBuddy -c \"Add :CFBundleGitBranch string \\\"${GIT_BRANCH}\\\"\" \"${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\"\nfi\nif [ -n \"$GIT_STATUS\" ]; then\n /usr/libexec/PlistBuddy -c \"Add :CFBundleGitStatus string \\\"${GIT_STATUS}\\\"\" \"${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\"\nfi\n"; showEnvVarsInLog = 0; }; A6A85E92266DAE1D134ACEEA /* [CP] Check Pods Manifest.lock */ = { @@ -886,6 +895,7 @@ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -914,6 +924,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 13.0; PROVISIONING_PROFILE_SPECIFIER = "$(PROFILE_UDID)"; SDKROOT = iphoneos; + SWIFT_STRICT_CONCURRENCY = complete; SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; }; @@ -965,6 +976,7 @@ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -990,6 +1002,7 @@ OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; PROVISIONING_PROFILE_SPECIFIER = "$(PROFILE_UDID)"; SDKROOT = iphoneos; + SWIFT_STRICT_CONCURRENCY = complete; SWIFT_VERSION = 5.0; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; @@ -1041,6 +1054,7 @@ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -1068,6 +1082,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 13.0; PROVISIONING_PROFILE_SPECIFIER = "$(PROFILE_UDID)"; SDKROOT = iphoneos; + SWIFT_STRICT_CONCURRENCY = complete; SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; }; @@ -1119,6 +1134,7 @@ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -1146,6 +1162,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 13.0; PROVISIONING_PROFILE_SPECIFIER = "$(PROFILE_UDID)"; SDKROOT = iphoneos; + SWIFT_STRICT_CONCURRENCY = complete; SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; }; diff --git a/ADUtils.xcodeproj/xcshareddata/xcschemes/ADUtilsApp.xcscheme b/ADUtils.xcodeproj/xcshareddata/xcschemes/ADUtilsApp.xcscheme index 00b6f52..0c44abb 100644 --- a/ADUtils.xcodeproj/xcshareddata/xcschemes/ADUtilsApp.xcscheme +++ b/ADUtils.xcodeproj/xcshareddata/xcschemes/ADUtilsApp.xcscheme @@ -1,6 +1,6 @@ UIFont { return FontHelper.shared.helveticaNeueDynamicFont.font(forTextStyle: textStyle) } @@ -21,11 +22,13 @@ private extension UIFont { private extension Font { + @MainActor static func ad_mainFont(forTextStyle textStyle: Font.TextStyle) -> Font { return FontHelper.shared.helveticaNeueDynamicFont.font(forTextStyle: textStyle) } } +@MainActor private class FontHelper { static let shared = FontHelper() @@ -40,6 +43,7 @@ private class FontHelper { }() } +@MainActor class DynamicFontTest: QuickSpec { override class func spec() { @@ -58,23 +62,24 @@ class DynamicFontTest: QuickSpec { .caption1, .caption2 ] - let labels = types.map { (type) -> UILabel in - let label = UILabel() - label.font = UIFont.ad_mainFont(forTextStyle: type) - if #available(iOS 11.0, *) { - label.adjustsFontForContentSizeCategory = true + + it("should layout labels properly") { + let labels = types.map { (type) -> UILabel in + let label = UILabel() + label.font = UIFont.ad_mainFont(forTextStyle: type) + if #available(iOS 11.0, *) { + label.adjustsFontForContentSizeCategory = true + } + label.text = "Lorem sizzle pimpin' sit amizzle" + label.numberOfLines = 0 + return label } - label.text = "Lorem sizzle pimpin' sit amizzle" - label.numberOfLines = 0 - return label - } - let stackView = UIStackView(arrangedSubviews: labels) - stackView.frame = CGRect(x: 0.0, y: 0.0, width: 200.0, height: 1000.0) - stackView.axis = .vertical - stackView.distribution = .fillEqually + let stackView = UIStackView(arrangedSubviews: labels) + stackView.frame = CGRect(x: 0.0, y: 0.0, width: 200.0, height: 1000.0) + stackView.axis = .vertical + stackView.distribution = .fillEqually - it("should layout labels properly") { stackView.layoutIfNeeded() assertSnapshot(matching: stackView, as: .image, named: "DynamicFontLayoutTest") assertSnapshot( diff --git a/ADUtilsTests/LayoutGuideConstraintsTests.swift b/ADUtilsTests/LayoutGuideConstraintsTests.swift index e0f4f80..aa17e48 100644 --- a/ADUtilsTests/LayoutGuideConstraintsTests.swift +++ b/ADUtilsTests/LayoutGuideConstraintsTests.swift @@ -11,6 +11,7 @@ import SnapshotTesting import Quick import ADUtils +@MainActor class LayoutGuideConstraintsTests: QuickSpec { override class func spec() { diff --git a/ADUtilsTests/LayoutTests/ViewLayoutTest.swift b/ADUtilsTests/LayoutTests/ViewLayoutTest.swift index c67f44d..d4d2986 100644 --- a/ADUtilsTests/LayoutTests/ViewLayoutTest.swift +++ b/ADUtilsTests/LayoutTests/ViewLayoutTest.swift @@ -12,6 +12,7 @@ import ADUtils import Nimble import UIKit +@MainActor class ViewLayout: QuickSpec { override class func spec() { diff --git a/ADUtilsTests/MKMapViewDequeuingTest.swift b/ADUtilsTests/MKMapViewDequeuingTest.swift index 5ffb096..f1b081a 100644 --- a/ADUtilsTests/MKMapViewDequeuingTest.swift +++ b/ADUtilsTests/MKMapViewDequeuingTest.swift @@ -22,6 +22,7 @@ class MapViewAnnotationView: MKAnnotationView { } +@MainActor class MKMapViewDequeuingTest: QuickSpec { override class func spec() { describe("Dequeuing") { diff --git a/ADUtilsTests/NSLayoutConstraintUtilsTests.swift b/ADUtilsTests/NSLayoutConstraintUtilsTests.swift index 7665a80..184d4ab 100644 --- a/ADUtilsTests/NSLayoutConstraintUtilsTests.swift +++ b/ADUtilsTests/NSLayoutConstraintUtilsTests.swift @@ -11,6 +11,7 @@ import Nimble import ADUtils import UIKit +@MainActor class NSLayoutConstraintUtilsTests: QuickSpec { override class func spec() { diff --git a/ADUtilsTests/NavigationControllerObserverTest.swift b/ADUtilsTests/NavigationControllerObserverTest.swift index 266935c..7c0efe8 100644 --- a/ADUtilsTests/NavigationControllerObserverTest.swift +++ b/ADUtilsTests/NavigationControllerObserverTest.swift @@ -39,6 +39,7 @@ private class NavigationControllerDelegate: NSObject, UINavigationControllerDele } } +@MainActor class NavigationControllerObserverTest : QuickSpec { override class func spec() { @@ -51,7 +52,12 @@ class NavigationControllerObserverTest : QuickSpec { beforeSuite { UIApplication.shared.keyWindow?.rootViewController = navigationController UIApplication.shared.keyWindow?.makeKeyAndVisible() - UIApplication.shared.keyWindow?.layer.speed = 100 // speed up animations + navigationController.view.window?.layer.speed = 100 // speed up animations + // ???: (Alexandre Podlewski) 10/10/2023 setting the layer speed of the ViewController's view is more + // reliable than setting it on the window because it does not involve global state. + // When setting the key window speed, there can be some race condition with the afterSuite {} where + // the speed is reseted to 1. + navigationController.view.layer.speed = 100 let viewController = UIViewController() navigationController.pushViewController(viewController, animated: false) waitUntil(timeout: .seconds(1)) { done in @@ -63,10 +69,6 @@ class NavigationControllerObserverTest : QuickSpec { } } - afterSuite { - UIApplication.shared.keyWindow?.layer.speed = 1 // reset default animations speed - } - beforeEach { navigationController.popToRootViewController(animated: false) @@ -86,9 +88,8 @@ class NavigationControllerObserverTest : QuickSpec { waitUntil(timeout: .seconds(1)) { done in // When navigationController.popViewController(animated: true) - - // We need to wait the end of the animation - DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + navigationController.executeAfterTransition { + // We need to wait the end of the animation // Then expect(navigationController.viewControllers.count).to(equal(1)) expect(observerDelegate.observedViewControllers).to(contain(viewControllerToObserve)) @@ -119,16 +120,15 @@ class NavigationControllerObserverTest : QuickSpec { waitUntil(timeout: .seconds(1)) { done in // When navigationController.popViewController(animated: true) - - // We need to wait the end of the animation - DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + navigationController.executeAfterTransition { + // We need to wait the end of the animation // Then expect(navigationController.viewControllers.count).to(equal(2)) expect(observerDelegate2.observedViewControllers).to(contain(viewControllerToObserve2)) // When navigationController.popViewController(animated: true) - DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + navigationController.executeAfterTransition { // Then expect(navigationController.viewControllers.count).to(equal(1)) expect(observerDelegate.observedViewControllers).to(contain(viewControllerToObserve1)) @@ -151,9 +151,8 @@ class NavigationControllerObserverTest : QuickSpec { // When removeObserverAction(viewControllerToObserve) navigationController.popViewController(animated: true) - - // We need to wait the end of the animation - DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + navigationController.executeAfterTransition { + // We need to wait the end of the animation // Then expect(navigationController.viewControllers.count).to(equal(1)) expect(observerDelegate.observedViewControllers).to(beEmpty()) @@ -180,6 +179,7 @@ class NavigationControllerObserverTest : QuickSpec { } } + // Passed 100 times it("should forward navigation controller delegate methods") { // Given let viewControllerToObserve = UIViewController() @@ -195,9 +195,8 @@ class NavigationControllerObserverTest : QuickSpec { waitUntil(timeout: .seconds(1)) { done in // When navigationController.popViewController(animated: true) - - // We need to wait the end of the animation - DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + navigationController.executeAfterTransition { + // We need to wait the end of the animation // Then expect(navigationControllerDelegate.willShowViewControllers).toNot(beEmpty()) expect(navigationControllerDelegate.didShowViewControllers).toNot(beEmpty()) @@ -206,6 +205,7 @@ class NavigationControllerObserverTest : QuickSpec { } } + // Passed 100 times it("should clean observer of view controllers not in the stack") { // Given let viewControllerToObserve = UIViewController() @@ -224,27 +224,37 @@ class NavigationControllerObserverTest : QuickSpec { waitUntil(timeout: .seconds(1)) { done in // When navigationController.popViewController(animated: true) - + XCTAssertNotNil(navigationController.transitionCoordinator) // We need to wait the end of the animation - DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + navigationController.executeAfterTransition { // Then - expect(navigationController.viewControllers.count).to(equal(1)) expect(observerDelegate.observedViewControllers).to(equal([viewControllerToObserve])) // We now try to push/pop the viewControllerNotInTheStack navigationController.pushViewController(viewControllerNotInTheStack, animated: false) - navigationController.popViewController(animated: true) - - // We need to wait the end of the animation - DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { - // The viewControllerNotInTheStack should not be observed anymore - expect(navigationController.viewControllers.count).to(equal(1)) - expect(observerDelegate.observedViewControllers).to(equal([viewControllerToObserve])) - done() + DispatchQueue.main.async { + navigationController.popViewController(animated: true) + XCTAssertNotNil(navigationController.transitionCoordinator) + navigationController.executeAfterTransition { + // We need to wait the end of the animation + // The viewControllerNotInTheStack should not be observed anymore + expect(navigationController.viewControllers.count).to(equal(1)) + expect(observerDelegate.observedViewControllers).to(equal([viewControllerToObserve])) + done() + } } } } } } } + +extension UINavigationController { + func executeAfterTransition(_ block: @escaping () -> Void, file: StaticString = #file, line: UInt = #line) { + XCTAssertNotNil(transitionCoordinator, "no transition is in progress", file: file, line: line) + transitionCoordinator?.animate(alongsideTransition: nil, completion: { _ in + block() + }) + } +} diff --git a/ADUtilsTests/NavigationControllerPopCompletionTests.swift b/ADUtilsTests/NavigationControllerPopCompletionTests.swift index 58947f1..f661368 100644 --- a/ADUtilsTests/NavigationControllerPopCompletionTests.swift +++ b/ADUtilsTests/NavigationControllerPopCompletionTests.swift @@ -11,7 +11,7 @@ import Nimble import Quick import ADUtils -class CompletionBehavior: Behavior { +class CompletionBehavior: AsyncBehavior { override class func spec(_ context: @escaping () -> Bool) { var isAnimated: Bool! @@ -20,7 +20,7 @@ class CompletionBehavior: Behavior { isAnimated = context() } - it("should call completion once pop is done") { + it("should call completion once pop is done") { @MainActor in // Given let rootViewController = UIViewController() let navigationController = UINavigationController(rootViewController: rootViewController) @@ -34,12 +34,12 @@ class CompletionBehavior: Behavior { } // Then - expect(completionCalled).toEventually(beTrue()) + await expect(completionCalled).toEventually(beTrue()) } } } -class NavigationControllerPopCompletionTests: QuickSpec { +class NavigationControllerPopCompletionTests: AsyncSpec { override class func spec() { let isAnimated = true diff --git a/ADUtilsTests/OptionalUnwrapTest.swift b/ADUtilsTests/OptionalUnwrapTest.swift index 0bc931d..2033ca1 100644 --- a/ADUtilsTests/OptionalUnwrapTest.swift +++ b/ADUtilsTests/OptionalUnwrapTest.swift @@ -11,15 +11,23 @@ import Quick import ADUtils import Nimble -private class Test { - var testValue = 0.0 +private class Test: @unchecked Sendable { + private var _testValue = 0.0 + var testValue: Double { + get { + return synchronize(self) { _testValue } + } + set { + synchronize(self) { _testValue = newValue } + } + } func increaseValue(in test: Test) { test.testValue += 1.0 } } -private func delay(_ duration: TimeInterval, block: @escaping () -> ()) { +private func delay(_ duration: TimeInterval, block: @Sendable @escaping () -> ()) { let time = DispatchTime.now() + duration DispatchQueue.main.asyncAfter(deadline: time, execute: block) } diff --git a/ADUtilsTests/RegisterableViewTest.swift b/ADUtilsTests/RegisterableViewTest.swift index 49d4c3c..ea295c2 100644 --- a/ADUtilsTests/RegisterableViewTest.swift +++ b/ADUtilsTests/RegisterableViewTest.swift @@ -97,6 +97,7 @@ private enum Constants { static let supplementaryKind = "supplementaryKind" } +@MainActor class RegisterableViewTest: QuickSpec { override class func spec() { diff --git a/ADUtilsTests/StackViewBuilderTests.swift b/ADUtilsTests/StackViewBuilderTests.swift index 296a4a1..505184f 100644 --- a/ADUtilsTests/StackViewBuilderTests.swift +++ b/ADUtilsTests/StackViewBuilderTests.swift @@ -18,6 +18,7 @@ private extension UIView { } } +@MainActor class StackViewBuilderTests: QuickSpec { override class func spec() { diff --git a/ADUtilsTests/StackViewUtilsTests.swift b/ADUtilsTests/StackViewUtilsTests.swift index 33636e0..8207d0d 100644 --- a/ADUtilsTests/StackViewUtilsTests.swift +++ b/ADUtilsTests/StackViewUtilsTests.swift @@ -11,6 +11,7 @@ import Nimble import ADUtils import UIKit +@MainActor class StackViewUtilsTests: QuickSpec { override class func spec() { diff --git a/ADUtilsTests/UIButtonBackgroundColorTests.swift b/ADUtilsTests/UIButtonBackgroundColorTests.swift index e0e633b..5b60adf 100644 --- a/ADUtilsTests/UIButtonBackgroundColorTests.swift +++ b/ADUtilsTests/UIButtonBackgroundColorTests.swift @@ -11,6 +11,7 @@ import Quick import ADUtils import SnapshotTesting +@MainActor class UIButtonBackgroundColorTests: QuickSpec { override class func spec() { diff --git a/ADUtilsTests/UIImageColorTests.swift b/ADUtilsTests/UIImageColorTests.swift index f98296b..cd231c5 100644 --- a/ADUtilsTests/UIImageColorTests.swift +++ b/ADUtilsTests/UIImageColorTests.swift @@ -11,6 +11,7 @@ import Quick import ADUtils import SnapshotTesting +@MainActor class UIImageColorTests: QuickSpec { override class func spec() { diff --git a/ADUtilsTests/UINavigationItemBackItemTests.swift b/ADUtilsTests/UINavigationItemBackItemTests.swift index abcbe4c..0c38a7c 100644 --- a/ADUtilsTests/UINavigationItemBackItemTests.swift +++ b/ADUtilsTests/UINavigationItemBackItemTests.swift @@ -10,6 +10,7 @@ import Nimble import Quick import ADUtils +@MainActor class UINavigationItemBackItemTests: QuickSpec { override class func spec() { diff --git a/ADUtilsTests/UIScreenPixelDimensionTests.swift b/ADUtilsTests/UIScreenPixelDimensionTests.swift index 679cb87..b2edfee 100644 --- a/ADUtilsTests/UIScreenPixelDimensionTests.swift +++ b/ADUtilsTests/UIScreenPixelDimensionTests.swift @@ -17,6 +17,7 @@ private class TestScreen: UIScreen { } } +@MainActor class UIScreenPixelDimensionTests: QuickSpec { override class func spec() { diff --git a/ADUtilsTests/UITableViewDefaultFooterCellsTests.swift b/ADUtilsTests/UITableViewDefaultFooterCellsTests.swift index b426f63..f9524e6 100644 --- a/ADUtilsTests/UITableViewDefaultFooterCellsTests.swift +++ b/ADUtilsTests/UITableViewDefaultFooterCellsTests.swift @@ -10,6 +10,7 @@ import Nimble import Quick import ADUtils +@MainActor class UITableViewDefaultFooterCellsTests: QuickSpec { override class func spec() { diff --git a/ADUtilsTests/UITableViewHeaderFooterViewLayoutTests.swift b/ADUtilsTests/UITableViewHeaderFooterViewLayoutTests.swift index cc85de0..d6c0cf8 100644 --- a/ADUtilsTests/UITableViewHeaderFooterViewLayoutTests.swift +++ b/ADUtilsTests/UITableViewHeaderFooterViewLayoutTests.swift @@ -59,7 +59,7 @@ extension UITableView { } } -class HeaderFooterBehavior: Behavior { +class HeaderFooterBehavior: AsyncBehavior { override class func spec(_ context: @escaping () -> ExtremityViewType) { var viewType: ExtremityViewType! @@ -68,7 +68,7 @@ class HeaderFooterBehavior: Behavior { viewType = context() } - it("should set and layout the extremity view") { + it("should set and layout the extremity view") { @MainActor in // Given let tableView = UITableView( frame: CGRect(origin: .zero, size: CGSize(width: 320.0, height: 528.0)), @@ -93,7 +93,7 @@ class HeaderFooterBehavior: Behavior { } -class UITableViewHeaderFooterViewLayoutTests: QuickSpec { +class UITableViewHeaderFooterViewLayoutTests: AsyncSpec { override class func spec() { itBehavesLike(HeaderFooterBehavior.self) { .header } diff --git a/ADUtilsTests/ViewInsertionWithMarginTest.swift b/ADUtilsTests/ViewInsertionWithMarginTest.swift index cda90b5..2d44e63 100644 --- a/ADUtilsTests/ViewInsertionWithMarginTest.swift +++ b/ADUtilsTests/ViewInsertionWithMarginTest.swift @@ -29,6 +29,7 @@ class IntrinsicContentSizeView : UIView { } } +@MainActor class ViewInsertionWithMargin: QuickSpec { override class func spec() { @@ -215,9 +216,7 @@ class ViewInsertionWithMargin: QuickSpec { if #available(iOS 11.0, *) { describe("Constrain in superview's safe area layout guide") { var viewController: UIViewController! - var view: UIView { - return viewController.view - } + var view: UIView! var subview: UIView! let insets = UIEdgeInsets(top: 10.0, left: 20.0, bottom: 30.0, right: 40.0) @@ -225,6 +224,7 @@ class ViewInsertionWithMargin: QuickSpec { viewController = UIViewController() viewController.additionalSafeAreaInsets = UIEdgeInsets(top: 40, left: 30, bottom: 20, right: 10) viewController.view = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 200.0, height: 200.0)) + view = viewController.view view.backgroundColor = UIColor.white subview = IntrinsicContentSizeView(contentSize: CGSize(width: 500, height: 500)) subview.backgroundColor = UIColor.red @@ -497,9 +497,7 @@ class ViewInsertionWithMargin: QuickSpec { describe("Constrain in superview's safe area layout guide with directional edges") { var viewController: UIViewController! - var view: UIView { - return viewController.view - } + var view: UIView! var subview: UIView! let insets = NSDirectionalEdgeInsets(top: 10.0, leading: 20.0, bottom: 30.0, trailing: 40.0) @@ -507,6 +505,7 @@ class ViewInsertionWithMargin: QuickSpec { viewController = UIViewController() viewController.additionalSafeAreaInsets = UIEdgeInsets(top: 40, left: 30, bottom: 20, right: 10) viewController.view = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 200.0, height: 200.0)) + view = viewController.view view.backgroundColor = UIColor.white subview = IntrinsicContentSizeView(contentSize: CGSize(width: 500, height: 500)) subview.backgroundColor = UIColor.red diff --git a/ADUtilsTests/__Snapshots__/DynamicFontTest/spec.DynamicFontLayoutTest.png b/ADUtilsTests/__Snapshots__/DynamicFontTest/spec.DynamicFontLayoutTest.png index e6eb57b..e51e40a 100644 Binary files a/ADUtilsTests/__Snapshots__/DynamicFontTest/spec.DynamicFontLayoutTest.png and b/ADUtilsTests/__Snapshots__/DynamicFontTest/spec.DynamicFontLayoutTest.png differ diff --git a/ADUtilsTests/__Snapshots__/DynamicFontTest/spec.DynamicFontLayoutXXLTest.png b/ADUtilsTests/__Snapshots__/DynamicFontTest/spec.DynamicFontLayoutXXLTest.png index bc7624e..606e237 100644 Binary files a/ADUtilsTests/__Snapshots__/DynamicFontTest/spec.DynamicFontLayoutXXLTest.png and b/ADUtilsTests/__Snapshots__/DynamicFontTest/spec.DynamicFontLayoutXXLTest.png differ diff --git a/ADUtilsTests/__Snapshots__/DynamicFontTest/spec.SwiftUIDynamicFontLayoutTest.png b/ADUtilsTests/__Snapshots__/DynamicFontTest/spec.SwiftUIDynamicFontLayoutTest.png index 45e1a7f..3c9ce9a 100644 Binary files a/ADUtilsTests/__Snapshots__/DynamicFontTest/spec.SwiftUIDynamicFontLayoutTest.png and b/ADUtilsTests/__Snapshots__/DynamicFontTest/spec.SwiftUIDynamicFontLayoutTest.png differ diff --git a/ADUtilsTests/__Snapshots__/DynamicFontTest/spec.SwiftUIDynamicFontLayoutXXLTest.png b/ADUtilsTests/__Snapshots__/DynamicFontTest/spec.SwiftUIDynamicFontLayoutXXLTest.png index 3cb4cb9..337cf14 100644 Binary files a/ADUtilsTests/__Snapshots__/DynamicFontTest/spec.SwiftUIDynamicFontLayoutXXLTest.png and b/ADUtilsTests/__Snapshots__/DynamicFontTest/spec.SwiftUIDynamicFontLayoutXXLTest.png differ diff --git a/CHANGELOG.md b/CHANGELOG.md index b29abb9..4a227fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +### Added +- Add Swift Concurrency support + +### Removed +- Drop support for iOS 11 and iOS 12 +- Drop support for Swift below 5.7 + ## [11.5.0] - 2023-09-19 ### Updated diff --git a/Gemfile.lock b/Gemfile.lock index d75279e..b7ccec1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -3,12 +3,12 @@ GEM specs: CFPropertyList (3.0.6) rexml - activesupport (7.0.6) + activesupport (7.0.8) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) tzinfo (~> 2.0) - addressable (2.8.4) + addressable (2.8.5) public_suffix (>= 2.0.2, < 6.0) algoliasearch (1.27.5) httpclient (~> 2.8, >= 2.8.3) @@ -16,17 +16,17 @@ GEM artifactory (3.0.15) atomos (0.1.3) aws-eventstream (1.2.0) - aws-partitions (1.782.0) - aws-sdk-core (3.176.1) + aws-partitions (1.831.0) + aws-sdk-core (3.185.0) aws-eventstream (~> 1, >= 1.0.2) aws-partitions (~> 1, >= 1.651.0) aws-sigv4 (~> 1.5) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.68.0) - aws-sdk-core (~> 3, >= 3.176.0) + aws-sdk-kms (1.72.0) + aws-sdk-core (~> 3, >= 3.184.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.127.0) - aws-sdk-core (~> 3, >= 3.176.0) + aws-sdk-s3 (1.136.0) + aws-sdk-core (~> 3, >= 3.181.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.6) aws-sigv4 (1.6.0) @@ -37,10 +37,10 @@ GEM cork nap open4 (~> 1.3) - cocoapods (1.12.1) + cocoapods (1.13.0) addressable (~> 2.8) claide (>= 1.0.2, < 2.0) - cocoapods-core (= 1.12.1) + cocoapods-core (= 1.13.0) cocoapods-deintegrate (>= 1.0.3, < 2.0) cocoapods-downloader (>= 1.6.0, < 2.0) cocoapods-plugins (>= 1.0.0, < 2.0) @@ -54,8 +54,8 @@ GEM molinillo (~> 0.8.0) nap (~> 1.0) ruby-macho (>= 2.3.0, < 3.0) - xcodeproj (>= 1.21.0, < 2.0) - cocoapods-core (1.12.1) + xcodeproj (>= 1.23.0, < 2.0) + cocoapods-core (1.13.0) activesupport (>= 5.0, < 8) addressable (~> 2.8) algoliasearch (~> 1.0) @@ -81,7 +81,7 @@ GEM concurrent-ruby (1.2.2) cork (0.3.0) colored2 (~> 3.1) - danger (9.3.1) + danger (9.3.2) claide (~> 1.0) claide-plugins (>= 0.9.2) colored2 (~> 3.1) @@ -99,7 +99,7 @@ GEM rake (> 10) thor (~> 0.19) declarative (0.0.20) - digest-crc (0.6.4) + digest-crc (0.6.5) rake (>= 12.0.0, < 14.0.0) domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) @@ -108,7 +108,7 @@ GEM escape (0.0.4) ethon (0.16.0) ffi (>= 1.15.0) - excon (0.100.0) + excon (0.104.0) faraday (1.10.3) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) @@ -140,7 +140,7 @@ GEM faraday_middleware (1.2.0) faraday (~> 1.0) fastimage (2.2.7) - fastlane (2.213.0) + fastlane (2.216.0) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.8, < 3.0.0) artifactory (~> 3.0) @@ -161,6 +161,7 @@ GEM google-apis-playcustomapp_v1 (~> 0.1) google-cloud-storage (~> 1.31) highline (~> 2.0) + http-cookie (~> 1.0.5) json (< 3.0.0) jwt (>= 2.1.0, < 3) mini_magick (>= 4.9.4, < 5.0.0) @@ -172,7 +173,7 @@ GEM security (= 0.1.3) simctl (~> 1.6.3) terminal-notifier (>= 2.0.0, < 3.0.0) - terminal-table (>= 1.4.5, < 2.0.0) + terminal-table (~> 3) tty-screen (>= 0.6.3, < 1.0.0) tty-spinner (>= 0.8.0, < 1.0.0) word_wrap (~> 1.0.0) @@ -180,16 +181,16 @@ GEM xcpretty (~> 0.3.0) xcpretty-travis-formatter (>= 0.0.3) fastlane-plugin-changelog (0.16.0) - ffi (1.15.5) + ffi (1.16.2) fourflusher (2.3.1) fuzzy_match (2.0.4) gh_inspector (1.1.3) git (1.18.0) addressable (~> 2.8) rchardet (~> 1.8) - google-apis-androidpublisher_v3 (0.45.0) + google-apis-androidpublisher_v3 (0.50.0) google-apis-core (>= 0.11.0, < 2.a) - google-apis-core (0.11.0) + google-apis-core (0.11.1) addressable (~> 2.5, >= 2.5.1) googleauth (>= 0.16.2, < 2.a) httpclient (>= 2.8.1, < 3.a) @@ -218,10 +219,9 @@ GEM google-cloud-core (~> 1.6) googleauth (>= 0.16.2, < 2.a) mini_mime (~> 1.0) - googleauth (1.6.0) + googleauth (1.8.1) faraday (>= 0.17.3, < 3.a) jwt (>= 1.4, < 3.0) - memoist (~> 0.16) multi_json (~> 1.11) os (>= 0.9, < 2.0) signet (>= 0.16, < 2.a) @@ -238,10 +238,9 @@ GEM rexml kramdown-parser-gfm (1.1.0) kramdown (~> 2.0) - memoist (0.16.2) mini_magick (4.12.0) - mini_mime (1.1.2) - minitest (5.18.1) + mini_mime (1.1.5) + minitest (5.20.0) molinillo (0.8.0) multi_json (1.15.0) multipart-post (2.3.0) @@ -265,7 +264,7 @@ GEM trailblazer-option (>= 0.1.1, < 0.2.0) uber (< 0.2.0) retriable (3.1.2) - rexml (3.2.5) + rexml (3.2.6) rouge (2.0.7) ruby-macho (2.5.1) ruby2_keywords (0.0.5) @@ -274,7 +273,7 @@ GEM addressable (>= 2.3.5) faraday (>= 0.17.3, < 3) security (0.1.3) - signet (0.17.0) + signet (0.18.0) addressable (~> 2.8) faraday (>= 0.17.5, < 3.a) jwt (>= 1.5, < 3.0) @@ -283,8 +282,8 @@ GEM CFPropertyList naturally terminal-notifier (2.0.0) - terminal-table (1.8.0) - unicode-display_width (~> 1.1, >= 1.1.1) + terminal-table (3.0.2) + unicode-display_width (>= 1.1.1, < 3) thor (0.20.3) trailblazer-option (0.1.2) tty-cursor (0.7.1) @@ -299,10 +298,10 @@ GEM unf (0.1.4) unf_ext unf_ext (0.0.8.2) - unicode-display_width (1.8.0) + unicode-display_width (2.5.0) webrick (1.8.1) word_wrap (1.0.0) - xcodeproj (1.22.0) + xcodeproj (1.23.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) diff --git a/Modules/ADUtils/DeselectableView.swift b/Modules/ADUtils/DeselectableView.swift index 74a6b3d..cdd6943 100644 --- a/Modules/ADUtils/DeselectableView.swift +++ b/Modules/ADUtils/DeselectableView.swift @@ -15,6 +15,7 @@ import UIKit * This is a protocol to factorize table and collection view selection methods */ +@MainActor protocol DeselectableView: AnyObject { var selectedIndexPaths: [IndexPath]? { get } func deselect(atIndexPath indexPath: IndexPath, animated: Bool) diff --git a/Modules/ADUtils/DynamicFont.swift b/Modules/ADUtils/DynamicFont.swift index 1abe19f..eb5ddc3 100644 --- a/Modules/ADUtils/DynamicFont.swift +++ b/Modules/ADUtils/DynamicFont.swift @@ -12,6 +12,7 @@ import SwiftUI /** The DynamicFontProvider protocol provides a font depending on parameters */ +@MainActor public protocol DynamicFontProvider { /** @@ -34,6 +35,7 @@ public protocol DynamicFontProvider { The DynamicFont provides an implemementation of DynamicFontProvider depending on a plist resource and/or a default implementation, meaning providing the system preferred font for each font */ +@MainActor public struct DynamicFont: DynamicFontProvider { private let provider: DynamicFontProvider @@ -85,6 +87,7 @@ private struct DefaultDynamicFontProvider: DynamicFontProvider { } } +@MainActor private struct CustomFontDynamicFontProvider: DynamicFontProvider { let fontDescription: FontDescription diff --git a/Modules/ADUtils/NSLayoutConstraint+Utils.swift b/Modules/ADUtils/NSLayoutConstraint+Utils.swift index 4c92345..3baae47 100644 --- a/Modules/ADUtils/NSLayoutConstraint+Utils.swift +++ b/Modules/ADUtils/NSLayoutConstraint+Utils.swift @@ -20,6 +20,7 @@ public extension NSLayoutConstraint { } public extension Collection where Element == NSLayoutConstraint { + @MainActor func activate() { guard let array = self as? [NSLayoutConstraint] else { NSLayoutConstraint.activate(self.map { $0 }) @@ -28,6 +29,7 @@ public extension Collection where Element == NSLayoutConstraint { NSLayoutConstraint.activate(array) } + @MainActor func deactivate() { guard let array = self as? [NSLayoutConstraint] else { NSLayoutConstraint.deactivate(self.map { $0 }) diff --git a/Modules/ADUtils/ProxyDetector.swift b/Modules/ADUtils/ProxyDetector.swift index b955022..1629f03 100644 --- a/Modules/ADUtils/ProxyDetector.swift +++ b/Modules/ADUtils/ProxyDetector.swift @@ -8,12 +8,12 @@ import Foundation import UIKit -public typealias ProxyDetectorWindowProvider = () -> UIWindow? +public typealias ProxyDetectorWindowProvider = @MainActor @Sendable () -> UIWindow? /// ProxyDetector check on proxy use and display notification if in use -public class ProxyDetector { +public final class ProxyDetector: Sendable { - private var windowProvider: ProxyDetectorWindowProvider + private let windowProvider: ProxyDetectorWindowProvider /** Check on proxy use and display an alert view if activated @@ -41,11 +41,13 @@ public class ProxyDetector { // (Benjamin Lavialle) 2018-01-18 Do not notify proxy on simulator return } - DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: { - // (Benjamin Lavialle) 2017-10-03 Do not weak self, we need to keep self to complete action - guard let topMostViewController = self.topMostViewController else { return } - self.notifyIfProxyActivated(in: topMostViewController) - }) + Task { [weak self] in + do { + try await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000)) + guard let topMostViewController = await self?.topMostViewController else { return } + await self?.notifyIfProxyActivated(in: topMostViewController) + } catch {} + } } /** @@ -53,11 +55,12 @@ public class ProxyDetector { - parameter viewController: the view controller presenting the alert view - note: The alert view is dismissed on its own after one second */ + @MainActor public func notifyIfProxyActivated(in viewController: UIViewController) { guard isProxyActivated else { return } let alertController = UIAlertController( title: "Proxy", - message: "HTTP PRoxy is activated (\(proxyName ?? "unknown"))", + message: "HTTP Proxy is activated (\(proxyName ?? "unknown"))", preferredStyle: .alert ) alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: nil)) @@ -78,6 +81,7 @@ public class ProxyDetector { return httpProxy ?? httpsProxy } + @MainActor private var topMostViewController: UIViewController? { var viewController = windowProvider()?.rootViewController while let presentedViewController = viewController?.presentedViewController { diff --git a/Modules/ADUtils/RegisterableView.swift b/Modules/ADUtils/RegisterableView.swift index 7808300..3e49ebb 100644 --- a/Modules/ADUtils/RegisterableView.swift +++ b/Modules/ADUtils/RegisterableView.swift @@ -45,6 +45,7 @@ public enum RegisterableView { } public extension RegisterableView { + @MainActor var nib: UINib? { switch self { case let .nib(cellClass): @@ -73,6 +74,7 @@ public extension RegisterableView { } } +@MainActor public protocol CollectionView { func register(cell: RegisterableView) func register(header: RegisterableView) diff --git a/Modules/ADUtils/StackViewBuilder.swift b/Modules/ADUtils/StackViewBuilder.swift index 0681dd4..428995e 100644 --- a/Modules/ADUtils/StackViewBuilder.swift +++ b/Modules/ADUtils/StackViewBuilder.swift @@ -7,8 +7,7 @@ import UIKit -// Update with @resultBuilder once Swift 5.4 is available -@_functionBuilder +@resultBuilder public struct UIViewBuilder { // swiftlint:disable:this convenience_type public static func buildBlock(_ views: UIView...) -> [UIView] { @@ -23,6 +22,7 @@ public struct UIViewBuilder { // swiftlint:disable:this convenience_type * - parameter distribution: The distribution of the arranged views along the stack view’s axis. * - parameter arrangedSubviews: The view builder for arranged subviews */ +@MainActor public func HStackView(spacing: CGFloat = 0, alignment: UIStackView.Alignment = .fill, distribution: UIStackView.Distribution = .fill, @@ -42,6 +42,7 @@ public func HStackView(spacing: CGFloat = 0, * - parameter distribution: The distribution of the arranged views along the stack view’s axis. * - parameter arrangedSubviews: The view builder for arranged subviews */ +@MainActor public func VStackView(spacing: CGFloat = 0, alignment: UIStackView.Alignment = .fill, distribution: UIStackView.Distribution = .fill, diff --git a/Package.swift b/Package.swift index ff820b3..01b4f98 100644 --- a/Package.swift +++ b/Package.swift @@ -5,8 +5,8 @@ import PackageDescription let package = Package( name: "ADUtils", platforms: [ - .iOS(.v10), - .tvOS(.v10) + .iOS(.v13), + .tvOS(.v13) ], products: [ .library( diff --git a/Podfile b/Podfile index 6760170..09f2363 100644 --- a/Podfile +++ b/Podfile @@ -2,7 +2,7 @@ platform :ios, '13.0' use_frameworks! target 'ADUtilsApp' do - pod 'SwiftLint', '~> 0.42.0' + pod 'SwiftLint', '~> 0.36' end target 'ADUtilsTests' do @@ -26,6 +26,11 @@ post_install do |installer| config.build_settings['EXPANDED_CODE_SIGN_IDENTITY'] = "" config.build_settings['CODE_SIGNING_REQUIRED'] = "NO" config.build_settings['CODE_SIGNING_ALLOWED'] = "NO" + + if target.name.include?("ADUtils") + # Enable complete concurrency checks + config.build_settings['SWIFT_STRICT_CONCURRENCY'] = "complete" + end end end end diff --git a/Podfile.lock b/Podfile.lock index 2f84964..97c81f4 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,10 +1,10 @@ PODS: - - ADUtils (11.5.0): - - ADUtils/objc (= 11.5.0) - - ADUtils/objc (11.5.0): + - ADUtils (12.0.0): + - ADUtils/objc (= 12.0.0) + - ADUtils/objc (12.0.0): - ADUtils/Swift - - ADUtils/Security (11.5.0) - - ADUtils/Swift (11.5.0) + - ADUtils/Security (12.0.0) + - ADUtils/Swift (12.0.0) - Nimble (12.0.1) - OCMock (3.9.1) - Quick (7.0.2) @@ -16,7 +16,7 @@ DEPENDENCIES: - Nimble (~> 12.0) - OCMock (~> 3.9) - Quick (~> 7.0) - - SwiftLint (~> 0.42.0) + - SwiftLint (~> 0.36) SPEC REPOS: trunk: @@ -30,12 +30,12 @@ EXTERNAL SOURCES: :path: "./" SPEC CHECKSUMS: - ADUtils: 8b9f64a118d331268d20e67617c7f037f6c59000 + ADUtils: 12fd5ba501a61bddfbf8ae68b335b1cfdbece393 Nimble: b279b3ca9e094508778aab5c76417be158d3ad04 OCMock: 9491e4bec59e0b267d52a9184ff5605995e74be8 Quick: efab97aca76d60be86c15daa533b2cdfbe1a74d3 SwiftLint: 4fa9579c63416865179bc416f0a92d55f009600d -PODFILE CHECKSUM: a085ff0ceae15032eb100393c6df98696f11d7af +PODFILE CHECKSUM: 6914b9ce5e1410eb72fa1a28f44d2a94b4424fca -COCOAPODS: 1.12.1 +COCOAPODS: 1.13.0