라이징 테스트를 앞두고...

YJ·2023년 1월 27일
0

라이징 캠프 13기

목록 보기
11/11
post-thumbnail

일단 라이징 테스트 전에
가이드라인(?)을 주셔서
그동안 공부했던 거랑 가이드라인 같이 복습해보도록 하겠습니다!

1. 가이드라인 숙지

1.1 API

struct Constant{
    static let BASE_URL = "URL 주소 입력하기"
    static let KOBIS_BASE_URL = "http://www.kobis.or.kr/kobisopenapi////"
}

1.2 폰트

폰트 적용 방법

  1. 폰트 파일을 프로젝트에 넣는다.
    • 파일을 넣을 때 현재 프로젝트를 target으로 설정한다.
    • 해당 프로젝트에는 ios/Configuration/Font 폴더 내에 있음
  2. Info.plist에 폰트 정보를 추가한다.
    • Fonts provided by application 내에 확장자를 포함한 폰트 파일 이름을 입력한다.
  3. 폰트 이름 찾기
    • 등록하려는 폰트를 더블클릭하여 mac의 서체 관리자(Font Book)에 저장한다.
    • 서체 관리자에서 해당 폰트 정보를 눌러 PostScript 이름을 찾는다. (ex. NotoSansCJKkr-Medium)
  4. extension을 활용하여 swift 코드 내에서 편리하게 폰트를 적용한다.
    • ex. label.font = .NotoSans(.medium, size: 16)
extension UIFont {
    public enum NotoSansType: String {
        case bold = "Bold"
        case medium = "Medium"
        case regular = "Regular"
    }

    static func NotoSans(_ type: NotoSansType, size: CGFloat) -> UIFont {
        return UIFont(name: "NotoSansCJKkr-\(type.rawValue)", size: size)!
    }
}

1.3 기본 ViewController

Navigation Bar

Navigation touchBar
        self.navigationController?.navigationBar.titleTextAttributes = [ .font : UIFont.NotoSans(.medium, size: 16), ]
        self.view.backgroundColor = .white

디바이스 크기 여백 등의 정보를 담은 Struct
아래와 같은 방식으로 width, height, 노치 디자인, 상태바, 네비게이션 바, 탭 바, 디바이스 위쪽 아래쪽 여백 코드로 조절!

 // MARK: 디바이스의 위쪽 여백 (Safe Area 위쪽 여백)
    // ** 위쪽 여백의 전체 높이 : topInset + statusBarHeight + navigationBarHeight(존재하는 경우) **
    static var topInset: CGFloat {
        return UIApplication.shared.windows.first?.safeAreaInsets.top ?? 0
    }

[참고 사이트]

https://kapeli.com/cheat_sheets/iOS_Design.docset/Contents/Resources/Documents/index

1.4 날짜 표기법

extension Date {
    init(year: Int, month: Int, day: Int) {
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyy:MM:dd"
        self = dateFormatter.date(from: "\(year):\(month):\(day)") ?? Date()
    }

1.5 숫자 표기법

멘토님이 강조하셨던 것!

// MARK: comma
    //숫자 표현하는 법
    // ex. "1234567890".insertComma == "1,234,567,890"
    var insertComma: String {
        let numberFormatter = NumberFormatter()
        numberFormatter.numberStyle = .decimal
        if let _ = self.range(of: ".") {
            let numberArray = self.components(separatedBy: ".")
            if numberArray.count == 1 {
                var numberString = numberArray[0]
                if numberString.isEmpty {
                    numberString = "0"
                }
                guard let doubleValue = Double(numberString)
                    else { return self }
                return numberFormatter.string(from: NSNumber(value: doubleValue)) ?? self
            } else if numberArray.count == 2 {
                var numberString = numberArray[0]
                if numberString.isEmpty {
                    numberString = "0"
                }
                guard let doubleValue = Double(numberString)
                    else {
                        return self
                }
                return (numberFormatter.string(from: NSNumber(value: doubleValue)) ?? numberString) + ".\(numberArray[1])"
            }
        }
        else {
            guard let doubleValue = Double(self)
                else {
                    return self
            }
            return numberFormatter.string(from: NSNumber(value: doubleValue)) ?? self
        }
        return self
    }

1.6 UI와 관련한 코드

Color, Button

extension UIColor {
    // MARK: hex code를 이용하여 정의
    // ex. UIColor(hex: 0xF5663F)
    convenience init(hex: UInt, alpha: CGFloat = 1.0) {
        self.init(
            red: CGFloat((hex & 0xFF0000) >> 16) / 255.0,
        )
    }
 extension UIButton {
    func showIndicator() {
        let indicator = UIActivityIndicatorView()
        let buttonHeight = self.bounds.size.height
        let buttonWidth = self.bounds.size.width
        indicator.center = CGPoint(x: buttonWidth / 2, y: buttonHeight / 2)
        self.addSubview(indicator)
        indicator.startAnimating()
    }
    
    func dismissIndicator() {
        for view in self.subviews {
            if let indicator = view as? UIActivityIndicatorView {
                indicator.stopAnimating()
                indicator.removeFromSuperview()
            }
        }
    }

1.7 UI분리

화면 전환, 경고창/팝업 띄우기,화면 전환 닫기, NavigationBar

화면 전환

// MARK: RootViewController로 이동
    @IBAction func changeRootViewControllerButtonTouchUpInside(_ sender: UIButton) {
        self.navigationController?.popToRootViewController(animated: true)
    }
    
    // MARK: 새로운 window로 화면전환
    @IBAction func changeWindowButtonTouchUpInside(_ sender: UIButton) {
        let splashStoryboard = UIStoryboard(name: "SplashStoryboard", bundle: nil)
        let splashViewController = splashStoryboard.instantiateViewController(identifier: "SplashViewController")
        self.changeRootViewController(splashViewController)
    }

화면 닫기

// MARK: RootViewController로 이동
    @IBAction func changeRootViewControllerButtonTouchUpInside(_ sender: UIButton) {
        self.navigationController?.popToRootViewController(animated: true)
    }
    
    // MARK: 새로운 window로 화면전환
    @IBAction func changeWindowButtonTouchUpInside(_ sender: UIButton) {
        let splashStoryboard = UIStoryboard(name: "SplashStoryboard", bundle: nil)
        let splashViewController = splashStoryboard.instantiateViewController(identifier: "SplashViewController")
        self.changeRootViewController(splashViewController)
    }

경고창, 팝업 띄우기

// MARK: 제목만 있는 경고창
    @IBAction func alertWithTitleButtonTouchUpInside(_ sender: UIButton) {
        self.presentAlert(title: "제목만 있는 경고창")

//나머지도 이런 식으로 작성하면 됩니다!

1.8 Network

로그인 데이터 따로 관리

class SignInDataManager {
    func postSignIn(_ parameters: SignInRequest, delegate: SignInViewController) {
        AF.request("\(Constant.BASE_URL)/signin", method: .post, parameters: parameters, encoder: JSONParameterEncoder(), headers: nil)
            .validate()
            .responseDecodable(of: SignInResponse.self) { response in
                switch response.result {
                case .success(let response):
                    // 성공했을 때
                    if response.isSuccess, let result = response.result {
                        delegate.didSuccessSignIn(result)
                    }
                    // 실패했을 때
                    else {
                        switch response.code {
                        case 2000: delegate.failedToRequest(message: "상황에 맞는")
                        case 3000: delegate.failedToRequest(message: "에러 메시지로")
                        case 4000: delegate.failedToRequest(message: "사용자에게 적절한")
                        default: delegate.failedToRequest(message: "피드백을 주세요")
                        }
                    }
                case .failure(let error):
                    print(error.localizedDescription)
                    delegate.failedToRequest(message: "서버와의 연결이 원활하지 않습니다")
                }
            }
    }
}

엔티티 타입 적는 법

struct SignInRequest: Encodable {
    var id: String
    var password: String
}

API 데이터 관리

class BoxOfficeDataManager {
    func searchDailyBoxOfficeList(targetDt: String, delegate: BoxOfficeViewController) {
        let url = "\(Constant.KOBIS_BASE_URL)/boxoffice/searchDailyBoxOfficeList.json"
            + "?key=\(KobisKey.DAILY_BOXOFFICE_KEY)"
            + "&targetDt=\(targetDt)"
        AF.request(url, method: .get, parameters: nil, encoding: JSONEncoding.default, headers: nil)
            .validate()
            .responseDecodable(of: BoxOfficeResponse.self) { response in
                switch response.result {
                case .success(let response):
                    delegate.didRetrieveBoxOffice(result: response.boxOfficeResult)
                case .failure(let error):
                    print(error.localizedDescription)
                    delegate.failedToRequest(message: "서버와의 연결이 원활하지 않습니다")
                }
            }
    }
}

가이드라인으로 주신 부분들 복습했고,
이외에도 내가 개발하면서 터득했던 라이브러리 사용법, 각종 오류 해결법 등도 참고해서 라이징테스트 할 때 꼭 도움이 되었으면 좋겠다!!

2. 오류들 정리

(1) 2주차 과제 오류 모음

https://velog.io/@yoogail/내가자주-겪는-오류-this-class-is-not-key-value-coding-compliant-for-the-key
https://seungchan.tistory.com/entry/Swift-Storyboard-doesnt-contain-a-view-controller-with-identifier
https://youjean.tistory.com/3

(2) 3주차 과제 오류 모음

Multiple commands produce Error 해결법
[iOS / Error] Unable to load contents of file list... 에러 해결
[Swift] Storyboard doesn't contain a view controller with identifier
Invalid nib registered for identifier
xcode invalid redeclaration of type name - Google Search

[iOS - Error] setValue:forUndefinedKey 에러
Swift - "[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key ~ ."
[iOS - swift] storyboard 에러- 1. NSUnknownKeyException, this class is not key value coding-compliant for the key // 2. Unexpectedly found nil while implicitly unwrapping an Optional value
Swift - "[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key ~ ."
IOS) TableView의 Section을 다뤄보자

(3) 4주차 과제 오류

Could not insert new outlet connection 해결법
[Swift 5] ViewController has no initializers
Label hide very slow
(내가)자주 겪는 오류들: #this class is not key value coding-compliant for the key
Programming Log: [iOS] "loaded the ~ nib but the view outlet was not set." 문제
iOS 레이아웃이 깨졌을때 할 수 있는 방법?!
[Swift] UIButton의 title을 확인하는 방법(강제 추출, 옵셔널 바인딩)

(4) 5주차 과제 오류

로그인 구현 오류
Swift 카카오 연동 로그인
Expected declaration
swift Thread 1 signal Sigabrt 에러 해결 방법
[iOS / Error] Unable to load contents of file list... 에러 해결

네이버 지도 오류
Main thread warning with CLLocationManager.locationServicesEnabled()
NMapsMap 네이버 맵 링크 에러

기타 오류
Xcode Buildtime Error: 'Unable to load contents of file list: '.../Info.plist' (in target 'xxxx')
'Method' is ambiguous for type lookup in this context, Error in Alamofire
[swift] unrecognized selector sent to instance 오류
iOS 시작 가이드
Main thread warning with CLLocationManager.locationServicesEnabled()

3. 라이징테스트...

5주내내 과제 구현하면서 완전 저 표정으로 지냈던 것 같다...
완전 ???????????????????????? 그 잡채...
그래도 5주동안 오류 해결하는 능력도 길러지고
과제 완성도도 눈에 띄게 좋아지면서(잘난척이 아니라 정말 사실이다...오토레이아웃도 못 했었는데....)
iOS에 더 흥미를 붙였던 것 같다.

라이징테스트 볼 수 있다고 들었을 때 속으로 얼마나 기뻤는 지 모른다.
라이징캠프 1차 목표가 라이징테스트 보기였기 때문에...
과제를 열심히 한 보람이 있었따...😭

종강하고 나서 바로 시작하기도 했고, 매일 방학이 방학이 아닌 것처럼
살다보니 번아웃이 밥 먹듯이 찾아왔었는데
어찌저찌 잘 이겨내고 여기까지 와서 뿌듯하다.

이왕 여기까지 온 거 완주가 최종 목표니까
라이징캠프 처음 시작했을 때 마음가짐 잊지 말고,
끝까지 최선을 다해서 잘 해내고 싶다...!

경험이 주는 힘을 얻어가는 이번 방학이 되고 싶다

profile
why not?!?!

0개의 댓글