Skip to content

πŸ“[DailyPin-μž₯μ†ŒκΈ°λ‘] μ•±μŠ€ν† μ–΄ μΆœμ‹œ ν”„λ‘œμ νŠΈ

Notifications You must be signed in to change notification settings

ji-yeon224/DailyPin

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ“ DailyPin - μž₯μ†Œ 일기

DailyPin_ReadMe

πŸ”— μ•±μŠ€ν† μ–΄ λ°”λ‘œκ°€κΈ°


πŸ—“οΈ ν”„λ‘œμ νŠΈ

  • 개인 ν”„λ‘œμ νŠΈ
  • 2023.09.25 ~ 2323.10.25(4μ£Ό)
  • μ΅œμ†Œ 지원 버전 iOS 15.0

πŸ“– 핡심 κΈ°λŠ₯

  • μ›ν•˜λŠ” μž₯μ†Œλ₯Ό κ²€μƒ‰ν•˜μ—¬ 지도에 μœ„μΉ˜λ₯Ό 보여주고 기둝을 등둝할 수 μžˆμŠ΅λ‹ˆλ‹€.
  • μ›ν•˜λŠ” μœ„μΉ˜λ₯Ό μ§€λ„μ—μ„œ μ°Ύμ•„ 길게 νƒ­ν•˜μ—¬ ν•΄λ‹Ή μœ„μΉ˜μ— 기둝을 등둝할 수 μžˆμŠ΅λ‹ˆλ‹€.
  • μ‚¬μš©μžκ°€ λ“±λ‘ν•œ μœ„μΉ˜ 리슀트λ₯Ό λ³Ό 수 있고, ν•΄λ‹Ή μœ„μΉ˜μ— λ“±λ‘λ˜ 기둝을 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.
  • 달λ ₯을 톡해 λ‚ μ§œ 별 기둝을 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.

πŸ’» κΈ°μˆ μŠ€νƒ

  • MVVM
  • RxSwift, RxGesture
  • UIKit, MapKit
  • Alamofire, Codable
  • Realm
  • SnapKit, Autolayout
  • DiffableDataSource, CompositionalLayout
  • Firebase
    • Google Crashlytics
    • Push Notification
  • FSCalendar, FloatingPanel, Toast
  • Google Place API

πŸ’‘ ν”„λ‘œμ νŠΈ λͺ©ν‘œ

  • DiffableDataSource와 CompositionalLayout을 μ΄μš©ν•˜μ—¬ CollectionView κ΅¬ν˜„
  • API 톡신 μ‹œ Alamofire와 Router νŒ¨ν„΄μ„ μ μš©ν•˜μ—¬ μ½”λ“œμ˜ 가독성을 높이고, μž¬μ‚¬μš©μ„±μ„ λ†’μž„
  • MVVM νŒ¨ν„΄μ„ 톡해 λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ„ λΆ„λ¦¬ν•˜μ—¬ ViewController의 역할을 μ€„μž„
  • UI κ΅¬ν˜„ μ‹œ λΉ„μŠ·ν•œ ꡬ쑰의 View듀을 λͺ¨λ“ˆν™” ν•˜μ—¬ μž¬μ‚¬μš©μ„±μ„ λ†’μž„
  • Localizing을 톡해 ν•œκ΅­μ–΄μ™€ μ˜μ–΄ 버전 λŒ€μ‘

🚨 νŠΈλŸ¬λΈ”μŠˆνŒ…

Google PlaceAPI 인덱슀 μ—λŸ¬λ‘œ App Crash Issue

  • Google PlaceAPIλ₯Ό 톡해 μ§€λ„μ—μ„œ μž„μ˜μ˜ μž₯μ†Œ 정보λ₯Ό μ„ νƒν•˜μ—¬ μ£Όμ†Œκ°’μ„ μ‘λ‹΅λ°›λŠ” κ³Όμ •μ—μ„œ μ£Όμ†Œ 정보가 λͺ…ν™•ν•˜μ§€ μ•Šμ€ 곳을 μ„ νƒν•˜μ˜€μ„ λ•Œ 응닡 κ°’μ˜ ν˜•μ‹μ΄ 달라져 인덱슀 μ—λŸ¬κ°€ λ°œμƒν•˜μ˜€κ³ , μΆœμ‹œλœ μ•±μ—μ„œ 비정상 μ’…λ£Œκ°€ λ˜λŠ” λ¬Έμ œκ°€ λ°œμƒν•˜μ˜€λ‹€.
  • Firebase Crahlyticsλ₯Ό 톡해 앱이 비정상 μ’…λ£Œ 지점을 νŒŒμ•…ν•˜μ˜€κ³ , 쑰건문을 톡해 응닡 κ°’μœΌλ‘œ λ°›λŠ” μ£Όμ†Œ λ°°μ—΄μ˜ 길이에 따라 이름 μ„€μ • 방식을 μ„ νƒν•˜μ—¬ ν•΄κ²°ν•˜μ˜€λ‹€.

