Add first bit of network functionality

This commit is contained in:
Ben Kreeger 2025-11-21 13:52:04 -06:00
parent 9ec81b4ee0
commit 36a58874e0
Signed by: kreeger
GPG Key ID: D5CF8683D4BE4B50
15 changed files with 259 additions and 5 deletions

View File

@ -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 = "<group>"; };
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 = "<group>";
};
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 */;
}

View File

@ -0,0 +1,102 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "2610"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C3BB763A2ED0C38800D56534"
BuildableName = "Meow.app"
BlueprintName = "Meow"
ReferencedContainer = "container:Meow.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C3BB76472ED0C38900D56534"
BuildableName = "MeowTests.xctest"
BlueprintName = "MeowTests"
ReferencedContainer = "container:Meow.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C3BB76512ED0C38900D56534"
BuildableName = "MeowUITests.xctest"
BlueprintName = "MeowUITests"
ReferencedContainer = "container:Meow.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C3BB763A2ED0C38800D56534"
BuildableName = "Meow.app"
BlueprintName = "Meow"
ReferencedContainer = "container:Meow.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C3BB763A2ED0C38800D56534"
BuildableName = "Meow.app"
BlueprintName = "Meow"
ReferencedContainer = "container:Meow.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -4,11 +4,29 @@
<dict>
<key>SchemeUserState</key>
<dict>
<key>Meow.xcscheme_^#shared#^_</key>
<key>App.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>C3BB763A2ED0C38800D56534</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>C3BB76472ED0C38900D56534</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>C3BB76512ED0C38900D56534</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>

8
Meow/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
.DS_Store
/.build
/Packages
xcuserdata/
DerivedData/
.swiftpm/configuration/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc

27
Meow/Package.swift Normal file
View File

@ -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"]
),
]
)

View File

@ -0,0 +1,10 @@
//
// FactsResponse.swift
// Meow
//
import Foundation
struct FactsResponse: Codable {
let data: [String]
}

View File

@ -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<T: Decodable>(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)
}
}

View File

@ -0,0 +1,12 @@
//
// MeowError.swift
// Meow
//
import Foundation
public enum MeowError: LocalizedError {
case requestError(String)
case connectionError(String)
case responseError(Int, String)
}

View File

@ -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)
}
}