Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[team11][iOS] - 서버 통신 구현 마무리 및 위치 정보 관련 수정 #305

Open
wants to merge 16 commits into
base: team11
Choose a base branch
from

Conversation

wnsxor1993
Copy link
Collaborator

@wnsxor1993 wnsxor1993 commented Jun 10, 2022

안녕하세요 두두. 금주 2차 PR 올립니다.
�API 통신 관련 문제를 먼저 급하게 해결하느라
저번에 말씀주신 리뷰를 거의 반영하지 못 하였습니다ㅠㅠ

상기의 부분, 모쪼록 참조 부탁드리겠습니다.
금일 PR도 잘 부탁드리겠습니다!ㅎㅎ

[team11][iOS] - 서버 통신 구현 마무리 및 위치 정보 관련 수정

💡 Issue

  1. 숙소 리스트 화면(AccomodationsView)와 상세 숙소 화면(DetailPageView)의 네트워크 연결
  2. DetailPageView 구현 마무리
  3. 위치 정보를 받아오는 타이밍과 관련된 문제 해결 및 현재 위치를 기준으로 가까운 여행지 Cell의 거리를 보여주는 기능 추가
  4. 수정된 API로 인한 수정

📝 Tasks

Task1

🔹 작업

  • 네트워크 통신을 통해 AccomodationsView, DetailPageView에 필요한 데이터를 받아와서 뷰에 그려주는 로직 구현
    • Repository 객체에서 서버로부터 받아온 JSON 데이터를 DTO 형식으로 만들고, 해당 DTO를 뷰에서 바로 사용할 수 있는 Entity 형태로 만들어 VC로 알려주는 흐름 구현
    • 하나의 Entity에 필요한 이미지가 여러 개일 경우, DispatchGroup을 이용해 모든 이미지가 다 넘어왔을 때 Entity를 만들어 VC에 알려주는 로직 구현

🔹 고민과 해결

  • DetailPageView의 경우, 숙소 사진에 해당하는 여러 개의 이미지를 받게 되고 이미지를 하나하나 넘겨주는 흐름보다는 모든 이미지를 받은 후에 하나의 Entity를 만들어 VC로 넘겨주는 것이 더 적절하다고 생각했습니다. 하지만 서버로부터 이미지를 받는 과정은 비동기로 처리되기 때문에, 사진을 모두 넘겨 받은 시점을 확인하기가 어려웠습니다.
    이 문제를 해결하기 위해 DispatchGroupSerialQueue를 사용했고, 이미지 하나를 받으면 해당 이미지 데이터를 SerialQueue에서 배열에 넣어주고, 모든 이미지를 받아온 후에는 DispatchGroup.leave() 메소드를 이용해 필요한 Task들이 끝났음을 알렸습니다. 그리고 endQueue를 이용해 모든 Task가 끝났을 때 Entity를 만들어 VC에 넘겨주도록 했습니다.
    let dispatchGroup = DispatchGroup()
    let serialQueue = DispatchQueue.init(label: "SerialQueue")
    var imageDataArray = [Data]() // 숙소 이미지 데이터를 담아놓을 배열
    var hostImage = Data() // 호스트 이미지 데이터를 받아놓을 변수
    
    dispatchGroup.enter()
    
    DTO.roomImages.forEach {
        fetchImage(url: $0.imagePath) { imageData in
            serialQueue.async {
                imageDataArray.append(imageData)
            }
        }
    }
    
    fetchImage(url: DTO.host.profileImagePath) { imageData in
        serialQueue.async {
            hostImage = imageData
        }
        dispatchGroup.leave()
    }
    
    let endQueue = DispatchQueue.init(label: "EndQueue", attributes: .concurrent)
    
    dispatchGroup.notify(queue: endQueue) {
        // 위에서 받아온 이미지들을 이용해 Entity 만들기
        let roomDescription = ...
    
        // 만든 Entity를 VC에 넘겨주기
        delegate?.didFetchData(detailRoomInfo)
    }

Task2

🔹 작업

  • 상세 페이지 화면과 데이터 연결
  • DetailTextCell 내의 버튼 액션 연결 구현 방식 변경

🔹 고민과 해결

  • 이전에 DetailTextCell 내부에 있는 버튼의 액션에 대한 권한을 VC에게 넘겨주기 위해 delegate를 활용하였으나, 이로인해 DataSource가 VC를 알고 인스턴스를 소유하는 형태가 되어버림.
    이와 같은 형태는 올바르지 않은 듯 보여서 버튼 액션이 실행되면 Notification을 전달하여 VC가 적절한 행위를 하도록 이어지는 방식으로 수정.

Task3

🔹 작업

  • Location 권한 호출 및 데이터 접근 위치 변경

