Skip to content

학습거리

songju edited this page Nov 3, 2020 · 13 revisions

2020.10.27 화요일

UICollectionView Cell 사이에 seperator를 적용 시킬 방법

borderWidth나 borderColor를 설정하는 방법 말고 어떤 방법이 있을까

git add 생략하고 바로 commit하는 방법


2020.10.28 수요일

MVVM & ViewModel 적용시킬 방법

  • View와 VC의 관계, 구조에 대해 고민해보기

2020.11.01 추가

  • MVVM이란?

  • MVVM은 Model - View - ViewModel의 약자로 소프트웨어 아키텍처 패턴이다.

    • Model은 데이터, View는 유저 인터페이스, ViewModel은 뷰와 모델 사이를 연결
  • MVC에서 파생된 모델로 모델과 뷰 사이 뿐만 아니라 뷰와 컨트롤러간의 의존성도 고려해 각 단위가 독립적으로 작성되고 테스트될 수 있도록 설계된 패턴이다.

  • Model(모델)의 역할

    • 데이터 모델, 데이터 접근 레이어, 비즈니스 로직등이 포함 되어있다.
    • 모델은 데이터를 얻거나 저장하거나 지우거나 업데이트에 대해 알고 있다. 이 작업들은 뷰모델에 의해 시작되며 모델이 데이터에 대한 작업을 마치면 뷰 모델에게 결과를 알린다.
    • 모델은 뷰모델이 소유하고 있고, 뷰나 뷰모델이 모델에 대해 들여다볼 수 없다.
  • View(뷰)의 역할

    • 사용자 이벤트를 수신하고 데이터를 표시하는 유저 인터페이스를 책임진다. ViewController도 View의 일부
    • 해당 이벤트들은 뷰모델에 전달되어 처리해야할 책임이 있고, 뷰는 뷰모델의 변경사항을 감지하고 뷰모델이 업데이트한 데이터를 보여준다.
    • 뷰와 모델 사이에 연결이 없고 뷰모델에 의해 연결된다.
  • ViewModel(뷰모델)의 역할

    • 뷰 모델은 로직을 담당하고 있고, 유저가 뷰에서 어떤 액션을 취할 때 모델을 변경하거나 되었을 때 해당 모델을 업데이트하고 뷰모델에게 결과를 알리고 뷰를 갱신하는 책임을 가지고 있다.

  • 특징
    • MVVM에서의 ViewModel은 View와 1:n관계를 이룬다.


뷰모델은 뷰를 나타내주기 위한 모델이자 Presentation Logic을 처리하는 역할을 한다. 따라서, 뷰에 어떤 뷰모델을 연결하느냐에 따라 로직처리가 달라지고, 모델이 변경되면 관련된 뷰모델을 사용하는 뷰가 자동으로 업데이트되는 방식이다.


각각의 뷰는 자신이 사용할 뷰모델을 선택하여 바인딩하여 업데이트를 받게되고, 뷰는 단순히 유저 인터페이스를 표시하기 위한 로직만을 담당하며, 가장 이상적인 형태는 일부 유저 인터페이스를 제외하면 오로지 메소드를 호출하는 생성자만 존재하는 것이다.


뷰와 뷰모델간에는 데이터 바인딩을 통해 특정 뷰의 속성과 뷰모델의 속성을 연결한 뒤 뷰모델 속성이 변경되면 자동으로 뷰를 업데이트 한다.


UIMenuController.shared 의 menuItem의 설정 범위

  • 싱글톤으로 이용했을 때 UIMenuController에 관련된 모든 뷰들이 영향을 받지 않는건지..? 코드가 있는 뷰에서만 적용이 되는데 그 이유가 뭘까

UIMenuController 작동 메소드 구조에 대해


2020.10.31 토요일

Apple에서 로그인에 제공해 주는 UI와 기능들이 존재

  • AuthenticationServices
    1. Project Target -> Signing & Capabilities -> + Capability click -> Sign in with Apple 추가

    2. Button 생성

      lazy var signInButton: ASAuthorizationAppleIDButton = {
        let button = ASAuthorizationAppleIDButton()
        return button
      }()
    3. Button에 Action을 추가

      //MARK:- configureSignInButton()
      signInButton.addTarget(self, action: #selector(signUpButtonTouched), for: .touchUpInside)
      
      @objc private func signUpButtonTouched() {
        let appleIDProvider = ASAuthorizationAppleIDProvider()
        let request = appleIDProvider.createRequest()
        request.requestedScopes = [.fullName, .email]
      
        let authorizationController = ASAuthorizationController(authorizationRequests: [request])
        authorizationController.delegate = self
        authorizationController.presentationContextProvider = self
        authorizationController.performRequests()
      }
    4. ASAuthorizationControllerDelegate 설정

      • didCompleteWithAuthorization: 성공했을 경우
        • authorization를 통해 사용자의 정보를 받아올 수 있다.
        if let appleIDToken = authorization.credential as? ASAuthorizationAppleIDCredential {
          ... 생략 ...
        }
      • didCompleteWithError: 실패했을 경우

      **주의사항 처음 로그인 시에만 fullName, email 변수를 받아올 수 있고 이 후 로그인 시에는 nil값을 받아오기 때문에 처음 로그인 할 경우 fullName, email 값을 따로 저장해 두어야 한다. keychain에 저장하라고 하는데 정확히는 모르겠다...

    5. ASAuthorizationControllerPresentationContextProviding 설정

      • presentationAnchor() : 로그인 뷰를 띄우는 곳을 설정?
      func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
        guard let window = view.window else { return ASPresentationAnchor() }
        return window
      }

