Skip to content

Latest commit

 

History

History
164 lines (119 loc) · 7.72 KB

19차 스터디.md

File metadata and controls

164 lines (119 loc) · 7.72 KB

통신Data CollectionViewCell image처리 + CoreData 톺아보기


  • Carousel 효과를 준 Custom CollectionView를 만듬
  • Cell을 xib로 커스텀해서 통신받은 imgURL로 이미지 처리함 But 문제발생
let soldierDataTask: URLSessionDataTask = session.dataTask(with: URL(string: APIConstants.soldierURL)!){ (data: Data?, response: URLResponse?, error: Error?) in
            
            if let error = error {
                print(error.localizedDescription)
                return
            }
            guard let data = data else { return }
            
            do {
                let apiResponse: SoldierModel = try JSONDecoder().decode(SoldierModel.self, from: data)
                
                self.soldiers = apiResponse.data
              
                DispatchQueue.main.async {
                    
                    self.pagingCollectionView.reloadData()
                 
                }
                
                
            }catch(let err){
                print(err.localizedDescription)
            }
            
        }
        
        soldierDataTask.resume()
  • 위와같이 통신을 ViewDidAppear()에서 처리 디스패치큐를 통해 메인에서 reloadData( )
DispatchQueue.global().async {
guard let imageURL = URL(string: self.soldiers[indexPath.row].soldierprofile) else  {
                return
        }
            guard let imageData: Data = try? Data(contentsOf: imageURL) else {
                return
            }
            
            DispatchQueue.main.async {
//                if let index: IndexPath = self.pagingCollectionView.indexPath(for: cell){
//                    if index.row == indexPath.row{
                        
                        cell.soldierProfileImageView.image = UIImage(data: imageData)
                        
//                    }else{
//                        cell.soldierProfileImageView.image = UIImage(named: "mainBtnMail")
//
//                    }
//                }
            }
        }
  • cellForItemAt 메소드에서 다음과 같은 코드를 실행
  • TableView, CollectionView 에서도 셀을 재활용하기 때문에 indexPath값에 맞는 데이터와 이미지를 넣어줘야 재활용 후에도 데이터가 뒤바뀌지 않음
  • 그래서 if let index 부분과 if index.row == indexPath.row 부분이 필요 [내가보는 indexPath값과 데이터처리의 indexPath값을 동기화]
  • But , 주석부분을 포함한 코드로 실행시 이미지가 1,2번째 셀에만 들어가게 되는 버그 발생 [prepareForReuse()도 사용했으나 같은 버그 계속 발생]
  • 메인에서 indexPath부분을 지우면 정상적으로 데이터 처리가 되어짐

Why?

  • 이미지크기가 커지면(기준은 모호함) 로딩에 시간이 걸리기 때문에 indexPath값을 맞게 넣어줘야 하지만 지금은 이미지의 크기가 작아서(2메가바이트 미만) 셀과 데이터를 로딩하는데 매우 빠르기 때문에 오히려 indexPath를 맞춰주는 작업을 하면 데이터가 맞게 들어가지지가 않음
  • 즉, 이미지캐싱과 데이터 그리고 셀의 indexPath 재사용 동기화문제라고 볼 수 있음

킹피셔쓰는게 편할 수도?(킹피셔로 이미지 처리시 문제가 해결될지 모르겠음 시간내어서 해보기로!) 또한 데이터의 이미지 처리를 셀로 넘겨줘서 셀에서 처리시키는 방법으로도 해결 가능해 보임


CoreData 참고자료

스크린샷 2020-06-12 오후 1 28 52

  • Project중간에 CoreData사용을 위해서 Data Model 생성

스크린샷 2020-06-12 오후 1 29 10

  • Codegen -> Manual/None 으로 필수로 바꿔줘야함 , 서브클래스가 남아 있어서 눈에 보이지 않는 에러와 충돌 발생
  • CoreData Model에 원하는 id, Devices 등 데이터의 모델 생성 String, int64 등등
  1. Codegen: 해당 entity에 대한 클래스 선언을 자동으로 만들어 주는 옵션을 설정합니다.
    • None/Manual: 관련 파일을 자동으로 만들어주지 않습니다. 개발자는 DataModel을 선택한 상태에서 Editor-Create NSManagedObject Subclass 항목을 클릭하여 클래스 선언 파일과 프로퍼티 extension 파일을 빌드시마다 추가시켜 주고, 이를 수동으로 관리해야 합니다.
    • Class Definition: 클래스 선언 파일과 프로퍼티 관련 extension 파일을 빌드시마다 자동으로 추가시켜줍니다. 따라서 관련된 파일을 전혀 추가시켜줄 필요가 없습니다.(그래서도 안됩니다. 만약 수동으로 추가시켜준 상태에서 빌드를 시도하면 컴파일 에러가 발생합니다.)
    • Category/Extension: 프로퍼티 관련 extension파일만 자동으로 추가시켜 줍니다. 즉, 클래스 선언에는 사용자가 원하는 로직을 자유롭게 추가할 수 있습니다.

CoreData사용을 위한 핵심!! AppDelegate에 해당 코드 추가!! [안하면 작동 불가]

class AppDelegate: UIResponder, UIApplicationDelegate {

    // MARK: - CoreData
    lazy var persistentContainer: NSPersistentContainer = {
        let container = NSPersistentContainer(name: "Users") // 파일명 적기(CoreDataModel)
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error {
                fatalError("Unresolved error, \((error as NSError).userInfo)")
            }
        })
        return container
    }()
  • CoreData를 앱의 메인 컨테이너에 불러오는 작업이라 생각됨 (코어데이터가 로드되지 않으면 당연히 쓸 수 없다!_!)
class CoreDataManager {
    static let shared: CoreDataManager = CoreDataManager()
    
    let appDelegate: AppDelegate? = UIApplication.shared.delegate as? AppDelegate
    lazy var context = appDelegate?.persistentContainer.viewContext
    
    let modelName: String = "Users"
    
    func getUsers(ascending: Bool = false) -> [Users] {
        var models: [Users] = [Users]()
        
        if let context = context {
            let idSort: NSSortDescriptor = NSSortDescriptor(key: "id", ascending: ascending)
            let fetchRequest: NSFetchRequest<NSManagedObject>
                = NSFetchRequest<NSManagedObject>(entityName: modelName)
            fetchRequest.sortDescriptors = [idSort]
            
            do {
                if let fetchResult: [Users] = try context.fetch(fetchRequest) as? [Users] {
                    models = fetchResult
                }
            } catch let error as NSError {
                print("Could not fetch🥺: \(error), \(error.userInfo)")
            }
        }
        return models
    }
}
  • CoreDataManager를 만들어서 context를 통한 접근(직접적인 접근이 아닌 간접접근으로 더욱 안전한게 데이터 관리)을 해주는 클래스 생성
  • get,save,delete 메소드를 만들고 이를 통해 중간의 데이터 모델 값을 수정하는 작업도 구현 가능

  • MainViewController에서 다음과 같은 코드로 사용 가능
 fileprivate func saveNewUser(_ id: Int64, name: String) {
        CoreDataManager.shared
            .saveUser(id: id, name: name, age: 16, date: Date(),
                      devices: [Devices.iMac.rawValue, Devices.iPad.rawValue]) { onSuccess in
                        print("saved = \(onSuccess)")
        }
    }
//사용자등록을 하는 Action과 붙여서 사용