Skip to content

공통 모듈

Hiju edited this page Dec 1, 2021 · 37 revisions

Extensions

UIAlertController

UIColor

UIFont

UIButton

UIView

NSAttributedString

UIResponder

//
//  UIResponder+Extension.swift
//  Booster
//
//  Created by Hani on 2021/11/08.
//

import UIKit

extension UIResponder {
    static var identifier: String {
        String(describing: self)
    }
}

TimeInterval

extension TimeInterval {
    private var seconds: Int {
        return Int(self) % 60
    }

    private var minutes: Int {
        return (Int(self) / 60) % 60
    }

    private var hours: Int {
        return Int(self) / 3600
    }

    func stringToMinutesAndSeconds() -> String {
        return "\(hours)h \(minutes)m"
    }
}

NWPathMonitor

var rx: Observable<NWPath> {
    Observable.create { [weak self] observer in
      self?.pathUpdateHandler = { path in
        observer.onNext(path)
      }
      self?.start(queue: DispatchQueue.global())
      return Disposables.create { self?.cancel() }
    }
  }

reachability를 Rx로 사용가능 하기 위해 정의

Managers

HealthStoreManager (Deprecated)

CoreDataManager (Deprecated)

HealthKitManager

//
//  HealthKitManager.swift
//  Booster
//
//  Created by Hani on 2021/11/23.
//

import Foundation
import HealthKit
import RxSwift

typealias HealthQuantityType = HealthKitManager.HealthQuantityType
typealias HealthUnit = HealthKitManager.HealthUnit

final class HealthKitManager {
    enum HealthQuantityType: CaseIterable {
        case steps, runing, energy

        var quantity: HKQuantityType? {
            switch self {
            case .steps: return .quantityType(forIdentifier: .stepCount)
            case .runing: return .quantityType(forIdentifier: .distanceWalkingRunning)
            case .energy: return .quantityType(forIdentifier: .activeEnergyBurned)
            }
        }
    }

    enum HealthUnit: CaseIterable {
        case count, kilometer, calorie
        var unit: HKUnit {
            switch self {
            case .count: return .count()
            case .kilometer: return .meterUnit(with: .kilo)
            case .calorie: return .kilocalorie()
            }
        }
    }

    enum HealthKitError: Error {
        case optionalCasting
        case removeAllDataFail
    }

    static let shared = HealthKitManager()

    private var healthStore: HKHealthStore?

    private init() {
        if HKHealthStore.isHealthDataAvailable() {
            healthStore = HKHealthStore()
        }
    }

    func requestAuthorization(shareTypes: Set<HKSampleType>, readTypes: Set<HKSampleType>) -> Single<Bool> {

        return Single.create { [weak self] single in
            self?.healthStore?.requestAuthorization(toShare: shareTypes, read: readTypes) { (success, error) in
                guard error == nil
                else { return  }

                return single(.success(success))
            }

            return Disposables.create()
        }
    }

    func requestStatisticsCollectionQuery(type: HKQuantityType,
                                          predicate: NSPredicate,
                                          interval: DateComponents,
                                          anchorDate: Date) -> Single<HKStatisticsCollection> {
        return Single.create { [weak self] single in
            let query = HKStatisticsCollectionQuery(
                quantityType: type,
                quantitySamplePredicate: predicate,
                options: .cumulativeSum,
                anchorDate: anchorDate,
                intervalComponents: interval
            )

            query.initialResultsHandler = { _, hkStatisticsCollection, _ in
                if let hkStatisticsCollection = hkStatisticsCollection {
                    return single(.success(hkStatisticsCollection))
                }
            }

            self?.healthStore?.execute(query)

            return Disposables.create()
        }
    }

    func requestStatisticsQuery(type: HKQuantityType, predicate: NSPredicate) -> Single<HKStatistics?> {
        return Single.create { [weak self] single in
            let query = HKStatisticsQuery(
                quantityType: type,
                quantitySamplePredicate: predicate,
                options: [.cumulativeSum, .duration]) { _, statistics, _ in
                    return single(.success(statistics))
                }

            self?.healthStore?.execute(query)

            return Disposables.create()
        }
    }

