Skip to content

Commit

Permalink
Merge pull request #79 from 417-72KI/config-folder-composition
Browse files Browse the repository at this point in the history
Change folder composition in config directory
  • Loading branch information
417-72KI authored Oct 10, 2023
2 parents 4de41c0 + c93a9bc commit 8c3c829
Show file tree
Hide file tree
Showing 37 changed files with 457 additions and 66 deletions.
15 changes: 15 additions & 0 deletions Demo/BuildConfigSwiftDemo/Sources/Data/BaseEntities.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//
// BaseEntities.swift
// BuildConfigSwiftDemo
//
// Created by 417.72KI on 2023/10/05.
// Copyright © 2023 417.72KI. All rights reserved.
//

import Foundation

protocol Request: Encodable, Hashable {
}

protocol Response: Decodable, Hashable {
}
14 changes: 14 additions & 0 deletions Demo/BuildConfigSwiftDemo/Sources/Data/LoginRequest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// LoginRequest.swift
// BuildConfigSwiftDemo
//
// Created by 417.72KI on 2023/10/05.
// Copyright © 2023 417.72KI. All rights reserved.
//

import Foundation

struct LoginRequest: Request {
var id: String
var password: String
}
14 changes: 14 additions & 0 deletions Demo/BuildConfigSwiftDemo/Sources/Data/LoginResponse.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// LoginResponse.swift
// BuildConfigSwiftDemo
//
// Created by 417.72KI on 2023/10/05.
// Copyright © 2023 417.72KI. All rights reserved.
//

import Foundation

struct LoginResponse: Response {
var accessToken: String
var refreshToken: String
}
19 changes: 19 additions & 0 deletions Demo/BuildConfigSwiftDemo/Sources/Data/SearchResponse.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// SearchResponse.swift
// BuildConfigSwiftDemo
//
// Created by 417.72KI on 2023/10/05.
// Copyright © 2023 417.72KI. All rights reserved.
//

import Foundation

struct SearchResponse: Response {
var items: [Item]
}

extension SearchResponse {
struct Item: Response {
var name: String
}
}
61 changes: 61 additions & 0 deletions Demo/BuildConfigSwiftDemo/Sources/Repository/APIClient.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//
// APIClient.swift
// BuildConfigSwiftDemo
//
// Created by 417.72KI on 2023/10/05.
// Copyright © 2023 417.72KI. All rights reserved.
//

import Foundation

struct APIClient {
var config: BuildConfig.Api
var session: URLSession
}

extension APIClient {
var host: URL { URL(string: "https://\(config.host)")! }

var decoder: JSONDecoder {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
return decoder
}

var encoder: JSONEncoder {
let encoder = JSONEncoder()
encoder.keyEncodingStrategy = .convertToSnakeCase
return encoder
}
}

extension APIClient {
func endpoint<E>(_ keyPath: KeyPath<BuildConfig.Api.Endpoint, E>) -> E {
config.endpoint[keyPath: keyPath]
}
}

extension APIClient {
func login(id: String, password: String) async throws -> LoginResponse {
let endpoint = endpoint(\.login)
let url = host.appendingPathComponent(endpoint.path)
var request = URLRequest(url: url)
request.httpMethod = endpoint.method
request.httpBody = try encoder.encode(LoginRequest(id: id, password: password))
let (data, _) = try await session.data(for: request)
return try decoder.decode(LoginResponse.self, from: data)
}

@available(iOS 16.0, *)
func search(_ text: String) async throws -> SearchResponse {
let endpoint = endpoint(\.search)
let url = host.appendingPathComponent(endpoint.path)
.appending(queryItems: [
URLQueryItem(name: "text", value: text)
])
var request = URLRequest(url: url)
request.httpMethod = endpoint.method
let (data, _) = try await session.data(for: request)
return try decoder.decode(SearchResponse.self, from: data)
}
}
4 changes: 2 additions & 2 deletions Demo/BuildConfigSwiftDemo/Sources/UI/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@
import SwiftUI

struct ContentView: View {
var config: BuildConfig = .default
@Environment(\.buildConfig) var config: BuildConfig

var body: some View {
VStack {
Text("isDebug: \(String(config.isDebug))")
Text("Environment: \(config.environment)")
Text("API version: \(config.apiVersion, format: .number)")
Text("API version: \(config.api.version, format: .number)")
Text("PI: \(config.pi, format: .number)")
}
}
Expand Down
13 changes: 3 additions & 10 deletions Demo/BuildConfigSwiftDemo/Sources/UI/DemoApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,13 @@

import SwiftUI

@main
struct DemoApp: App {
static var buildConfig: BuildConfig = .default

var body: some Scene {
WindowGroup {
ContentView()
.environment(\.buildConfig, Self.buildConfig)
}
}
}

// MARK: - For Debug
#if DEBUG
private extension DemoApp {
var isTesting: Bool {
ProcessInfo.processInfo.environment["XCTestConfigurationFilePath"] != nil
}
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//
// BuildConfig+Environment.swift
// BuildConfigSwiftDemo
//
// Created by 417.72KI on 2023/10/09.
// Copyright © 2023 417.72KI. All rights reserved.
//

import SwiftUI

struct BuildConfigKey: EnvironmentKey {
static let defaultValue = BuildConfig.default
}

extension EnvironmentValues {
var buildConfig: BuildConfig {
get { self[BuildConfigKey.self] }
set { self[BuildConfigKey.self] = newValue }
}
}
15 changes: 15 additions & 0 deletions Demo/BuildConfigSwiftDemo/Sources/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//
// main.swift
// BuildConfigSwiftDemo
//
// Created by 417.72KI on 2023/10/09.
// Copyright © 2023 417.72KI. All rights reserved.
//

import SwiftUI

if let clazz = NSClassFromString("FakeApp") as? any App.Type {
clazz.main()
} else {
DemoApp.main()
}
64 changes: 64 additions & 0 deletions Demo/BuildConfigSwiftDemoTests/APIClientTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//
// APIClientTests.swift
// BuildConfigSwiftDemoTests
//
// Created by 417.72KI on 2023/10/05.
// Copyright © 2023 417.72KI. All rights reserved.
//

import XCTest
import StubNetworkKit

@testable import BuildConfigSwiftDemo

final class APIClientTests: XCTestCase {
var config = BuildConfig.fake.api

var apiClient: APIClient!

override func setUpWithError() throws {
apiClient = APIClient(config: config,
session: defaultStubSession)
}

override func tearDownWithError() throws {
clearStubs()
}

func testLogin() async throws {
stub {
Scheme.is("https")
Host.is("localhost")
Path.is("/login")
Method.isPost()
Body.isJson(["id": "john_doe", "password": "password"])
}.responseJson(["access_token": "foo", "refresh_token": "bar"])

let response = try await apiClient.login(id: "john_doe",
password: "password")
XCTAssertEqual("foo", response.accessToken)
XCTAssertEqual("bar", response.refreshToken)
}

@available(iOS 16.0, *)
func testSearch() async throws {
stub {
Scheme.is("https")
Host.is("localhost")
Path.is("/search")
Method.isGet()
QueryParams.contains(["text": "寿限無"])
}.responseJson([
"items": [
["name": "foo"],
["name": "bar"],
["name": "baz"],
]
])

let response = try await apiClient.search("寿限無")
XCTAssertEqual(3, response.items.count)
XCTAssertEqual("foo", response.items.first?.name)
XCTAssertEqual("baz", response.items.last?.name)
}
}
25 changes: 9 additions & 16 deletions Demo/BuildConfigSwiftDemoTests/BuildConfigSwiftDemoTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,19 @@ import XCTest
final class BuildConfigTests: XCTestCase {
func testDefault() {
let buildConfig = BuildConfig.default
XCTAssertEqual(1, buildConfig.apiVersion)
XCTAssertEqual("develop", buildConfig.environment)
XCTAssertFalse(buildConfig.isDebug)
XCTAssertEqual(1, buildConfig.api.version)
XCTAssertEqual("api-dev.example.com", buildConfig.api.host)
XCTAssertEqual("debug", buildConfig.environment)
XCTAssertTrue(buildConfig.isDebug)
XCTAssertEqual(3.14, buildConfig.pi, accuracy: 0.01)
}

func testLoad() {
let buildConfig = BuildConfig.load(
from: #"""
{
"api_version": 100,
"environment": "staging",
"is_debug": true,
"pi": 3.14
}
"""#.data(using: .utf8)!
)
XCTAssertEqual(100, buildConfig.apiVersion)
func testLoad() throws {
let buildConfig = BuildConfig.fake
XCTAssertEqual(100, buildConfig.api.version)
XCTAssertEqual("localhost", buildConfig.api.host)
XCTAssertEqual("staging", buildConfig.environment)
XCTAssertTrue(buildConfig.isDebug)
XCTAssertFalse(buildConfig.isDebug)
XCTAssertEqual(3.14, buildConfig.pi, accuracy: 0.01)
}
}
25 changes: 25 additions & 0 deletions Demo/BuildConfigSwiftDemoTests/FakeApp.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// FakeApp.swift
// BuildConfigSwiftDemoTests
//
// Created by 417.72KI on 2023/10/09.
// Copyright © 2023 417.72KI. All rights reserved.
//

import SwiftUI

@objc(FakeApp)
final class FakeApp: NSObject, App {
override init() { super.init() }

var body: some Scene {
WindowGroup {
Text("This is a fake app.")
.foregroundStyle(Color.white)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background {
Color.black
}
}
}
}
30 changes: 30 additions & 0 deletions Demo/BuildConfigSwiftDemoTests/TestHelper.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// TestHelper.swift
// BuildConfigSwiftDemoTests
//
// Created by 417.72KI on 2023/10/05.
// Copyright © 2023 417.72KI. All rights reserved.
//

import Foundation
@testable import BuildConfigSwiftDemo

final class TestHelper {
private init() {}
}

extension TestHelper {
static var bundle: Bundle { Bundle(for: self.self) }
}

extension TestHelper {
static func path(forResource name: String, ofType ext: String) -> String? {
bundle.path(forResource: name, ofType: ext)
}
}

extension BuildConfig {
static var fake: Self {
.load(from: TestHelper.path(forResource: "test_config", ofType: "json")!)
}
}
23 changes: 23 additions & 0 deletions Demo/BuildConfigSwiftDemoTests/test_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"api": {
"version": 100,
"host": "localhost",
"endpoint": {
"login": {
"path": "/login",
"method": "POST"
},
"profile": {
"path": "/profile",
"method": "GET"
},
"search": {
"path": "/search",
"method": "GET"
}
}
},
"environment": "staging",
"is_debug": false,
"pi": 3.14
}
2 changes: 0 additions & 2 deletions Demo/Resources/BuildConfig/.env/debug.yml

This file was deleted.

Loading

0 comments on commit 8c3c829

Please sign in to comment.