diff --git a/Package.swift b/Package.swift index f42c420..b8a1729 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 5.8 +// swift-tools-version: 5.10 import PackageDescription @@ -20,7 +20,10 @@ let package = Package( .target( name: "ZodiacKit", dependencies: [], - path: "Sources" + path: "Sources", + swiftSettings: [ + .enableExperimentalFeature("StrictConcurrency") + ] ), .testTarget( name: "ZodiacKitTests", diff --git a/Sources/ZodiacKit/Defaults/DefaultZodiacs.swift b/Sources/ZodiacKit/Defaults/DefaultZodiacs.swift index 415946d..04d6918 100644 --- a/Sources/ZodiacKit/Defaults/DefaultZodiacs.swift +++ b/Sources/ZodiacKit/Defaults/DefaultZodiacs.swift @@ -8,6 +8,7 @@ import Foundation /// Contains the default set of Western Zodiac signs and their associated date ranges. /// These defaults are used by `ZodiacService` if no custom zodiacs are provided. +@ZodiacActor public let defaultZodiacs: [WesternZodiac] = [ WesternZodiac( sign: .aquarius, diff --git a/Sources/ZodiacKit/Helper/ZodiacActor.swift b/Sources/ZodiacKit/Helper/ZodiacActor.swift new file mode 100644 index 0000000..177fb9f --- /dev/null +++ b/Sources/ZodiacKit/Helper/ZodiacActor.swift @@ -0,0 +1,39 @@ +// +// Project: +// Author: Mark Battistella +// Website: https://markbattistella.com +// + +import Foundation + +/// A global actor to manage concurrency for shared resources. +/// +/// The `GlobalActor` is used to ensure that certain shared resources are accessed +/// in a thread-safe manner. By isolating code to this actor, we prevent data races +/// and ensure safe access to globally accessible variables. +/// +/// Usage: +/// ```swift +/// @ZodiacActor +/// public var sharedResource: SomeType = ... +/// +/// @ZodiacActor +/// public func performThreadSafeOperation() { +/// // Code that interacts with sharedResource +/// } +/// ``` +/// +/// You can also use `await` to call functions isolated to this actor: +/// ```swift +/// await GlobalActor.run { +/// performThreadSafeOperation() +/// } +/// ``` +/// +/// - Note: Make sure to use the `@ZodiacActor` attribute to isolate global variables or functions. +@globalActor +public actor ZodiacActor { + + /// The shared instance of the global actor. + public static let shared = ZodiacActor() +} diff --git a/Sources/ZodiacKit/Model/WesternZodiac.swift b/Sources/ZodiacKit/Model/WesternZodiac.swift index aaa53fb..ad87926 100644 --- a/Sources/ZodiacKit/Model/WesternZodiac.swift +++ b/Sources/ZodiacKit/Model/WesternZodiac.swift @@ -7,7 +7,7 @@ import Foundation /// Represents a zodiac sign with its date range -public struct WesternZodiac: Codable { +public struct WesternZodiac: Codable, Sendable { /// The zodiac sign public let sign: WesternZodiacSign @@ -19,7 +19,7 @@ public struct WesternZodiac: Codable { public let endDate: DateRange /// Represents a date with day and month only - public struct DateRange: Codable { + public struct DateRange: Codable, Sendable{ /// The day of the date range public let day: Int diff --git a/Sources/ZodiacKit/Model/WesternZodiacSign.swift b/Sources/ZodiacKit/Model/WesternZodiacSign.swift index 308cd93..e6fbd42 100644 --- a/Sources/ZodiacKit/Model/WesternZodiacSign.swift +++ b/Sources/ZodiacKit/Model/WesternZodiacSign.swift @@ -13,7 +13,7 @@ public typealias Colour = NSColor #endif /// Represents Western zodiac signs -public enum WesternZodiacSign: String, ZodiacSign { +public enum WesternZodiacSign: String, ZodiacSign, Sendable { /// Available list of Western zodiac signs case aquarius, aries, cancer, capricorn, gemini, leo, libra, diff --git a/Sources/ZodiacKit/Services/ZodiacService.swift b/Sources/ZodiacKit/Services/ZodiacService.swift index 607ab61..2f7c85c 100644 --- a/Sources/ZodiacKit/Services/ZodiacService.swift +++ b/Sources/ZodiacKit/Services/ZodiacService.swift @@ -7,6 +7,7 @@ import Foundation /// A service for determining a person's Western and Chinese zodiac signs based on their birthdate. +@ZodiacActor public struct ZodiacService { /// A collection of WesternZodiac objects representing the zodiac signs. @@ -26,12 +27,12 @@ public struct ZodiacService { /// - Parameter zodiacs: An array of WesternZodiac objects. If not provided, /// a default set is used. /// - Throws: An error if the provided zodiacs are invalid. - public init(zodiacs: [WesternZodiac] = defaultZodiacs) throws { - try validator.validate(zodiacs: zodiacs) - self.zodiacs = zodiacs - self.dayOfYearToZodiac = ZodiacService.mapZodiacsToDaysOfYear(zodiacs: zodiacs, - validator: validator) - } + public init(zodiacs: [WesternZodiac] = defaultZodiacs) throws { + try validator.validate(zodiacs: zodiacs) + self.zodiacs = zodiacs + self.dayOfYearToZodiac = ZodiacService.mapZodiacsToDaysOfYear(zodiacs: zodiacs, + validator: validator) + } /// Maps zodiac periods to days of the year. /// diff --git a/Tests/ZodiacKitTests/Dates.swift b/Tests/ZodiacKitTests/Dates.swift index 2fa393d..e69c636 100644 --- a/Tests/ZodiacKitTests/Dates.swift +++ b/Tests/ZodiacKitTests/Dates.swift @@ -12,6 +12,7 @@ extension ZodiacKitTests { /// Tests that the service correctly identifies the Western Zodiac sign based on the /// start date of each sign's period. + @ZodiacActor func testStartDateReturnsCorrectSign() { let testCases: [(day: Int, month: Int, expectedSign: WesternZodiacSign)] = [ // Each tuple represents the boundary start date for a zodiac sign and the expected sign. @@ -37,6 +38,7 @@ extension ZodiacKitTests { /// Tests that the service correctly identifies the Western Zodiac sign based on the /// end date of each sign's period. + @ZodiacActor func testEndDateReturnsCorrectSign() { let testCases: [(day: Int, month: Int, expectedSign: WesternZodiacSign)] = [ // Each tuple represents the boundary end date for a zodiac sign and the expected sign. @@ -63,6 +65,7 @@ extension ZodiacKitTests { /// Tests that the service correctly identifies the Western Zodiac sign for dates falling /// in the middle of each sign's period. + @ZodiacActor func testDateInMiddleReturnsCorrectSign() { let orderedSigns: [WesternZodiacSign] = [ .aquarius, .pisces, .aries, .taurus, .gemini, diff --git a/Tests/ZodiacKitTests/Setup.swift b/Tests/ZodiacKitTests/Setup.swift index 546c2cf..a153802 100644 --- a/Tests/ZodiacKitTests/Setup.swift +++ b/Tests/ZodiacKitTests/Setup.swift @@ -23,6 +23,7 @@ class ZodiacKitTests: XCTestCase { /// Sets up necessary instances before each test is run. This includes initializing the /// `validator` and `service` variables. + @ZodiacActor override func setUp() { super.setUp() self.validator = DateValidator()