diff --git a/Client/iOS/TodoList/AppDelegate.swift b/Client/iOS/TodoList/AppDelegate.swift index 47ee1edae..df3d8fb6e 100644 --- a/Client/iOS/TodoList/AppDelegate.swift +++ b/Client/iOS/TodoList/AppDelegate.swift @@ -10,9 +10,20 @@ import UIKit @main class AppDelegate: UIResponder, UIApplicationDelegate { - static let middleWare = CardDataMiddleWare { - DebugDataTask(api: Team13API()) + static let dataTask = DataTask(api: Team13API()) + static let middleWare = CardDataMiddleWare(persistenceProvider: BoardPersistenceProvider()) { + dataTask } + func applicationDidFinishLaunching(_ application: UIApplication) { + // TODO:- 사용자 쿠키를 받아오지 못했을때 처리 + Self.dataTask?.fetchUser(completionHandler: { result in + switch result { + case .success: + break + case .failure: + break + } + }) + } } - diff --git a/Client/iOS/TodoList/Base.lproj/Main.storyboard b/Client/iOS/TodoList/Base.lproj/Main.storyboard index 126d29559..1d344e225 100644 --- a/Client/iOS/TodoList/Base.lproj/Main.storyboard +++ b/Client/iOS/TodoList/Base.lproj/Main.storyboard @@ -22,6 +22,7 @@ + @@ -48,6 +49,7 @@ + @@ -106,7 +108,8 @@ - + + @@ -152,6 +155,7 @@ + @@ -179,9 +183,9 @@ - + - + @@ -254,7 +258,7 @@ - + @@ -329,7 +333,7 @@ - + diff --git a/Client/iOS/TodoList/Cells/TodoTableViewCell.swift b/Client/iOS/TodoList/Cells/TodoTableViewCell.swift index 59dc9e739..4890fc565 100644 --- a/Client/iOS/TodoList/Cells/TodoTableViewCell.swift +++ b/Client/iOS/TodoList/Cells/TodoTableViewCell.swift @@ -20,7 +20,7 @@ class TodoTableViewCell: UITableViewCell { roundedView.layer.masksToBounds = true } - func applyTextAllLabels(data card: CardData) { + func applyTextAllLabels(data card: Card) { setTitleLabelAttribute(card.title) setContentLabelAttribute(card.contents) } diff --git a/Client/iOS/TodoList/Cells/TodoTableViewCell.xib b/Client/iOS/TodoList/Cells/TodoTableViewCell.xib index 4ae39d680..ea2caf4b4 100644 --- a/Client/iOS/TodoList/Cells/TodoTableViewCell.xib +++ b/Client/iOS/TodoList/Cells/TodoTableViewCell.xib @@ -20,7 +20,7 @@ - + - - - - - - - - - - - + @@ -94,7 +84,7 @@ - + diff --git a/Client/iOS/TodoList/DB/PersistenceProvider.swift b/Client/iOS/TodoList/DB/PersistenceProvider.swift index 9bff4baf0..5aac95e11 100644 --- a/Client/iOS/TodoList/DB/PersistenceProvider.swift +++ b/Client/iOS/TodoList/DB/PersistenceProvider.swift @@ -3,9 +3,9 @@ import CoreData // MARK:- Cards struct Card { - let cardId: Int - let cardTitle: String - let cardContent: String + let id: Int + let title: String + let contents: String let boardName: String } @@ -16,22 +16,28 @@ enum BoardType: CustomStringConvertible { case done var description: String { - return "\(self)" + switch self { + case .todo: + return "TODO" + case .progress: + return "DOING" + case .done: + return "DONE" + default: + return "" + } } } // MARK:- DataManager protocol PersistenceProvider { func insertCard(boardType: BoardType, id: Int, title: String, content: String) + func insertBoard(type: BoardType, cards: [Card]) func fetchAllCard(where boardType: BoardType) -> [Card] } class BoardPersistenceProvider: PersistenceProvider { - static var shared = BoardPersistenceProvider() - - private init() {} - private lazy var persistentContainer: NSPersistentContainer = { let container = NSPersistentContainer(name: "Model") container.loadPersistentStores { description, error in @@ -50,12 +56,23 @@ class BoardPersistenceProvider: PersistenceProvider { return NSEntityDescription.entity(forEntityName: "TaskBoard", in: context) } + func insertBoard(type: BoardType, cards: [Card]) { + let fetchAllCard = fetchAllCard(where: type) + for card in cards { + if fetchAllCard.contains(where: { $0.id == card.id }) { + // TODO:- id 가 같지만 수정되어 필드값이 하나라도 다를 수 있다 -> 모든 필드가 같은지 비교 필요 + continue + } + insertCard(boardType: type, id: card.id, title: card.title, content: card.contents) + } + } + func insertCard(boardType: BoardType, id: Int, title: String, content: String) { guard let entity = entity else { return } let object = NSManagedObject(entity: entity, insertInto: context) - object.setValue(boardType, forKey: "type") + object.setValue(boardType.description, forKey: "type") object.setValue(id, forKey: "cardId") object.setValue(content, forKey: "content") object.setValue(title, forKey: "title") @@ -66,7 +83,8 @@ class BoardPersistenceProvider: PersistenceProvider { do { let fetchResults = try context.fetch(TaskBoard.fetchRequest()) as! [TaskBoard] let results = fetchResults - .map{ Card.init(cardId: Int($0.cardId), cardTitle: $0.title!, cardContent: $0.content!, boardName: $0.type!) } + .map{ Card(id: Int($0.cardId), title: $0.title!, contents: $0.content!, boardName: $0.type!) } + if boardType == .all { return results } diff --git a/Client/iOS/TodoList/Network/CardsFetchingDataTask.swift b/Client/iOS/TodoList/Network/CardsFetchingDataTask.swift index 2ddcbf274..25fdcc652 100644 --- a/Client/iOS/TodoList/Network/CardsFetchingDataTask.swift +++ b/Client/iOS/TodoList/Network/CardsFetchingDataTask.swift @@ -11,8 +11,14 @@ struct Team13API: ServerAPI { let endpoint: String = "http://13.125.216.180:8080" } +protocol APIService { + func fetchUser(completionHandler: @escaping (Result) -> Void) + func fetchAll(dataType: T.Type, completionHandler: @escaping (Result) -> Void) + func requestDelete(parameter: Int, completionHandler: @escaping (Result) -> Void) +} + -class DataTask: SessionDataTask { +class DataTask: SessionDataTask, APIService { private let encoder = JSONEncoder() private let decoder = JSONDecoder() private let api: ServerAPI @@ -78,6 +84,29 @@ class DataTask: SessionDataTask { }.resume() } + func requestDelete(parameter: Int, completionHandler: @escaping (Result) -> Void) { + guard var url = api.toURL(path: .all) else { + completionHandler(.failure(.invalidURL)) + return + } + + url.appendPathComponent("\(parameter)") + var request = URLRequest(url: url) + request.httpMethod = "DELETE" + + self.session.dataTask(with: request) { data, response, error in + guard + let data = data, + let cardResponse = try? self.decoder.decode(CardAPIResponse.self, from: data) + else { + completionHandler(.failure(.notConnect)) + return + } + + completionHandler(.success(cardResponse.cardId)) + }.resume() + } + private func makeRequestContainCookie(with url: URL) -> URLRequest? { var request = URLRequest(url: url) guard let baseUrl = api.toURL(path: .base), diff --git a/Client/iOS/TodoList/Network/CardsFetchingDebugDataTask.swift b/Client/iOS/TodoList/Network/CardsFetchingDebugDataTask.swift index dfe8b1109..1ea960700 100644 --- a/Client/iOS/TodoList/Network/CardsFetchingDebugDataTask.swift +++ b/Client/iOS/TodoList/Network/CardsFetchingDebugDataTask.swift @@ -47,6 +47,29 @@ class DebugDataTask: SessionDataTask { }.resume() } + func requestDelete(parameter: Int, completionHandler: @escaping (Result) -> Void) { + guard var url = api.toURL(path: .all) else { + completionHandler(.failure(.invalidURL)) + return + } + + url.appendPathComponent("\(parameter)") + var request = URLRequest(url: url) + request.httpMethod = "DELETE" + + self.session.dataTask(with: request) { data, response, error in + guard + let data = data, + let cardResponse = try? self.decoder.decode(CardAPIResponse.self, from: data) + else { + completionHandler(.failure(.notConnect)) + return + } + + completionHandler(.success(cardResponse.cardId)) + } + } + private func makeURLComponents(from string: String? = nil, using parameter: [String: String]? = nil) -> URLComponents? { if let string = string { diff --git a/Client/iOS/TodoList/Network/ResourcesNetwork/CardAPIResponse.swift b/Client/iOS/TodoList/Network/ResourcesNetwork/CardAPIResponse.swift new file mode 100644 index 000000000..120a0964b --- /dev/null +++ b/Client/iOS/TodoList/Network/ResourcesNetwork/CardAPIResponse.swift @@ -0,0 +1,5 @@ +import Foundation + +struct CardAPIResponse: Codable { + var cardId: Int +} diff --git a/Client/iOS/TodoList/Network/ResourcesNetwork/CardMap.swift b/Client/iOS/TodoList/Network/ResourcesNetwork/CardMap.swift index 8536d9ce0..c2c9f144d 100644 --- a/Client/iOS/TodoList/Network/ResourcesNetwork/CardMap.swift +++ b/Client/iOS/TodoList/Network/ResourcesNetwork/CardMap.swift @@ -11,6 +11,6 @@ struct CardMap: Codable { var cardMap: TodoList struct TodoList: Codable { - var todo: [CardData] + var TODO: [CardData] } } diff --git a/Client/iOS/TodoList/Repository/CardDataMiddleWare.swift b/Client/iOS/TodoList/Repository/CardDataMiddleWare.swift index 27a9c674c..4e168b7c2 100644 --- a/Client/iOS/TodoList/Repository/CardDataMiddleWare.swift +++ b/Client/iOS/TodoList/Repository/CardDataMiddleWare.swift @@ -2,11 +2,9 @@ import UIKit class CardDataMiddleWare { - private var fetchResultInBoard = [TodoBoard: Result<[CardData], DataTaskError>]() { + private var fetchResultInBoard = [TodoBoard: Result<[Card], DataTaskError>]() { willSet { - for board in newValue.keys { - guard let result = newValue[board] else { continue } NotificationCenter .default @@ -15,42 +13,57 @@ class CardDataMiddleWare { object: self, userInfo: ["result":result] ) - } } } - private var dataTask: DebugDataTask? + private var dataTask: APIService? + private let persistenceProvider: PersistenceProvider? - init(_ dataTaskGenerator: ()->DebugDataTask?) { + init(persistenceProvider: PersistenceProvider, dataTaskGenerator: ()-> APIService?) { + self.persistenceProvider = persistenceProvider self.dataTask = dataTaskGenerator() } func fetchAllCards() { + guard let resultFromCoredata = persistenceProvider?.fetchAllCard(where: .all) else { + return + } + dataTask?.fetchAll(dataType: CardMap.self) { [weak self] (result: Result) in - guard let self = self else { return } - + for board in TodoBoard.allCases { - switch result { - case .success(let cardMap): - - self.fetchResultInBoard[board] = Result.success(cardMap.cardMap.todo) + let todos = cardMap.cardMap.TODO.map{ Card(id: $0.id, title: $0.title, contents: $0.contents, boardName: $0.boardName) } + // TODO:- board의 이름에 따라 type값이 달라져야 한다 + self.persistenceProvider?.insertBoard(type: .todo, cards: todos) + self.fetchResultInBoard[board] = Result.success(todos) case .failure(let error): - Log.error(error) + self.fetchResultInBoard[board] = Result.success(resultFromCoredata) } } } } - func getCards(in board: TodoBoard) -> Result<[CardData], DataTaskError> { + func deleteCard(id cardId: Int, at boardType: TodoBoard) { + dataTask?.requestDelete(parameter: cardId) { [weak self] result in + + guard let id = try? result.get(), var fetchedResult = try? self?.fetchResultInBoard[boardType]?.get() else { + return + } + + fetchedResult.removeAll(where: {data in data.id == id}) + self?.fetchResultInBoard[boardType] = Result.success(fetchedResult) + } + } + + func getCards(in board: TodoBoard) -> Result<[Card], DataTaskError> { guard fetchResultInBoard.keys.contains(board), let result = fetchResultInBoard[board] else { return Result.failure(DataTaskError.notConnect) } - return result } } diff --git a/Client/iOS/TodoList/SceneDelegate.swift b/Client/iOS/TodoList/SceneDelegate.swift index cf626ea15..fc9481b48 100644 --- a/Client/iOS/TodoList/SceneDelegate.swift +++ b/Client/iOS/TodoList/SceneDelegate.swift @@ -13,9 +13,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { - // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. - // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. - // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). guard let _ = (scene as? UIWindowScene) else { return } } diff --git a/Client/iOS/TodoList/View/TodoRepositoryRespondWrapper.swift b/Client/iOS/TodoList/View/TodoRepositoryRespondWrapper.swift index b57e647f4..1eaf7845c 100644 --- a/Client/iOS/TodoList/View/TodoRepositoryRespondWrapper.swift +++ b/Client/iOS/TodoList/View/TodoRepositoryRespondWrapper.swift @@ -9,7 +9,7 @@ class TodoRepositoryRespondWrapper: NSObject { var todoTableView: TodoTableView var todoBoard: TodoBoard - private(set) var dataSource = [CardData]() + private(set) var dataSource = [Card]() var badgeDelegate: TodoBadgeDelegate? init(_ tableView: TodoTableView, in board: TodoBoard) { @@ -28,7 +28,7 @@ class TodoRepositoryRespondWrapper: NSObject { guard let info = noti.userInfo, - let result = info["result"] as? Result<[CardData], DataTaskError> + let result = info["result"] as? Result<[Card], DataTaskError> else { return } @@ -51,13 +51,13 @@ class TodoRepositoryRespondWrapper: NSObject { return (0.. Int { - dataSource.count + let result = (dataSource.count * 2) - 1 + return result < 0 ? 0 : result } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + + if 0 < indexPath.row, indexPath.row.isMultiple(of: 2) == false { + let cell = UITableViewCell() + cell.heightAnchor.constraint(equalToConstant: 16).isActive = true + cell.contentView.backgroundColor = UIColor(named: "gray6") + return cell + } + guard let cell = tableView.dequeueReusableCell(withIdentifier: TodoTableViewCell.cellName, for: indexPath) as? TodoTableViewCell else { return UITableViewCell() } - cell.applyTextAllLabels(data: dataSource[indexPath.row]) - + cell.applyTextAllLabels(data: dataSource[indexPath.row / 2]) + cell.isUserInteractionEnabled = true return cell } } -extension TodoRepositoryRespondWrapper: UITableViewDelegate { } +extension TodoRepositoryRespondWrapper: UITableViewDelegate { + + func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { + + if let cell = tableView.cellForRow(at: indexPath) as? TodoTableViewCell { + cell.roundedView.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMinXMinYCorner] + } + + let action = UIContextualAction( + style: .destructive, + title: "Delete") + { [weak self] action, view, completion in + + guard let self = self else { return } + + AppDelegate.middleWare.deleteCard(id: self.dataSource[indexPath.row/2].id, at: self.todoBoard) + completion(true) + action.backgroundColor = .red + } + + return UISwipeActionsConfiguration(actions: [action]) + } + + func tableView(_ tableView: UITableView, didEndEditingRowAt indexPath: IndexPath?) { + if let indexPath = indexPath, let cell = tableView.cellForRow(at: indexPath) as? TodoTableViewCell { + cell.roundedView.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMinXMinYCorner, .layerMaxXMaxYCorner, .layerMaxXMinYCorner] + } + } +} diff --git a/Client/iOS/TodoList/ViewControllers/ViewController.swift b/Client/iOS/TodoList/ViewControllers/ViewController.swift index 0a96c2d0c..846ff92e1 100644 --- a/Client/iOS/TodoList/ViewControllers/ViewController.swift +++ b/Client/iOS/TodoList/ViewControllers/ViewController.swift @@ -6,6 +6,7 @@ protocol TodoEndPointViewController { } class ViewController: UIViewController { + override func viewDidLoad() { super.viewDidLoad() }