diff --git a/.swiftpm/xcode/package.xcworkspace/xcuserdata/nmariniello.xcuserdatad/UserInterfaceState.xcuserstate b/.swiftpm/xcode/package.xcworkspace/xcuserdata/nmariniello.xcuserdatad/UserInterfaceState.xcuserstate index a38753d..89828e8 100644 Binary files a/.swiftpm/xcode/package.xcworkspace/xcuserdata/nmariniello.xcuserdatad/UserInterfaceState.xcuserstate and b/.swiftpm/xcode/package.xcworkspace/xcuserdata/nmariniello.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Package.swift b/Package.swift index 6e187d0..a60e4a0 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.3 +// swift-tools-version:5.9 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription diff --git a/Sources/SunKit/Angle.swift b/Sources/SunKit/Angle.swift index e82fa68..10c5c9a 100644 --- a/Sources/SunKit/Angle.swift +++ b/Sources/SunKit/Angle.swift @@ -18,39 +18,40 @@ import Foundation -public struct Angle : Equatable { +public struct Angle: Equatable, Hashable, Codable, Sendable { public static var zero: Angle = .init() - public init() { _radians = 0 } + public init() { + _radians = 0 + } - public init(radians: Double) { _radians = radians } + public init(radians: Double) { + _radians = radians + } - public init(degrees: Double) { _radians = degrees * Double.pi / 180.0 } + public init(degrees: Double) { + _radians = degrees * Double.pi / 180.0 + } public var degrees: Double { - get { return _radians * 180.0 / Double.pi } + get { _radians * 180.0 / Double.pi } set { _radians = newValue * Double.pi / 180 } } public var radians: Double { - get { return _radians } + get { _radians } set { _radians = newValue } } - - public static func ==(lhs: Angle, rhs: Angle) -> Bool { - return lhs.radians == rhs.radians - } - private var _radians: Double - public static func degrees(_ value:Double) -> Angle{ - return .init(degrees: value) + public static func degrees(_ value: Double) -> Angle { + .init(degrees: value) } - public static func radians(_ value:Double) -> Angle{ - return .init(radians: value) + public static func radians(_ value: Double) -> Angle { + .init(radians: value) } } diff --git a/Sources/SunKit/DMS.swift b/Sources/SunKit/DMS.swift index 0c634a9..ee8fb55 100644 --- a/Sources/SunKit/DMS.swift +++ b/Sources/SunKit/DMS.swift @@ -19,7 +19,7 @@ import Foundation /// DMS format to express angles -public struct DMS: Equatable{ +public struct DMS: Equatable, Hashable, Codable, Sendable { public var degrees: Double public var minutes: Double diff --git a/Sources/SunKit/EclipticCoordinates.swift b/Sources/SunKit/EclipticCoordinates.swift index 834b65c..85c65c1 100644 --- a/Sources/SunKit/EclipticCoordinates.swift +++ b/Sources/SunKit/EclipticCoordinates.swift @@ -19,7 +19,7 @@ import Foundation -public struct EclipticCoordinates { +public struct EclipticCoordinates: Equatable, Hashable, Codable, Sendable { public static let obliquityOfTheEcliptic: Angle = .init(degrees: 23.439292) diff --git a/Sources/SunKit/EquatorialCoordinates.swift b/Sources/SunKit/EquatorialCoordinates.swift index c954af3..e235ad0 100644 --- a/Sources/SunKit/EquatorialCoordinates.swift +++ b/Sources/SunKit/EquatorialCoordinates.swift @@ -18,9 +18,7 @@ import Foundation - - -public struct EquatorialCoordinates{ +public struct EquatorialCoordinates: Equatable, Hashable, Codable, Sendable { public private(set) var rightAscension: Angle? // rightAscension.degrees refers to h format public private(set) var declination: Angle //delta diff --git a/Sources/SunKit/HMS.swift b/Sources/SunKit/HMS.swift index d1c00d8..291b319 100644 --- a/Sources/SunKit/HMS.swift +++ b/Sources/SunKit/HMS.swift @@ -20,7 +20,7 @@ import Foundation /// Time expressed in HMS format -public struct HMS: Equatable{ +public struct HMS: Equatable, Hashable, Codable, Sendable { public var hours: Double public var minutes: Double diff --git a/Sources/SunKit/HorizonCoordinates.swift b/Sources/SunKit/HorizonCoordinates.swift index c3cb33b..fd43998 100644 --- a/Sources/SunKit/HorizonCoordinates.swift +++ b/Sources/SunKit/HorizonCoordinates.swift @@ -18,7 +18,7 @@ import Foundation -public struct HorizonCoordinates{ +public struct HorizonCoordinates: Equatable, Hashable, Codable, Sendable { public var altitude: Angle public var azimuth: Angle diff --git a/Sources/SunKit/Sun.swift b/Sources/SunKit/Sun.swift index ddff7ca..c8a4672 100644 --- a/Sources/SunKit/Sun.swift +++ b/Sources/SunKit/Sun.swift @@ -19,7 +19,8 @@ import Foundation import CoreLocation -public class Sun { +public struct Sun: Identifiable, Sendable { + public let id: UUID = UUID() /*-------------------------------------------------------------------- Public get Variables @@ -27,7 +28,7 @@ public class Sun { public private(set) var location: CLLocation public private(set) var timeZone: TimeZone - public private(set) var date: Date = Date() + public private(set) var date: Date /*-------------------------------------------------------------------- Sun Events during the day @@ -70,22 +71,22 @@ public class Sun { ///Date at which morning Blue Hour starts. Sun at -6 degrees elevation = civil dusk public var morningBlueHourStart: Date{ - return civilDawn + civilDawn } ///Date at which morning Blue Hour ends. Sun at -4 degrees elevation = morning golden hour start public var morningBlueHourEnd: Date { - return morningGoldenHourStart + morningGoldenHourStart } ///Date at which evening Blue Hour starts. Sun at -4 degrees elevation = evening golden hour end public var eveningBlueHourStart: Date{ - return eveningGoldenHourEnd + eveningGoldenHourEnd } ///Date at which morning Blue Hour ends. Sun at -6 degrees elevation = Civil Dawn public var eveningBlueHourEnd: Date { - return civilDusk + civilDusk } @@ -102,12 +103,12 @@ public class Sun { // Sun azimuth for (Location,Date) in Self public var azimuth: Angle { - return self.sunHorizonCoordinates.azimuth + sunHorizonCoordinates.azimuth } // Sun altitude for (Location,Date) in Self public var altitude: Angle { - return self.sunHorizonCoordinates.altitude + sunHorizonCoordinates.altitude } public private(set) var sunEquatorialCoordinates: EquatorialCoordinates = .init(declination: .zero) @@ -132,12 +133,12 @@ public class Sun { /// Longitude of location public var longitude: Angle { - return .init(degrees: self.location.coordinate.longitude) + .init(degrees: location.coordinate.longitude) } /// Latitude of Location public var latitude: Angle { - return .init(degrees: self.location.coordinate.latitude) + .init(degrees: location.coordinate.latitude) } /// Returns daylight time in seconds @@ -162,9 +163,9 @@ public class Sun { /// Returns True if is night public var isNight: Bool { if !isCircumPolar { - return date < sunrise || date > sunset + date < sunrise || date > sunset } else { - return isAlwaysNight + isAlwaysNight } } @@ -219,23 +220,27 @@ public class Sun { /// Returns true if for (Location,Date) is always daylight (e.g Tromso city in Winter) public var isAlwaysNight: Bool { - return sunset - TWO_HOURS_IN_SECONDS < sunrise + sunset - TWO_HOURS_IN_SECONDS < sunrise } /*-------------------------------------------------------------------- Initializers *-------------------------------------------------------------------*/ - public init(location: CLLocation,timeZone: Double) { - let timeZoneSeconds: Int = Int(timeZone * SECONDS_IN_ONE_HOUR) - self.timeZone = TimeZone.init(secondsFromGMT: timeZoneSeconds) ?? .current + public init(location: CLLocation, timeZone: TimeZone, date: Date = Date()) { + self.timeZone = timeZone self.location = location + self.date = date + refresh() } - public init(location: CLLocation,timeZone: TimeZone) { - self.timeZone = timeZone + init(location: CLLocation, timeZone: Double, date: Date = Date()) { + let timeZoneSeconds: Int = Int(timeZone * SECONDS_IN_ONE_HOUR) + self.timeZone = TimeZone.init(secondsFromGMT: timeZoneSeconds) ?? .current self.location = location + self.date = date + refresh() } @@ -247,7 +252,7 @@ public class Sun { Changing date of interest *-------------------------------------------------------------------*/ - public func setDate(_ newDate: Date) { + public mutating func setDate(_ newDate: Date) { let newDay = calendar.dateComponents([.day,.month,.year], from: newDate) let oldDay = calendar.dateComponents([.day,.month,.year], from: date) @@ -266,7 +271,7 @@ public class Sun { /// - Parameters: /// - newLocation: New location /// - newTimeZone: New timezone for the given location. Is highly recommanded to pass a Timezone initialized via .init(identifier: ) method - public func setLocation(_ newLocation: CLLocation,_ newTimeZone: TimeZone) { + public mutating func setLocation(_ newLocation: CLLocation,_ newTimeZone: TimeZone) { timeZone = newTimeZone location = newLocation refresh() @@ -274,7 +279,7 @@ public class Sun { /// Changing only the location /// - Parameter newLocation: New Location - public func setLocation(_ newLocation: CLLocation) { + public mutating func setLocation(_ newLocation: CLLocation) { location = newLocation refresh() } @@ -284,7 +289,7 @@ public class Sun { /// - Parameters: /// - newLocation: New Location /// - newTimeZone: New Timezone express in Double. For timezones which differs of half an hour add 0.5, - public func setLocation(_ newLocation: CLLocation,_ newTimeZone: Double) { + public mutating func setLocation(_ newLocation: CLLocation,_ newTimeZone: Double) { let timeZoneSeconds: Int = Int(newTimeZone * SECONDS_IN_ONE_HOUR) timeZone = TimeZone(secondsFromGMT: timeZoneSeconds) ?? .current location = newLocation @@ -298,14 +303,14 @@ public class Sun { /// Changing only the timezone. /// - Parameter newTimeZone: New Timezone - public func setTimeZone(_ newTimeZone: TimeZone) { + public mutating func setTimeZone(_ newTimeZone: TimeZone) { timeZone = newTimeZone refresh() } /// Is highly recommanded to use the other method to change timezone. This will be kept only for backwards retrocompatibility. /// - Parameter newTimeZone: New Timezone express in Double. For timezones which differs of half an hour add 0.5, - public func setTimeZone(_ newTimeZone: Double) { + public mutating func setTimeZone(_ newTimeZone: Double) { let timeZoneSeconds: Int = Int(newTimeZone * SECONDS_IN_ONE_HOUR) timeZone = TimeZone(secondsFromGMT: timeZoneSeconds) ?? .current refresh() @@ -431,7 +436,7 @@ public class Sun { /// Compute civil dusk and Civil Dawn time /// /// - Parameter needToComputeAgainSunEvents: True if Sunrise,Sunset and all the others daily sun events have to be computed. - private func refresh(needToComputeSunEvents: Bool = true) { + private mutating func refresh(needToComputeSunEvents: Bool = true) { updateSunCoordinates() if(needToComputeSunEvents){ @@ -484,7 +489,7 @@ public class Sun { /// Updates Horizon coordinates, Ecliptic coordinates and Equatorial coordinates of the Sun - private func updateSunCoordinates() { + private mutating func updateSunCoordinates() { //Step1: //Convert LCT to UT, GST, and LST times and adjust the date if needed let gstHMS = uT2GST(self.date) @@ -832,3 +837,19 @@ public class Sun { } } + +extension Sun: Equatable { + public static func == (lhs: Sun, rhs: Sun) -> Bool { + lhs.location == rhs.location && + lhs.timeZone == rhs.timeZone && + lhs.date == rhs.date + } +} + +extension Sun: Hashable { + public func hash(into hasher: inout Hasher) { + hasher.combine(location) + hasher.combine(timeZone) + hasher.combine(date) + } +} diff --git a/Tests/SunKitTests/UT_Sun.swift b/Tests/SunKitTests/UT_Sun.swift index 4af9635..194e324 100644 --- a/Tests/SunKitTests/UT_Sun.swift +++ b/Tests/SunKitTests/UT_Sun.swift @@ -98,13 +98,13 @@ final class UT_Sun: XCTestCase { var expectedSolarNoon = createDateCustomTimeZone(day: 19, month: 11, year: 2022, hour: 11, minute: 48, seconds: 21,timeZone: timeZoneUnderTest) - var expectednauticalDawn = createDateCustomTimeZone(day: 19, month: 11, year: 2022, hour: 5, minute: 52, seconds: 21,timeZone: timeZoneUnderTest) + let expectednauticalDawn = createDateCustomTimeZone(day: 19, month: 11, year: 2022, hour: 5, minute: 52, seconds: 21,timeZone: timeZoneUnderTest) - var expectednauticalDusk = createDateCustomTimeZone(day: 19, month: 11, year: 2022, hour: 17, minute: 44, seconds: 45,timeZone: timeZoneUnderTest) + let expectednauticalDusk = createDateCustomTimeZone(day: 19, month: 11, year: 2022, hour: 17, minute: 44, seconds: 45,timeZone: timeZoneUnderTest) - var expectedastronomicalDawn = createDateCustomTimeZone(day: 19, month: 11, year: 2022, hour: 5, minute: 19, seconds: 25,timeZone: timeZoneUnderTest) + let expectedastronomicalDawn = createDateCustomTimeZone(day: 19, month: 11, year: 2022, hour: 5, minute: 19, seconds: 25,timeZone: timeZoneUnderTest) - var expectedastronomicalDusk = createDateCustomTimeZone(day: 19, month: 11, year: 2022, hour: 18, minute: 17, seconds: 20,timeZone: timeZoneUnderTest) + let expectedastronomicalDusk = createDateCustomTimeZone(day: 19, month: 11, year: 2022, hour: 18, minute: 17, seconds: 20,timeZone: timeZoneUnderTest) //Step4: Check if the output are close to the expected ones @@ -349,7 +349,7 @@ final class UT_Sun: XCTestCase { let location: CLLocation = .init(latitude: 34.052235, longitude: -118.243683) - let sun = Sun.init(location: location, timeZone: pst) + var sun = Sun.init(location: location, timeZone: pst) sun.setDate(SunKit.createDateCustomTimeZone(day: 11, month: 3, year: 2023, hour: 22, minute: 00, seconds: 00, timeZone: pst)) XCTAssertEqual(sun.sunrise.toString(pst), "03/11, 06:08") @@ -372,7 +372,7 @@ final class UT_Sun: XCTestCase { //Step1: Creating sun instance in Naples and with timezone +1 (No daylight saving) let timeZoneUnderTest: TimeZone = .init(secondsFromGMT: UT_Sun.timeZoneNaples * Int(SECONDS_IN_ONE_HOUR)) ?? .current let timeZoneDaylightSaving: TimeZone = .init(secondsFromGMT: UT_Sun.timeZoneNaplesDaylightSaving * Int(SECONDS_IN_ONE_HOUR)) ?? .current - let sunUnderTest = Sun.init(location: UT_Sun.naplesLocation, timeZone: timeZoneUnderTest) + var sunUnderTest = Sun.init(location: UT_Sun.naplesLocation, timeZone: timeZoneUnderTest) //Step2: Setting 19/01/22 17:31 as date. (No daylight saving) let dateUnderTest = createDateCustomTimeZone(day: 19, month: 1, year: 2022, hour: 17, minute: 31, seconds: 00,timeZone: timeZoneUnderTest) @@ -406,7 +406,7 @@ final class UT_Sun: XCTestCase { //Step1: Creating Sun instance in Naples and with timezone +1 let timeZoneNaples: TimeZone = .init(secondsFromGMT: UT_Sun.timeZoneNaples * Int(SECONDS_IN_ONE_HOUR)) ?? .current - let sunUnderTest = Sun.init(location: UT_Sun.naplesLocation, timeZone: timeZoneNaples) + var sunUnderTest = Sun.init(location: UT_Sun.naplesLocation, timeZone: timeZoneNaples) //Step2: Setting 19/11/22 20:00 as date. (No daylight saving) let dateUnderTest = createDateCustomTimeZone(day: 19, month: 11, year: 2022, hour: 20, minute: 00, seconds: 00,timeZone: timeZoneNaples) @@ -430,7 +430,7 @@ final class UT_Sun: XCTestCase { // Performance of setDate function that will refresh all the sun variables //Step1: Creating sun instance in Naples with timezone +1 - let sunUnderTest = Sun.init(location: UT_Sun.naplesLocation, timeZone: Double(UT_Sun.timeZoneNaples)) + var sunUnderTest = Sun.init(location: UT_Sun.naplesLocation, timeZone: Double(UT_Sun.timeZoneNaples)) //Step2: Setting 19/11/22 20:00 as date. let dateUnderTest = createDateCurrentTimeZone(day: 19, month: 11, year: 2022, hour: 20, minute: 00, seconds: 00)