2020.11.01 일요일

MarkdownView 사용방법

  1. pod "MarkdownView"
  2. UIView를 상속받은 MarkdownView 생성
    @IBOutlet weak var markdownView: MyMarkdownView!
  3. 생성한 markdownView에 데이터 로드
    let text = """
               ### 타이틀!
    
               #### 부제목
                 - 카테고리1
                 - 카테고리2
                   ```swift
                   let a =123   
                   ```
               """
    markdownView.load(markdown: text)
  4. 실행결과



클래스 인스턴스 사이의 강력 참조 순환(Strong Reference Cycle)

  • 두 클래스 인스턴스가 서로 강력 참조를 쥐고 있다면, 각 인스턴스는 서로 살게 유지한다.

예제

class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?
    deinit { println("\(name) is being deinitialized") }
}
 
class Apartment {
    let number: Int
    init(number: Int) { self.number = number }
    var tenant: Person?
    deinit { println("Apartment #\(number) is being deinitialized") }
}

각자의 클래스는 서로를 속성으로 옵셔널 타입으로 가진다. 아래처럼 인스턴스를 초기화 한다고 가정해보자.

var john: Person?
var number73: Apartment?

john = Person(name: "John")
number73 = Apartment(number: 73)

두 인스턴스가 만들어지고 할당 된 후에 강력 참조가 어떻게 보이는지를 나타낸 그림이다.



이제 두 인스턴스를 연결하도록 Person는 apartment를 가지고, apartment는 tenant를 가지도록 한다. 느낌표를 사용하여 옵셔널 변수인 john과 number73를 언래핑하고 인스턴스에 접근하여 각 인스턴스의 속성에 설정한다.


john!.apartment = number73
number73!.tenant = john

이 다음 두 객체를 nil로 설정한 후 강력 참조가 어떻게 보이는지 알아보자.

john = nil
number73 = nil


Person 인스턴스와 Apartment 인스턴스 간의 강력 참조는 끊어지지 않고 남아있는 모습이다.


클래스 인스턴스 간의 강력 참조 순환 해결 방안

  • 약한 참조 (Weak Reference)
    • 약한 참조는 인스턴스가 다른 인스턴스 참조를 강력하게 유지하지 않으며, ARC는 참조된 인스턴스를 버리는 것을 멈추지 않게 한다.

    • 약한 참조는 속성이나 변수 선언 앞에 weak 키워드를 앞에 놓는다.

    • 참조가 어느 순간 “값 없음”을 참조하게 될 때, 약한 참조를 사용하여 참조 순환을 피하도록 한다. 참조가 값을 항상 가진다면, 미소유 참조를 대신 사용한다. 위의 Apartment 예제에서 apartment가 특정 시점에 tenant가 없음을 가지는 것이 적합하면, 약한 참조는 참조 순환을 깨는 적합한 방법이다.


  • 미소유 참조 (Unowned References)
    • 약한 참조와 비슷하게, 미소유 참조는 인스턴스에 대한 참조를 강하게 하지 않는다.

    • 약한 참조와는 다르게 미소유 참조는 항상 값이 있음을 가정한다. 이는 미소유 참조는 항상 옵셔널이 아닌 타입으로 정의된다. 속성이나 변수 선언 전에 unowned 키워드를 붙인다.

    • 미소유 참조는 옵셔널이 아니기 때문에, 사용할 때마다 미소유 참조를 언래핑할 필요가 없다. 미소유 참조는 항상 직접적으로 접근하는 것이 가능하다. 그러나 ARC는 인스턴스 참조를 할당 해제할 때 참조를 nil로 설정할 수 없다. 이는 옵셔널이 아닌 타입의 변수는 nil로 설정될 수 없기 때문이다.

references (예제도 참고 가능)


2020.11.02 월요일

didset vs willset

  • 스위프트는 프로퍼티 옵저버로 didSet, willSet을 제공한다. 얘네들의 역할은 프로퍼티의 값이 변경되기 직전, 직후를 감지하고 따라서 이때 원하는 작업을 수행 할 수 있다.

var myProperty: Int = 10{
   didSet(oldVal){
      //myProperty의 값이 변경된 직후에 호출, oldVal은 변경 전 myProperty의 값
   }
   willSet(newVal){
      //myProperty의 값이 변경되기 직전에 호출, newVal은 변경 될 새로운 값
   }
}

references


Clone this wiki locally