Compositional Layout μ…€ 동적 높이

  • λ‚΄λΆ€ μ»¨ν…μΈ μ˜ 높이에 따라 λ™μ μœΌλ‘œ 높이λ₯Ό μ‘°μ ˆν•˜λ„λ‘ estimatedλ₯Ό μ‚¬μš©ν•˜μ˜€λŠ”λ°, estimated둜 μ •ν™•ν•œ 높이 계산이 λ˜μ§€ μ•Šμ•„ 잘 적용이 μ•ˆλ˜μ—ˆλ‹€.
  • View의 Drawing Cycle을 κ³ λ €ν•˜μ—¬ 데이터가 셀에 μ‚½μž…λœ ν›„ layoutIfNeeded()λ₯Ό ν˜ΈμΆœν•˜μ—¬ λ ˆμ΄μ•„μ›ƒ μ—…λ°μ΄νŠΈλ₯Ό μš”μ²­ν•˜μ—¬ ν•΄κ²°ν•˜μ˜€λ‹€.
private func configureDataSource() {
    let cellRegistration = UICollectionView.CellRegistration<InfoCollectionViewCell, Record> { cell, indexPath, itemIdentifier in
        cell.titleLabel.text = itemIdentifier.title
        cell.address.text = itemIdentifier.placeInfo[0].address
        cell.dateLabel.text = DateFormatter.convertDate(date: itemIdentifier.date)
        cell.layoutIfNeeded() // λ ˆμ΄μ•„μ›ƒ μ—…λ°μ΄νŠΈ μš”μ²­
        
    }
    
    dataSource = UICollectionViewDiffableDataSource(collectionView: collectionView, cellProvider: { collectionView, indexPath, itemIdentifier in
        let cell = collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: itemIdentifier)
        return cell
    })
}

MVVM νŒ¨ν„΄ 적용 μ‹œ μ—λŸ¬ 핸듀링

  • MVVM νŒ¨ν„΄μ„ 적용과 Repository νŒ¨ν„΄μ„ μ΄μš©ν•˜μ—¬ Realm에 CRUD μž‘μ—…μ„ μˆ˜ν–‰ν•˜λ©΄μ„œ ν•˜λ‚˜μ˜ κΈ°λŠ₯이 μ—¬λŸ¬ νŒŒμΌμ„ 거쳐 ViewController에 전달이 λ˜λŠ” ꡬ쑰가 λ˜μ—ˆλ‹€. λ‘œμ§μ„ λΆ„λ¦¬ν•˜κΈ° μœ„ν•΄ μ—¬λŸ¬ 단계λ₯Ό κ±°μ³€μ§€λ§Œ μ—λŸ¬ 핸듀링 μž‘μ—…μ„ κ³„μ†ν•΄μ„œ μˆ˜ν–‰ν•΄μ•Ό ν–ˆκΈ° λ•Œλ¬Έμ— 같은 do-catch문을 μ—¬λŸ¬ 번 μž‘μ„±ν•˜λŠ” λΉ„νš¨μœ¨ λ¬Έμ œκ°€ λ°œμƒν•˜μ˜€λ‹€.
  • Observable νƒ€μž…μ„ 톡해 μ—λŸ¬ λ°œμƒ μ‹œ 값을 errorDescription 값을 λ³€κ²½ν•˜μ˜€κ³  ViewControllerμ—μ„œ bindν•˜μ—¬ μ—λŸ¬ 메세지λ₯Ό Alertλ₯Ό 톡해 μ‚¬μš©μžμ—κ²Œ λ³΄μ—¬μ£Όμ—ˆλ‹€.
// RecordViewModel.swift

var errorDescription: Observable<String?> = Observable(nil)

func savePlace(_ location: PlaceElement?) -> Place? {
    guard let data = location else {
        errorDescription.value = InvalidError.noExistData.errorDescription
        return nil
    }
    
    let place = Place(placeId: data.id, address: data.formattedAddress, placeName: data.displayName.placeName, latitude: data.location.latitude, longitude: data.location.longitude)
    
    do {
        try placeRepository.createItem(place)
        NotificationCenter.default.post(name: .databaseChange, object: nil, userInfo: ["changeType": "save"])
        return place
    } catch {
        errorDescription.value = DataBaseError.createError.errorDescription
        return nil
    }
    
}
// RecordViewController.swift

private func bindData() {
    viewModel.errorDescription.bind { data in
        if let message = data {
            self.showOKAlert(title: "", message: message) { }
        }
    }
}

그림자 λ Œλ”λ§ 이슈

  • view에 그림자λ₯Ό λ„£μ—ˆμ„ λ•Œ 디버그 μ°½μ—μ„œ 그림자 λ Œλ”λ§μ— λ§Žμ€ λΉ„μš©μ΄ λ“€κΈ° λ•Œλ¬Έμ— shadowPathλ₯Ό λ³€κ²½ν•˜λΌλŠ” λ‚΄μš©μ˜ κ²½κ³ κ°€ λ‚˜νƒ€λ‚¬λ‹€.
  • UIBezierPath둜 그림자λ₯Ό view의 크기에 맞게 생성 ν›„ layoutSubView()λ‚΄μ—μ„œ shadowPath κ°’μœΌλ‘œ μ§€μ •ν•˜μ—¬ ν•΄κ²°ν•˜μ˜€λ‹€.