    func save(count: Double,
              start: Date,
              end: Date,
              quantity: HealthQuantityType,
              unit: HealthUnit) {
        guard let healthStore = healthStore,
                let type = quantity.quantity
        else { return }

        let unit = unit.unit
        let countQuantity = HKQuantity(unit: unit, doubleValue: count)
        let sample = HKQuantitySample(type: type,
                                      quantity: countQuantity,
                                      start: start,
                                      end: end)

        healthStore.save(sample) { _, _ in }
    }
    
    func removeAll() -> Single<Bool> {
        return Single.create { [weak self] single in
            let predicate = HKQuery.predicateForObjects(from: HKSource.default())

            for type in HealthQuantityType.allCases {
                guard let quantity = type.quantity
                else { continue }

                self?.healthStore?.deleteObjects(of: quantity, predicate: predicate) { (isDeleted, count, error) in
                    if !isDeleted,
                       let error = error {
                        single(.failure(error))
                        return
                    }

                    single(.success(isDeleted))
                }
            }
            return Disposables.create()
        }
    }
}

Core Data Manager

    func save(attributes: [String: Any],
              type name: String,
              completion handler: @escaping (Result<Void, Error>) -> Void) {
        guard let entity = NSEntityDescription.entity(forEntityName: name, in: container.viewContext)
        else { return }

        let backgroundContext = container.newBackgroundContext()

        backgroundContext.perform { [weak self] in
            guard let self = self
            else { return }

            let entityObject = NSManagedObject(entity: entity, insertInto: self.container.viewContext)
            attributes.forEach { entityObject.setValue($0.value, forKey: $0.key) }

            let context = self.container.viewContext

            do {
                try context.save()
                handler(.success(()))
            } catch let error {
                handler(.failure(error))
            }
        }
    }

    func save(attributes: [String: Any],
              type name: String) -> Observable<Void> {
        return Observable.create { observer in

            guard let entity = NSEntityDescription.entity(forEntityName: name, in: self.container.viewContext)
            else { return Disposables.create() }

            let backgroundContext = self.container.newBackgroundContext()

            backgroundContext.performAndWait { [weak self] in
                guard let self = self
                else { return }

                let entityObject = NSManagedObject(entity: entity, insertInto: self.container.viewContext)
                attributes.forEach { entityObject.setValue($0.value, forKey: $0.key) }

                let context = self.container.viewContext

                do {
                    try context.save()
                    observer.onNext(())
                } catch let error {
                    observer.onError(error)
                }
            }
            return Disposables.create()
        }
    }

    func save<DataType: NSManagedObject>(value: [String: Any],
              type name: String,
              predicate: NSPredicate,
              completion handler: @escaping (Result<DataType, Error>) -> Void) {
        let backgroundContext = container.newBackgroundContext()

        backgroundContext.perform { [weak self] in
            guard let self = self
            else { return }

            let request = NSFetchRequest<DataType>.init(entityName: name)
            request.predicate = predicate

            do {
                let objects = try self.container.viewContext.fetch(request)
                value.forEach { objects[0].setValue($0.value, forKey: $0.key) }
                try self.container.viewContext.save()
                handler(.success(objects[0]))
            } catch let error {
                handler(.failure(error))
            }
        }
    }

    func update(entityName: String, attributes: [String: Any], predicate: NSPredicate? = nil) -> Observable<Void> {
        return Observable.create { [weak self] observer in
            guard let self = self
            else { return Disposables.create() }

            let backgroundContext = self.container.newBackgroundContext()

            backgroundContext.perform {
                let request: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: entityName)
                request.predicate = predicate
                do {
                    let context = self.container.viewContext
                    let result = try context.fetch(request)
                    guard let updateModel = result.first as? NSManagedObject
                    else { return }

                    for element in attributes {
                        updateModel.setValue(element.value, forKey: element.key)
                    }

                    try context.save()

                    observer.onCompleted()
                } catch let error {
                    self.container.viewContext.rollback()
                    observer.onError(error)
                }
            }
            return Disposables.create()
        }
    }

    func fetch<DataType: NSManagedObject>() -> Observable<[DataType]> {
        return Observable.create { [weak self] observer in
            guard let self = self
            else { return Disposables.create() }

            let backgroundContext = self.container.newBackgroundContext()

            backgroundContext.perform {
                do {
                    let context = try self.container.viewContext.fetch(DataType.fetchRequest())
                    guard let context = context as? [DataType]
                    else { return }

                    observer.onNext(context)
                } catch let error {
                    observer.onError(error)
                }
            }
            return Disposables.create()
        }
    }

    func fetch<DataType: NSManagedObject>(completion handler: @escaping (Result<[DataType], Error>) -> Void) {
        let backgroundContext = container.newBackgroundContext()

        backgroundContext.perform { [weak self] in
            guard let self = self
            else { return }

            do {
                let context = try self.container.viewContext.fetch(DataType.fetchRequest())
                guard let context = context as? [DataType]
                else { return }

                handler(.success(context))
            } catch let error {
                handler(.failure(error))
            }
        }
    }

    func fetch<DataType: NSManagedObject>(entityName: String,
                                          predicate: NSPredicate,
                                          completion handler: @escaping (Result<[DataType], Error>) -> Void) {
        let backgroundContext = container.newBackgroundContext()

        backgroundContext.perform { [weak self] in
            guard let self = self
            else { return }

            let request = NSFetchRequest<DataType>.init(entityName: entityName)
            request.predicate = predicate
            do {
                let result = try self.container.viewContext.fetch(request)
                handler(.success(result))
            } catch let error {
                handler(.failure(error))
            }
        }
    }

    func fetch<DataType: NSManagedObject>(entityName: String,
                                          predicate: NSPredicate) -> Observable<[DataType]> {
        return Observable.create { [weak self] observer in
            guard let self = self
            else { return Disposables.create() }

            let backgroundContext = self.container.newBackgroundContext()

            backgroundContext.perform {
                let request = NSFetchRequest<DataType>.init(entityName: entityName)
                request.predicate = predicate
                do {
                    let result = try self.container.viewContext.fetch(request)
                    observer.onNext(result)
                } catch let error {
                    observer.onError(error)
                }
            }
            return Disposables.create()
        }
    }

    func delete<DataType: NSManagedObject>(entityName: String,
                                           predicate: NSPredicate,
                                           completion handler: @escaping (Result<DataType, Error>) -> Void) {
        let backgroundContext = container.newBackgroundContext()

        backgroundContext.perform { [weak self] in
            guard let self = self
            else { return }

            let request = NSFetchRequest<DataType>.init(entityName: entityName)
            request.predicate = predicate

            do {
                let objects = try self.container.viewContext.fetch(request)
                self.container.viewContext.delete(objects[0])
                try self.container.viewContext.save()
                handler(.success(objects[0]))
            } catch let error {
                handler(.failure(error))
            }
        }
    }

    func delete(entityName: String, completion handler: @escaping (Result<Void, Error>) -> Void) {
        let backgroundContext = container.newBackgroundContext()

        backgroundContext.perform { [weak self] in
            guard let self = self
            else { return }

            let request = NSFetchRequest<NSFetchRequestResult>.init(entityName: entityName)

            do {
                let delete = NSBatchDeleteRequest(fetchRequest: request)
                try self.container.viewContext.execute(delete)
                handler(.success(()))
            } catch let error {
                handler(.failure(error))
            }
        }
    }

    func delete(entityName: String, predicate: NSPredicate) -> Observable<Void> {
        return Observable.create { [weak self] observer in
            guard let self = self
            else { return Disposables.create() }

            let backgroundContext = self.container.newBackgroundContext()

            backgroundContext.perform {
                let request: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: entityName)
                request.predicate = predicate
                do {
                    let objects = try self.container.viewContext.fetch(request)
                    guard let model = objects.first as? NSManagedObject
                    else { return }
                    self.container.viewContext.delete(model)
                    try self.container.viewContext.save()
                    observer.onCompleted()
                } catch let error {
                    observer.onError(error)
                }
            }
            return Disposables.create()
        }
    }
  • 불러오기는 모두 다 불러오는 fetch & NSPredicate 사용하는 일부 fetch
  • 저장은 일반적인 save & update 가능
  • 삭제는 일부 delete & all delete

Booster🚀🔥

Info

Rule

Backlog

공통 모듈

구현 설명 및 기능 정리

Architecture

Architecture

회의록 & DailyScrum & 회고록

멘토링 피드백

멘토링 피드백
Clone this wiki locally