🔹 고민과 해결

  • 홈화면에서 Location 관련 작업을 진행하다보니 데이터를 받아오는 작업이 늦어지면서 좌표값을 0,0만 보내는 현상이 발생.
    이를 해결하기 위해 TabBarVC에 해당 로직들을 옮겨주었고, 적절한 Location 값을 받아오면 HomeVC에 해당 데이터를 넘겨줌과 동시에 Notification을 전달하여 올바른 API 호출을 할 수 있도록 수정.
    추가적으로 Location을 지속적으로 update하는데 이때마다 Notification을 전달할 수는 없으므로, HomeVC에 넘겨준 값과 현재 위치 값이 변경되지 않으면 알림을 보내지 않도록 조건을 추가.

@wnsxor1993 wnsxor1993 added the review-iOS Extra attention is needed label Jun 10, 2022
@wnsxor1993 wnsxor1993 requested a review from mienne June 10, 2022 05:25
@wnsxor1993 wnsxor1993 self-assigned this Jun 10, 2022
Copy link

@mienne mienne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수고하셨습니다~👍

switch manager.authorizationStatus {
case .authorizedAlways, .authorizedWhenInUse:
manager.startUpdatingLocation()
case .denied:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

처리해야 할 로직이 없다면 case .denied 제거해도 될거 같아요!

func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.first, let homeVC = self.homeVC, homeVC.nowLocation != location else { return }

NotificationCenter.default.post(name: NSNotification.Name("location"), object: self, userInfo: ["location": location])
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NotificationCenter 여러 곳에서 사용하기 때문에 하드코딩보다 Notification.Name 확장해서 사용하는 것은 어떨까요?

extension Notification.Name {

    static let location = Notification.Name(rawValue: "location")
}

NotificationCenter.default.post(name: .location, object: self, userInfo: ["location": location])


import Foundation

struct DetailDTO: Codable {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DTO 명시적으로 붙인 이유가 무엇인가요?

case .success(let data):
handler(data)
case .failure(let error):
print(error)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

convert 함수 호출할 때 dispatchGroup.enter 하고, fetchImage에서 성공 상황에서 코드가 문제 되지 않지만, 실패하게 되면 dispatchGroup.leave() 호출하지 않기 때문에 100% 크래시 발생하는 코드입니다. 그래서 failure 상황에서도 handler 호출하도록 변경이 필요합니다.

distanceOfDate = Calendar.current.dateComponents([.day], from: option?.dateRange?.lowerBound ?? Date(), to: option?.dateRange?.upperBound ?? Date()).day ?? 0
}

cell.configure(imageData: imageData,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

함수 매개변수가 많아진다면, 목적을 파악하기 어렵고 혼란을 만들 수 있기 때문에 하나로 최대한 줄이는 것은 어떨까요?
코드를 작성할 때 중요한 것은 한 눈에 목적을 파악할 수 있어야 합니다. 함수 매개변수가 많아질 때 명확한 의도가 있어야 합니다.

configure 함수 파라미터 개수가 어떤 목적으로 전달이 되는건지 알기 어렵기 때문에 CellModel 객체로 만들어서 전달해주는건 어떨까요?

var imageDataArray = [Data]()
var hostImage = Data()

dispatchGroup.enter()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고민했던 지점이 비동기 함수를 동기처럼 처리하여 이미지 처리를 하려고 했던 부분이 DispatchGroup 처리 방법도 있지만 Swift 5.5에서 나온 async, await 개선해볼 수 있습니다. 설명해야 할 부분은 많지만, 비동기 함수 실행을 동기처럼 처리할 수 있는 부분이여서 말씀드려요. 꼭 고쳐야 하는 부분이 아니라 시간이 있을 때 개선할 수 있는 작업으로 코멘트 남긴거니 너무 부담가지지 않으셨으면 좋겠어요~

참고: Swift 5.5: Asynchronous Looping With Async/Await

var imageData: Data

if item.imageData.count > 0 {
imageData = item.imageData
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
imageData = item.imageData
imageData = item.imageData.count > 0 ? item.imageData : Data()

case .firstCase:
case .mainImages:
return data.imageData.count
case .roomTitle:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

많은 케이스가 return 1 처리된다면, default: return 1로 1 리턴해야 하는 case 모두 처리할 수 있지 않을까요?

cell.configure(info: data.hostPage.roomInfo, name: data.hostPage.hostName, image: data.hostPage.hostFace, detail: data.hostPage.detailInfo)
let hostName = "호스트: \(data.hostInfo.name)님"
let hostImage = UIImage(data: data.hostInfo.profileImageData)
let detail = "최대인원 \(data.roomDescription.capacity)명 * 침실 \(data.roomDescription.numberOfBedRoom)개 * 침대 \(data.roomDescription.numberOfBed)개 * 욕실 \(data.roomDescription.numberOfBathRoom)개"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CellModel 객체를 만들어 내부에서 가지고 있는 속성으로 처리하는 것이 어떨까요?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
review-iOS Extra attention is needed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants