<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>lena_</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Tue, 19 Oct 2021 05:03:05 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. lena_. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/lena_" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Dynamic vs Static Library]]></title>
            <link>https://velog.io/@lena_/Dynamic-vs-Static-Library</link>
            <guid>https://velog.io/@lena_/Dynamic-vs-Static-Library</guid>
            <pubDate>Tue, 19 Oct 2021 05:03:05 GMT</pubDate>
            <description><![CDATA[<h3 id="목표">목표</h3>
<ul>
<li>Dynamic Library와 Static Library의 차이를 이해한다.</li>
<li>Library와 Framework의 차이를 이해한다.</li>
</ul>
<hr>
<p>직접 작성한 소스 이외에 다른 사람이 작성한 코드를 사용하고 싶을 때 우리는 라이브러리 또는 프레임워크를 추가한다.</p>
<blockquote>
<p>라이브러리 / 프레임워크
코드 및 리소스의 번들이다. (라이브러리에 리소스를 추가한 것을 프레임워크라고 설명하는 글도 있다.)
라이브러리와 프레임워크는 소스 코드의 제어권이 어디에 있냐의 차이이다.
라이브러리는 제어권이 나(개발자)에게, 프레임워크는 내가 아닌 프레임워크(<em>ex. UIKit</em>)가 갖는다.</p>
</blockquote>
<p>이렇게 추가한 라이브러리를 내 소스 코드에 연결(Link) 하는 방식에 따라 static과 dynamic으로 구분한다.</p>
<h3 id="static-library">Static Library</h3>
<p>static library는 <code>.a</code></p>
<p>소스 코드 + 라이브러리 코드를 merge 한 executable file을 만들어낸다. 이 때, 소스 코드와 라이브러리 코드가 copy 된다.</p>
<p>앱이 실행되면 복사된 이 executable file이 메모리에 로드된다.
<img src="https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/DynamicLibraries/art/address_space1_2x.png" alt=""></p>
<blockquote>
<p>많은 static lib를 사용할수록 executable file의 용량이 커짐 → 앱 실행 시 더 많은 메모리가 사용됨 → launch time이 길어짐</p>
</blockquote>
<h3 id="dynamic-library">Dynamic Library</h3>
<p>dynamic library는 <code>.dylib</code></p>
<p>컴파일 시 static lib처럼 코드를 copy 하는 것이 아니라 dynamic lib에 대한 참조(references)만 executable file에 포함한다. (executable file이 Static Library에 비해 조금 더 가볍다)</p>
<p>앱이 실행되면(=런타임에) 참조에 따라 lib의 코드를 메모리에 load 한다.
(.dylib를 로드 하는 로더를 Dynamic Loader라고 부르고 .dyld)</p>
<p><img src="https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/DynamicLibraries/art/address_space2_2x.png" alt="Untitled"></p>
<blockquote>
<p>dynamic lib가 많아질수록 executable file을 실행할 때(런타임에) 많은 메모리가 사용됨 → launch time 길어짐</p>
</blockquote>
<p><a href="https://developer.apple.com/videos/play/wwdc2017/413/">WWDC17</a> 에서 앱의 시작 시간을 개선하기 위해서 dylib를 줄일 것을 권장했기 때문에,
dylib를 많이 사용하는 것이 static lib을 사용하는 것보다 앱 실행속도가 더 느리다고 볼 수 있다.</p>
<p>또한, copy 이냐 reference 이냐 linking 방식의 차이이기 때문에 static은 라이브러리의 코드가 업데이트 되면 내 소스코드에서는 반영되지 않지만,
dylib의 경우에는 다시 컴파일 하지 않아도 업데이트 된 기능을 사용할 수 있다.</p>
<h3 id="결론">결론</h3>
<p>Static vs Dynamic은 Linking 방식의 차이이다.</p>
<hr>
<p><a href="https://zeddios.tistory.com/1308">https://zeddios.tistory.com/1308</a>
<a href="https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/DynamicLibraries/100-Articles/OverviewOfDynamicLibraries.html#//apple_ref/doc/uid/TP40001873-SW2">Overview of Dynamic Libraries</a>
⬇️ about Swift modules, libraries, packages, closed source frameworks, command line tools and more.
<a href="https://theswiftdev.com/deep-dive-into-swift-frameworks/">https://theswiftdev.com/deep-dive-into-swift-frameworks/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Swift] Dynamic Dispatch]]></title>
            <link>https://velog.io/@lena_/Swift-Dynamic-Dispatch</link>
            <guid>https://velog.io/@lena_/Swift-Dynamic-Dispatch</guid>
            <pubDate>Sun, 10 Oct 2021 06:54:52 GMT</pubDate>
            <description><![CDATA[<p><a href="https://jcsoohwancho.github.io/2019-10-11-Dynamic-Dispatch%EC%99%80-%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94/">Dynamic-Dispatch</a></p>
<hr>
<p>객체 지향 언어의 특징인 다형성을 Overloading과 Overriding을 통해 구현할 수 있다.
다음과 같이 override한 경우, 실제 호출될 method를 스위프트는 어떻게 결정할까?</p>
<pre><code class="language-swift">class Parent {
  func someMethod() {
    print(&quot;Parent method&quot;)
  }
}

class Child: Parent {
  override func someMethod() {
    print(&quot;Child method&quot;)
  }
}

let object: Parent = Child()
object.someMethod() // ?</code></pre>
<p>스위프트는 class(참조 타입)의 경우 dynamic dispatch 방식을 채택하고 있다.
컴파일 시점이 아닌, 런타임에 호출될 타입을 결정하는 dynamic dispatch가 이루어진다.
따라서 위의 예제에서는 실제 타입인 <code>Child</code>의 method가 호출된다.</p>
<h2 id="static-vs-dynamic-dispatch">Static vs Dynamic Dispatch</h2>
<p>Dynamic Dispatch가 아닌 Static Dispatch 방식으로 타입을 결정할 수도 있다.
Static Dispatch는 컴파일 시점에 호출할 타입을 결정한다.
따라서, Dynamic Dispatch보다 성능상 이점이 있지만 다형성을 활용할 수 없다는 단점이 있다.</p>
<h2 id="increasing-performance-by-reducing-dynamic-dispatch">Increasing Performance by Reducing Dynamic Dispatch</h2>
<p><a href="https://developer.apple.com/swift/blog/?id=27">Increasing Performance by Reducing Dynamic Dispatch</a></p>
<p>공식 블로그에서 Static Dispatch를 유도하기 위한 방법으로 다음 3가지를 제시하고 있다.</p>
<ul>
<li>오버라이드가 필요 없다면 <code>final</code> 키워드 사용</li>
<li><code>private</code> 키워드 사용</li>
<li>WMO(Whole Module Optimization)을 사용</li>
</ul>
<p>오버라이드가 필요 없다면 <code>final</code> 키워드 사용
: <code>final</code> 키워드를 붙인 class, method, property는 오버라이딩이 불가능하므로, Static Dispatch가 일어난다.</p>
<p><code>private</code>을 사용하는 경우에도, 한 파일 안에서만 참조(접근)되는 것이 보장되므로, 컴파일러가 Static Dispatch로 타입을 결정 할 수 있다.</p>
<p>WMO(Whole Module Optimization) 옵션을 활성화한다면 모듈의 파일을 개별적으로 컴파일 하지 않고 모듈 전체를 하나의 덩어리로 취급해서 컴파일 하기 때문에, <code>internal</code> 키워드만으로 다른 모듈에서 override 되지 않음을 보장(final 추론 가능)할 수 있다.
(xcode 8부터 default가 활성화)</p>
<hr>
<p>읽어보면 좋은 레퍼런스들
<a href="https://brunch.co.kr/@joonwonlee/14">https://brunch.co.kr/@joonwonlee/14</a>
<a href="https://swift.org/blog/whole-module-optimizations/">https://swift.org/blog/whole-module-optimizations/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Article] Responder Chain to Handle Events]]></title>
            <link>https://velog.io/@lena_/ResponderChain</link>
            <guid>https://velog.io/@lena_/ResponderChain</guid>
            <pubDate>Fri, 03 Sep 2021 13:03:36 GMT</pubDate>
            <description><![CDATA[<p><a href="https://developer.apple.com/documentation/uikit/touches_presses_and_gestures/using_responders_and_the_responder_chain_to_handle_events">Apple Developer Documentation</a></p>
<hr>
<p>앱은 responder object를 통해 event를 받고 핸들링합니다. 여기서 responder object는 UIResponder의 인스턴스를 말하는데, UIView, UIViewController, UIApplication의 subsclass들이 일반적으로 responder object가 됩니다.</p>
<p>Responder object는 raw event data를 받고 처리하거나 다른 responder object에게 전달(forwarding) 해야 합니다. app이 event를 받으면 UIKit이 자동으로 적절한 responder object(first respnder)에게 전달합니다.</p>
<blockquote>
<p>iOS → App(UIApplication) → (First) Responder object</p>
</blockquote>
<p>처리되지 않은 이벤트는 유효한 responder chain 내에서 responder에서 responder로 전달됩니다.</p>
<p>which is the dynamic configuration of your app’s responder objects.</p>
<p><img src="https://docs-assets.developer.apple.com/published/7c21d852b9/f17df5bc-d80b-4e17-81cf-4277b1e0f6e4.png" alt="https://docs-assets.developer.apple.com/published/7c21d852b9/f17df5bc-d80b-4e17-81cf-4277b1e0f6e4.png"></p>
<p>Figure 1 Responder chains in an app</p>
<p>다이어그램은 responder chain에 따라 하나의 responder에서 다른 responder로 어떻게 이벤트가 이동하는지를 나타냅니다.</p>
<ul>
<li>text field가 이벤트를 처리하지 못하면, UIKit가 text field의 parent UIView 객체로 이벤트를 보냅니다. = background view</li>
<li>background view가 window의 root view이면 responder chain이 view가 속한 view controller에게로 전환됩니다(divert). view controller가 window에게 이벤트를 전달합니다.</li>
<li>window가 이벤트를 처리하지 못하면 UIKit는 이벤트를 UIApplication에 전달합니다.</li>
<li>가능하다면, UIApplication은 UIApplicationDelegate에게 이벤트를 전달합니다.<ul>
<li>UIApplicationDelegate가 UIResponder의 instance 이면서 responder chain이 일어나지 않았을 경우에만</li>
</ul>
</li>
</ul>
<h2 id="determining-an-events-first-responder">Determining an Event&#39;s First Responder</h2>
<p>UIKit는 이벤트 타입에 따라 first responder object를 지정합니다.</p>
<ul>
<li>touch event — 터치를 받은 view</li>
<li>press event — focus된 object</li>
<li>shake motion event — 개발자 (or UIKit)가 지정한 object</li>
<li>remote control event — 개발자 (or UIKit)가 지정한 object</li>
<li>editing menu message — 개발자 (or UIKit)가 지정한 object</li>
</ul>
<p>accelerometers, gyroscopes, magnetometer와 관련된 motion event들은 responder chain를 따르지 않습니다. 대신 Core Motion이 이벤트를 지정된 object에게 directly 전달합니다.</p>
<blockquote>
<p>accelerometers : shake
gyroscopes : 회전
magnetometer : ?</p>
</blockquote>
<p>Controls은 관련된 target object와 action message를 사용해서 직접적으로 커뮤니케이션 합니다. (target- action)</p>
<p>user가 control을 사용해서 상호작용하면 control은 action message를 target object에 보냅니다.</p>
<p>action message는 event가 아니지만, responder chain을 이용합니다.</p>
<p>target object가 <code>nil</code> 이면, UIKit는 action method를 구현한 객체부터 responder chain으로 target을 탐색(traverse)합니다.</p>
<blockquote>
<p>UIControl은 action message를 전달할 target을 찾기 위해 responder chain을 이용하기도 한다.</p>
</blockquote>
<p>예를 들면, UIKit editing menu가 cut, copy, paste 와 같은 동작을 구현할 responder object를 찾기 위해 responder chain이 일어납니다.</p>
<p>Gesture recognizer가 view보다 먼저 touch and press 이벤트를 받습니다. 뷰의 gesture recognizer가 이벤트를 인식하는데 실패하면 UIKit가 이벤트를 뷰에게 보냅니다.</p>
<p>view가 이벤트를 처리하지 못하면, UIKit는 responder chain에 따라 전달합니다.</p>
<h2 id="determining-which-responder-contained-a-touch-event">Determining Which Responder Contained a Touch Event</h2>
<p>UIKit은 view를 기반으로 한 hit-testing으로 touch event가 발생한 뷰를 결정합니다. 특히 view 계층 구조 내  view 객체의 가장자리를 비교해서  결정합니다. <code>hitTest(_:with:)</code> 메소드는 뷰의 hierarchy를 돌면서 가장 깊은 subview를 찾고, 그 객체가 touch event를 받을 first responder가 됩니다.</p>
<blockquote>
<p>view를 기반으로 한 hit-testing으로 touch event를 처리할 뷰를 결정한다.
hit-testing은 UIView 안에 정의된 <code>hitTest(_:with:)</code> 를 통해 이루어진다.</p>
</blockquote>
<p>touch가 발생하면, UIKit은 UITouch object를 생성하고, 뷰와 연결시킵니다. touch 위치 또는 다른 parameters 변화에 따라 UIKit는 동일한 UITouch object를 새로운 정보와 함께 업데이트합니다. 하지만 touch 객체의 view property는 변하지 않습니다. touch가 끝나면 UIKit이 UITouch object를 release 합니다.</p>
<blockquote>
<p>touch event가 발생하면 UIKit가 UITouch 객체를 생성해서 (hit-testing을 통해 찾은) view와 연결한다.
touch 객체의 view property에 assign. touch 위치가 변하더라도, view 속성 값은 변하지 않는다.
touch가 끝나면(<code>touchesEnded()</code>가 호출되면) UIKit가 touch object를 release(메모리에서 해제) 한다.</p>
</blockquote>
<pre><code class="language-swift">// UIResponder
func touchesBegan()
func touchesMoved()
func touchesEnded()
func touchesCancelled() // 전화, 문자 등으로 터치 이벤트가 interrupte 된 경우</code></pre>
<h2 id="altering-the-responder-chain">Altering the Responder Chain</h2>
<p>responder object의 <code>next</code> property를 오버라이딩해서 responder chain을 변경할 수 있습니다.</p>
<p>이미 많은 UIKit class들이 next property를 오버라이딩해서 특정 객체를 리턴하고 있습니다. 가령:</p>
<ul>
<li>UIView : view가 view controller의 root view라면, next responder는 view controller가 됩니다. 그렇지 않은 경우, next responder는 view의 super view가 됩니다.</li>
<li>UIViewController : view controller가 window의 root view라면, next responder는 window 객체가 됩니다.</li>
<li>view controller가 다른 view controller에 의해 present 된 경우에는, next responder는 presenting view controller가 됩니다.</li>
<li>UIWindow : window 객체의 next responder는 UIApplication 객체입니다.</li>
<li>UIApplication : next responder는 app delegate가 됩니다. 단, app delegate가 UIResponder의 인스턴스이면서 view, view controller, app 객체 자체가 아닌 경우에만 해당됩니다.</li>
</ul>
<pre><code class="language-swift">@available(iOS 2.0, *)
open class UIResponder : NSObject, UIResponderStandardEditActions {

  open var next: UIResponder? { get }

  open var canBecomeFirstResponder: Bool { get } // default is NO
  open func becomeFirstResponder() -&gt; Bool

  open var canResignFirstResponder: Bool { get } // default is YES
  open func resignFirstResponder() -&gt; Bool

  open var isFirstResponder: Bool { get }
  // ...
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Swift] Property Wrapper]]></title>
            <link>https://velog.io/@lena_/Property-Wrapper</link>
            <guid>https://velog.io/@lena_/Property-Wrapper</guid>
            <pubDate>Tue, 31 Aug 2021 06:09:16 GMT</pubDate>
            <description><![CDATA[<p>Swift 5.1에서 추가된 개념이다.
Property Wrapper(감싸는) 이름답게 중복이 되는 로직을 포함하는 타입을 정의한다.</p>
<p>Struct, Enum, Class에서 Wrapping 하고 싶은 property 앞에 @State 같은 attribute를 명시해서 사용한다.</p>
<h2 id="example">Example</h2>
<p>WWDC 19 예제 코드,
UserDefaults에 값을 저장하고 가져오기 위한 코드가 중복된다.</p>
<pre><code class="language-swift">class UserManager {
  static var useTouchID: Bool {
    get { UserDefaults.standard.bool(forKey: &quot;useTouchID&quot;) }
    set { UserDefaults.standard.set(newValue, forKey: &quot;useTouchID&quot;) }
  }

  static var userEmail: String? {
    get { UserDefaults.standard.string(forKey: &quot;userEmail&quot;) }
    set { UserDefaults.standard.set(newValue, forKey: &quot;userEmail&quot;) }
  }

  static var isLoggedIn: Bool {
    get { UserDefaults.standard.bool(forKey: &quot;isLoggedIn&quot;) }
    set { UserDefaults.standard.set(newValue, forKey: &quot;isLoggedIn&quot;) }
  }
}</code></pre>
<p>Property Wrapper를 이용해 중복 코드를 wrapping 해보면 아래와 같고,</p>
<pre><code class="language-swift">@propertyWrapper
struct UserDefault&lt;T&gt; {

  let key: String
  let defaultValue: T

  var wrappedValue: T {
    get { UserDefaults.standard.object(forKey: key) as? T ?? defaultValue }
    set { UserDefaults.standard.set(newValue, forKey: key) }
  }
}</code></pre>
<p>이를 다음과 같이 사용할 수 있다.</p>
<pre><code class="language-swift">class UserManager {
  @UserDefault(key: &quot;useTouchID&quot;, defaultValue: false)
  static var useTouchID: Bool

  @UserDefault(key: &quot;userEmail&quot;, defaultValue: nil)
  static var userEmail: String?

  @UserDefault(key: &quot;isLoggedIn&quot;, defaultValue: false)
  static var isLoggedIn: Bool
}</code></pre>
<h2 id="➕">➕</h2>
<p>DI에 활용</p>
<ul>
<li>DIContainer 객체로부터 인스턴스를 get 하는 로직을 Property wrapper로 감싼 예제이다.</li>
<li>ViewController 안에서 View Model를 생성하지 않고 주입받을 수 있다.</li>
</ul>
<pre><code class="language-swift">@propertyWrapper
struct Dependency&lt;T&gt; {
  var wrappedValue: T {
    DIContainer.shared.resolve()
  }
}

// in ViewController
@Dependency var moviesViewModel: MoviesViewModel</code></pre>
<p><del>정리</del></p>
<p>property 앞에 attribute가 붙으면, Property Wrapper의 wrappedValue로 사용하겠다는 의미이다.
Property Wrapper를 사용해서 코드의 중복을 줄일 수 있다.</p>
<p><a href="https://zeddios.tistory.com/1221">Property Wrapper</a>
<a href="https://eunjin3786.tistory.com/233">[DI] DI Container, IOC Container 개념과 예제</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Swift] Struct, Class]]></title>
            <link>https://velog.io/@lena_/Swift-Struct-Class</link>
            <guid>https://velog.io/@lena_/Swift-Struct-Class</guid>
            <pubDate>Fri, 27 Aug 2021 06:19:40 GMT</pubDate>
            <description><![CDATA[<h2 id="comparing-structures-and-classes">Comparing Structures and Classes</h2>
<h3 id="class">Class</h3>
<ul>
<li>클래스는 단일 상속만 가능하다.</li>
<li>instance / type method, instance / type property를 갖는다.</li>
<li>reference 타입이다.<ul>
<li>클래스의 인스턴스는 전달될 때 포인터를 전달한다 = 전달되는 곳 어디서든 원본에 접근이 가능함</li>
</ul>
</li>
</ul>
<pre><code class="language-swift">class Point {
    var x = 0.0
    var y = 0.0
}

let point = Point()
var comparePoint = point // 인스턴스를 복사
comparePoint.x = 5
print(comparePoint.x, point.x) // 5.0, 5.0</code></pre>
<h3 id="struct">Struct</h3>
<ul>
<li>구조체는 상속이 불가능하다.</li>
<li>instance / type method, instance / type property를 갖는다.</li>
<li>value 타입이다.<ul>
<li>구조체의 인스턴스는 복사된다.(쓰기 시 복사 방식, 값이 실제로 변경(set)될 때 실제 복사가 이뤄짐, 원본이 변경되지 않음)</li>
<li>상속은 불가능하지만, 프로토콜 채택은 가능하다.<pre><code class="language-swift">struct Point {
var x = 0.0
var y = 0.0
}
</code></pre>
</li>
</ul>
</li>
</ul>
<p>let point = Point()
var comparePoint = point // 인스턴스를 복사
comparePoint.x = 5
print(comparePoint.x, point.x) // 5.0, 0.0</p>
<pre><code>### Class and Struct
-  서로 다른 자료형을 하나로 묶는다.
-  이렇게 묶은 자료형들을 새로운 type으로 사용할 수 있다.
-  클래스와 구조체 안에서 함수와 프로퍼티를 정의할 수 있다.
-  extension이 가능하다.

### Example code
```swift
/* struct */
struct Sample {
    // 가변 프로퍼티(값 변경 가능)
    var mutableProperty: Int = 100

    // 불변 프로퍼티(값 변경 불가능)
    let immutableProperty: Int = 100

    // 타입 프로퍼티(static 키워드 사용: 타입 자체가 사용하는 프로퍼티)
    static var typeProperty: Int = 100

    // 인스턴스 메서드(인스턴스가 사용하는 메서드)
    func instanceMethod(){
        print(&quot;instance method&quot;)
    }

    // 타입 메서드(static 키워드 사용: 타입 자체가 사용하는 메서드)
    static func typeMethod(){
        print(&quot;type method&quot;)
    }
}

var mutable: Sample = Sample() // 가변 인스턴스 생성
//인스턴스에서는 타입 프로퍼티나 타입 메서드를 사용할 수 없음
//mutable.typeProperty = 400 // 컴파일 오류
//mutable.typeMethod() // 컴파일 오류

Sample.typeProperty = 300 //구조체의 타입 프로퍼티 사용(새로운 value 할당)
Sample.typeMethod // type method //구조체의 타입 메서드 호출

let immutable: Sample = Sample() // 불변 인스턴스 생성
//immutable.mutableProperty = 200 // 불변 인스턴스는 가변 프로퍼티라도 인스턴스 생성 후 수정 불가 // 컴파일 오류

/* note */
// 타입(static) or 인스턴스 분류할 수 있고, 
// 타입은 그 자체로, 인스턴스는 생성 후 사용 가능함을 의미한다.

/* class */
// class의 type method는 상속 시 override 할 수 없다.

class SuperClass {
    // 인스턴스 메서드
    func instanceMethod(){
        print(&quot;instance method&quot;)
    }

    // 타입 메서드(상속 시 override 불가능)
    static func typeMethod(){
        print(&quot;type method&quot;)
    }
}

class SubClass: SuperClass {
    override func instanceMethod() {
        print(&quot;override instance method&quot;)
    }  
}

var superClass: SuperClass = SuperClass()
superClass.instanceMethod() // prints &quot;instance method&quot;

var subClass: SubClass = SubClass()
subClass.instanceMethod() // prints &quot;override instance method&quot;</code></pre><h2 id="conclusion">Conclusion</h2>
<ul>
<li>구조체는 클래스에 비해 시스템 리소스를 적게 사용한다.</li>
<li>자료형을 캡슐화하는 것이 주목적이라면 구조체를 사용하는 것을 권장</li>
<li>상속이 필요없고, 인스턴스 전달 시 참조보다는 복사가 예상될 경우 구조체 사용을 권장</li>
<li>그 외의 경우 클래스를 사용 - 대부분의 사용자 데이터는 클래스로 정의되어야 한다.</li>
</ul>
<h2 id="references">References</h2>
<p><a href="https://zeddios.tistory.com/12">https://zeddios.tistory.com/12</a>
<a href="https://developer.apple.com/documentation/swift/choosing_between_structures_and_classes">https://developer.apple.com/documentation/swift/choosing_between_structures_and_classes</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[URLSession]]></title>
            <link>https://velog.io/@lena_/URLSession</link>
            <guid>https://velog.io/@lena_/URLSession</guid>
            <pubDate>Fri, 30 Jul 2021 03:43:59 GMT</pubDate>
            <description><![CDATA[<p><del><a href="https://developer.apple.com/documentation/foundation/urlsession">공식문서</a> 읽기</del></p>
<h3 id="declaration">Declaration</h3>
<pre><code class="language-swift">class URLSession : NSObject</code></pre>
<h3 id="overview">Overview</h3>
<p>URLSession class와 관련 class들은 URL을 통해 데이터를 다운로드, 업로드하기 위한 API를 제공합니다.
또한, App이 실행(running) 중이 아니거나, 중지되더라도 background에서 다운로드할 수 있도록 지원합니다.</p>
<p>URLSessionDelegate, URLSessionTaskDelegate를 사용해서 authentication을 지원하거나 redirection, task completion 같은 이벤트를 받도록 할 수 있습니다.</p>
<blockquote>
<p>Note
The URLSession API involves many different classes that work together in a fairly complex way which may not be obvious if you read the reference documentation by itself. Before using the API, read the overview in the URL Loading System topic. The articles in the Essentials, Uploading, and Downloading sections offer examples of performing common tasks with URLSession.</p>
</blockquote>
<p>URLSession API는 다양한 class들과 함께 작업하기 때문에, API를 사용하기 전에 <a href="https://developer.apple.com/documentation/foundation/url_loading_system">URL Loading System topic</a>에 대한 overview를 먼저 읽어보십시오.</p>
<p>Your app creates one or more URLSession instances, each of which coordinates a group of related data-transfer tasks. For example, if you’re creating a web browser, your app might create one session per tab or window, or one session for interactive use and another for background downloads. Within each session, your app adds a series of tasks, each of which represents a request for a specific URL (following HTTP redirects, if necessary).</p>
<h3 id="types-of-url-sessions">Types of URL Sessions</h3>
<p>The tasks within a given URL session share a common session configuration object, 
which defines connection behavior, like the maximum number of simultaneous connections to make to a single host, whether connections can use the cellular network, and so on.</p>
<p>URLSession에 추가된 task는 common session co
nfiguration를 공유하는데, 연결 동작을 정의합니다.(connection behavior)
예를 들면, 하나의 호스트에 동시에 연결될 수 있는 최대 개수나
연결이 cellular network를 사용할 수 있는지 등이 있습니다.</p>
<p>URLSession은 singleton 세션인 <code>shared</code>를 제공하는데, 이것은 configuration object가 없습니다.
세션을 만들 때, 커스터마이징이 가능하지 않지만 basic request와 같은 제한적인 요청을 할 때 사용하기 좋습니다.
shared가 아닌 다른 세션을 만들 때는 다음 세 가지 중 하나의 configuration을 지정해야 합니다:</p>
<ul>
<li>default : shared 세션과 비슷하게 동작하지만, delegate를 지정할 수 있습니다. (to obtain data incrementally)</li>
<li>Ephemeral : 캐시, 쿠기, credential 정보를 디스크에 쓰지(write)하지 않습니다.</li>
<li>Background : app이 실행 중이 아닐 때, background에서 업로드, 다운로드 task를 수행합니다.</li>
</ul>
<h3 id="using-a-session-delegate">Using a Session Delegate</h3>
<p>Session Delegate를 구현해서 다양한 이벤트가 발생했을 때 정보를 얻을 수 있습니다. 예를 들면:</p>
<ul>
<li>Authentication fails. -- 인증에 실패할 때</li>
<li>Data arrives from the server. -- 서버로부터 데이터가 내려왔을 때</li>
<li>Data becomes available for caching. -- 데이터를 캐싱해야 할 때</li>
</ul>
<p>만약 delegate로 부터 제공되는 기능이 필요 없다면, 세션을 생성할 때 nil을 넘겨서 delegate 없이 API를 사용할 수 있습니다.</p>
<blockquote>
<p>Important
session 객체는 delegate와 강한 참조 관계이므로, session을 invalidate 하지 않으면 app이 종료되게 전까지 memory leak이 발생할 것입니다.</p>
</blockquote>
<p>Each task you create with the session calls back to the session’s delegate, 
using the methods defined in URLSessionTaskDelegate. You can also intercept these callbacks before they reach the session delegate by populating a separate delegate that’s specific to the task.</p>
<h3 id="asynchronicity-and-url-sessions">Asynchronicity and URL Sessions</h3>
<p>URLSession API는 비동기로 동작합니다.</p>
<ul>
<li>In Swift or Objective-C, completion handler block를 제공해서 task가 완료되었을 때 실행할 작업을 전달할 수 있습니다.</li>
<li>In Swift or Objective-C, delegate method를 사용해서 전송이 진행 중이거나 완료된 직후 callbacks을 받을 수 있습니다.</li>
</ul>
<h3 id="thread-safety">Thread Safety</h3>
<p>URLSession은 thread-safe 하기 때문에, 어떤 thread context 에서도 session을 생성할 수 있습니다.
만약, delegate method가 completion handlers를 호출한다면, 작업은 자동으로 알맞은 delegate queue에 스케줄링될 것입니다.
When your delegate methods call the provided completion handlers, the work is automatically scheduled on the correct delegate queue.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Dependency Injection in Swift]]></title>
            <link>https://velog.io/@lena_/Dependency-Injection-in-Swift</link>
            <guid>https://velog.io/@lena_/Dependency-Injection-in-Swift</guid>
            <pubDate>Wed, 21 Jul 2021 11:43:59 GMT</pubDate>
            <description><![CDATA[<p>Dependency Injection, DI</p>
<h2 id="dependency">Dependency</h2>
<p>의존성이란 하나의 객체가 다른 객체에 강하게 결합(coupling)되어 있는 것을 의미한다.
다음 예제에서 <code>Vehicle</code> class의 init 안에서 <code>RaceCarEngine</code> class를 생성하고 있기 때문에,
<code>Vehicle</code> class는 <code>RaceCarEngine</code> class와 강하게 결합하고 있는 구조이다.</p>
<pre><code class="language-swift">class Vehicle
{
    var engine: Propulsion

    init() {
        engine = RaceCarEngine()
    }

    func forward() {
        engine.move()
    }
}</code></pre>
<h2 id="dependency-injection">Dependency Injection</h2>
<p>이렇게 객체 간 강하게 결합되어 있는 구조는 모듈화 되지 않으므로, 코드의 유지보수에 좋지 않다.
결합도를 낮출 수 있게 개선해보면 다음과 같다:</p>
<pre><code class="language-swift">class Vehicle
{
    var engine: Propulsion

    init(engine: Propulsion) {
        self.engine = engine
    }

    func forward() {
        engine.move()
    }
}</code></pre>
<p>init 안에서 파라미터로 <code>Propulsion</code> 타입을 주입(Injection)받음으로써, <code>Vehicle</code> class가 더 이상 <code>RaceCarEngine</code> class를 알지 않도록 바뀌었다!</p>
<p>이제, 아래와 같이 <code>Vehicle</code> class의 engine으로 <code>RaceCarEngine</code> 외에 다른 class를 주입할 수 있게 되었다:</p>
<pre><code class="language-swift">let rocket = RocketEngine()

var car = Vehicle(engine: rocket)
car.forward()</code></pre>
<p>이처럼 _Protocol_과 _init_을 이용해 의존성을 주입하게 되면...</p>
<ul>
<li>(when rocket engine class isn’t ready yet) 테스트 목적으로 fake class를 만들어 사용할 수 있고,</li>
<li>객체 간 결합도가 낮아졌으므로, Protocol을 따르는 어떤 class든 주입할 수 있다(unit testing).</li>
</ul>
<p>의존성 주입을 위해 반드시 Protocol을 사용해야 하는 것은 아니다.
subclassing이나, generic, opaque type을 활용할 수도 있다.</p>
<blockquote>
<p>정리하면🙂</p>
</blockquote>
<ul>
<li>의존성 주입은 객체 간 강한 결합(coupling)을 피하기 위해 사용한다.</li>
<li>의존성을 주입함으로써 unit test가 용이해진다.</li>
</ul>
<hr>
<ul>
<li><a href="https://learnappmaking.com/dependency-injection-swift/">https://learnappmaking.com/dependency-injection-swift/</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[UINib, File Owner]]></title>
            <link>https://velog.io/@lena_/UINib</link>
            <guid>https://velog.io/@lena_/UINib</guid>
            <pubDate>Sat, 17 Jul 2021 11:59:42 GMT</pubDate>
            <description><![CDATA[<p>Custom View(Cell)를 만들기 위해 xib 파일을 만들기도 하는데,
xib, nib, file&#39;s owner 등 관련 개념을 정리해보자.</p>
<h2 id="xib-nib">.xib .nib</h2>
<p>.xib는 <a href="https://www.google.com/url?sa=t&amp;rct=j&amp;q=&amp;esrc=s&amp;source=web&amp;cd=&amp;cad=rja&amp;uact=8&amp;ved=2ahUKEwjpg7HO5-nxAhWKKqYKHfC0AgYQFjAHegQIChAD&amp;url=https%3A%2F%2Fko.wikipedia.org%2Fwiki%2FXML&amp;usg=AOvVaw1BuM_1kKijDP9OVi1T9KcN">XML</a>(태그 형태의 마크업 언어)로 이루어진 파일로, XCode가 인터페이스 빌더를 통해 시각적으로 제어 가능하도록 제공하고 있다.
.xib 파일을 컴파일한 것이 .nib 파일이다.</p>
<h2 id="uinib">UINib</h2>
<p><code>UINib</code> class는 nib 파일의 컨텐츠를 래핑하는 객체다.
An object that contains Interface Builder nib files.</p>
<p><code>UINib</code> 객체는 nib 파일의 컨텐츠(view)를 메모리에 캐시하고 있다가, instantiate할 때(<code>.instantiate</code>) 언아카이빙한다.
A UINib object caches the contents of a nib file in memory, ready for unarchiving and instantiation. </p>
<pre><code class="language-swift">let nib = UINib(named: &quot;Name&quot;, bundle: nil)
let contents = nib.instantiate(withOwner: self, options: nil)</code></pre>
<h2 id="loadnibnamed">loadNibNamed</h2>
<p><code>UINib</code> class로 nib 파일을 로드하는 방법말고도, <code>Bundle</code> class에 정의된 <code>loadNibNamed</code> 메서드를 통해 로드할 수 있다.</p>
<pre><code class="language-swift">
func loadFromNib() -&gt; UIView? {
        guard let view = Bundle.main.loadNibNamed(name, owner: nil, options: nil)?.first as? UIView else {
            return nil
        }
        return view
    }</code></pre>
<h2 id="files-owner">File&#39;s Owner</h2>
<p>모든 nib 파일에는 File&#39;s Owner 객체가 정의되어 있는데, File&#39;s Owner로 지정한 객체는 nib 파일 내 뷰와 연결될 수 있다. 즉, outlet과 IBAction으로 연결할 수 있다.
주로 View Controller 객체가 File&#39;s Owner가 된다.</p>
<h3 id="storyboard">storyboard</h3>
<p>storyboard는 xib의 번들(여러 xib가 모인 것)이다.</p>
<hr>
<p><em>references</em></p>
<ul>
<li><a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/LoadingResources/CocoaNibs/CocoaNibs.html">docs</a></li>
<li><a href="https://soooprmx.com/nib-%ED%8C%8C%EC%9D%BC%EB%A1%9C%EB%B6%80%ED%84%B0-ui-%EA%B4%80%EB%A0%A8-%EA%B0%9D%EC%B2%B4%EB%A5%BC-%EB%A1%9C%EB%94%A9%ED%95%98%EA%B8%B0/#fn-7226-2-1">blog</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[UIWindow]]></title>
            <link>https://velog.io/@lena_/UIWindow</link>
            <guid>https://velog.io/@lena_/UIWindow</guid>
            <pubDate>Tue, 13 Jul 2021 17:08:39 GMT</pubDate>
            <description><![CDATA[<p>프로젝트를 새로 생성하면 SceneDelegate 안에 window 프로퍼티가 다음과 같이 선언되어 있다.</p>
<pre><code class="language-swift">class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?

}</code></pre>
<p>UIWindow 객체는 UIView를 상속하는데, 공식 문서에서는 다음 두 가지 상황에서만 windows 사용해야 한다고 설명하고 있다.</p>
<pre><code class="language-swift">@MainActor class UIWindow : UIView</code></pre>
<ul>
<li>app의 컨텐츠를 표시하기 위해 mian window를 제공해야 하거나</li>
<li>추가적인 컨텐츠를 표시하기 위해 추가 window를 생성해야 할 때</li>
</ul>
<blockquote>
<p>You use windows only when you need to do the following:
Provide a main window to display your app’s content.
Create additional windows (as needed) to display additional content.</p>
</blockquote>
<p>app의 컨텐츠를 표시하기 위한 mian window는 Xcode 템플릿이 스토리보드를 이용해 자동으로 제공해주고 있다. 따라서, Main 스토리보드를 사용하지 않는다면 직접 window 객체를 생성해야 한다.</p>
<p>그 외에 iOS 13부터 지원하는 멀티 윈도우를 구현하려면 window 객체를 추가로 생성해야 한다.
멀티 윈도우는 주로 또 따른 <a href="https://developer.apple.com/documentation/uikit/uiscreen">스크린</a>에 컨텐츠를 표시하기 위해 사용된다.
(extra windows are commonly used to display content on an external screen, as described in Displaying Content on a Connected Screen.)</p>
<p>UIWindow 객체를 다음과 같은 작업을 할 때 사용할 수도 있다.</p>
<ul>
<li>window의 z축을 설정해서 다른 윈도우들 사이에서 보이기(visibility)를 조정할 때</li>
<li>window를 표시해서 키보드의 타겟이 되도록 할 때</li>
<li>window 좌표를 바꾸거나, window의 좌표계를 기준으로 다른 좌표를 설정할 때</li>
<li>window의 root view controller를 변경할 때</li>
<li>window가 표시되고 있는 스크린은 변경할 때</li>
</ul>
<blockquote>
<ul>
<li>Setting the z-axis level of your window, which affects the visibility of the window relative to other windows.</li>
</ul>
</blockquote>
<ul>
<li>Showing windows and making them the target of keyboard events.</li>
<li>Converting coordinate values to and from the window’s coordinate system.</li>
<li>Changing the root view controller of a window.</li>
<li>Changing the screen on which the window is displayed.</li>
</ul>
<p>한 줄 요약.😉
UIWindow는 UIView를 상속받는 app의 컨테이너 뷰로, 
app의 컨텐츠를 표시하거나 연결된 다른 스크린에 표시하기 위한 추가 window가 필요할 때 사용된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Application life cycle in iOS]]></title>
            <link>https://velog.io/@lena_/AppLifeCycle</link>
            <guid>https://velog.io/@lena_/AppLifeCycle</guid>
            <pubDate>Mon, 05 Jul 2021 13:13:26 GMT</pubDate>
            <description><![CDATA[<p><a href="https://manasaprema04.medium.com/application-life-cycle-in-ios-f7365d8c1636">해당 글</a> 번역(의역)과 이해에 도움이 되는 내용을 덧붙였습니다.😊</p>
<hr>
<p>application life cycle은 app의 시작부터 종료까지 발생하는 event의 연속이다.</p>
<h3 id="steps-involved-from-device-reboot-to-app-launch">Steps involved from device reboot to app launch:</h3>
<p>phone을 켜고 app icon을 터치하면 SpringBoard가 app을 실행한다(launch).</p>
<blockquote>
<p>SpringBoard is the standard application that manages the iPhone’s home screen. Other tasks include starting WindowServer, launching and bootstrapping applications and setting some of the device’s settings on startup</p>
</blockquote>
<p>SpringBoard가 app의 launch screen을 보여주는 동안, 우리의 앱은 필요한 라이브러리들을 로드에서 메모리 위로 올린다.
그리고 app이 실행되고 application delegate가 notifications을 받게된다.</p>
<p><code>AppDelegate</code>가 application delegate이다. AppDelegate는 UIResponder class를 상속받고, <a href="https://developer.apple.com/documentation/uikit/uiapplicationdelegate">UIApplicationDelegate</a> 프로토콜을 구현하고 있다.</p>
<pre><code class="language-swift">class AppDelegate: UIResponder, UIApplicationDelegate { ... }</code></pre>
<p>_UIApplicationDelegate_를 구현해서 user events(such as app launch, app goes into background or foreground, app is terminated, a push notification was opened etc.)에 대한 알림을 받도록 한다.</p>
<p>_UIResponder_를 상속받아서 AppDelegate가 user events에 반응할 수 있게 하고, UIApplicationDelegate의 delegate가 AppDelegate이 되도록 한다.</p>
<blockquote>
<p><strong>Summary</strong></p>
</blockquote>
<ul>
<li>SpringBoard라는 기본 앱이 app icon을 터치하면 app을 실행하고(@main 호출)</li>
<li>@main(entry point into app)이 실행되며 AppDelegate object가 생성되고, application의 delegate가 되며,</li>
<li>(app) event cycle을 manage 한다.</li>
</ul>
<h3 id="execution-states-for-apps">Execution States for Apps:</h3>
<ul>
<li><strong>Not Running</strong> state : The app has not been launched or terminated by the system.</li>
<li><strong>Inactive</strong> state : The app is entering the foreground state but not receiving events.</li>
<li><strong>Active</strong> state: The app enters the foreground state and can process event.</li>
<li><strong>Background</strong> state : In this state, if there is executable code, it will execute and if there is no executable code or the execution is complete, the application will be suspended immediately.</li>
<li><strong>Suspended</strong> state : The app is in the background(in memory) but is not executing code and if system does not have enough memory, it will terminate the app.</li>
</ul>
<p>AppDelegate가 app의 실행 상태(State)에 따라 cycle을 돌면서 manage 한다.</p>
<blockquote>
<p><em><strong>Not Running</strong> : app이 실행되지 않았거나 시스템에 의해 종료된 상태
<strong>Inactive</strong> : app이 foreground 상태에 진입했지만, (user) event를 받지는 못하는 상태
<strong>Active</strong>: pp이 foreground 상태에 진입했지만, (user) event를 처리할 수 있는 상태
<strong>Background</strong> : 이 상태에서는 실행 가능한 코드가 있더라도 즉시 중지된다.
<strong>Suspended</strong> : app이 background 상태에 있으면서 실행 가능한 코드가 없을 때, 시스템이 충분한 메모리가 없으면 app을 중지시킨다.</em></p>
</blockquote>
<p><img src="https://miro.medium.com/max/1400/1*iPfml96pSwHHgzxdhVcS_g.png" alt=""></p>
<h3 id="interview-questions-on-app-life-cycle">Interview Questions on App life cycle:</h3>
<ul>
<li><p>앱이 foreground에 있을 때와 background에 있을 때 어떤 제약사항이 있는가?
background 상태에서는 코드가 실행되지 않고 즉시 중지된다. foreground 상태이더라도 Active 상태여야 event를 처리할 수 있다.</p>
</li>
<li><p>상태 변화에 따라 다른 동작을 처리하기 위한 앱델리게이트 메서드들을 설명하시오.
앱의 상태는 Not Running, Inactive, active, background, Suspended로 구분할 수 있는데, UIApplicationDelegate 프로토콜을 채택하여 AppDelegate가 구현한 메서드 안에서 상태에 따른 동작을 처리할 수 있다.</p>
</li>
<li><p>SceneDelegate에 대해 설명하시오. 
iOS 13부터 추가된 객체로, iOS가 멀티 윈도우를 지원하게 되면서 AppDelegate의 역할 일부를 SceneDelegate가 하도록 변경되었다. 
SceneDelegate는 UISceneDelegate의 subclass인 UIWindowSceneDelegate를 채택하며, app life cycle에 따른 동작을 관리할 수 있다.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Decoding시 key가 없는 경우]]></title>
            <link>https://velog.io/@lena_/DecodingKeyNotFound</link>
            <guid>https://velog.io/@lena_/DecodingKeyNotFound</guid>
            <pubDate>Sun, 04 Jul 2021 14:29:44 GMT</pubDate>
            <description><![CDATA[<p>다음과 같이 struct를 정의하고,</p>
<pre><code class="language-swift">struct Room: Decodable {
    let hotelID: Int
    let title: String
    let price: Double
    let wishlist: Bool
    let longitude: Double
    let latitude: Double
    let rate: Int
    let options: [String]
    let img: String
}</code></pre>
<p>json data를 모델 객체로 decoding 할 때 해당하는 key가 없는 경우,
<code>init(from decoder: Decoder)</code>을 직접 구현해서 처리할 수 있다.</p>
<pre><code class="language-swift">// 다음과 같이 데이터가 내려올 경우
[
    {
        &quot;hotelId&quot;: 1,
        &quot;title&quot;: &quot;title&quot;,
        &quot;price&quot;: 100,
        &quot;wishlist&quot;: false,
    }
]

init(from decoder: Decoder) throws {
  // 구현부를 작성.
}</code></pre>
<p>CodingKeys를 정의하고, decodeIfPresent를 이용해 key가 없을 경우에 기본 값을 정의한다.</p>
<pre><code class="language-swift">enum CodingKeys: String, CodingKey {
        case hotelID = &quot;hotelId&quot;
        case title, price, wishlist, longitude, latitude, rate, options, img
    }

init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    hotelID = try container.decode(Int.self, forKey: .hotelID)
    title = try container.decode(String.self, forKey: .title)
    price = try container.decode(Double.self, forKey: .price)
    wishlist = try container.decode(Bool.self, forKey: .wishlist)
    longitude = try container.decodeIfPresent(Double.self, forKey: .longitude) ?? 0
    latitude = try container.decodeIfPresent(Double.self, forKey: .longitude) ?? 0
    rate = try container.decodeIfPresent(Int.self, forKey: .longitude) ?? 0
    options = try container.decodeIfPresent([String].self, forKey: .options) ?? []
    img = try container.decodeIfPresent(String.self, forKey: .options) ?? &quot;&quot;
}</code></pre>
<p>decodeIfPresent 대신 decode를 사용할  수도 있다.</p>
<pre><code class="language-swift">longitude = (try? container.decode(Double.self, forKey: .longitude)) ?? 0</code></pre>
<p>decodeIfPresent는 optional, decode은 non-optional 타입을 리턴한다.</p>
<blockquote>
</blockquote>
<p><strong>decodeIfPresent</strong>
Return Value: A decoded value of the requested type, or nil if the Decoder does not have an entry associated with the given key, or if the value is a null value.</p>
<p>정리🙃.
model(struct, class)의 속성을 optional로 정의하고 싶지 않으면서,
decoding 시 keyNotFound error를 핸들링하기 위해
init(decoder:) 구현부를 직접 작성할 수 있다.</p>
<hr>
<p><em>ref.</em>
<a href="https://jinnify.tistory.com/71">https://jinnify.tistory.com/71</a>
<a href="https://zeddios.tistory.com/577">https://zeddios.tistory.com/577</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Github OAuth Login]]></title>
            <link>https://velog.io/@lena_/Github-OAuth-Login</link>
            <guid>https://velog.io/@lena_/Github-OAuth-Login</guid>
            <pubDate>Thu, 06 May 2021 15:00:17 GMT</pubDate>
            <description><![CDATA[<h3 id="방법1---url-scheme">방법1 - URL Scheme</h3>
<p>1️⃣ TargetApp - Info - URL Scheme(Authorization callback URL) 설정하기</p>
<p>2️⃣ UIApplication.shared.open(url:)으로 url 열기</p>
<pre><code class="language-swift">let clientID = &quot;a1b2c3d4&quot;
let scope = &quot;repo gist user&quot; 
let urlString = &quot;https://github.com/login/oauth/authorize&quot; 
var components = URLComponents(string: urlString)! 
components.queryItems = [URLQueryItem(name: &quot;client_id&quot;, value: self.clientID),
             URLQueryItem(name: &quot;scope&quot;, value: self.scope)] 
UIApplication.shared.open(url)</code></pre>
<p>3️⃣ login 성공 후 redirect, Github으로 부터 리턴받은 code를 가지고 access token 얻기</p>
<pre><code class="language-swift">// SceneDelegate.swift

    func scene(_ scene: UIScene, openURLContexts URLContexts: Set&lt;UIOpenURLContext&gt;) {
        if let url = URLContexts.first?.url {
            print(url)
            let code = url.absoluteString.components(separatedBy: &quot;code=&quot;).last ?? &quot;&quot;
            print(code)
            requestAccessToken(with: code)
        }
    }

    func requestAccessToken(with code: String) {
        let url = &quot;https://github.com/login/oauth/access_token&quot;
        let parameters = [&quot;client_id&quot;: APIRequestManager.clientID,
                          &quot;client_secret&quot;: APIRequestManager.clientSecret,
                          &quot;code&quot;: code]
        var request = URLRequest(url: URL(string: url)!)
        request.httpMethod = &quot;POST&quot;
        request.addValue(&quot;application/json&quot;, forHTTPHeaderField: &quot;Content-Type&quot;)
        request.addValue(&quot;application/json&quot;, forHTTPHeaderField: &quot;Accept&quot;)
        guard let bodyData = try? JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted) else { return }
        request.httpBody = bodyData

        URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in
            guard let data = data else { return }
            guard let json = try? JSONSerialization.jsonObject(with: data, options: []) else { return }
            print(json)
            if let data = json as? [String: Any] {
                // handle data
            }
        }).resume()
    }</code></pre>
<h3 id="방법2---aswebauthenticationsession">방법2 - ASWebAuthenticationSession</h3>
<ul>
<li><code>ASWebAuthenticationSession</code> 객체 생성하기</li>
<li><code>ASWebAuthenticationPresentationContextProviding</code> 프로토콜 채택해서 <code>presentationAnchor</code> 함수 구현하기</li>
</ul>
<pre><code class="language-swift">class LoginViewController: UIViewController {

    var authenticationSession: ASWebAuthenticationSession?
    let authURL = URL(string: &quot;https://github.com/login/oauth/authorize?client_id=\(APIRequestManager.clientID)&quot;)
    let callbackUrlScheme = &quot;baseballapp&quot;

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    private func auth() {
        authenticationSession = ASWebAuthenticationSession.init(url: authURL!, callbackURLScheme: callbackUrlScheme, completionHandler: { (callbackURL:URL?, error:Error?) in
                print(callbackURL)
                guard error == nil,
                    let callbackURL = callbackURL,
                    let queryItems = URLComponents(string: callbackURL.absoluteString)?.queryItems,
                    let code = queryItems.first(where: { $0.name == &quot;code&quot; })?.value else {
                    print(&quot;An error occurred when attempting to sign in.&quot;)
                    return
                }
            self.requestAccessToken(with: code)
        })
        authenticationSession?.presentationContextProvider = self
        authenticationSession?.start()
    }

    func requestAccessToken(with code: String) {
        let url = &quot;https://github.com/login/oauth/access_token&quot;
        let parameters = [&quot;client_id&quot;: APIRequestManager.clientID,
                          &quot;client_secret&quot;: APIRequestManager.clientSecret,
                          &quot;code&quot;: code]
        var request = URLRequest(url: URL(string: url)!)
        request.httpMethod = &quot;POST&quot;
        request.addValue(&quot;application/json&quot;, forHTTPHeaderField: &quot;Content-Type&quot;)
        request.addValue(&quot;application/json&quot;, forHTTPHeaderField: &quot;Accept&quot;)
        guard let bodyData = try? JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted) else { return }
        request.httpBody = bodyData

        URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in
            guard let data = data else { return }
            guard let json = try? JSONSerialization.jsonObject(with: data, options: []) else { return }
            print(json)
            if let data = json as? [String: Any] {
                // handle data
            }
        }).resume()
    }

    @IBAction func login() {
        auth()
    }
}

extension LoginViewController: ASWebAuthenticationPresentationContextProviding {

    func presentationAnchor(for session: ASWebAuthenticationSession) -&gt; ASPresentationAnchor {
        return self.view.window ?? ASPresentationAnchor()
    }
}</code></pre>
<hr>
<p><a href="https://d2.naver.com/helloworld/24942">https://d2.naver.com/helloworld/24942</a>
<a href="https://medium.com/@jungkim/%EC%95%B1%EC%97%90%EC%84%9C-oauth2-%EC%97%B0%EB%8F%99%ED%95%98%EA%B8%B0-dadcce91e921">https://medium.com/@jungkim/앱에서-oauth2-연동하기-dadcce91e921</a>
<a href="https://www.raywenderlich.com/19364429-implementing-oauth-with-aswebauthenticationsession">https://www.raywenderlich.com/19364429-implementing-oauth-with-aswebauthenticationsession</a>
<a href="https://zeddios.tistory.com/1102">https://zeddios.tistory.com/1102</a>
and... <a href="https://deptno.github.io/posts/2018/Github-OAuth-404/">https://deptno.github.io/posts/2018/Github-OAuth-404/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[iOS13+, UITableViewDiffableDataSource]]></title>
            <link>https://velog.io/@lena_/iOS13-UITableViewDiffableDataSource</link>
            <guid>https://velog.io/@lena_/iOS13-UITableViewDiffableDataSource</guid>
            <pubDate>Sun, 02 May 2021 13:19:29 GMT</pubDate>
            <description><![CDATA[<h2 id="what-is-uitableviewdiffabledatasource">What is UITableViewDiffableDataSource?</h2>
<p>iOS 13+ 부터 <code>UITableViewDataSource</code>을 대신해 table view에 어떤 cell을, 얼마나(how many), 어느 section에 display 할 지 등을 configure 할 수 있는 UITableViewDiffableDataSource가 지원됩니다.
(UICollectionViewDiffableDataSource도 마찬가지)</p>
<p>기존 UITableViewDataSource를 사용하는 방식과 가장 큰 차이점은 <code>reloadData</code>, <code>performBatchUpdates</code>가 없다는 것입니다.</p>
<p>UITableViewDiffableDataSource은 data에 변화가 생길 경우, update 이전과 이후를 비교해서 animate 합니다.</p>
<blockquote>
<p>The diffable part of UICollectionViewDiffableDataSource means that whenever you update the items you’re displaying, the collection view will automatically calculate the difference between the updated collection and the one previously shown. This will in turn cause the collection view to animate the changes, such as updates, insertions and deletions.</p>
</blockquote>
<h2 id="benefits">Benefits</h2>
<p>UITableViewDiffableDataSource를 사용해서 얻는 이점은</p>
<ul>
<li>data 변화를 사용자가 인지할 수 있는 애니메이션이 지원됩니다.</li>
<li>자동으로 데이터가 동기화(synchronization) 됩니다.</li>
<li>전체적으로 작성하는 코드의 양을 줄일 수 있습니다.</li>
</ul>
<blockquote>
<ul>
<li>Automatic data change animations: Whenever you add, update or delete data, you can get the data change animation automatically.</li>
</ul>
</blockquote>
<ul>
<li>Automatic data synchronization: To utilize collection view’s standard animation without UICollectionViewDiffableDataSource, you’d have to manually manage and synchronize data changes between the collection view and the data source. If you have a misalignment in one of the synchronization operations, you’d see an error like this:
Assertion error because the collection view update is invalid</li>
<li>Reduced code: Overall, you can write less code and benefit from the collection view’s data change animations and data synchronization.</li>
</ul>
<h2 id="using-diffable-data-source">Using Diffable Data Source</h2>
<blockquote>
<ul>
<li>Hashable section, item</li>
</ul>
</blockquote>
<ul>
<li>UICollectionViewDiffableDataSource</li>
<li>NSDiffableDataSourceSnapshot</li>
</ul>
<p>1️⃣ <strong>Hashable Section type, Item type 선언</strong></p>
<pre><code>UITableViewDiffableDataSource&lt;SectionIdentifierType, ItemIdentifierType&gt; : NSObject where SectionIdentifierType : Hashable, ItemIdentifierType : Hashable</code></pre><p>UITableViewDiffableDataSource은 위와 같은 형태의 generic class이므로, section과 item이 될 Hashable한 객체를 선언해야 합니다.
UITableViewDiffableDataSource은 Snapshot 객체를 이용해 data의 변화를 감지하는데, Snapshot이 indexPath 대신 unique identifiers에 의존하기 때문에 section과 item type은 Hashable protocol을 준수하는 타입이어야 합니다.</p>
<pre><code class="language-swift">class Section: Hashable {
    var id = UUID()
    var title: String
    var item: [Dish]

    init(title: String, item: [Dish]) {
        self.title = title
        self.item = item
    }

    func hash(into hasher: inout Hasher) {
       hasher.combine(id)
     }

     static func == (lhs: Section, rhs: Section) -&gt; Bool {
       lhs.id == rhs.id
     }
}


class Dish: Decodable {
    let detailHash: String
    let title: String
}

extension Dish: Hashable {
    static func == (lhs: Dish, rhs: Dish) -&gt; Bool {
        return lhs.detailHash == rhs.detailHash
    }

    func hash(into hasher: inout Hasher) {
        hasher.combine(detailHash)
    }
}</code></pre>
<p>2️⃣ <strong>UITableViewDiffableDataSource 생성</strong>
<code>UITableViewDiffableDataSource</code>의 initializer로</p>
<ul>
<li><p>dataSource와 연결할 tableView와,</p>
</li>
<li><p>cellProvider(escaping closure 형태)를 제공해야 합니다.
<code>UITableViewDataSource</code>의 <code>cellForRowAt:</code> 메서드를 구현하는 것과 유사합니다.</p>
<pre><code class="language-swift">func makeDataSource() -&gt; UITableViewDiffableDataSource&lt;Section, Dish&gt; {
  let reuseIdentifier = cellReuseIdentifier
  return UITableViewDiffableDataSource(
      tableView: tableView,
      cellProvider: { tableView, indexPath, dish in
          let cell = tableView.dequeueReusableCell(
              withIdentifier: reuseIdentifier,
              for: indexPath)

          cell.textLabel?.text = dish.title

          return cell
      }
  )
}</code></pre>
<p>3️⃣ <strong>NSDiffableDataSourceSnapshot 생성</strong>
NSDiffableDataSourceSnapshot 객체를 통해 dataSource가 data의 변화를 감지합니다.</p>
<pre><code class="language-swift">func updateSnapshot(animatingChange: Bool = false) {
  var snapshot = NSDiffableDataSourceSnapshot&lt;Section, Dish&gt;()

  snapshot.appendSections(sections)
  sections.forEach { section in
    snapshot.appendItems(section.item, toSection: section)
  }
  dataSource.apply(snapshot, animatingDifferences: animatingChange)
}</code></pre>
</li>
</ul>
<hr>
<p><a href="https://www.appcoda.com/diffable-data-source/">https://www.appcoda.com/diffable-data-source/</a>
<a href="https://www.raywenderlich.com/8241072-ios-tutorial-collection-view-and-diffable-data-source">https://www.raywenderlich.com/8241072-ios-tutorial-collection-view-and-diffable-data-source</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Concurrency Programming]]></title>
            <link>https://velog.io/@lena_/Concurrency-Programming</link>
            <guid>https://velog.io/@lena_/Concurrency-Programming</guid>
            <pubDate>Sun, 21 Mar 2021 06:08:05 GMT</pubDate>
            <description><![CDATA[<h2 id="concurrency-programming">Concurrency Programming</h2>
<p>논리적으로 동시에 여러 일을 하는 것.
멀티스레딩 하는 것을 의미한다.
이에 비해 병렬성(Parallelism) 프로그래밍은 물리적으로(멀티 코어) 동시에 일을 하는 것을 말한다.</p>
<h2 id="multithread">Multithread</h2>
<p>하나의 프로세스(프로그램이 실행되어 메모리 위로 올라온 것)에는 하나 이상의 스레드가 존재한다(main thread).
thread는 코드 실행을 위한 하나의 흐름으로 생각하면 된다. thread를 하나 이상(main) 사용하는 것이 멀티스레딩이다.</p>
<h2 id="dispatch-queue">Dispatch Queue</h2>
<p>iOS는 멀티스레딩을 Queue로 관리하는데 serial, concurrent 2종류가 있다.</p>
<ul>
<li>serial queue: 한 번에 하나의 클로저 실행 - <code>main</code></li>
<li>concurrent queue: 여러 개의 스레드를 사용해서 동시 실행 - <code>global</code></li>
</ul>
<p>Queue(FIFO)를 기반으로 동작하기 때문에 항상 순차적으로 (큐에 먼저 추가된 것부터) task가 실행된다.</p>
<h3 id="sync-async">Sync, Async</h3>
<p>큐에 closure를 추가할 때 두 가지 방법이 있다.</p>
<ul>
<li>sync: 클로저가 종료될 때까지 current queue의 실행을 중지한다.(blocking)</li>
<li>async: current queue의 실행 흐름을 방해하지 않으면서 클로저를 실행한다.</li>
</ul>
<pre><code class="language-swift">DispatchQueue.main.async {
    // code
}

DispatchQueue.global().sync {
    // code
}</code></pre>
<p>여기서 current queue는 dispatch main queue가 될 수도 있고 background queue가 될 수도 있다.</p>
<blockquote>
<p>Current queue?
It may not be obvious what the source, or current, queue is, because it’s not always explicitly defined in the code. For example, if you call your sync statement inside viewDidLoad, your current queue will be the Main dispatch queue. If you call the same function inside a URLSession completion handler, your current queue will be a background queue.</p>
</blockquote>
<h3 id="dispatchqos">DispatchQoS</h3>
<p>main queue와 달리 global queue에 접근할 때는 <code>DispatchQoS</code>를 파라미터로 넘길 수 있는데,
DispatchQoS는 실행 우선 순위를 의미한다.</p>
<blockquote>
<p>The quality of service, or the execution priority, to apply to tasks.</p>
</blockquote>
<ul>
<li>DispatchQoS.userInteractive : high priority, only do something short and quick</li>
<li>DispatchQoS.userInitiated : high priority, but might take a little bit of time</li>
<li>DispatchQoS.default : default quality-of-service class</li>
<li>DispatchQoS.utility : long-running background processes, low priority</li>
<li>DispatchQoS.background : not directly initiated by user, so can run as slow as needed</li>
</ul>
<h2 id="gcd-dispatch">GCD, Dispatch</h2>
<p>OS X, iOS는 개발자가 직접 스레드 관리하지 않고도 비동기식으로 작업을 수행할 수 있도록 기술(framework)을 제공한다. GCD는 앱의 스레드와 관련된 코드를 시스템 수준으로 이동시켜서, 필요한 만큼 스레드를 생성하고 작업을 스케줄링한다.
<a href="https://developer.apple.com/documentation/dispatch">GCD</a>는 iOS가 제공하는 멀티스레딩을 위한 Framework 이다.</p>
<blockquote>
<p>Dispatch Queue는 GCD의 일부이다.
프로그래머가 실행할 task를 Dispatch Queue에 추가하면 GCD는 task에 맞는 스레드를 자동으로 생성해서 실행하고 작업이 종료되면 스레드를 제거한다.</p>
</blockquote>
<h2 id="operation-queues">Operation Queues</h2>
<p>동시성 프로그래밍을 위해 Operation Queue를 사용하는 방법도 있다.
Dispatch Queue는 항상 FIFO로 작업을 실행하지만, Operation Queue는 작업의 실행 순서를 결정할 때 다른 요인을 고려한다.
이러한 요소 중 가장 중요한 것은 주어진 작업이 다른 작업의 완료 여부에 의존(depends on)하는지 여부이다.
task를 정의할 때, 종속성을 구성하고 이를 사용하여 task에 대한 복잡한 실행 순서 그래프를 만들 수 있다.
operation queue는 항상 동시(concurrent)에 operation을 실행하지만, 필요에 따라 종속성을 이용해 순차 실행하도록 할 수 있다.</p>
<pre><code class="language-swift">let firstOperation = BlockOperation {
            for i in 1...10 {
                print(&quot;👻\(i)&quot;)
            }
        }

let secondOperation = BlockOperation {
           for i in 1...10 {
                print(&quot;💜\(i)&quot;)
            }
        }

let queue = OperationQueue()

// options

// 동시 작업할 operation 개수 지정
// 1로 설정할 경우 하나의 operation이 실행 및 완료된 이후 다음 operation이 실행된다.
queue.maxConcurrentOperationCount = 1

// 의존성 추가
// secondOperation이 끝나고 firstOperation을 수행
firstrOperation.addDependency(secondOperation)

// add queue
queue.addOperation(firstOperation)
queue.addOperation(secondOperation)

// 의존성을 추가한 경우 다음과 같이 출력된다.
💜1
💜2
💜3
💜4
💜5
💜6
💜7
💜8
💜9
💜10
👻1
👻2
👻3
👻4
👻5
👻6
👻7
👻8
👻9
👻10</code></pre>
<h2 id="dispatch-queue-vs-operation-queue">Dispatch Queue vs. Operation Queue</h2>
<ul>
<li>Operation Queue에서는 동시에 실행할 수 있는 Operation의 최대 수를 지정할 수 있다.</li>
<li>Operation Queue에서는 KVO(Key Value Observing)을 사용할 수 있는 많은 프로퍼티들이 있다.</li>
<li>Operation Queue에서는 Operation을 일시 중지, 다시 시작 및 취소를 할 수 있다.</li>
<li>Operation Queue는 KVO를 사용해 작업 진행 상황을 감시해야 할 경우 적합하다.</li>
<li>Dispatch Queue는 작업이 복잡하지 않고 간단한 이벤트를 비동기적으로 처리할 때 적합하다.</li>
</ul>
<hr>
<p><a href="https://zeddios.tistory.com/509">Concurrency Programming</a>
<a href="https://zeddios.tistory.com/512">Operation</a>
<a href="https://medium.com/flawless-app-stories/concurrency-visualized-part-1-sync-vs-async-c433ff7b3ebe">sync vs async</a>
See also... <a href="https://dev.to/railwaymen/asynchronous-programming-with-swift-introduction-to-combine-4f3g">Combine</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Codable, NSCoding]]></title>
            <link>https://velog.io/@lena_/Codable-NSCoding</link>
            <guid>https://velog.io/@lena_/Codable-NSCoding</guid>
            <pubDate>Sun, 14 Mar 2021 01:07:54 GMT</pubDate>
            <description><![CDATA[<p>모델 객체의 인스턴스를 데이터 타입으로 인코딩하거나, 데이터를 모델 객체 타입으로 디코딩할 때 Codable과 NSCoding 프로토콜을 사용할 수 있다.</p>
<h2 id="codable">Codable</h2>
<blockquote>
<p><strong>Codable</strong>
typealias Codable = Encodable &amp; Decodable</p>
</blockquote>
<ul>
<li>Encodable -- 객체를 원하는 데이터 형태로 변환(encoding)</li>
<li>Decodable -- 데이터를 모델 객체로 변환(decoding)</li>
</ul>
<p>class, struct, enum에 모두 Codable protocol을 채택할 수 있다.</p>
<h3 id="codingkey-protocol">CodingKey Protocol</h3>
<p>CodingKey 프로토콜을 채택해서 json 데이터의 key와, 속성의 이름과 다르게 지정할 수 있다.</p>
<h2 id="decodable">Decodable</h2>
<p>decoder를 이용해서 init()을 구현할 수 있다. (JSON으로부터 데이터 빼내기)</p>
<pre><code class="language-swift">extension User: Decodable {
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decode(Int.self, forKey: .id)
        name = try container.decode(String.self, forKey: .name)
        birth = try container.decode(String.self, forKey: .birth)
        phoneNum = try container.decode(String.self, forKey: .phoneNum)
    }
}</code></pre>
<h2 id="encodable">Encodable</h2>
<p>decode와 반대로 encode 함수를 작성한다.</p>
<pre><code class="language-swift">// encode 함수 직접 구현
func encode(to encoder: Encoder) throws {
    var container = encoder.container(keyedBy: Codingkeys.self)
    try contrainer.encode(someDate, forKey: .someDate)
}</code></pre>
<blockquote>
<p>Codable은 속성의 타입이 지정되어 있는(Int, String...) 경우에만 사용할 수 있다.
struct 안의 struct를 갖고 있거나, Any 타입을 속성으로 갖고 있는 타입은 Codable을 채택할 수 없다.</p>
</blockquote>
<h3 id="read-data-from-arrays">Read Data from Arrays</h3>
<p>배열 형태로부터 Data를 얻는 예제는 다음과 같다.</p>
<pre><code class="language-swift">let json = &quot;&quot;&quot;
[
    {
        &quot;name&quot;: &quot;Banana&quot;,
        &quot;points&quot;: 200,
        &quot;description&quot;: &quot;A banana grown in Ecuador.&quot;
    },
    {
        &quot;name&quot;: &quot;Orange&quot;,
        &quot;points&quot;: 100
    }
]
&quot;&quot;&quot;.data(using: .utf8)!

struct GroceryProduct: Codable {
    var name: String
    var points: Int
    var description: String?
}

let decoder = JSONDecoder()
let products = try decoder.decode([GroceryProduct].self, from: json)</code></pre>
<h3 id="see-also">See Also</h3>
<ul>
<li><a href="https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types">Article</a></li>
</ul>
<h2 id="nscoding">NSCoding</h2>
<p>다이나믹하게 다형성으로 동작하는 방식이 필요한 경우에는 NSCoding 프로토콜을 사용하는 것이 권장된다.
(Codable과 동시에 채택도 가능하다)</p>
<pre><code class="language-swift">class NBeverage : NSObject, NSCoding {
    private var brand : String = &quot;&quot;
    required init?(coder: NSCoder) {
        self.brand = coder.decodeObject(forKey: &quot;brand&quot;) as! String
    }

    func encode(with coder: NSCoder) {
        coder.encode(brand, forKey: &quot;brand&quot;)
    }
}</code></pre>
<p><code>required init</code>과 <code>encode</code> 메서드 구현이 필요하다.</p>
<h2 id="nskeyedarchiver">NSKeyedArchiver</h2>
<p>NSCoding 프로토콜을 채택한 객체는 NSKeyedArchiver로 인코딩, NSKeyedUnarchiver로 디코딩한다.
아카이브 한 결과는 Data 타입이 되고, json 형식은 불가능하다.
(json 형식으로 데이터를 전송하려면 UserDefault or Plist 파일을 이용해서 저장하는 방법이 있다)</p>
<pre><code class="language-swift">static func unarchive(with text: Data) -&gt; Any? {
        do {
            let object = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(text)
            return object
        }
        catch {
            print(error)
        }
        return nil
    }

    static func archive&lt;T&gt;(with things: T) -&gt; Data {
        do {
            let archived = try NSKeyedArchiver.archivedData(withRootObject: things, requiringSecureCoding: false)
            return archived
        }
        catch {
            print(error)
        }
        return Data()
    }</code></pre>
<p>Codable과 NSCoding의 차이는 다음 블로그 글을 참고😉</p>
<ul>
<li><a href="https://codesquad-yoda.medium.com/codable-vs-nscoding-%EC%B0%A8%EC%9D%B4%EC%A0%90-4b47e240c0b8">https://codesquad-yoda.medium.com/codable-vs-nscoding-차이점-4b47e240c0b8</a></li>
</ul>
<hr>
<p><a href="https://shark-sea.kr/entry/Swift-Codable-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0">https://shark-sea.kr/entry/Swift-Codable-알아보기</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Swift] Metatype, ObjectIdentifier]]></title>
            <link>https://velog.io/@lena_/Swift-Metatype-ObjectIdentifier</link>
            <guid>https://velog.io/@lena_/Swift-Metatype-ObjectIdentifier</guid>
            <pubDate>Fri, 05 Mar 2021 09:07:12 GMT</pubDate>
            <description><![CDATA[<ul>
<li>Dynamic types</li>
</ul>
<hr>
<h2 id="metatype-type">Metatype Type</h2>
<p>_-meta_는 추상화를 의미하는 접두어.
어떤 class, struct, enum에 <code>.Type</code>을 붙여 타입을 타입으로 추상화한 것이 metatype 이다.</p>
<p>_결국 Metatype도 Type이다. _</p>
<ul>
<li>class의 metatype : <code>SomeClass.Type</code></li>
<li>protocol의 metatype : <code>SomeProtocol.Protocol</code></li>
</ul>
<p>metatype을 구하고 싶다면 타입 뒤에 <code>.self</code>를 붙이면 된다. <code>SomeClass.self</code></p>
<h2 id="typeof">type(of:)</h2>
<blockquote>
<p>Generic Function
<a href="https://developer.apple.com/documentation/swift/2885064-type"><strong>type(of:)</strong></a>
Returns the dynamic type of a value.</p>
</blockquote>
<p>스위프트 표준 라이브러리에서 제공하는 <code>type(of:)</code> 함수를 이용해서 <code>Metatype</code>을 구할 수도 있다.</p>
<pre><code class="language-swift">func type&lt;T, Metatype&gt;(of value: T) -&gt; Metatype</code></pre>
<p>간단한 예제.</p>
<pre><code class="language-swift">var number = 5
type(of: number) // Int.Type
// 값에 대한 메타 타입을 알려준다.</code></pre>
<h2 id="instance-from-metatype">instance from metatype</h2>
<p>Use an initializer expression to construct an instance of a type from that type’s metatype value. For class instances, the initializer that’s called must be marked with the required keyword or the entire class marked with the final keyword.</p>
<pre><code class="language-swift">class AnotherSubClass: SomeBaseClass {
    let string: String
    required init(string: String) {
        self.string = string
    }
    override class func printClassName() {
        print(&quot;AnotherSubClass&quot;)
    }
}
let metatype: AnotherSubClass.Type = AnotherSubClass.self
let anotherInstance = metatype.init(string: &quot;some string&quot;)</code></pre>
<h2 id="objectidentifier">ObjectIdentifier</h2>
<blockquote>
<p>Structure
<a href="https://developer.apple.com/documentation/swift/objectidentifier"><strong>ObjectIdentifier</strong></a>
A unique identifier for a class instance or metatype.</p>
</blockquote>
<p>class 인스턴스와 metatype만 있는 <strong>unique identifier</strong>다.
struct, enum, function에는 identifier라는 개념이 없다.</p>
<p>식별값을 나타내는 구조체로, <code>.init(AnyObject)</code>로 생성할 수 있다.
<code>hashValue</code>를 instance property로 갖고 있다.</p>
<p>This unique identifier is only valid for comparisons during the lifetime of the instance.</p>
<p>In Swift, only class instances and metatypes have unique identities. There is no notion of identity for structs, enums, functions, or tuples.</p>
<p>✔️ 인스턴스의 값을 비교 할 것인지, 타입을 비교할 것인지에 따라 적절히 활용할 것</p>
<hr>
<p><a href="https://docs.swift.org/swift-book/ReferenceManual/Types.html#grammar_metatype-type">docs-metatype-type</a>
<a href="https://sujinnaljin.medium.com/swift-self-type-protocol-self%EA%B0%80-%EB%AD%94%EB%94%94%EC%9A%94-7839f6aacd4">medium-metatype</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Equatable, Hashable
]]></title>
            <link>https://velog.io/@lena_/Equatable-Hashable</link>
            <guid>https://velog.io/@lena_/Equatable-Hashable</guid>
            <pubDate>Wed, 03 Mar 2021 02:35:35 GMT</pubDate>
            <description><![CDATA[<h2 id="equatable">Equatable</h2>
<blockquote>
<p>Protocol
<strong>Equatable</strong>
A type that can be compared for value equality.</p>
</blockquote>
<p><code>Equatable</code> 프로토콜을 채택한 타입(class, value, enum)은 <code>==</code>, <code>!=</code> 연산자로 값의 같음, 같지 않음을 비교할 수 있다.
스위프트 표준 라이브러리의 대부분의 기본 데이터 타입은 Equatable 프로토콜을 따르고 있다.</p>
<pre><code class="language-swift">class StreetAddress {
    let number: String
    let street: String
    let unit: String?

    init(_ number: String, _ street: String, unit: String? = nil) {
        self.number = number
        self.street = street
        self.unit = unit
    }
}

extension StreetAddress: Equatable {
    static func == (lhs: StreetAddress, rhs: StreetAddress) -&gt; Bool {
        return
            lhs.number == rhs.number &amp;&amp;
            lhs.street == rhs.street &amp;&amp;
            lhs.unit == rhs.unit
    }
}</code></pre>
<p>Equatable 프로토콜을 준수하기 위해서는
<code>static func == (lhs: StreetAddress, rhs: StreetAddress) -&gt; Bool</code> 함수의 구현부를 작성해야 한다. <code>==</code> 함수 정의에 따라 타입을 비교(같은지, 같지 않은지)할 수 있다.</p>
<h2 id="hashable">Hashable</h2>
<blockquote>
<p>Protocol
<a href="https://developer.apple.com/documentation/swift/hashable"><strong>Hashable</strong></a>
A type that can be hashed into a Hasher to produce an integer hash value.</p>
</blockquote>
<p>integer 해쉬 값을 가질 수 있는 타입을 의미한다.
Hashable Protocol을 준수한 타입은 Dictionary나, Set의 key가 될 수 있다.
다음 <code>hash</code> 메서드를 필수로 구현해야 한다.
hash 메서드를 구현해서 hash value를 얻을 수 있다.</p>
<pre><code class="language-swift">func hash(into: inout Hasher)
// Hashes the essential components of this value by feeding them into the given hasher.</code></pre>
<p>Hashable은 Equatable 상속하고 있으므로, <code>==</code> 함수도 필수로 구현해야 한다.
<code>==</code> 으로 비교할 때와 HashValue를 사용하는 경우를 이해하고, 비교할 속성을 잘 정의할 수 있어야 한다.</p>
<h2 id="see-also">See Also</h2>
<ul>
<li><a href="https://developer.apple.com/documentation/swift/comparable">Comparable</a></li>
<li><a href="https://developer.apple.com/documentation/swift/sequence">Sequence</a></li>
<li><a href="https://swieeft.github.io/2020/02/26/defer.html">defer</a></li>
</ul>
<hr>
<p><a href="https://zeddios.tistory.com/227">https://zeddios.tistory.com/equatable</a>
<a href="https://velog.io/@dev-lena/Hashable-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C">https://velog.io/@dev-lena/Hashable</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Autolayout in iOS]]></title>
            <link>https://velog.io/@lena_/Autolayout-in-iOS</link>
            <guid>https://velog.io/@lena_/Autolayout-in-iOS</guid>
            <pubDate>Sat, 20 Feb 2021 14:54:53 GMT</pubDate>
            <description><![CDATA[<h1 id="layout">Layout</h1>
<p>인터페이스의 layout을 결정하는 것.
layout을 설정하는 방법으로 크게 3가지가 있다.</p>
<ul>
<li>Frame-Based</li>
<li>Autoresizing masks</li>
<li>Auto Layout</li>
</ul>
<h2 id="frame-based">Frame Based</h2>
<ul>
<li>모든 뷰에 대해 개별적인 설정이 필요하다는 단점이 있다.<pre><code>let myView = UIView()
override func viewDidLoad() {
  myView.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
}</code></pre><h2 id="autolayout">Autolayout</h2>
객체 간의 제약 조건으로 layout을 결정하는 것.
스토리보드에서 작업하는 것과 코드로 구현하는 방법이 있는데, 다음은 layout anchor를 이용하여 코드로 autolayout을 잡아준 예제이다.</li>
</ul>
<pre><code class="language-swift">let myView = UIView()

override func viewDidLoad() {
    super.viewDidLoad()

    myView.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(myView) // 반드시 오토레이아웃 잡기 전에 서브뷰 해줘야 한다.

    // constraint
    myView.topAnchor.constraint(equalTo: numberOfPlayersSegmentedControl.bottomAnchor,
                                                         constant: 20).isActive = true
    myView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor,
                                                             constant: 20).isActive = true
    myView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor,
                                                              constant: -20).isActive = true
}</code></pre>
<blockquote>
<p><strong>Instance Property</strong>
<a href="https://developer.apple.com/documentation/uikit/uiview/1622572-translatesautoresizingmaskintoco"><strong>translatesAutoresizingMaskIntoConstraints</strong></a>
A Boolean value that determines whether the view’s autoresizing mask is translated into Auto Layout constraints.</p>
</blockquote>
<p>코드로 제약조건을 주기 전에 <code>translatesAutoresizingMaskIntoConstraints</code>를 false로 설정해야 하는데, 그 이유는 코드로 view를 생성했을 경우 기본 <code>true</code>로 설정되어 있고, 그럴 경우에는 view에 제약조건을 추가로 줄 수 없기 때문이다.
따라서, view에 동적으로 autolayout을 주고 싶다면 이 프로퍼티의 값을 반드시 <code>false</code>로 설정해야 한다.</p>
<h3 id="constraint">Constraint</h3>
<p>오토레이아웃은 기본적으로 선형방정식의 형태로 제약사항을 설정한다.</p>
<blockquote>
<p>FirstItem.Attribute = SecondItem.Attribute + Constant</p>
</blockquote>
<p>Attribute</p>
<ul>
<li>top</li>
<li>bottom</li>
<li>leading</li>
<li>trailing</li>
</ul>
<h3 id="safearealayoutguide">safeAreaLayoutGuide</h3>
<p>위의 코드 예제에서 사용된 safeAreaLayoutGuide는 iOS 11 부터 사용 가능하다.
iOS 11 미만의 버전에서는 top/bottomLayoutGuide를 사용한다. 
safeAreaLayoutGuide는 navigation 또는 status bars 아래에 표시되는 content area의 크기를 알려준다.</p>
<h3 id="uilayoutpriority">UILayoutPriority</h3>
<p>UILayoutPriority로 제약조건의 우선순위를 설정할 수 있다. 1에서 1000까지의 값을 가질 수 있지만 Required는 1000, DefaultHigh는 750, DefaultLow는 250의 값으로 사용하는 것이 권장된다. 세 가지 우선순위로 제약조건이 걸려있는 경우 가장 높은 1000부터 우선순위를 가지며, 1000이 없어진 경우 750, 750이 없어진 경우 250, 순차적으로 제약조건이 연결된다.</p>
<h3 id="isactive">isActive</h3>
<p><code>Constraint</code>에 isActive 프로퍼티 값으로 제약조건을 활성/비활성화 할 수 있다.</p>
<h3 id="intrinsiccontentsize">IntrinsicContentSize</h3>
<p>UILabel, UIButton, UIImageView 등 대부분의 뷰에는 컨텐츠 고유의 사이즈가 있다.
그래서 레이블을 만들 때 직접 너비와 높이를 지정하지 않아도 고유 사이즈(Intrinsic size)가 있어 자동으로 설정된다.
but, 컨테이너 역할을 하는 일부 뷰들은 고유 사이즈가 없기 때문에 가령, UIView를 넣고 오토 레이아웃을 중앙으로 잡으면 오류가 발생한다.</p>
<h3 id="content-hugging--compression-resistance">Content Hugging &amp; Compression Resistance</h3>
<p>Content Hugging은 고유 사이즈의 최대 크기에 제한을 두는 것.
반면, Compression Resistance는 최소 크기에 제한을 두는 것이다.
즉 Content Hugging와 Compression Resistance에 우선순위를 주어서 고유 사이즈보다 작아질수도, 커질 수도 있다.</p>
<p>✔️<a href="https://abhimuralidharan.medium.com/ios-content-hugging-and-content-compression-resistance-priorities-476fb5828ef">예제</a></p>
<h3 id="-scroll-view-autolayout">+) Scroll View Autolayout</h3>
<p>Scroll View에 오토레이아웃을 줄 때에는 contentSize가 반드시 지정되어야 정상적으로 레이아웃을 잡을 수 있다.</p>
<hr>
<p><a href="https://zeddios.tistory.com/474">https://zeddios.tistory.com/translatesAutoresizingMaskIntoConstraints</a>
<a href="https://academy.realm.io/kr/posts/ios-autolayout/">https://academy.realm.io/kr/posts/ios-autolayout/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Git]]></title>
            <link>https://velog.io/@lena_/Git</link>
            <guid>https://velog.io/@lena_/Git</guid>
            <pubDate>Sun, 14 Feb 2021 05:38:16 GMT</pubDate>
            <description><![CDATA[<h2 id="commit">Commit</h2>
<p>커밋은 git 저장소에 작업 디렉토리에 있는 파일의 스냅샷을 기록하는 것.
디렉토리 전체를 복사 붙여넣기 하는 것과 유사하지만 훨씬 유용하다.</p>
<p>git은 커밋을 가능한 가볍게 유지하기 위해 커밋할 때마다 이전 버전과 현재 버전의 변경내역을 저장하고, 이전 커밋을 가리킨다.</p>
<blockquote>
<p>$ git commit -m &quot;commit message&quot;</p>
</blockquote>
<h2 id="branch">Branch</h2>
<p>브랜치는 특정 커밋을 가리키는 포인터다. 따라서 브랜치를 많이 만들어도 메모리나 디스크 공간에 부담이 되지 않는다.</p>
<p>다음 명령어로 브랜치를 생성하면, 현재 작업 중이던 브랜치의 마지막 커밋을 가리키는 브랜치가 생성된다.</p>
<blockquote>
<p>$ git branch feature</p>
</blockquote>
<h3 id="head">HEAD</h3>
<p><code>HEAD</code>는 현재 작업 중인 로컬 브랜치를 가리킨다.
위의 브랜치를 만드는 명령어는 브랜치를 새로 만들기만 할 뿐, 현재 작업 중인 브랜치(HEAD의 포인터)를 변경하지는 않는다.</p>
<p>작업할 브랜치를 변경하는 것은 <code>checkout</code> 명령으로 가능하다.</p>
<blockquote>
<p>$ git checkout feature</p>
</blockquote>
<h2 id="merge">Merge</h2>
<p>브랜치 간 병합.</p>
<ul>
<li>Fast-forward</li>
<li>3-way merge</li>
</ul>
<p>Fast-forward는 현재 브랜치가 가리키는 커밋이 merge 할 브랜치가 가리키는 커밋의 조상일 경우의 merge 방식이고, 3-way merge는 그렇지 않을 경우이다.</p>
<p>Fast-forward merge는 단순히 머지할 브랜치가 가리키는 커밋으로 포인터를 이동시키는 것 뿐인 반면,
3-way merge는 각 브랜치의 마지막 커밋과 공통 조상의 마지막 커밋을 비교해서 새로운 커밋을 만들고, 브랜치가 그 커밋을 가리키도록 이동시킨다.</p>
<h3 id="merge-conflict">Merge Conflict</h3>
<p>merge 하려는 두 브랜치에서 동일한 위치의 코드를 수정했을 때, merge conflict가 발생할 수 있다.</p>
<pre><code>$ git merge iss53
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.</code></pre><p>이 경우, <code>git status</code>로 어떤 파일을 merge 할 수 없었는지 확인할 수 있다.</p>
<pre><code>$ git status
On branch master
You have unmerged paths.
  (fix conflicts and run &quot;git commit&quot;)

Unmerged paths:
  (use &quot;git add &lt;file&gt;...&quot; to mark resolution)

    both modified:      index.html

no changes added to commit (use &quot;git add&quot; and/or &quot;git commit -a&quot;)</code></pre><p>충돌을 해결하기 위해 코드를 수동으로 수정해주어야 하며(올바르지 않은 쪽 코드를 제거하는 등), 수정된 내용을 커밋할 때 merge에 대한 내용으로 자세히 작성해야 한다.</p>
<h2 id="rebase">Rebase</h2>
<p>브랜치 간 작업을 합치는 또 다른 방법.
리베이스는 기본적으로 커밋들을 모아 복사한 뒤, 다른 곳에 떨궈 놓는다.
리베이스를 하면 커밋의 흐름을 보기 좋게 한 줄로 만들 수 있다는 장점이 있다.
(rebase는 말그대로 base를 재설정한다는 의미로, 커밋 흐름을 조작하는 것이다)</p>
<blockquote>
<p><a href="https://velog.io/@godori/Git-Rebase">git rebase 동작 원리 이해하기</a></p>
</blockquote>
<h2 id="fetch">Fetch</h2>
<p>로컬에는 없지만 remote 저장소에는 있는 데이터(커밋)을 모두 가져오기.</p>
<blockquote>
<p>$ git fetch origin</p>
</blockquote>
<p>git fetch는 데이터를 로컬로 가져오기만 할 뿐, merge는 하지 않는다. merge 까지 한 번에 하려면 <code>git pull</code>을 사용할 수 있다.</p>
<h2 id="pull">Pull</h2>
<p>git fetch + git merge origin main = git pull</p>
<hr>
<p><a href="https://learngitbranching.js.org/?locale=ko">https://learngitbranching.js.org/?locale=ko</a>
<a href="https://git-scm.com/book/ko/v2">https://git-scm.com/book/ko/v2</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[iOS] Animation]]></title>
            <link>https://velog.io/@lena_/iOS-Animation</link>
            <guid>https://velog.io/@lena_/iOS-Animation</guid>
            <pubDate>Sun, 07 Feb 2021 10:35:21 GMT</pubDate>
            <description><![CDATA[<p>iOS에서 애니메이션을 사용할 수 있는 방법은 크게 2가지</p>
<ul>
<li>UIKit, UIView API ✔️</li>
<li>Core Animation API</li>
</ul>
<h2 id="uiviewanimate">UIView.animate</h2>
<ul>
<li>Closure 기반</li>
<li>애니메이션이 동작하는 동안 user interface disable</li>
</ul>
<pre><code class="language-swift">UIView.animate(withDuration: 0.5,
               delay: 0.4,
               options: [.curveEaseOut],
               animations: { [weak self] in
                 self?.view.layoutIfNeeded()
      }, completion: nil)</code></pre>
<ul>
<li><p>options: <code>UIView.AnimationOptions</code></p>
<ul>
<li><code>.allowUserInteraction</code> : 애니메이션 중에 user interface를 활성화</li>
<li><code>.repeat</code> : 애니메이션 무한 반복</li>
<li><code>.curveEaseInOut</code> : 빨라졌다가 다시 느려지는 애니메이션 옵션</li>
<li><code>.curveEaseIn</code> : 점점 빨라짐</li>
<li><code>.curveEaseOut</code> : 점점 느려짐 </li>
<li><code>.transitionFlipFromLeft</code> : 왼쪽에서부터 뒤집히는 애니메이션</li>
<li><code>.transition~</code> 시작하는 옵션들은 UIView.transition() 으로 호출해야 한다.</li>
</ul>
</li>
<li><p>애니메이션이 가능한 뷰 속성(animatable property)</p>
<ul>
<li><code>frame</code>, <code>bounds</code>, <code>center</code>, <code>transform</code>, <code>background</code>, <code>alpha</code></li>
<li><code>layoutSubviews()</code> 없이 애니메이션 효과가 나타나는, 뷰의 built-in animation 속성들을 의미한다.</li>
</ul>
</li>
</ul>
<h2 id="view-life-cycle">View Life Cycle</h2>
<p>view의 라이프 사이클을 돌면서 적절하게 animate 함수를 실행한다.</p>
<ul>
<li><code>viewDidLoad</code> : view가 메모리에 올라온 상태 —<code>prepareAnimation()</code></li>
<li><code>viewWillAppear</code> : <code>prepareAnimation()</code></li>
<li><code>viewDidAppear</code> : view가 스크린에 나타난 뒤에 호출, 이 때 애니메이션을 실행 — <code>showAnimation()</code></li>
</ul>
<pre><code class="language-swift">func prepareAnimation() {
    nameLabelCenterX.constant -= self.view.bound.width
}

func showAnimation() {
    nameLabelCenterX.constant = 0 // constraint 변경해도 레이아웃을 업데이트하지 않으면 애니메이션 효과가 보이지 않음

    UIView.animate(withDuration: 0.5,
                   delay: 0.3,
                   usingSpringWithDamping: 0.6, // 0 ~ 1.0, 0에 가까울수록 세게
                   initialSpringVelocity: 1.0, // 0 ~ 1.0,  0에 가까울수록 빠르게
                   options: .allowUserInteraction,
                   animations: { self.view.layoutIfNeeded() },
                   completion: nil)

    UIView.transition(with: imageView,
                      duration: 0.3,
                      options: .transitionFlipFromLeft,
                      animations: nil,
                      completion: nil)
}</code></pre>
<h2 id="layoutifneeded">layoutIfNeeded</h2>
<blockquote>
<p>UIView의 <em>instance method</em>
Lays out the subviews immediately, if layout updates are pending.</p>
</blockquote>
<p><code>layoutIfNeeded</code>는 subview의 레이아웃을 즉시 갱신한다.  = <code>layoutSubviews()</code> 호출</p>
<h3 id="layoutsubviews">layoutSubviews</h3>
<ul>
<li>서브뷰의 레이아웃을 즉시 업데이트 시켜주는 메소드</li>
<li>호출되면 해당 뷰의 모든 서브뷰들이 <code>layoutSubViews()</code>를 연달아 호출한다.</li>
<li>따라서 비용이 많이 드는 메서드. 직접 호출하는 것이 지양된다.</li>
<li>이 메소드는 시스템에 의해서 뷰의 값이 다시 계산되어야 하는 시점(update cycle)에 자동으로 호출된다. ✔️</li>
<li>layoutSubviews를 유도할 수 있는 여러 방법이 존재한다(일종의 layoutSubviews() 호출을 예약하는 행위). ✔️✔️</li>
<li><code>layoutSubviews()</code>가 호출되고나면 <code>viewDidLayoutSubviews()</code>가 호출된다.</li>
</ul>
<blockquote>
<p>The <a href="https://tech.gc.com/demystifying-ios-layout/">update cycle</a> is the point at which control returns to the main run loop after the app finishes running all your event handling code.</p>
</blockquote>
<p>아래와 같이 뷰의 레이아웃이 변경될 경우, update cycle에 레이아웃이 업데이트될 수 있도록 시스템이 자동으로 뷰의 업데이트를 예약한다.</p>
<ul>
<li>뷰의 크기를 변경하거나</li>
<li>뷰에 서브뷰를 추가하거나</li>
<li>사용자가 스크롤뷰를 스크롤 하거나</li>
<li>디바이스를 회전시키거나</li>
<li>뷰의 오토레이아웃 constraint 값을 변경시켰을 때</li>
</ul>
<p>시스템이 자동으로 뷰의 업데이트를 예약하는 것 말고, 수동으로 예약하는 방법도 있다.</p>
<ul>
<li><code>setNeedsLayout()</code></li>
<li><code>layoutIfNeeded()</code></li>
</ul>
<h3 id="setneedslayout">setNeedsLayout</h3>
<p>비용이 가장 적게 드는 방법.
이 메서드를 호출한 뷰는 재계산되어야 하는 뷰라고 수동으로 체크되며, update cycle에 <code>layoutSubviews()</code> 호출한다.</p>
<h3 id="layoutifneeded-1">layoutIfNeeded</h3>
<p>setNeedsLayout과 마찬가지로 수동으로 layoutSubviews를 예약하는 메서드지만,
차이점은 update cycle이 돌 때까지 기다리지 않고 즉시 <code>layoutSubviews()</code>를 호출한다.</p>
<hr>
<p><a href="https://jinnify.tistory.com/66">https://jinnify.tistory.com/66</a>
<a href="https://developer.apple.com/documentation/uikit/uiview/animationoptions">https://developer.apple.com/documentation/uikit/uiview/animationoptions</a>
<a href="https://baked-corn.tistory.com/105">https://baked-corn.tistory.com/105</a></p>
]]></description>
        </item>
    </channel>
</rss>