Pasted image 20231111142454

private func shadow() {
    backView.layer.cornerRadius = backView.frame.size.width / 2
    backView.layer.shadowColor = UIColor.black.cgColor
    backView.layer.shadowOpacity = 0.4
    backView.layer.shadowOffset = CGSize(width: 0, height: 0)
    backView.layer.shadowRadius = 1
    backView.layer.shadowPath = UIBezierPath(arcCenter: CGPoint(x: backView.bounds.width/2, y: backView.bounds.height/2), radius: backView.bounds.width / 2, startAngle: 0, endAngle: 2 * .pi, clockwise: true).cgPath
}

Content Type

  • Geocoding API 연동 μ‹œ Router νŒ¨ν„΄μ„ μ μš©ν•˜μ—¬ κ΅¬ν˜„ν•˜λŠ” 쀑 http load failed 였λ₯˜κ°€ λ°œμƒν•˜μ˜€λ‹€.
  • HTTPHeaders에 Content-Type에 λŒ€ν•œ 섀정을 ν•˜μ§€ μ•Šμ•„ νŒŒλΌλ―Έν„° 값이 μ •μƒμ μœΌλ‘œ μ „μ†‘λ˜μ§€ μ•Šμ•˜κΈ° λ•Œλ¬Έμ— application/json 을 header 값에 μΆ”κ°€ν•˜μ—¬ ν•΄κ²°ν•˜μ˜€λ‹€.

Pasted image 20231111142522

✍🏻 회고

πŸ”— 회고

  • 첫 앱을 μΆœμ‹œλ₯Ό ν•˜κΈ° μœ„ν•΄ κ°œλ°œν•˜λ©΄μ„œ 생각보닀 κ³ λ €ν•΄μ•Ό ν•  μš”μ†Œκ°€ ꡉμž₯히 λ§Žλ‹€λŠ” 것을 μƒˆμ‚Ό κΉ¨λ‹¬μ•˜λ‹€. 생각보닀 κ³ λ €ν•΄μ•Ό ν•  μ˜ˆμ™Έ 사항듀이 λ§Žμ•˜λ‹€. λ‹€μ–‘ν•œ μ˜ˆμ™Έμ²˜λ¦¬λ₯Ό κ΅¬ν˜„ν•˜κ³ , ν™”λ©΄ μ „ν™˜ μ‹œ κ°’ 전달과 μ—…λ°μ΄νŠΈ λ“± 깊게 생각해야 ν•  μš”μ†Œλ“€μ΄ λ§Žμ•˜λ‹€.
  • 기획 λ‹¨κ³„μ—μ„œ λ‚΄κ°€ 맀우 λΆ€μ‘±ν–ˆμŒμ„ κΉ¨λ‹¬μ•˜λ‹€. UI ꡬ성도 잘 λ– μ˜€λ₯΄μ§€ μ•Šμ•„μ„œ λŒ€λž΅μ μΈ μŠ€ν† λ¦¬λ³΄λ“œλ§Œ κ·Έλ Έλ”λ‹ˆ κ³„μ†ν•΄μ„œ μˆ˜μ •ν•˜λŠ” 일이 λ°œμƒν–ˆκ³ , ν•„μš”ν•œ κΈ°λŠ₯μ΄μ§€λ§Œ 기획 λ‹¨κ³„μ—μ„œ 놓쳐버린 κΈ°λŠ₯듀도 λ§Žμ•˜λ‹€.
  • λ©”λͺ¨λ¦¬ λˆ„μˆ˜μ— κ΄€λ ¨ν•˜μ—¬ 잘 λŒ€μ‘μ„ ν•˜μ§€ λͺ»ν•œ 것 κ°™λ‹€. ν΄λ‘œμ € ꡬ문을 많이 μ‚¬μš©ν•˜κΈ° λ•Œλ¬Έμ— λ§Žμ€ λ©”λͺ¨λ¦¬ λˆ„μˆ˜κ°€ μžˆμ„ 것 같은데 ν•΄λ‹Ή λΆ€λΆ„μ—μ„œλ„ 더 κ³΅λΆ€ν•˜μ—¬ μ—…λ°μ΄νŠΈλ₯Ό ν•΄μ•Όκ² λ‹€λŠ” 생각을 ν•˜κ²Œ λ˜μ—ˆλ‹€.

πŸ—“οΈ κ°œλ°œμΌμ •

πŸ”— 개발 일지

About

πŸ“[DailyPin-μž₯μ†ŒκΈ°λ‘] μ•±μŠ€ν† μ–΄ μΆœμ‹œ ν”„λ‘œμ νŠΈ

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages