Skip to content

Commit

Permalink
Updated sample app
Browse files Browse the repository at this point in the history
  • Loading branch information
afterxleep committed Feb 13, 2024
1 parent 98b490f commit 694ab3c
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 121 deletions.
8 changes: 5 additions & 3 deletions docs/ExampleApp/WireKitSample.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 52;
objectVersion = 54;
objects = {

/* Begin PBXBuildFile section */
Expand Down Expand Up @@ -52,6 +52,7 @@
D6E759AA29FD0B4000E058E3 /* URLProtocolMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLProtocolMock.swift; sourceTree = "<group>"; };
D6E759AC29FD14BB00E058E3 /* todoItems.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = todoItems.json; sourceTree = "<group>"; };
D6E759B129FD156000E058E3 /* TestHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestHelpers.swift; sourceTree = "<group>"; };
D6F8AC372B7AEBAD009F9E3F /* WireKit */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = WireKit; path = ../..; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand All @@ -76,6 +77,7 @@
D68B254B257DD2550019B48A = {
isa = PBXGroup;
children = (
D6F8AC372B7AEBAD009F9E3F /* WireKit */,
D68B2556257DD2550019B48A /* WireKitSample */,
D6E759A129FD0A0D00E058E3 /* WireKitSampleTests */,
D68B2555257DD2550019B48A /* Products */,
Expand Down Expand Up @@ -413,7 +415,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_ASSET_PATHS = "\"WireKitSample/Preview Content\"";
DEVELOPMENT_TEAM = JM9222EF99;
DEVELOPMENT_TEAM = 83QBHAG7HD;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = WireKitSample/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
Expand All @@ -435,7 +437,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_ASSET_PATHS = "\"WireKitSample/Preview Content\"";
DEVELOPMENT_TEAM = JM9222EF99;
DEVELOPMENT_TEAM = 83QBHAG7HD;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = WireKitSample/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
Expand Down

This file was deleted.

125 changes: 44 additions & 81 deletions docs/ExampleApp/WireKitSample/TodoList/TodoListViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

import Foundation
import WireKit
import Combine
import SwiftUI

class TodoListViewModel: ObservableObject {
Expand All @@ -18,7 +17,6 @@ class TodoListViewModel: ObservableObject {
@Published var loadingMessage = "Loading Data..."

let apiClient: WKAPIClient
private var cancellables = [AnyCancellable]()

private enum Constants {
static let apiURL = "https://jsonplaceholder.typicode.com"
Expand All @@ -28,97 +26,62 @@ class TodoListViewModel: ObservableObject {

init(apiClient: WKAPIClient) {
self.apiClient = apiClient
loadData()
Task {
await loadData()
}
}

private func loadData() {

// Perform a request from the TodoAPI
// You can pass parameters and other data here.
apiClient.dispatch(TodoAPI.FindAll())

// Since we are displaying results in the UI, receive on Main Thread
.receive(on: DispatchQueue.main)

// Observe for Returned values
.sink(
receiveCompletion: { result in

// Hide our loader on completion
self.isLoading = false

// Act on when we get a result or failure
switch result {
case .failure(let error):
// Handle API response errors here (WKNetworkRequestError)
print("Error loading data: \(error)")
default: break
}
},
receiveValue: { value in

// Populate the observable property in the UI
self.todoItems = value
})

// Store the cancellable in a set (to hold a ref)
.store(in: &cancellables)
private func loadData() async {
isLoading = true
do {
let todos: [Todo] = try await apiClient.dispatch(TodoAPI.FindAll())
DispatchQueue.main.async {
self.todoItems = todos
self.isLoading = false
}
} catch {
DispatchQueue.main.async {
print("Error loading data: \(error)")
self.isLoading = false
}
}
}

// Quick helper method to set color and icon for each todo
func todoImage(todoItem: Todo) -> (name: String, color: Color) {
todoItem.completed ? (name: Constants.completedImage, color: .green) : (name: Constants.defaultImage, color: .red)
}

func delete(at offsets: IndexSet) {
for o in offsets {
guard let id = todoItems[o].id else { return }
todoItems.remove(at: o)
apiClient.dispatch(TodoAPI.Delete(id))
.receive(on: DispatchQueue.main)
.sink(
receiveCompletion: { result in
switch result {
case .failure(let error):
print("Error deleting data: \(error)")
default:
print("Item Deleted Correctly")
// You could reload the list from the server here
// We're not doing it as this is a Mock API
//self.loadData()
break
}
},
receiveValue: { value in
print("Response: \(value)")
// The received value is empty
})
.store(in: &cancellables)
Task {
for index in offsets {
guard let id = todoItems[index].id else { continue }
do {
let _: Empty = try await apiClient.dispatch(TodoAPI.Delete(id))
DispatchQueue.main.async {
self.todoItems.remove(at: index)
}
} catch {
DispatchQueue.main.async {
print("Error deleting data: \(error)")
}
}
}
}
}

func add(todo: Todo) {
apiClient.dispatch(TodoAPI.Add(todo))
.receive(on: DispatchQueue.main)
.sink(
receiveCompletion: { result in
switch result {
case .failure(let error):
print("Error adding data: \(error)")
default:
print("Item Added Correctly")
// You could reload the list from the server here
// We're not doing it as this is a Mock API
//self.loadData()
break
}
},
receiveValue: { value in
print("Response: \(value)")
// The received value is empty
})
.store(in: &cancellables)

Task {
do {
let _: Todo = try await apiClient.dispatch(TodoAPI.Add(todo))
DispatchQueue.main.async {
self.todoItems.append(todo)
print("Item Added Correctly")
}
} catch {
DispatchQueue.main.async {
print("Error adding data: \(error)")
}
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,11 @@

import Foundation
import XCTest
import Combine
import WireKit
@testable import WireKitSample

class TodoListViewModelTests: XCTestCase {

private var todoItemsCancellable: AnyCancellable?

func testListModelWorks() {

func testListModelWorks() async {
// Note that we're using non SSL urls here as URLProtocol does
let url = URL(string: "https://jsonplaceholder.typicode.com/todos")

Expand All @@ -26,6 +21,7 @@ class TodoListViewModelTests: XCTestCase {
URLProtocolMock.testURLs = [url: jsonData]
} catch {
XCTFail("Error loading JSON data: \(error)")
return
}

// Use the Mock
Expand All @@ -37,25 +33,18 @@ class TodoListViewModelTests: XCTestCase {

// Initialize the APIClient and pass along a custom dispatcher
let baseURL = "https://jsonplaceholder.typicode.com"

// Then
let dispatcher = WKNetworkDispatcher(urlSession: session)
let apiClient = WKAPIClient(baseURL: baseURL, networkDispatcher: dispatcher)

let expectation = XCTestExpectation(description: "Wait for the response")
let model = TodoListViewModel(apiClient: apiClient)
todoItemsCancellable = model.$todoItems
.dropFirst() // To skip the initial (empty) value
.sink { todoItems in
XCTAssertGreaterThan(todoItems.count, 0, "Todo items should be loaded")
expectation.fulfill()
}
wait(for: [expectation], timeout: 1)
}

deinit {
todoItemsCancellable?.cancel()
// Wait for the model to load data
let expectation = XCTestExpectation(description: "Wait for the response")
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
XCTAssertGreaterThan(model.todoItems.count, 0, "Todo items should be loaded")
expectation.fulfill()
}

wait(for: [expectation], timeout: 2)
}

}

0 comments on commit 694ab3c

Please sign in to comment.