{"id":15527131,"url":"https://github.com/markbattistella/zodiackit","last_synced_at":"2025-04-23T12:23:04.148Z","repository":{"id":179839729,"uuid":"664191681","full_name":"markbattistella/ZodiacKit","owner":"markbattistella","description":"ZodiacKit provides a simple way to determine the zodiac sign for a given date.","archived":false,"fork":false,"pushed_at":"2025-04-14T14:39:06.000Z","size":608,"stargazers_count":8,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-14T15:43:38.210Z","etag":null,"topics":["astrology","hacktoberfest","ios","macos","star-sign","swift","swift-package-manager","swiftui","tvos","visionos","zodiac","zodiac-sign"],"latest_commit_sha":null,"homepage":"https://swiftpackageindex.com/markbattistella/ZodiacKit/documentation","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/markbattistella.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["markbattistella"],"custom":["https://www.paypal.me/markbattistella/5AUD","https://www.paypal.me/markbattistella/10AUD","https://www.paypal.me/markbattistella/20AUD"]}},"created_at":"2023-07-09T07:29:00.000Z","updated_at":"2025-04-14T14:38:47.000Z","dependencies_parsed_at":null,"dependency_job_id":"cbefa007-402c-4532-ba98-a02f0b91a9a4","html_url":"https://github.com/markbattistella/ZodiacKit","commit_stats":null,"previous_names":["markbattistella/zodiackit"],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/markbattistella%2FZodiacKit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/markbattistella%2FZodiacKit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/markbattistella%2FZodiacKit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/markbattistella%2FZodiacKit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/markbattistella","download_url":"https://codeload.github.com/markbattistella/ZodiacKit/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250431435,"owners_count":21429474,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["astrology","hacktoberfest","ios","macos","star-sign","swift","swift-package-manager","swiftui","tvos","visionos","zodiac","zodiac-sign"],"created_at":"2024-10-02T11:04:35.529Z","updated_at":"2025-04-23T12:23:04.130Z","avatar_url":"https://github.com/markbattistella.png","language":"Swift","funding_links":["https://github.com/sponsors/markbattistella","https://www.paypal.me/markbattistella/5AUD","https://www.paypal.me/markbattistella/10AUD","https://www.paypal.me/markbattistella/20AUD"],"categories":[],"sub_categories":[],"readme":"\u003c!-- markdownlint-disable MD033 MD041 --\u003e\n\u003cdiv align=\"center\"\u003e\n\n\u003cimg src=\"https://raw.githubusercontent.com/markbattistella/ZodiacKit/main/zodiackit-icon.png\" width=\"128\" height=\"128\" alt=\"Swift Package logo\"/\u003e\n\n# ZodiacKit\n\n![Swift Versions](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fmarkbattistella%2FZodiacKit%2Fbadge%3Ftype%3Dswift-versions)\n\n![Platforms](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fmarkbattistella%2FZodiacKit%2Fbadge%3Ftype%3Dplatforms)\n\n![Licence](https://img.shields.io/badge/Licence-MIT-white?labelColor=blue\u0026style=flat)\n\n\u003c/div\u003e\n\n`ZodiacKit` is a Swift package that determines **Western** and **Chinese** zodiac signs from a given date. It supports multiple astrological systems, provides extensive zodiac metadata, and includes strong validation with clear error handling.\n\n## Description\n\n`ZodiacKit` makes it easy to:\n\n- Fetch a user's **Western zodiac sign** based on one of four systems (Tropical, Sidereal, Equal-Length, or Astronomical).\n- Determine their **Chinese zodiac sign** based on the lunar new year.\n- Access traits, elements, ruling planets, emojis, and compatibility details.\n- Validate zodiac date ranges with automatic error detection.\n\n`ZodiacKit` includes a `ZodiacService` class and robust enums for both `Western` and `Chinese` signs.\n\n## Installation\n\nAdd `ZodiacKit` to your Swift project using Swift Package Manager.\n\n```swift\ndependencies: [\n  .package(url: \"https://github.com/markbattistella/ZodiacKit\", from: \"x.y.z\")\n]\n```\n\n\u003e [!CAUTION]\n\u003e `v1.x` to `v2.x` has some breaking changes.\n\u003e `v2.x` to `v3.x` has some breaking changes.\n\n## Usage\n\nRemember to import the `ZodiacKit` module:\n\n```swift\nimport ZodiacKit\n```\n\n### Basic Usage\n\nThe default usage of the ZodiacKit package is quite straightforward. Here's an example:\n\n```swift\nlet zodiacService = try ZodiacService()\nlet dateComponents = DateComponents(year: 1991, month: 5, day: 29)\nlet birthDate = Calendar.current.date(from: dateComponents)\n\nlet westernZodiacSign = try? zodiacService.getWesternZodiac(from: birthDate!)\nlet chineseZodiacSign = try? zodiacService.getChineseZodiac(from: birthDate!)\n\n// westernZodiacSign.name: Gemini \n// chineseZodiacSign.name: Goat\n```\n\nThis will give you the corresponding information and attributes based on the date provided.\n\nYou can then use the properties of the `Western` and `Chinese` to get information about those zodiac signs.\n\n#### Custom date ranges (Western only)\n\nIf you want to use custom zodiac date ranges instead of the defaults (for the `Western`), you can do so by passing a custom array of `Zodiac` structs during `ZodiacService` initialisation:\n\n```swift\nlet customZodiacs: [Zodiac] = [\n  Zodiac(\n    sign: .aquarius,\n    startDate: .init(day: 22, month: 1),\n    endDate: .init(day: 19, month: 2)\n  ),\n  // ...\n]\n\nlet zodiacService = try? ZodiacService(system: .custom(customZodiacs))\n```\n\n### In-app usage\n\n#### UIKit\n\nHere's an example of how to use ZodiacKit in a UIKit `UIViewController`.\n\n```swift\nimport UIKit\nimport ZodiacKit\n\nclass ViewController: UIViewController {\n    override func viewDidLoad() {\n        super.viewDidLoad()\n\n        do {\n            let zodiacService = try ZodiacService()\n            let dateComponents = DateComponents(year: 1991, month: 5, day: 29)\n            let birthDate = Calendar.current.date(from: dateComponents)\n\n            let westernZodiacSign = try zodiacService.getWesternZodiac(from: birthDate!)\n            let chineseZodiacSign = try zodiacService.getChineseZodiac(from: birthDate!)\n\n            // Use signs...\n            print(westernZodiacSign.name) // Gemini\n            print(chineseZodiacSign.name) // Goat\n        } catch {\n            print(\"Failed to get zodiac sign: \\(error)\")\n        }\n    }\n}\n```\n\n#### SwiftUI\n\nBelow is an example of using ZodiacKit in a SwiftUI view.\n\n```swift\nimport SwiftUI\nimport ZodiacKit\n\nstruct ContentView: View {\n  @StateObject private var zodiacService = ZodiacService()\n\n  @State private var westernZodiacSign: WesternZodiacSign?\n  @State private var chineseZodiacSign: ChineseZodiacSign?\n\n  var body: some View {\n    VStack {\n      if let westernSign = westernZodiacSign, let chineseSign = chineseZodiacSign {\n        Text(\"Your zodiac sign is \\(westernSign.name)\")\n        Text(\"Your Chinese Zodiac sign is: \\(chineseSign.name)\")\n      } else {\n        Text(\"Failed to get zodiac sign\")\n      }\n    }\n    .task {\n      do {\n        let dateComponents = DateComponents(year: 1991, month: 5, day: 29)\n        let birthDate = Calendar.current.date(from: dateComponents)\n        westernZodiacSign = try zodiacService.getWesternZodiac(from: birthDate!)\n        chineseZodiacSign = try zodiacService.getChineseZodiac(from: birthDate!)\n      } catch {\n        print(\"Failed to get zodiac sign: \\(error)\")\n      }\n    }\n  }\n}\n```\n\n#### AppKit\n\nHere's how to use ZodiacKit in an AppKit `NSViewController`.\n\n```swift\nimport AppKit\nimport ZodiacKit\n\nclass ViewController: NSViewController {\n    override func viewDidLoad() {\n        super.viewDidLoad()\n\n        do {\n            let zodiacService = try ZodiacService()\n            let dateComponents = DateComponents(year: 1991, month: 5, day: 29)\n            let birthDate = Calendar.current.date(from: dateComponents)\n\n            let westernZodiacSign = try zodiacService.getWesternZodiac(from: birthDate!)\n            let chineseZodiacSign = try zodiacService.getChineseZodiac(from: birthDate!)\n\n            // Use signs...\n            print(westernZodiacSign.name) // Gemini\n            print(chineseZodiacSign.name) // Goat\n        } catch {\n            print(\"Failed to get zodiac sign: \\(error)\")\n        }\n    }\n}\n```\n\n## Default Date Ranges\n\n### Tropical System\n\nThis is the one most commonly used in Western astrology.\n\n| Zodiac Sign  | Start Date   | End Date     |\n|--------------|--------------|--------------|\n| Aries        | 21 March     | 19 April     |\n| Taurus       | 20 April     | 20 May       |\n| Gemini       | 21 May       | 20 June      |\n| Cancer       | 21 June      | 22 July      |\n| Leo          | 23 July      | 22 August    |\n| Virgo        | 23 August    | 22 September |\n| Libra        | 23 September | 22 October   |\n| Scorpio      | 23 October   | 21 November  |\n| Sagittarius  | 22 November  | 21 December  |\n| Capricorn    | 22 December  | 19 January   |\n| Aquarius     | 20 January   | 18 February  |\n| Pisces       | 19 February  | 20 March     |\n\n### Sidereal System (Vedic)\n\nBased on the actual position of constellations in the sky, accounting for precession.\n\n| Zodiac Sign  | Start Date   | End Date     |\n|--------------|--------------|--------------|\n| Aries        | 14 April     | 14 May       |\n| Taurus       | 15 May       | 15 June      |\n| Gemini       | 16 June      | 16 July      |\n| Cancer       | 17 July      | 16 August    |\n| Leo          | 17 August    | 16 September |\n| Virgo        | 17 September | 16 October   |\n| Libra        | 17 October   | 15 November  |\n| Scorpio      | 16 November  | 15 December  |\n| Sagittarius  | 16 December  | 14 January   |\n| Capricorn    | 15 January   | 12 February  |\n| Aquarius     | 13 February  | 14 March     |\n| Pisces       | 15 March     | 13 April     |\n\n### Equal-Length System\n\nEach sign gets approximately 30.4 days, ignoring constellation size. Based on reconstructed Hellenistic tradition.\n\n| Zodiac Sign  | Start Date   | End Date     |\n|--------------|--------------|--------------|\n| Aries        | 16 April     | 11 May       |\n| Cetus        | 12 May       | 6 June       |\n| Taurus       | 7 June       | 2 July       |\n| Gemini       | 3 July       | 28 July      |\n| Cancer       | 29 July      | 23 August    |\n| Leo          | 24 August    | 18 September |\n| Virgo        | 19 September | 14 October   |\n| Libra        | 15 October   | 9 November   |\n| Scorpio      | 10 November  | 5 December   |\n| Ophiuchus    | 6 December   | 31 December  |\n| Sagittarius  | 1 January    | 26 January   |\n| Capricorn    | 27 January   | 21 February  |\n| Aquarius     | 22 February  | 20 March     |\n| Pisces       | 21 March     | 15 April     |\n\n### Astronomical (IAU) System\n\nThis system follows the actual star boundaries defined by the International Astronomical Union. Sign durations vary significantly.\n\n| Zodiac Sign  | Start Date   | End Date     |\n|--------------|--------------|--------------|\n| Aries        | 19 April     | 13 May       |\n| Taurus       | 14 May       | 21 June      |\n| Gemini       | 22 June      | 20 July      |\n| Cancer       | 21 July      | 10 August    |\n| Leo          | 11 August    | 16 September |\n| Virgo        | 17 September | 30 October   |\n| Libra        | 31 October   | 23 November  |\n| Scorpio      | 24 November  | 29 November  |\n| Ophiuchus    | 30 November  | 17 December  |\n| Sagittarius  | 18 December  | 20 January   |\n| Capricorn    | 21 January   | 16 February  |\n| Aquarius     | 17 February  | 11 March     |\n| Pisces       | 12 March     | 18 April     |\n\n## Validation\n\n`ZodiacKit` includes built-in validation to ensure the consistency and correctness of the provided zodiac sign data.\n\nDuring the initialisation of `ZodiacService`, the package performs several checks:\n\n1. Ensures no duplicate signs exist.\n2. Ensures all expected signs are included (internal use only).\n3. Ensures every day of the year is covered by one and only one zodiac.\n4. Ensures there are no overlapping date ranges.\n5. Ensures the date ranges are continuous from day 1 through 366.\n\n## Error Handling\n\n`ZodiacKit` performs several validations to ensure data consistency and accuracy. If an issue is found during initialisation or zodiac lookup, it throws a `ZodiacError`.\n\nBelow are the possible errors:\n\n| Error Case | Description |\n|-|-|\n| `invalidDateComponents(date:)` | The provided `Date` has missing or invalid components such as a nil day or month. |\n| `couldNotConstructLeapDate(month:day:)` | A valid leap-year date could not be formed from the given month and day, likely due to an invalid combination (e.g. February 30). |\n| `couldNotGetDayOfYear(adjustedDate:)` | The system failed to calculate the day of the year from the adjusted date, often due to invalid or out-of-range values. |\n| `duplicateZodiacsFound(duplicates:)` | There are multiple definitions for the same zodiac sign, indicating a configuration conflict. |\n| `missingZodiacs(missing:)` | Some expected zodiac signs were not defined, making the system incomplete. |\n| `missingDays(missingDays:)` | One or more calendar days aren’t assigned to any zodiac sign, causing coverage gaps. |\n| `overlappingDays(days:)` | Some days are assigned to more than one zodiac sign, violating the one-sign-per-day rule. |\n| `nonContinuousRanges` | Zodiac ranges don't form a complete, gap-free sequence from day 1 through 366. |\n| `invalidData` | Zodiac data is corrupted or couldn't be parsed correctly. |\n| `dayNumberNotFound(dayNumber:)` | No zodiac sign could be determined for a specific day of the year, typically due to misconfiguration. |\n\n### Example\n\n```swift\n/// A demo view that shows zodiac sign results from multiple Western systems and the Chinese zodiac. Users can select a date, and the relevant signs are calculated and displayed.\nstruct ZodiacDemo: View {\n  @StateObject private var serviceTropical = ZodiacService(system: .tropical)\n  @StateObject private var serviceSidereal = ZodiacService(system: .sidereal)\n  @StateObject private var serviceEqual = ZodiacService(system: .equalLength)\n  @StateObject private var serviceIAU = ZodiacService(system: .astronomicalIAU)\n\n  @State private var western: (\n    tropical: Western,\n    sidereal: Western,\n    equal: Western,\n    iau: Western\n  )? = nil\n\n  @State private var chinese: Chinese? = nil\n  @State private var selectedDate: Date = .now\n\n  var body: some View {\n    Form {\n      if let western {\n        Section(\"Western Zodiac\") {\n          LabeledContent(\"Tropical\", value: western.tropical.name)\n          LabeledContent(\"Sidereal\", value: western.sidereal.name)\n          LabeledContent(\"Equal Length\", value: western.equal.name)\n          LabeledContent(\"Astronomical IAU\", value: western.iau.name)\n        }\n      }\n\n      if let chinese {\n        Section(\"Chinese Zodiac\") {\n          LabeledContent(\"Chinese\", value: chinese.name)\n        }\n      }\n\n      DatePicker(\n        \"Select a date\",\n        selection: $selectedDate,\n        displayedComponents: [.date]\n      )\n      .datePickerStyle(.graphical)\n    }\n    .task { loadZodiac(for: selectedDate) }\n    .onChange(of: selectedDate) { _, newDate in\n      loadZodiac(for: newDate)\n    }\n    .scrollBounceBehavior(.basedOnSize)\n  }\n\n  /// Loads zodiac signs for all supported systems based on the given date.\n  /// Western signs are loaded from four different systems, and Chinese zodiac is shared across.\n  private func loadZodiac(for date: Date) {\n    Task {\n      do {\n        let tropical = try serviceTropical.getWesternZodiac(from: date)\n        let sidereal = try serviceSidereal.getWesternZodiac(from: date)\n        let equal = try serviceEqual.getWesternZodiac(from: date)\n        let iau = try serviceIAU.getWesternZodiac(from: date)\n\n        self.western = (tropical, sidereal, equal, iau)\n        self.chinese = try serviceTropical.getChineseZodiac(from: date)\n      } catch {\n        print(\"Failed to get zodiac signs: \\(error.localizedDescription)\")\n        self.western = nil\n        self.chinese = nil\n      }\n    }\n  }\n}\n```\n\n## Attributes\n\n### `WesternZodiacSign`\n\n| Attribute | Description | Example / Values |\n|-----------|-------------|------------------|\n| `name` | The capitalised name of the sign. | `\"Leo\"` |\n| `emoji` | An emoji representing the astrological glyph. | ♈️, ♉️, ♊️ |\n| `element` | The associated classical element. | `\"Fire\"`, `\"Earth\"` |\n| `elementEmoji` | Emoji for the element. | 🔥, 🌍 |\n| `characteristics` | Primary personality descriptors. | `[\"Bold\", \"Loyal\", \"Dramatic\"]` |\n| `colorHEX` | Associated HEX colour value. | `#FFD700`, `#FF4500` |\n| `color` | Platform-agnostic colour (derived from `colorHEX`). | `.init(hex: \"#FFD700\")` |\n| `rulingPlanetName` | Modern ruling planet. | `\"Sun\"`, `\"Mars\"` |\n| `traditionalRulingPlanetName` | Traditional ruling planet (if different). | `\"Earth\"` |\n| `rulingPlanetSymbol` | Planetary symbol. | `☉`, `♂` |\n| `modality` | Cardinal, Fixed, or Mutable. | `\"Fixed\"` |\n| `polarity` | Astrological polarity. | `\"Positive\"` |\n| `rulingHouse` | Governing astrological house. | `\"5th House\"` |\n| `brightestStar` | Brightest star in constellation. | `\"Regulus\"` |\n| `yinYang` | Yin or Yang classification. | `\"Yang\"` |\n| `season` | Season associated with the sign. | `\"Summer\"` |\n| `symbol` | Symbolic name of the sign. | `\"Lion\"` |\n| `symbolEmoji` | Emoji representation of symbol. | 🦁 |\n| `birthstone` | Traditional birthstone. | `\"Peridot\"` |\n| `strengths` | Strong qualities. | `[\"Creative\", \"Warm-hearted\"]` |\n| `weaknesses` | Common flaws. | `[\"Arrogant\", \"Stubborn\"]` |\n| `keyTraits` | Distilled trait summary. | `[\"Leader\", \"Passionate\"]` |\n| `bestMatches` | Highly compatible signs. | `[.aries, .sagittarius]` |\n| `averageMatches` | Neutral compatibility signs. | `[.gemini, .libra]` |\n| `conflictingMatches` | Possible tension with these signs. | `[.taurus, .scorpio]` |\n| `harmfulMatches` | Signs with strong incompatibility. | `[.capricorn, .virgo]` |\n\n### `ChineseZodiacSign`\n\n| Attribute | Description | Example / Values |\n|-----------|-------------|------------------|\n| `name` | The capitalised name of the zodiac animal. | `\"Tiger\"` |\n| `emoji` | Emoji representing the animal. | 🐅 |\n| `element` | Classical element associated with the sign. | `\"Wood\"` |\n| `elementEmoji` | Emoji for the element. | 🪵 |\n| `characteristics` | Descriptive traits. | `[\"Courageous\", \"Ambitious\"]` |\n| `colorHEX` | HEX value for sign colour. | `#FFA500` |\n| `color` | Platform-agnostic colour object. | `.init(hex: \"#FFA500\")` |\n| `rulingPlanetName` | Associated planet. | `\"Jupiter\"` |\n| `traditionalRulingPlanetName` | Optional traditional ruler. | `\"Saturn\"` |\n| `rulingPlanetSymbol` | Planetary symbol. | ♃ |\n| `modality` | Yin or Yang descriptor. | `\"Yang\"` |\n| `polarity` | Energetic polarity (Positive/Negative). | `\"Positive\"` |\n| `rulingHouse` | Seasonal/directional alignment. | `\"East\"` |\n| `brightestStar` | Notable star in the sign's constellation. | `\"Aldebaran\"` |\n| `yinYang` | Yin or Yang classification. | `\"Yang\"` |\n| `season` | Season most aligned with the sign. | `\"Spring\"` |\n| `symbol` | Animal or mythical creature. | `\"Tiger\"` |\n| `symbolEmoji` | Emoji for symbol. | 🐅 |\n| `birthstone` | Birthstone of the sign. | `\"Jade\"` |\n| `strengths` | Positive traits. | `[\"Brave\", \"Energetic\"]` |\n| `weaknesses` | Typical challenges. | `[\"Reckless\", \"Impatient\"]` |\n| `keyTraits` | Key summarised traits. | `[\"Fierce\", \"Leader\", \"Adventurous\"]` |\n| `bestMatches` | Highly compatible signs. | `[.horse, .dragon]` |\n| `averageMatches` | Moderately compatible. | `[.rat, .rooster]` |\n| `conflictingMatches` | Signs with potential conflict. | `[.monkey, .snake]` |\n| `harmfulMatches` | Traditionally avoided matches. | `[.goat, .ox]` |\n\n## Contributing\n\nContributions are more than welcome. If you find a bug or have an idea for an enhancement, please open an issue or provide a pull request. Please follow the code style present in the current code base when making contributions.\n\n## Licence\n\nThe Zodiac Signs package is released under the MIT license. See LICENCE for more information.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarkbattistella%2Fzodiackit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmarkbattistella%2Fzodiackit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarkbattistella%2Fzodiackit/lists"}