Giter Club home page Giter Club logo

pdfviewer's Introduction

PDF Viewer

📖 목차

  1. 📢 소개
  2. 👤 팀원
  3. ⏱️ 타임라인
  4. 📊 UML & 파일트리
  5. 📱 실행 화면
  6. 🤔 고민한 점
  7. 🔗 참고 링크

1. 📢 소개

URL로 PDF 파일을 추가하세요! 북마크, 하이라이트, 메모도 가능합니다!

핵심 개념 및 경험

  • MVVM
    • 프로젝트의 가독성을 높이기 위해 MVVM 패턴을 이용해 프로젝트 파일 분리
  • Combine
    • ViewModel의 데이터를 View에 바인딩하기 위해 Combine 사용
  • PDFKit
    • PDF 파일을 활용하고 사용자에게 보여주기 위해 PDFKit 사용
  • Realm
    • 일기의 데이터를 로컬에 저장하기 위해 Realm을 이용한 저장 기능 구현
  • UISearchController
    • UISearchController를 이용한 검색 기능 구현
  • Unit Test
    • ViewModel이 정상적으로 동작하는지 확인하기 위해 Unit Test 구현

2. 👤 팀원

Erick

3. ⏱️ 타임라인

프로젝트 기간 : 2023.10.09 ~ 2023.10.17

날짜 내용
2023.10.09 ▫️ 사용 기술 선정
▫️ PDFListViewController UI 구현
2023.10.10 ▫️ PDFListCell, PDFDetailViewController UI 구현
▫️ PDFData 엔티티 생성
2023.10.11 ▫️ PDFViewerUseCase 생성
▫️ PDFListViewModel 생성
▫️ PDFViewerCoordinator 생성
▫️ PDFDetailViewModel 생성
2023.10.16 ▫️ 북마크 기능 추가
▫️ 메모 기능 추가
▫️ realm을 이용한 로컬 데이터 저장 기능 구현

리펙토링 기간 : 2023.11.21 ~ 2023.12.3

날짜 내용
2023.11.21 ▫️ UISearchController를 이용한 검색 기능 추가
2023.11.27 ▫️ PDF 페이지 번호를 보여주는 기능 추가
2023.11.28 ▫️ highlight 기능 추가
▫️ highlight 데이터를 저장할 수 있도록 realm 마이그레이션
2023.12.01 ▫️ highlight 삭제 기능 추가
2023.12.03 ▫️ ViewModel 테스트 추가

4. 📊 UML & 파일트리

UML

파일트리

PDFViewer
├── Application
│   ├── AppDelegate.swift
│   ├── SceneDelegate.swift
│   └── DIContainer
│       └── DIContainer.swift
├── Domain
│   └── Entity
│       └── PDFData.swift
├── Presentation
│   ├── PDFList
│   │   ├── ViewModel
│   │   │   └── PDFListViewModel.swift
│   │   └── View
│   │       ├── PDFListCell.swift
│   │       └── PDFListViewController.swift
│   └── PDFDetail
│       ├── ViewModel
│       │   └── PDFDetailViewModel.swift
│       └── View
│           ├── PDFDetailViewController.swift
│           ├── PDFMemoViewController.swift
│           └── PageNumberView.swift
├── Data
│   ├── DTO
│   │   └── PDFDTO.swift
│   ├── Repositories
│   │   ├── Repository.swift
│   │   └── RealmRepository.swift
│   └── Translater
│       └── RealmTranslater.swift
├── Error
│   └── RepositoryError.swift
├── Utils
│   ├── Extensions
│   │   ├── Character+.swift
│   │   ├── String+.swift
│   │   ├── UITextView+.swift
│   │   └── UIViewController+.swift
│   ├── Manager
│   │   └── AlertManager.swift
│   └── NameSpace.swift
└── Resources
   ├── Assets.xcassets
   └── Info.plist

5. 📱 실행 화면

PDF 추가 및 삭제 PDF 검색
북마크 북마크 이동
하이라이트 메모

6. 🤔 고민한 점

1️⃣ 파일 분리

프로젝트의 가독성을 높이기 위해 파일 분리와 그룹화에 대해 고민을 했습니다. MVVM 패턴을 사용하여 Model, ViewModel, View를 나누는 것을 시작으로 그 외에 객체와 파일을 역할 별로 나누어 그룹화했습니다.

