Last active
September 5, 2021 00:34
-
-
Save sonatard/4cba83467a0ecfb421afea32ec0574a5 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
import ComposableArchitecture | |
struct ToDo : Identifiable, Equatable{ | |
var id = UUID() | |
var title: String | |
var done: Bool | |
} | |
struct ToDoState : Equatable { | |
var loading: Bool = false | |
var error: String = "" | |
var todos: [ToDo] = [] | |
var completedToDos: [ToDo] { | |
get{ | |
return todos.filter { $0.done } | |
} | |
} | |
var unCompletedToDos: [ToDo] { | |
get{ | |
return todos.filter { !$0.done } | |
} | |
} | |
} | |
enum ToDoAction: Equatable { | |
case get | |
case getResponse(Result<[ToDo], ApiError>) | |
case append | |
case toggle(UUID) | |
case toggleResponse(Result<ToDo, ApiError>) | |
} | |
struct ToDoEnvironment { | |
var mainQueue: AnySchedulerOf<DispatchQueue> | |
var getAPI: () -> Effect<[ToDo], ApiError> | |
var toggleAPI: (UUID) -> Effect<ToDo, ApiError> | |
} | |
struct ApiError: Error, Equatable {} | |
let toDoReducer = Reducer< | |
ToDoState, | |
ToDoAction, | |
ToDoEnvironment> { state, action, environment in | |
switch action { | |
case .get: | |
state.loading = true | |
return environment.getAPI() | |
.delay(for: 2, scheduler: environment.mainQueue.animation()) | |
.catchToEffect() | |
.map(ToDoAction.getResponse) | |
case let .getResponse(.success(todos)): | |
state.todos = todos | |
state.loading = false | |
return .none | |
case let .getResponse(.failure(error)): | |
state.error = error.localizedDescription | |
state.loading = false | |
return .none | |
case .append: | |
state.todos += [ToDo(title: "追加", done: false)] | |
return .none | |
case let .toggle(id): | |
state.loading = true | |
return environment.toggleAPI(id) | |
.delay(for: 2, scheduler: environment.mainQueue.animation()) | |
.catchToEffect() | |
.map(ToDoAction.toggleResponse) | |
case let .toggleResponse(.success(toDo)): | |
state.todos.indices.forEach { | |
if (state.todos[$0].id == toDo.id) { | |
// 本来はAPIで返ってくる値をそのまま更新すればいいがAPI実装してない都合 | |
state.todos[$0].done = !state.todos[$0].done | |
} | |
} | |
state.loading = false | |
return .none | |
case let .toggleResponse(.failure(error)): | |
state.error = error.localizedDescription | |
state.loading = false | |
return .none | |
} | |
} | |
let initToDos = [ | |
ToDo(title: "タスク1", done: false), | |
ToDo(title: "タスク2", done: true), | |
] | |
struct ContentView: View { | |
var body: some View { | |
VStack{ | |
ToDoView( | |
store: Store( | |
initialState: ToDoState(), | |
reducer: toDoReducer, | |
environment: ToDoEnvironment( | |
mainQueue: .main, | |
getAPI: { Effect(value: initToDos) }, | |
toggleAPI: { id in | |
Effect(value: ToDo(id: id, title: "dummy", done: false)) | |
} | |
) | |
) | |
) | |
} | |
} | |
} | |
struct ToDoView: View { | |
let store: Store<ToDoState, ToDoAction> | |
var body: some View { | |
WithViewStore(self.store) { viewStore in | |
ZStack { | |
VStack{ | |
if viewStore.error != "" { | |
Text(viewStore.error) | |
} | |
Button(action: { viewStore.send(.append) }){ Text("追加") } | |
Text("全ToDo") | |
ToDoListView(todos: viewStore.todos, | |
toggle: { id in viewStore.send(.toggle(id))}) | |
Text("未完了ToDo") | |
ToDoListView(todos: viewStore.unCompletedToDos, | |
toggle: { id in viewStore.send(.toggle(id))}) | |
Text("完了済みToDo") | |
ToDoListView(todos: viewStore.completedToDos, | |
toggle:{ id in viewStore.send(.toggle(id))}) | |
}.onAppear { viewStore.send(.get) } | |
if viewStore.loading { | |
ProgressView() | |
} | |
} | |
} | |
} | |
} | |
struct ToDoListView: View { | |
let todos: [ToDo] | |
let toggle: (UUID) -> Void | |
var body: some View { | |
VStack{ | |
List { | |
ForEach(todos) { todo in | |
ToDoRow(todo: todo, toggle: toggle) | |
} | |
} | |
} | |
} | |
} | |
struct ToDoRow: View { | |
let todo: ToDo | |
let toggle: (UUID) -> Void | |
var body: some View { | |
HStack { | |
Text(todo.title) | |
Spacer() | |
Button(action: { toggle(todo.id) }){ | |
if (todo.done) { | |
Image(systemName: "checkmark.square.fill") | |
.foregroundColor(.green) | |
}else { | |
Image(systemName: "square") | |
} | |
} | |
} | |
} | |
} | |
struct ContentView_Previews: PreviewProvider { | |
static var previews: some View { | |
ContentView() | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
import Combine | |
let initToDos = [ | |
ToDo(title: "タスク1", done: false), | |
ToDo(title: "タスク2", done: true), | |
] | |
struct ToDo : Identifiable{ | |
let id = UUID() | |
var title: String | |
var done: Bool | |
} | |
struct ToDoList { | |
var todos: [ToDo] = initToDos | |
var completedToDos: [ToDo] { | |
get{ | |
return todos.filter { $0.done } | |
} | |
} | |
var unCompletedToDos: [ToDo] { | |
get{ | |
return todos.filter { !$0.done } | |
} | |
} | |
func toggle(id: UUID){ | |
// TODO | |
} | |
func append(title: String){ | |
// TODO | |
} | |
} | |
struct ContentView: View { | |
var body: some View { | |
VStack{ | |
ToDoView() | |
} | |
} | |
} | |
class AsyncState<Data>: ObservableObject { | |
var f: () -> Data | |
@Published var isLoading = false | |
@Published var error: Error? | |
@Published var data: Data? | |
init(f: @escaping () -> Data) { | |
self.f = f | |
} | |
func get() { | |
self.isLoading = true | |
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {// 1s sleep | |
self.data = self.f() | |
self.isLoading = false | |
} | |
} | |
} | |
struct ToDoView: View { | |
@StateObject private var state = AsyncState<ToDoList> { | |
return ToDoList() // APIコール | |
} | |
var body: some View { | |
ZStack { | |
VStack{ | |
Button(action: { state.todoList?.append(title: "新タスク") }){ Text("追加") } | |
Text("全ToDo") | |
ToDoListView(todos: state.todoList?.todos ?? [], | |
toggle: state.todoList?.toggle) | |
Text("未完了ToDo") | |
ToDoListView(todos: state.todoList?.unCompletedToDos ?? [], | |
toggle: state.todoList?.toggle) | |
Text("完了済みToDo") | |
ToDoListView(todos: state.todoList?.completedToDos ?? [], | |
toggle: state.todoList?.toggle) | |
}.onAppear { | |
state.get() | |
} | |
if state.isLoading { | |
ProgressView() | |
} | |
} | |
} | |
} | |
struct ToDoListView: View { | |
let todos: [ToDo] | |
let toggle: ((UUID) -> Void)? | |
var body: some View { | |
VStack{ | |
List { | |
ForEach(todos) { todo in | |
ToDoRow(todo: todo, toggle: toggle) | |
} | |
} | |
} | |
} | |
} | |
struct ToDoRow: View { | |
let todo: ToDo | |
let toggle: ((UUID) -> Void)? | |
var body: some View { | |
HStack { | |
Text(todo.title) | |
Spacer() | |
Button(action: { toggle?(todo.id) }){ | |
if (todo.done) { | |
Image(systemName: "checkmark.square.fill") | |
.foregroundColor(.green) | |
}else { | |
Image(systemName: "square") | |
} | |
} | |
} | |
} | |
} | |
struct ContentView_Previews: PreviewProvider { | |
static var previews: some View { | |
ContentView() | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
struct ToDoView: View { | |
@StateObject private var query = Query<ShopifyQuery.ToDoView>() | |
var body: some View { | |
VStack { | |
if query.fetching { | |
ProgressView() | |
} else if let errorContent = query.error?.content { | |
ErrorRetryButtonView( | |
error: errorContent, | |
retryAction: { query.refetch() } | |
) | |
} else if let data = query.response { | |
VStack { | |
Text("全タスク") | |
TasksView(fragment: data.todo.fragments.tasksViewFragment) | |
Text("未完了タスク") | |
UnCompletedTasksView(fragment: data.todo.fragments.unCompletedTasksViewFragment) | |
Text("完了タスク") | |
CompletedTasksView(fragment: data.todo.fragments.completedTasksViewFragment) | |
} | |
} | |
}.onFirstAppear(perform: { | |
query.watch(cachePolicy: .returnCacheDataAndFetch) | |
}) | |
} | |
} | |
struct TasksView: View { | |
let fragment: GraphQL.TasksViewFragment | |
var body: some View { | |
List { | |
ForEach(fragment.all.map { $0.fragments.taskViewFragment }) { task in | |
TaskView(task: task) | |
} | |
} | |
} | |
} | |
struct UnCompletedTasksView: View { | |
let fragment: GraphQL.UnCompletedTasksViewFragment | |
var body: some View { | |
List { | |
ForEach(fragment.unCompleted.map { $0.fragments.taskViewFragment }) { task in | |
TaskView(task: task) | |
} | |
} | |
} | |
} | |
struct CompletedTasksView: View { | |
let fragment: GraphQL.CompletedTasksViewFragment | |
var body: some View { | |
List { | |
ForEach(fragment.completed.map { $0.fragments.taskViewFragment }) { task in | |
TaskView(task: task) | |
} | |
} | |
} | |
} | |
struct TaskView: View { | |
@StateObject private var toggleMutation = Mutation<ShopifyMutation.ToggleToDo>() | |
let task: GraphQL.TaskViewFragment | |
var body: some View { | |
HStack { | |
Text(task.title) | |
Spacer() | |
Button(action: { | |
toggleMutation.perform(input: .init(id: task.id)) | |
}) { | |
if toggleMutation.executing { | |
ProgressView() | |
} else if task.done { | |
Image(systemName: "checkmark.square.fill") | |
.foregroundColor(.green) | |
} else { | |
Image(systemName: "square") | |
} | |
} | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
query ToDoView { | |
todo { | |
...TasksViewFragment | |
...UnCompletedTasksViewFragment | |
...CompletedTasksViewFragment | |
} | |
} | |
fragment TasksViewFragment on ToDo { | |
id | |
all { | |
...TaskViewFragment | |
} | |
} | |
fragment CompletedTasksViewFragment on ToDo { | |
id | |
completed { | |
...TaskViewFragment | |
} | |
} | |
fragment UnCompletedTasksViewFragment on ToDo { | |
id | |
unCompleted { | |
...TaskViewFragment | |
} | |
} | |
fragment TaskViewFragment on Task { | |
id | |
title | |
done | |
} | |
mutation ToggleTodo($input: ToggleToDoInput!) { | |
toggleToDo(input: $input) { | |
todo { | |
...TasksViewFragment | |
...UnCompletedTasksViewFragment | |
...CompletedTasksViewFragment | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
import ComposableArchitecture | |
struct ToDo : Identifiable, Equatable{ | |
let id = UUID() | |
var title: String | |
var done: Bool | |
} | |
struct ToDoState : Equatable { | |
var todos: [ToDo] = [ | |
ToDo(title: "タスク1", done: false), | |
ToDo(title: "タスク2", done: true), | |
] | |
var completedToDos: [ToDo] { | |
get{ | |
return todos.filter { $0.done } | |
} | |
} | |
var unCompletedToDos: [ToDo] { | |
get{ | |
return todos.filter { !$0.done } | |
} | |
} | |
} | |
enum ToDoAction: Equatable { | |
case append | |
case toggle(UUID) | |
} | |
struct ToDoEnvironment { | |
var mainQueue: AnySchedulerOf<DispatchQueue> | |
} | |
struct ApiError: Error, Equatable {} | |
let toDoReducer = Reducer<ToDoState, ToDoAction, ToDoEnvironment> { state, action, environment in | |
switch action { | |
case .append: | |
state.todos += [ToDo(title: "追加", done: false)] | |
return .none | |
case let .toggle(id): | |
state.todos.indices.forEach { | |
if (state.todos[$0].id == id) { | |
state.todos[$0].done = !state.todos[$0].done | |
} | |
} | |
return .none | |
} | |
} | |
struct ContentView: View { | |
var body: some View { | |
VStack{ | |
ToDoView( | |
store: Store( | |
initialState: ToDoState(), | |
reducer: toDoReducer, | |
environment: ToDoEnvironment( | |
mainQueue: .main | |
) | |
) | |
) | |
} | |
} | |
} | |
struct ToDoView: View { | |
let store: Store<ToDoState, ToDoAction> | |
var body: some View { | |
WithViewStore(self.store) { viewStore in | |
VStack{ | |
Button(action: { viewStore.send(.append) }){ Text("追加") } | |
Text("全ToDo") | |
ToDoListView(todos: viewStore.todos, | |
toggle: { id in viewStore.send(.toggle(id))}) | |
Text("未完了ToDo") | |
ToDoListView(todos: viewStore.unCompletedToDos, | |
toggle: { id in viewStore.send(.toggle(id))}) | |
Text("完了済みToDo") | |
ToDoListView(todos: viewStore.completedToDos, | |
toggle:{ id in viewStore.send(.toggle(id))}) | |
} | |
} | |
} | |
} | |
struct ToDoListView: View { | |
let todos: [ToDo] | |
let toggle: (UUID) -> Void | |
var body: some View { | |
VStack{ | |
List { | |
ForEach(todos) { todo in | |
ToDoRow(todo: todo, toggle: toggle) | |
} | |
} | |
} | |
} | |
} | |
struct ToDoRow: View { | |
let todo: ToDo | |
let toggle: (UUID) -> Void | |
var body: some View { | |
HStack { | |
Text(todo.title) | |
Spacer() | |
Button(action: { toggle(todo.id) }){ | |
if (todo.done) { | |
Image(systemName: "checkmark.square.fill") | |
.foregroundColor(.green) | |
}else { | |
Image(systemName: "square") | |
} | |
} | |
} | |
} | |
} | |
struct ContentView_Previews: PreviewProvider { | |
static var previews: some View { | |
ContentView() | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
struct ToDo : Identifiable{ | |
let id = UUID() | |
var title: String | |
var done: Bool | |
} | |
struct User { | |
let name = "やまだ" | |
} | |
class ToDoList :ObservableObject { | |
@Published var todos: [ToDo] = [ | |
ToDo(title: "タスク1", done: false), | |
ToDo(title: "タスク2", done: true), | |
] | |
var completedToDos: [ToDo] { | |
get{ | |
return todos.filter { $0.done } | |
} | |
} | |
var unCompletedToDos: [ToDo] { | |
get{ | |
return todos.filter { !$0.done } | |
} | |
} | |
func toggle(id: UUID){ | |
todos.indices.forEach { | |
if (todos[$0].id == id) { | |
todos[$0].done = !todos[$0].done | |
} | |
} | |
} | |
func append(title: String){ | |
todos.append(ToDo(title: title, done: false)) | |
} | |
} | |
struct ContentView: View { | |
var body: some View { | |
VStack{ | |
UserView() | |
ToDoView() | |
} | |
} | |
} | |
struct ToDoView: View { | |
@StateObject private var todoList = ToDoList() | |
var body: some View { | |
VStack{ | |
Button(action: { todoList.append(title: "新タスク") }){ Text("追加") } | |
Text("全ToDo") | |
ToDoListView(todos: todoList.todos, | |
toggle: todoList.toggle) | |
Text("未完了ToDo") | |
ToDoListView(todos: todoList.unCompletedToDos, | |
toggle: todoList.toggle) | |
Text("完了済みToDo") | |
ToDoListView(todos: todoList.completedToDos, | |
toggle: todoList.toggle) | |
} | |
} | |
} | |
struct UserView: View { | |
let user = User() | |
var body: some View { | |
HStack { | |
Text(user.name) | |
Spacer() | |
} | |
} | |
} | |
struct ToDoListView: View { | |
let todos: [ToDo] | |
let toggle: (UUID) -> Void | |
var body: some View { | |
VStack{ | |
List { | |
ForEach(todos) { todo in | |
ToDoRow(todo: todo, toggle: toggle) | |
} | |
} | |
} | |
} | |
} | |
struct ToDoRow: View { | |
let todo: ToDo | |
let toggle: (UUID) -> Void | |
var body: some View { | |
HStack { | |
Text(todo.title) | |
Spacer() | |
Button(action: { toggle(todo.id) }){ | |
if (todo.done) { | |
Image(systemName: "checkmark.square.fill") | |
.foregroundColor(.green) | |
}else { | |
Image(systemName: "square") | |
} | |
} | |
} | |
} | |
} | |
struct ContentView_Previews: PreviewProvider { | |
static var previews: some View { | |
ContentView() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment