diff --git a/Meow/Assets.xcassets/AccentColor.colorset/Contents.json b/App/Assets.xcassets/AccentColor.colorset/Contents.json similarity index 100% rename from Meow/Assets.xcassets/AccentColor.colorset/Contents.json rename to App/Assets.xcassets/AccentColor.colorset/Contents.json diff --git a/Meow/Assets.xcassets/AppIcon.appiconset/Contents.json b/App/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from Meow/Assets.xcassets/AppIcon.appiconset/Contents.json rename to App/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/Meow/Assets.xcassets/Contents.json b/App/Assets.xcassets/Contents.json similarity index 100% rename from Meow/Assets.xcassets/Contents.json rename to App/Assets.xcassets/Contents.json diff --git a/Meow/ContentView.swift b/App/ContentView.swift similarity index 100% rename from Meow/ContentView.swift rename to App/ContentView.swift diff --git a/Meow/MeowApp.swift b/App/MeowApp.swift similarity index 100% rename from Meow/MeowApp.swift rename to App/MeowApp.swift diff --git a/Meow.xcodeproj/project.pbxproj b/Meow.xcodeproj/project.pbxproj index 99b5784..c85a586 100644 --- a/Meow.xcodeproj/project.pbxproj +++ b/Meow.xcodeproj/project.pbxproj @@ -6,6 +6,10 @@ objectVersion = 77; objects = { +/* Begin PBXBuildFile section */ + C3A5E8D52ED0C60D002DB10E /* Meow in Frameworks */ = {isa = PBXBuildFile; productRef = C3A5E8D42ED0C60D002DB10E /* Meow */; }; +/* End PBXBuildFile section */ + /* Begin PBXContainerItemProxy section */ C3BB76492ED0C38900D56534 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; @@ -24,15 +28,16 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + C3A5E8D22ED0C5E8002DB10E /* Meow */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Meow; sourceTree = ""; }; C3BB763B2ED0C38800D56534 /* Meow.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Meow.app; sourceTree = BUILT_PRODUCTS_DIR; }; C3BB76482ED0C38900D56534 /* MeowTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MeowTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; C3BB76522ED0C38900D56534 /* MeowUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MeowUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ - C3BB763D2ED0C38800D56534 /* Meow */ = { + C3BB763D2ED0C38800D56534 /* App */ = { isa = PBXFileSystemSynchronizedRootGroup; - path = Meow; + path = App; sourceTree = ""; }; C3BB764B2ED0C38900D56534 /* MeowTests */ = { @@ -52,6 +57,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + C3A5E8D52ED0C60D002DB10E /* Meow in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -75,7 +81,8 @@ C3BB76322ED0C38800D56534 = { isa = PBXGroup; children = ( - C3BB763D2ED0C38800D56534 /* Meow */, + C3A5E8D22ED0C5E8002DB10E /* Meow */, + C3BB763D2ED0C38800D56534 /* App */, C3BB764B2ED0C38900D56534 /* MeowTests */, C3BB76552ED0C38900D56534 /* MeowUITests */, C3BB763C2ED0C38800D56534 /* Products */, @@ -108,10 +115,11 @@ dependencies = ( ); fileSystemSynchronizedGroups = ( - C3BB763D2ED0C38800D56534 /* Meow */, + C3BB763D2ED0C38800D56534 /* App */, ); name = Meow; packageProductDependencies = ( + C3A5E8D42ED0C60D002DB10E /* Meow */, ); productName = Meow; productReference = C3BB763B2ED0C38800D56534 /* Meow.app */; @@ -578,6 +586,13 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCSwiftPackageProductDependency section */ + C3A5E8D42ED0C60D002DB10E /* Meow */ = { + isa = XCSwiftPackageProductDependency; + productName = Meow; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = C3BB76332ED0C38800D56534 /* Project object */; } diff --git a/Meow.xcodeproj/project.xcworkspace/xcuserdata/bkreeger.xcuserdatad/UserInterfaceState.xcuserstate b/Meow.xcodeproj/project.xcworkspace/xcuserdata/bkreeger.xcuserdatad/UserInterfaceState.xcuserstate index 11a7778..aeb479c 100644 Binary files a/Meow.xcodeproj/project.xcworkspace/xcuserdata/bkreeger.xcuserdatad/UserInterfaceState.xcuserstate and b/Meow.xcodeproj/project.xcworkspace/xcuserdata/bkreeger.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Meow.xcodeproj/xcshareddata/xcschemes/App.xcscheme b/Meow.xcodeproj/xcshareddata/xcschemes/App.xcscheme new file mode 100644 index 0000000..17fb4a8 --- /dev/null +++ b/Meow.xcodeproj/xcshareddata/xcschemes/App.xcscheme @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Meow.xcodeproj/xcuserdata/bkreeger.xcuserdatad/xcschemes/xcschememanagement.plist b/Meow.xcodeproj/xcuserdata/bkreeger.xcuserdatad/xcschemes/xcschememanagement.plist index 03758d5..d3f60a2 100644 --- a/Meow.xcodeproj/xcuserdata/bkreeger.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/Meow.xcodeproj/xcuserdata/bkreeger.xcuserdatad/xcschemes/xcschememanagement.plist @@ -4,11 +4,29 @@ SchemeUserState - Meow.xcscheme_^#shared#^_ + App.xcscheme_^#shared#^_ orderHint 0 + SuppressBuildableAutocreation + + C3BB763A2ED0C38800D56534 + + primary + + + C3BB76472ED0C38900D56534 + + primary + + + C3BB76512ED0C38900D56534 + + primary + + + diff --git a/Meow/.gitignore b/Meow/.gitignore new file mode 100644 index 0000000..0023a53 --- /dev/null +++ b/Meow/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/Meow/Package.swift b/Meow/Package.swift new file mode 100644 index 0000000..f410d81 --- /dev/null +++ b/Meow/Package.swift @@ -0,0 +1,27 @@ +// swift-tools-version: 6.2 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "Meow", + platforms: [.iOS(.v18), .macOS(.v15)], + products: [ + // Products define the executables and libraries a package produces, making them visible to other packages. + .library( + name: "Meow", + targets: ["Meow"] + ), + ], + targets: [ + // Targets are the basic building blocks of a package, defining a module or a test suite. + // Targets can depend on other targets in this package and products from dependencies. + .target( + name: "Meow" + ), + .testTarget( + name: "MeowTests", + dependencies: ["Meow"] + ), + ] +) diff --git a/Meow/Sources/Meow/FactsResponse.swift b/Meow/Sources/Meow/FactsResponse.swift new file mode 100644 index 0000000..3e8ae97 --- /dev/null +++ b/Meow/Sources/Meow/FactsResponse.swift @@ -0,0 +1,10 @@ +// +// FactsResponse.swift +// Meow +// + +import Foundation + +struct FactsResponse: Codable { + let data: [String] +} diff --git a/Meow/Sources/Meow/Meow.swift b/Meow/Sources/Meow/Meow.swift new file mode 100644 index 0000000..0a53ca7 --- /dev/null +++ b/Meow/Sources/Meow/Meow.swift @@ -0,0 +1,43 @@ +// +// Meow.swift +// Meow +// + +import Foundation + +public struct Meow { + let baseURL: URL + let session: URLSession + + public init(baseURL: URL, session: URLSession = .shared) { + self.baseURL = baseURL + self.session = session + } + + public func getFacts() async throws -> [String] { + let request = try generateRequest(path: "/") + let response: FactsResponse = try await decodeRequest(request: request) + return response.data + } + + // MARK: - Private functionality + + private func generateRequest(path: String) throws -> URLRequest { + guard let url = URL(string: path, relativeTo: baseURL) else { + throw MeowError.requestError("Couldn't generate URL from path: \(path), baseURL: \(baseURL)") + } + return URLRequest(url: url) + } + + private func decodeRequest(request: URLRequest) async throws -> T { + let (data, response) = try await session.data(for: request) + guard let response = response as? HTTPURLResponse else { + throw MeowError.connectionError("Couldn't get HTTP response from request \(request)") + } + guard response.statusCode < 300 else { + let body = String(data: data, encoding: .utf8) + throw MeowError.responseError(response.statusCode, body ?? "unknown error") + } + return try JSONDecoder().decode(T.self, from: data) + } +} diff --git a/Meow/Sources/Meow/MeowError.swift b/Meow/Sources/Meow/MeowError.swift new file mode 100644 index 0000000..4db7cc7 --- /dev/null +++ b/Meow/Sources/Meow/MeowError.swift @@ -0,0 +1,12 @@ +// +// MeowError.swift +// Meow +// + +import Foundation + +public enum MeowError: LocalizedError { + case requestError(String) + case connectionError(String) + case responseError(Int, String) +} diff --git a/Meow/Tests/MeowTests/MeowTests.swift b/Meow/Tests/MeowTests/MeowTests.swift new file mode 100644 index 0000000..1456482 --- /dev/null +++ b/Meow/Tests/MeowTests/MeowTests.swift @@ -0,0 +1,19 @@ +// +// MeowTests.swift +// MeowTests +// + +import Foundation +import Testing +@testable import Meow + +@Suite("Meow Tests") +struct MeowTests { + private let baseURL = URL(string: "https://meow.meow")! + + @Test + func constructor() async throws { + let instance = Meow(baseURL: baseURL) + #expect(instance.baseURL == baseURL) + } +}