기존에 사용했던 MVC 패턴과 달리 MVVMViewModel의 분리가 가능하고 testable 한 구현이 가능하다고 생각하여 MVVM 패턴을 사용했습니다.

Application : 앱의 생명주기를 관리하는 AppDelegate, SceneDelegate
└ DIContainer : 의존성 주입을 관리하는 객체
Domain
└ Entity : 앱에서 사용하는 데이터 모델
Presentation
├ ViewModel : View에 수신된 이벤트 관리 및 사용자에게 보여줄 데이터를 관리하는 객체
└ View : UI 관리 및 사용자 이벤트 수신을 담당하는 객체
Data
├ DTO : 로컬 DB에서 사용하기 위한 데이터 모델
├ Repository : 데이터를 저장 및 수정합니다.
└ Translater : DTOEntity로 변환하는 객체
Util : 프로젝트에 필요한 기능을 제공하는 Util 객체와 Extension 코드
Resource : 프로젝트에 필요한 자원


2️⃣ ViewModel

MVVM으로 프로젝트를 설계하며 ViewModel을 구현하는데 많은 고민을 했습니다.

ViewModel의 큰 역할을 InputOutput으로 나누어 사용자 이벤트를 받아오는 메서드와 데이터를 전달하는 프로퍼티로 나누어 구현했습니다.

PDFListViewModel

protocol PDFListViewModelInput {
  func storePDF(title: String, urlString: String) throws
  func deletePDF(at index: Int) throws
  func searchPDF(_ text: String)
}

protocol PDFListViewModelOutput {
  var pdfDatasPublisher: Published<[PDFData]>.Publisher { get }
  var searchPDFDatasPublisher: Published<[PDFData]>.Publisher { get }
}

typealias PDFListViewModel = PDFListViewModelInput & PDFListViewModelOutput

PDFListViewController

// MARK: - SearchResults Updating
extension PDFListViewController: UISearchResultsUpdating {
  func updateSearchResults(for searchController: UISearchController) {
    guard let text = searchController.searchBar.text?.lowercased() else {
      return
    }
    
    viewModel.searchPDF(text)
  }
}

View가 사용자로부터 받아온 이벤트를 Input의 메서드를 이용해 ViewModel이 알 수 있도록 했습니다.

// MARK: - Data Binding
extension PDFListViewController {
  // ...
  
  private func bindingSearchPDFData() {
    viewModel.searchPDFDatasPublisher.sink { pdfDatas in
      if !pdfDatas.isEmpty {
        self.loadCollectionView(pdfDatas)
      }
    }.store(in: &cancellables)
  }
}

Output을 이용해 데이터 바인딩 하여, Input으로 인해 바뀐 데이터을 View가 바로 업데이트할 수 있도록 했습니다.


3️⃣ PDFKit

PDF를 보여주기 위해 Apple에서 제공하는 프레임워크인 PDFKit을 사용했습니다.

PDFView로 UI를 구현하고 PDFDocument를 넣어 PDF를 볼 수 있도록 했습니다.
PDFView의 메서드를 활용하여 페이지 이동과 북마크 기능 등을 구현했습니다.

final class PDFDetailViewController: UIViewController {
  
  // MARK: - Private Property
  private let pdfView: PDFView
    
  // MARK: - Configure UI Object
  private func configurePDFView(pdfDocument: PDFDocument?) {
    DispatchQueue.main.async {
      self.pdfView.document = pdfDocument
    }
  }
    
  // MARK: - Move PDF Page
  @objc private func tapNextButton() {
    pdfView.goToNextPage(nil)
  }
    
  // ...
}

4️⃣ Realm

사용자의 PDF 데이터를 저장하기 위해 Realm을 이용하여 Repository를 구현했습니다.
딕셔너리 형태의 데이터를 저장하기 위해 CoreData가 아닌 Realm을 사용했습니다.

RealmRepository

final class RealmRepository: Repository {
  
  // MARK: - Static Property
  static let shared = RealmRepository()
  
  // MARK: - Private Property
  private var realm: Realm
    
  // ...
}

DefaultPDFListViewModel

final class DefaultPDFListViewModel: PDFListViewModel {
  
  // MARK: - Private Property
  private let repository: Repository
  
  // ...
}

ViewModel에서 RealmRepository를 이용해서 사용자 데이터를 로컬에 영구저장할 수 있도록 했습니다.


7. 🔗 참고 링크

pdfviewer's People

Contributors

h-suo avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.