Skip to content

Instantly share code, notes, and snippets.

@sonatard
Last active September 5, 2021 00:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sonatard/4cba83467a0ecfb421afea32ec0574a5 to your computer and use it in GitHub Desktop.
Save sonatard/4cba83467a0ecfb421afea32ec0574a5 to your computer and use it in GitHub Desktop.
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()
}
}
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()
}
}
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")
}
}
}
}
}
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
}
}
}
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()
}
}
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