diff --git a/Classes/Bookmark/BookmarkStore.swift b/Classes/Bookmark/BookmarkStore.swift index c619e154..dce1fc14 100644 --- a/Classes/Bookmark/BookmarkStore.swift +++ b/Classes/Bookmark/BookmarkStore.swift @@ -9,26 +9,33 @@ import Foundation final class BookmarksStore { - static let shared = BookmarksStore() - private let archivePath: String = { - return FileManager.default - .urls(for: .documentDirectory, in: .userDomainMask) - // crash intentionally on startup if doesn't exist - .first! - .appendingPathComponent("bookmarks") - .path - }() - private var _bookmarks: Set + private let fileManager = FileManager.default + private var documentDirectory: FileManager.SearchPathDirectory { + return .documentDirectory + } + + private var archivePath: String { + guard let path = NSSearchPathForDirectoriesInDomains(documentDirectory, .userDomainMask, true).first else { + fatalError("App document directory cannot be found") + } + + let folder = URL(fileURLWithPath: path).appendingPathComponent("com.whoisryannystrom.freetime.bookmarks") + if !fileManager.fileExists(atPath: folder.path) { + try? fileManager.createDirectory(at: folder, withIntermediateDirectories: false, attributes: nil) + } + return folder.appendingPathComponent(token).path + } + + private var token: String + private var _bookmarks: Set = [] // MARK: Init - init() { - if let bookmarks = NSKeyedUnarchiver.unarchiveObject(withFile: archivePath) as? Set { - _bookmarks = bookmarks - } else { - _bookmarks = [] - } + init(_ userToken: String?) { + guard let userToken = userToken else { fatalError() } + token = userToken + refresh() } // MARK: Public API @@ -53,13 +60,31 @@ final class BookmarksStore { } var bookmarks: [BookmarkModel] { + refresh() return Array(_bookmarks) } // MARK: Private API - - func archive() { - NSKeyedArchiver.archiveRootObject(_bookmarks, toFile: self.archivePath) + + func refresh() { + if let bookmarks = NSKeyedUnarchiver.unarchiveObject(withFile: archivePath) as? Set { + _bookmarks = bookmarks + } } + func archive() { + NSKeyedArchiver.archiveRootObject(_bookmarks, toFile: archivePath) + } + + func clearStorage() { + do { + try FileManager.default.removeItem(atPath: archivePath) + } catch { + print("Can not delete bookmarks file") + } + } + + var bookmarkPath: String { + return archivePath + } } diff --git a/Classes/Bookmark/BookmarksViewController.swift b/Classes/Bookmark/BookmarksViewController.swift index 330df860..ab820c70 100644 --- a/Classes/Bookmark/BookmarksViewController.swift +++ b/Classes/Bookmark/BookmarksViewController.swift @@ -17,7 +17,7 @@ TabNavRootViewControllerType { private let client: GithubClient private let cellIdentifier = "bookmark_cell" - private let bookmarkStore = BookmarksStore.shared + private let bookmarkStore:BookmarksStore private var searchController: UISearchController { let controller = UISearchController(searchResultsController: nil) @@ -39,6 +39,8 @@ TabNavRootViewControllerType { init(client: GithubClient) { self.client = client + self.bookmarkStore = BookmarksStore(client.userSession?.token) + super.init(nibName: nil, bundle: nil) } diff --git a/Classes/Issues/IssuesViewController.swift b/Classes/Issues/IssuesViewController.swift index ec9625aa..3509e6db 100644 --- a/Classes/Issues/IssuesViewController.swift +++ b/Classes/Issues/IssuesViewController.swift @@ -282,7 +282,16 @@ IssueCommentSectionControllerDelegate { title: current.title.attributedText.string ) - return AlertAction.bookmark(bookmarkModel) + let store = BookmarksStore(client.userSession?.token) + let isNewBookmark = !store.contains(bookmark: bookmarkModel) + return AlertAction.toggleBookmark(isNewBookmark) { _ in + if isNewBookmark { + store.add(bookmark: bookmarkModel) + } else { + store.remove(bookmark: bookmarkModel) + } + Haptic.triggerNotification(.success) + } } @objc diff --git a/Classes/Repository/RepositoryViewController.swift b/Classes/Repository/RepositoryViewController.swift index dbd0bf76..aa68c563 100644 --- a/Classes/Repository/RepositoryViewController.swift +++ b/Classes/Repository/RepositoryViewController.swift @@ -112,7 +112,17 @@ NewIssueTableViewControllerDelegate { owner: repo.owner, hasIssueEnabled: repo.hasIssuesEnabled ) - return AlertAction.bookmark(bookmarkModel) + let store = BookmarksStore(client.userSession?.token) + let isNewBookmark = !store.contains(bookmark: bookmarkModel) + return AlertAction.toggleBookmark(isNewBookmark) { _ in + if isNewBookmark { + store.add(bookmark: bookmarkModel) + } + else { + store.remove(bookmark: bookmarkModel) + } + Haptic.triggerNotification(.success) + } } @objc diff --git a/Classes/Utility/AlertAction.swift b/Classes/Utility/AlertAction.swift index 76dc9b0f..6ec3cbcc 100644 --- a/Classes/Utility/AlertAction.swift +++ b/Classes/Utility/AlertAction.swift @@ -138,19 +138,8 @@ struct AlertAction { return UIAlertAction(title: Constants.Strings.clearAll, style: .destructive, handler: handler) } - static func bookmark(_ bookmark: BookmarkModel) -> UIAlertAction { - let isNewBookmark = !BookmarksStore.shared.contains(bookmark: bookmark) + static func toggleBookmark(_ isNewBookmark: Bool, handler: AlertActionBlock? = nil) -> UIAlertAction { let title = isNewBookmark ? Constants.Strings.bookmark : Constants.Strings.removeBookmark - return UIAlertAction( - title: title, - style: isNewBookmark ? .default : .destructive - ) { _ in - if isNewBookmark { - BookmarksStore.shared.add(bookmark: bookmark) - } else { - BookmarksStore.shared.remove(bookmark: bookmark) - } - Haptic.triggerNotification(.success) - } + return UIAlertAction(title: title, style: isNewBookmark ? .default : .destructive, handler: handler) } } diff --git a/FreetimeTests/BookmarkStoreTests.swift b/FreetimeTests/BookmarkStoreTests.swift index a8af9c21..5ea7078e 100644 --- a/FreetimeTests/BookmarkStoreTests.swift +++ b/FreetimeTests/BookmarkStoreTests.swift @@ -10,58 +10,67 @@ import XCTest @testable import Freetime final class BookmarkStoreTests: XCTestCase { - + var store: BookmarksStore! + override func setUp() { super.setUp() + store = BookmarksStore("user_token") + store.clear() // for a clean start } override func tearDown() { super.tearDown() - BookmarksStore.shared.clear() + store.clear() } func test_addBookMark() { let b1 = BookmarkModel(type: .repo, name: "GitHawk", owner: "rizwankce") - BookmarksStore.shared.add(bookmark: b1) + store.add(bookmark: b1) - XCTAssert(BookmarksStore.shared.bookmarks.count == 1) - XCTAssert(BookmarksStore.shared.bookmarks.first == b1) + XCTAssert(store.bookmarks.count == 1) + XCTAssert(store.bookmarks.first == b1) } func test_duplicateBookmarks() { let b1 = BookmarkModel(type: .repo, name: "GitHawk", owner: "rizwankce") let b2 = BookmarkModel(type: .repo, name: "GitHawk", owner: "rizwankce") - BookmarksStore.shared.add(bookmark: b1) - BookmarksStore.shared.add(bookmark: b2) + store.add(bookmark: b1) + store.add(bookmark: b2) - XCTAssert(BookmarksStore.shared.bookmarks.count == 1) + XCTAssert(store.bookmarks.count == 1) } func test_clearBookmarks() { let b1 = BookmarkModel(type: .repo, name: "GitHawk", owner: "rizwankce") - BookmarksStore.shared.add(bookmark: b1) - BookmarksStore.shared.clear() + store.add(bookmark: b1) + store.clear() - XCTAssert(BookmarksStore.shared.bookmarks.count == 0) + XCTAssert(store.bookmarks.isEmpty) } func test_removeBookmark() { let b1 = BookmarkModel(type: .repo, name: "GitHawk", owner: "rizwankce") - BookmarksStore.shared.add(bookmark: b1) - BookmarksStore.shared.remove(bookmark: b1) + store.add(bookmark: b1) + store.remove(bookmark: b1) - XCTAssert(BookmarksStore.shared.bookmarks.count == 0) + XCTAssert(store.bookmarks.count == 0) } - func test_containsBookmark() { + func test_bookmarksFromDifferentUser() { let b1 = BookmarkModel(type: .repo, name: "GitHawk", owner: "rizwankce") - - BookmarksStore.shared.add(bookmark: b1) - - XCTAssert(BookmarksStore.shared.contains(bookmark: b1)) + + let store1 = BookmarksStore("user_token1") + let store2 = BookmarksStore("user_token2") + + store1.add(bookmark: b1) + store2.add(bookmark: b1) + + XCTAssert(store1.bookmarks.count == 1) + XCTAssert(store2.bookmarks.count == 1) + XCTAssert(store1.bookmarkPath != store2.bookmarkPath) } }