<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Decoding Note</title>
    <link>https://jintaewoo.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Thu, 7 May 2026 04:34:25 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>진태우</managingEditor>
    <item>
      <title>[Swift] Closures</title>
      <link>https://jintaewoo.tistory.com/60</link>
      <description>&lt;p&gt;애플 문서를 보고 정리해본 내용입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;정의 및 특징&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;클로저는 코드 안에서 전달되어 사용할 수 있는 독립적인 기능을 가진 일급 객체입니다.&lt;/li&gt;
&lt;li&gt;일급 객체는 변수/상수로 선언하거나 전달이 가능하며, 반환값으로도 사용될 수 있는 객체입니다.&lt;/li&gt;
&lt;li&gt;클로저는 참조 타입입니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;표현 방식&lt;/h1&gt;
&lt;p&gt;아래는 일반적인 표현 방식의 형태입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1613912619506&quot; class=&quot;swift&quot; style=&quot;display: block; overflow: auto; padding: 15px; color: #383a42; background: #f6f7f8; font-size: 14px; border-radius: 3px; font-family: Menlo, Consolas, Monaco, monospace; border: 1px solid #dddddd; margin: 20px auto 0px; cursor: default; z-index: 1; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{ (parameters) -&amp;gt; returnType in
    // do something..
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래는 숫자의 기호(양수/음수)를 변경 하는 클로저를 선언하여 사용하는 예제입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1613912652581&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let operation = { (operand: Double) -&amp;gt; Double in 
    return -operand 
}

operation(4.0)    // -4.0&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위와 같이 선언한 클로저를 축약해서 표현할 수도 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1613912691049&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 리턴 타입 생략
// 타입 추론을 통해 파라미터의 타입과 리턴되는 값의 타입을 제거할 수 있습니다.
operation = {	(operand) in return -operand }

// return 키워드 생략
// 어떤 값을 리턴할지 알고 있기 때문에 return 명령어를 제거할 수 있습니다.
operation = {	(operand) in -operand }

// 파라미터 이름 생략
// 파라미터 이름을 따로 명시하지 않을 경우 순서대로 $0, $1, $2 등으로 축약해서 사용할 수 있습니다.
// 때문에 operand는 $0으로 변환해주면서 in이 필요 없어지게 됩니다.
operation = { -$0 }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;순차적으로 클로저를 축약해 보았습니다.&lt;/p&gt;
&lt;p&gt;하지만 문법을 간결하게 한다고 해서 무조건 좋은 것은 아니기 때문에 상황에 따라서 읽기 편하게 사용하면 될 것 같습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;Trailing Closure(후위 클로저)&lt;/h1&gt;
&lt;p&gt;함수의 마지막 파라미터로 클로저를 전달할 경우 표현할 수 있는 방식입니다.&lt;/p&gt;
&lt;p&gt;클로저를 사용하는 map 함수를 예제로 알아보겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1613912765923&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let numbers = [1, 2, 3, 4, 5]

// map 함수의 파라미터로 클로저가 전달됩니다.
let negativeNumbers = numbers.map({ -$0 })    // [-1, -2, -3, -4, -5]

// 함수의 마지막 파라미터로 클로저가 전달되면 괄호 밖으로 빼서 표현할 수 있습니다.
let floatNumbers = numbers.map() { $0 * 1.0 }    // [1.0, 2.0, 3.0, 4.0, 5.0]

// 클로저가 유일한 파라미터라면 괄호를 제거해도 됩니다.
let stringNumbers = numbers.map { String($0) }    // [&quot;1&quot;, &quot;2&quot;, &quot;3&quot;, &quot;4&quot;, &quot;5&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;Capturing&lt;/h1&gt;
&lt;p&gt;클로저는 그것이 정의된 지역 외부에 있는 상수 및 변수를 캡처할 수 있습니다. 캡처한 상수 및 변수는 나중에 없어지더라도 클로저 내에서 값을 참조하고 있기 때문에 접근하여 수정할 수 있습니다.&lt;/p&gt;
&lt;p&gt;Swift에서 값을 캡처 하는 가장 단순한 형태는 중첩 함수(nested function) 입니다.&lt;/p&gt;
&lt;p&gt;중첩 함수는 외부 함수의 인자와 외부 함수에서 정의한 상수 및 변수를 캡처할 수 있습니다.&lt;/p&gt;
&lt;p&gt;아래 예제를 보겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1613912783790&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func makeIncrementer(forIncrement amount: Int) -&amp;gt; () -&amp;gt; Int {
    var runningTotal = 0
    
    func incrementer() -&amp;gt; Int {
        runningTotal += amount
        return runningTotal
    }

    return incrementer
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;makeIncrementer&lt;/span&gt; 함수는 내부에 &lt;span data-token-index=&quot;2&quot; data-reactroot=&quot;&quot;&gt;incrementer&lt;/span&gt; 함수를 반환하는 형태의 중첩 함수입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;incrementer&lt;/span&gt; 함수만 따로 보면,&lt;/p&gt;
&lt;pre id=&quot;code_1613912842753&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;func incrementer() -&amp;gt; Int {
    runningTotal += amount
    return runningTotal
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;어디에도 runningTotal, amount가 선언되지 않았습니다.&lt;/p&gt;
&lt;p&gt;runningTotal은 함수 외부에서 선언된 변수이고, amount는 외부 함수에서 인자로 받은 값입니다.&lt;/p&gt;
&lt;p&gt;incrementer 함수에서 해당 값을 캡처해서 사용하고 있다는 것입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래는 함수의 호출 예제입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1613912872838&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let incrementByTen = makeIncrementer(forIncrement: 10)

incrementByTen()    // returns a value of 10
incrementByTen()    // returns a value of 20
incrementByTen()    // returns a value of 30

let incrementBySeven = makeIncrementer(forIncrement: 7)
incrementBySeven()    // returns a value of 7&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;클래스 인스턴스의 속성(property)에 클로저를 할당하고 클로저가 인스턴스 및 인스턴스의 멤버를 참조하면, 캡처링이 발생하면서 클로저와 인스턴스 사이에 강한 순환 참조가 생성됩니다. &lt;br /&gt;이런 경우, 인스턴스가 없어져도 강한 참조가 해제되지 않기 때문에 메모리 사이클이 발생합니다. &lt;br /&gt;이 문제를 해결하기 위해 캡처 리스트(capture list)를 사용해야 합니다.&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;escaping closure&lt;/h1&gt;
&lt;p&gt;클로저를 함수의 파라미터로 전달될 때, 전달된 클로저가 함수가 끝나고 함수 외부에서 실행될 수 있습니다.&lt;/p&gt;
&lt;p&gt;예를 들면 비동기 작업을 하는 함수에서 완료 핸들러로 클로저 파라미터를 사용합니다. 이때 클로저는 작업이 완료되고 함수 외부에서 호출하게 되는데, 이 경우에는 클로저 앞에 @escaping 키워드를 명시해야 컴파일 오류가 발생하지 않습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;일반적으로 클로저는 내부에서 사용되는 변수를 암시적으로 캡처하게 됩니다. 하지만 escaping 클로저의 경우 사용되는 변수를 명시적으로 표현해야 합니다.&lt;/p&gt;
&lt;p&gt;만약 self를 캡처하려면 self를 사용할 때 직접 명시해주거나, 캡처 리스트(capture list)에 포함하여 캡처한다는 의도를 명확하게 보여줘야 합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래는 escaping closure에서 self를 명시적으로 표현하는 예제입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1613913136462&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var completionHandlers = [() -&amp;gt; Void]()
func someFunctionWithEscapingClosure(completionHandler: @escaping () -&amp;gt; Void) {
    completionHandlers.append(completionHandler)
}

func someFunctionWithNonescapingClosure(closure: () -&amp;gt; Void) {
    closure()
}

class SomeClass {
    var x = 10
    func doSomething() {
        // self를 명시적으로 표현해야 에러가 발생하지 않음
        someFunctionWithEscapingClosure { self.x = 100 }

        // self를 암시적으로 사용할 수 있어 self를 명시하지 않아도 됨
        someFunctionWithNonescapingClosure { x = 200 }
    }
}

let instance = SomeClass()
instance.doSomething()
print(instance.x)
// Prints &quot;200&quot;

completionHandlers.first?()
print(instance.x)
// Prints &quot;100&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래는 캡처 리스트에 self를 추가하여 사용하는 예제입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1613913177750&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class SomeOtherClass {
    var x = 10
    func doSomething() {
				// capture list에 self 추가하여 캡처
        someFunctionWithEscapingClosure { [self] in x = 100 }
        someFunctionWithNonescapingClosure { x = 200 }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;AutoClosure&lt;/h1&gt;
&lt;p&gt;AutoClosure는 파라미터 값이 없으며 특정 표현을 감싸서 다른 함수의 파라미터로 사용할 수 있는 클로저입니다.&lt;/p&gt;
&lt;p&gt;AutoClosure를 사용하면 클로저가 호출될 때까지 코드가 실행되지 실행되지 않습니다. 이런 코드 지연의 특성을 이용하면 실행 시점을 제어할 수 있기 때문에 부작용을 줄이고, 복잡한 연산이 필요할 때 유용합니다.&lt;/p&gt;
&lt;p&gt;아래는 코드가 지연되어 사용되는 예제입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1613913202926&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var customersInLine = [&quot;Chris&quot;, &quot;Alex&quot;, &quot;Ewa&quot;, &quot;Barry&quot;, &quot;Daniella&quot;]
print(customersInLine.count)
// Prints &quot;5&quot;

let customerProvider = { customersInLine.remove(at: 0) }
print(customersInLine.count)    // 아직 클로저가 호출되지 않았기 때문에 값이 변하지 않았음
// Prints &quot;5&quot;

print(&quot;Now serving \(customerProvider())!&quot;)
// Prints &quot;Now serving Chris!&quot;

print(customersInLine.count)    // 클로저 호출 후 값이 변함
// Prints &quot;4&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;AutoClosure를 함수의 파라미터로 전달하는 예제입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1613913225830&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// customersInLine is [&quot;Alex&quot;, &quot;Ewa&quot;, &quot;Barry&quot;, &quot;Daniella&quot;]
func serve(customer customerProvider: () -&amp;gt; String) {
    print(&quot;Now serving \(customerProvider())!&quot;)
}

serve(customer: { customersInLine.remove(at: 0) } )
// Prints &quot;Now serving Alex!&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;serve 함수는 String을 반환하는 클로저를 파라미터로 받습니다.&lt;/p&gt;
&lt;p&gt;보통 클로저를 전달할 때, {}(중괄호)를 붙여야 하지만 @autoclosure 키워드를 명시해주면 {}(중괄호) 없이 클로저를 전달할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래는 @autoclosure 키워드를 사용한 예제입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1613913292060&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// customersInLine is [&quot;Ewa&quot;, &quot;Barry&quot;, &quot;Daniella&quot;]
func serve(customer customerProvider: @autoclosure () -&amp;gt; String) {
    print(&quot;Now serving \(customerProvider())!&quot;)
}

serve(customer: customersInLine.remove(at: 0))
// Prints &quot;Now serving Ewa!&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;Reference&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.swift.org/swift-book/LanguageGuide/Closures.html#&quot;&gt;https://docs.swift.org/swift-book/LanguageGuide/Closures.html#&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://jusung.gitbook.io/the-swift-language-guide/language-guide/07-closures&quot;&gt;https://jusung.gitbook.io/the-swift-language-guide/language-guide/07-closures&lt;/a&gt;&lt;/p&gt;</description>
      <category>Swift</category>
      <author>진태우</author>
      <guid isPermaLink="true">https://jintaewoo.tistory.com/60</guid>
      <comments>https://jintaewoo.tistory.com/60#entry60comment</comments>
      <pubDate>Sun, 21 Feb 2021 22:17:53 +0900</pubDate>
    </item>
    <item>
      <title>[Swift] Singleton Pattern</title>
      <link>https://jintaewoo.tistory.com/59</link>
      <description>&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #666666;&quot;&gt;Singleton Pattern에 대해 공부한 내용을 정리해 보려고 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;- 정의&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;싱글톤 패턴은 클래스 객체가 하나만 인스턴스화 되도록 보장하는 디자인 패턴입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;- 특징&lt;/b&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;어플리케이션 내에 하나만 존재해야 하는 객체일 경우 사용&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;static 변수로 선언하기 때문에 lazy하게 생성됨. 즉 사용하기 전엔 메모리에 올라가지 않음.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;생성되면 어플리케이션이 종료되기 전까지 메모리에 유지&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;상수값이기 때문에 값의 변화가 없어 thread-safe가 보장됨.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;하나의 인스턴스를 사용하기 때문에 인스턴스 내부의 데이터를 공유할 수 있음.&lt;/p&gt;
&lt;p&gt;어디서든 접근 및 수정이 가능하다는 의미.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;- 코드&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;아래는 싱글톤을 구현하는 예제입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1613212231341&quot; class=&quot;swift&quot; style=&quot;display: block; overflow: auto; padding: 15px; color: #383a42; background: #f6f7f8; font-size: 14px; border-radius: 3px; font-family: Menlo, Consolas, Monaco, monospace; border: 1px solid #dddddd; margin: 20px auto 0px; cursor: default; z-index: 1; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Singleton {
    // static -&amp;gt; lazy하게 생성
    // let -&amp;gt; thread-safe 보장
    static let shared = SingleTon()
		
    // private 권한을 설정하여 외부에서 인스턴스를 생성할 수 없게 함
    private init() {}    
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래와 같이 네트워크 관리 객체를 만들어 사용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1613212245388&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class NetworkManager {
    static let shared = NetworkManager(baseURL: API.baseURL)

    let baseURL: URL

    private init(baseURL: URL) {
        self.baseURL = baseURL
    }
}

// 싱글톤 인스턴스 호출
print(NetworkManager.shared)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;만약 싱글톤 객체 생성시 더 복잡한 설정이 필요하다면 아래와 같이 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1613212281028&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class NetworkManager {
    private static var sharedNetworkManager: NetworkManager = {
        let networkManager = NetworkManager(baseURL: API.baseURL)

        // 필요한 초기화 코드

        return networkManager
    }()

    let baseURL: URL

    private init(baseURL: URL) {
        self.baseURL = baseURL
    }

    class func shared() -&amp;gt; NetworkManager {
        return sharedNetworkManager
    }

}

// 싱글톤 인스턴스 호출
print(NetworkManager.shared())&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Cocoa 프레임워크에서 많은 싱글톤 패턴을 사용하고 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1613212320872&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let application = UIApplication.shared

let sharedURLSession = URLSession.shared

let defaultFileManager = FileManager.default

let standardUserDefaults = UserDefaults.standard

let defaultNotification = NotificationCenter.default

let defaultPaymentQueue = SKPaymentQueue.default()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;- 주의&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;싱글톤은 많이 사용되는 디자인 패턴입니다. 하지만 안티 패턴으로 간주되기도 합니다.&lt;/p&gt;
&lt;p&gt;싱글톤 인스턴스를 어디서든 쉽게 접근하고 수정할 수 있기 때문에 많은 곳에서 사용할 경우, 부작용이 발생할 수 있습니다. 또한 적은 양의 코드 수정으로도 다른 요소에서 버그가 발생될 수 있습니다.&lt;/p&gt;
&lt;p&gt;때문에 상황에 따라 잘 고려해서 사용해야 편리한 도구가 될 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;- Reference&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://cocoacasts.com/what-is-a-singleton-and-how-to-create-one-in-swift&quot;&gt;https://cocoacasts.com/what-is-a-singleton-and-how-to-create-one-in-swift&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://cocoacasts.com/are-singletons-bad/&quot;&gt;https://cocoacasts.com/are-singletons-bad/&lt;/a&gt;&lt;/p&gt;</description>
      <category>Swift/Clean Code</category>
      <category>Design Pattern</category>
      <category>singleton</category>
      <category>Swift</category>
      <author>진태우</author>
      <guid isPermaLink="true">https://jintaewoo.tistory.com/59</guid>
      <comments>https://jintaewoo.tistory.com/59#entry59comment</comments>
      <pubDate>Sat, 13 Feb 2021 19:33:53 +0900</pubDate>
    </item>
    <item>
      <title>[Swift] Coordinator Pattern</title>
      <link>https://jintaewoo.tistory.com/58</link>
      <description>&lt;p&gt;읽기 쉬운 코드 만들기를 목적으로 공부한 내용 중 Coordinator Pattern에 대해 정리해 보려고 합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;코디네이터 패턴은 UIViewController 간의 흐름을 제어하기 위한 패턴입니다.&lt;/p&gt;
&lt;p&gt;앱의 흐름을 담당하는 별도의 객체를 만들어 사용하기 때문에 UIViewController와는 독립적이며, 재사용에 용이합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;- 수행 기능&lt;/h3&gt;
&lt;p&gt;1. 화면 전환에 필요한 인스턴스 생성( UIViewController, ViewModel 등.. )&lt;/p&gt;
&lt;p&gt;2. 생성한 인스턴스의 종속성 주입(DI)&lt;/p&gt;
&lt;p&gt;3. 생성된 UIViewController의 화면 전환 (push or present)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래와 같이 컨트롤러 간의 화면 전환하는 코드를 UIViewController에서 많이 작성했을 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1612088840775&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let vc = HomeViewContrller()
navigationController?.pushViewController(vc, animated: true)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;여러 곳에서 해당 컨트롤러로 화면 전환을 해야할 경우, 위와 같은 코드를 여러 곳에서 사용해야 할 것입니다.&lt;/p&gt;
&lt;p&gt;만약 컨트롤러의 이름이나 추가적으로 값을 전달해야 할 경우에는 해당 코드를 모두 찾아서 변경해야겠죠.&lt;/p&gt;
&lt;p&gt;코디네이터를 사용하면 HomeViewController로 화면 전환하는 코디네이터를 만들어 재사용함으로써, 사용하기도 쉽고 유지보수에도 좋을 것입니다.&lt;/p&gt;
&lt;p&gt;물론 UIViewController에서 화면 전환에 대한 로직이 제거되어 좀 더 보기 쉽게 되겠죠!  &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;바로 코드로 GOGO  &lt;/p&gt;
&lt;p&gt;아래 순서대로 코드 작성을 해보겠습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;1. Coordinator 프로토콜 구현&lt;/p&gt;
&lt;p&gt;2. AppCoordinator 구현 ( AppDelegate에서 사용될 Coordinator)&lt;/p&gt;
&lt;p&gt;3. HomeCoordinator 구현 ( 초기 화면에 대한 Coordinator)&lt;/p&gt;
&lt;p&gt;AppDelegate에서 AppCoordinator 생성&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Coordinator 프로토콜 구현&lt;/h3&gt;
&lt;pre id=&quot;code_1612088995235&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;protocol Coordinator: AnyObject {
    
    var presenter: UINavigationController { get set }    // 1
    
    var childCoordinators: [Coordinator] { get set }    // 2
    
    func start()    // 3
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;간단히 설명하면,&amp;nbsp;&lt;/p&gt;
&lt;p&gt;1.&amp;nbsp;presenter는 화면 전환에 필요한 UINavigationController 입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1612089089798&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;presenter.pushViewController(vc, animated: true)          // push
presenter.present(vc, animated: true, completion: nil)    // present&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2.&amp;nbsp;childCoordinators는 화면 전환시 생성될 하위 Coordinator를 저장할 때 사용합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp; &amp;nbsp; coordinator 생성후 저장하지 않으면, 메모리에서 제거되기 때문에 꼭 저장해야합니다.&lt;/p&gt;
&lt;p&gt;3. start 함수는 컨트롤러 생성, 화면 전환 및 종속성 주입의 역할을 합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;AppCoordinator 구현&lt;/h3&gt;
&lt;pre id=&quot;code_1612089176741&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class AppCoordinator: NSObject, Coordinator {
    
    var presenter: UINavigationController

    var childCoordinators: [Coordinator]

    let window: UIWindow
    
    
    init(window: UIWindow) {
        self.window = window
        self.presenter = UINavigationController()
        self.childCoordinators = []
    }
    
    func start() {
        window.rootViewController = presenter    // 1
        
        let coordinator = HomeCoordinator(presenter: presenter)    // 2        
        childCoordinators.append(coordinator)    // 3
        coordinator.start()    // 4
        
        window.makeKeyAndVisible()
    }
    
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;AppCoordinator는 앱 시작시 초기 컨트롤러를 연결하기 위한 coordinator 입니다.&lt;/p&gt;
&lt;p&gt;AppDelegate에서 전달받은 window의 rootViewController와 HomeViewController를 연결하기 위한 내용입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;1. 초기화 메소드에서 생성한 UINavigationController를 rootViewController로 설정&lt;/p&gt;
&lt;p&gt;2. rootViewController로 설정된 presenter를 HomeCoordinator에 생성자 파라미터로 전달하여, HomeViewController와 연결되게 합니다.&lt;/p&gt;
&lt;p&gt;3. coordinator가 메모리에서 사라지지 않기 위해서는 인스턴스를 저장해야 합니다.&lt;/p&gt;
&lt;p&gt;4. start()를 호출하여 HomeViewController 인스턴스를 초기화 합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;HomeCoordinator 구현&lt;/h3&gt;
&lt;pre id=&quot;code_1612089294126&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class HomeCoordinator: NSObject, Coordinator {
    
    var presenter: UINavigationController
    
    var childCoordinators: [Coordinator]

    var viewModel: HomeViewModel?
    

    init(presenter: UINavigationController, viewModel: HomeViewModel? = HomeViewModel()) {
        self.presenter = presenter
        self.childCoordinators = []
        self.viewModel = viewModel
    }
    
    func start() { 
        let homeView = HomeViewController.instantiate()
        homeView.viewModel = viewModel
        presenter.pushViewController(homeView, animated: false)
    }
    
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;HomeViewController로 화면 전환할 수 있는 Coordinator 입니다.&lt;/p&gt;
&lt;p&gt;필요에 따라 viewModel이나 필요한 값을 주입할 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;AppDelegate에서 AppCoordinator 생성&lt;/h3&gt;
&lt;p&gt;앱의 초기 화면인 HomeViewController를 연결할 준비는 끝났습니다.&lt;/p&gt;
&lt;p&gt;아래 코드와 같이 AppCoordinator 생성 후 start 메소드만 실행하면 간단하게 연결됩니다~!!&lt;/p&gt;
&lt;pre id=&quot;code_1612089350002&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var window: UIWindow?
    
var appCoordinator: AppCoordinator?


func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -&amp;gt; Bool {
    window = UIWindow(frame: UIScreen.main.bounds)
    appCoordinator = AppCoordinator(window: window!)
    appCoordinator?.start()
    
    return true
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;- 마무리&lt;/h3&gt;
&lt;p&gt;화면 전환에 대한 코드를 따로 관리하도록 변경하면서, 재사용과 유지보수에 대해서 편하다는 느낌을 받았습니다.&lt;/p&gt;
&lt;p&gt;아직 미숙하지만, 좀 더 사용하면서 나의 스타일에 맞게 수정하면 더 사용하기 좋을 것 같습니다.&lt;/p&gt;
&lt;p&gt;UITabBarController를 연결하거나, childCoordinator에 추가된 인스턴스들을 해제하는 내용등 포스팅에 정리하지 못한 내용들은 &lt;a href=&quot;https://github.com/taewoojin/CoordinatorPattern_Example&quot;&gt;GitHub Repository&lt;/a&gt;에 업로드되어 있습니다.      &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;- Reference&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://khanlou.com/2015/10/coordinators-redux/&quot;&gt;https://khanlou.com/2015/10/coordinators-redux/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.hackingwithswift.com/articles/71/how-to-use-the-coordinator-pattern-in-ios-apps&quot;&gt;https://www.hackingwithswift.com/articles/71/how-to-use-the-coordinator-pattern-in-ios-apps&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://somevitalyz123.medium.com/coordinator-pattern-with-tab-bar-controller-33e08d39d7d&quot;&gt;https://somevitalyz123.medium.com/coordinator-pattern-with-tab-bar-controller-33e08d39d7d&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://labs.brandi.co.kr/2020/06/16/kimjh.html&quot;&gt;http://labs.brandi.co.kr/2020/06/16/kimjh.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://dev-wd.github.io/swift/coordinator/&quot;&gt;https://dev-wd.github.io/swift/coordinator/&lt;/a&gt;&lt;/p&gt;</description>
      <category>Swift</category>
      <category>Coordinator</category>
      <category>Design Pattern</category>
      <category>Swift</category>
      <author>진태우</author>
      <guid isPermaLink="true">https://jintaewoo.tistory.com/58</guid>
      <comments>https://jintaewoo.tistory.com/58#entry58comment</comments>
      <pubDate>Sun, 31 Jan 2021 19:41:12 +0900</pubDate>
    </item>
    <item>
      <title>[Swift] 보기 좋게? UIViewController 만들어 보기</title>
      <link>https://jintaewoo.tistory.com/56</link>
      <description>&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #666666;&quot;&gt;읽기 쉬운 코드 만들기를 목적으로 공부한 내용 중 일부를 정리해 보려고 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;코딩을 하다보면 복잡하고 유지보수 하기 힘들도록 레거시를 만들어 내는 경우가 많습니다... 뭔가 중구난방...&lt;/p&gt;
&lt;p&gt;그래서 기본 틀은 항상 유지할 수 있도록 공통 컨테이너를 만들어 사용하면 좋을 것 같았습니다.&lt;/p&gt;
&lt;p&gt;보기 좋은 떡이 먹기도 좋다더라...&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;BaseViewController&lt;/b&gt;라는 기본 컨트롤러를 서브클래싱하여 좀 더 깔끔한 코드를 만들어 본 내용을 간단히 정리해 보려 합니다.&lt;/p&gt;
&lt;p&gt;지극히 주관적인 내용이라 별 내용이 아닐 수 있지만... 피드백은 언제나 환영합니당~  &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1610264722641&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class BaseViewController: UIViewController {
    
    // MARK:- Rx
    
    var disposeBag = DisposeBag()
    

    // MARK:- View Life Cycle
    
    override func viewDidLoad() {
        setupLayout()
        setupConstraints()
        setupAttributes()
        setupLocalization()
        setData()
        setupBinding()
    }
    
    func setupLayout() {
        // Override Layout
    }
    
    func setupConstraints() {
        // Override Constraints
    }
    
    func setupAttributes() {
        // Override Attributes
    }
    
    func setupLocalization() {
        // Override Localization
    }
    
    func setData() {
        // Override Set Data
    }

    func setupBinding() {
        // Override Binding
    }
    
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위 코드는 viewDidLoad 에서 공통으로 실행될 만한 메소드가 실행되도록 구현했습니다.&lt;/p&gt;
&lt;p&gt;BaseViewController를 서브클래싱한 컨트롤러에서는 필요한 메소드를 override 하여 구현만 해주면 됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래 코드와 같이 서브클래싱하여 사용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1610264839418&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class ViewController: BaseViewController {
    
    // MARK:- UI
    
    let titleLabel = UILabel()
    
    let button = UIButton()
    
    
    // MARK:- View Life Cycle
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    override func setupLayout() {
        view.addSubview(titleLabel)
        view.addSubview(button)
    }
    
    override func setupConstraints() {
        titleLabel.snp.makeConstraints {
            $0.center.equalToSuperview()
        }
        
        button.snp.makeConstraints {
            $0.centerX.equalToSuperview()
            $0.top.equalTo(titleLabel.snp.bottom).offset(20)
        }
    }
    
    override func setData() {
        titleLabel.text = &quot;Hello~&quot;
    }
    
    override func setupBinding() {
        button.rx.tap
            .subscribe(onNext: {
                // Do Something
            })
            .disposed(by: disposeBag)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이러면 특정 컨트롤러에 필요한 데이터를 제외하고 항상 같은 구조로 코딩할 수 있을 것 입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;또한 UIAlertController를 띄워준다던지 로그인 여부를 체크하여 이동하는 등의 필요한 공통기능들을 구현하여 간단히 사용하고, 유지보수도 용이하도록 할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1610264868802&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;extension BaseViewController {
    func showAlert(
        title: String?,
        message: String?,
        confirmHandler: (() -&amp;gt; Void)? = nil,
        completion: (() -&amp;gt; Void)? = nil)
    {
        let alertView = UIAlertController(title: title, message: message, preferredStyle: .alert)
        let confirmAction = UIAlertAction(title: &quot;Confirm&quot;, style: .default) { _ in
            if let handler = confirmHandler {
                handler()
            }
        }
        alertView.addAction(confirmAction)
        present(alertView, animated: true, completion: completion)
    }
    
    func goToLoginView() {
        // 로그인 여부 체크 및 이동
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;- Reference&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/devxoul/Drrrible/blob/master/Drrrible/Sources/ViewControllers/BaseViewController.swift&quot;&gt;https://github.com/devxoul/Drrrible/blob/master/Drrrible/Sources/ViewControllers/BaseViewController.swift&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://tono18.tistory.com/22&quot;&gt;https://tono18.tistory.com/22&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Swift/Clean Code</category>
      <category>BaseViewController</category>
      <category>Cleancode</category>
      <author>진태우</author>
      <guid isPermaLink="true">https://jintaewoo.tistory.com/56</guid>
      <comments>https://jintaewoo.tistory.com/56#entry56comment</comments>
      <pubDate>Sun, 10 Jan 2021 16:48:46 +0900</pubDate>
    </item>
    <item>
      <title>[Swift] xib 파일로 View 관리하기</title>
      <link>https://jintaewoo.tistory.com/55</link>
      <description>&lt;p&gt;Main 스토리보드에 뷰를 만들어 사용하다보니 뷰가 많아지고 로드할 때마다 너무 오랜 시간이 걸리게 되었습니다.&lt;/p&gt;
&lt;p&gt;그래서 큰 틀을 제외한 나머지는 xib 파일로 관리하는게 효율적이라고 생각했습니다.&lt;/p&gt;
&lt;p&gt;이 포스팅에서는 xib 파일의 뷰컨트롤러를 간단하게 읽어서 사용하는 방법과 꼭 체크해야할 부분에 대해 정리해 보았습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-09-28 오후 11.08.24.png&quot; data-origin-width=&quot;729&quot; data-origin-height=&quot;522&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/loP0o/btqJRk1lijY/2HJlvZsSUtkDJW2KkItez0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/loP0o/btqJRk1lijY/2HJlvZsSUtkDJW2KkItez0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/loP0o/btqJRk1lijY/2HJlvZsSUtkDJW2KkItez0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FloP0o%2FbtqJRk1lijY%2F2HJlvZsSUtkDJW2KkItez0%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-09-28 오후 11.08.24.png&quot; data-origin-width=&quot;729&quot; data-origin-height=&quot;522&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;처음엔 위와 같이 UIViewController 파일을 만들 때, xib 파일까지 같이 만들어 보겠습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-09-28 오후 11.10.48.png&quot; data-origin-width=&quot;926&quot; data-origin-height=&quot;422&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HAFF4/btqJINqLkyH/F8KdYWbWuQ2eH3h5DsLcd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HAFF4/btqJINqLkyH/F8KdYWbWuQ2eH3h5DsLcd0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HAFF4/btqJINqLkyH/F8KdYWbWuQ2eH3h5DsLcd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHAFF4%2FbtqJINqLkyH%2FF8KdYWbWuQ2eH3h5DsLcd0%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-09-28 오후 11.10.48.png&quot; data-origin-width=&quot;926&quot; data-origin-height=&quot;422&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;그럼 자동으로 xib 파일에 클래스가 잘 연결이 되어 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-09-28 오후 11.13.13.png&quot; data-origin-width=&quot;927&quot; data-origin-height=&quot;688&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cAMXZk/btqJMw236Sn/rmWhibNCKlkcoNJy1565qk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cAMXZk/btqJMw236Sn/rmWhibNCKlkcoNJy1565qk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cAMXZk/btqJMw236Sn/rmWhibNCKlkcoNJy1565qk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcAMXZk%2FbtqJMw236Sn%2FrmWhibNCKlkcoNJy1565qk%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-09-28 오후 11.13.13.png&quot; data-origin-width=&quot;927&quot; data-origin-height=&quot;688&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;File's Owner의 view도 잘 연결되 어 있는걸 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;위 그림의 연결된 view 이름은 구분되기 쉽게 xibView로 변경해둔 것입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이번엔 UIViewController는 이미 있는 상태에서 xib 파일을 따로 만들어 보면 아래와 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-09-28 오후 11.15.36.png&quot; data-origin-width=&quot;926&quot; data-origin-height=&quot;494&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTKs2T/btqJScotflZ/rYR9B11eMxX33ZJRqTqAM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTKs2T/btqJScotflZ/rYR9B11eMxX33ZJRqTqAM0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTKs2T/btqJScotflZ/rYR9B11eMxX33ZJRqTqAM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTKs2T%2FbtqJScotflZ%2FrYR9B11eMxX33ZJRqTqAM0%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-09-28 오후 11.15.36.png&quot; data-origin-width=&quot;926&quot; data-origin-height=&quot;494&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;File's Owner의 view가 연결되지 않은 상태를 볼 수 있습니다. 이 부분이 연결되었는지 꼭 체크해줘야 에러가 발생하지 않습니다.&lt;/p&gt;
&lt;p&gt;물론 클래스도 자동으로 설정되어 있지 않으므로 체크해야 합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래와 같이 연결해주면 간단히 해결됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1518&quot; data-origin-height=&quot;1022&quot; data-filename=&quot;스크린샷 2020-09-28 오후 11.19.56.png&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2iuR7/btqJRmkxWoL/gfqkLW6xRZhuPMyZk2pQgK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2iuR7/btqJRmkxWoL/gfqkLW6xRZhuPMyZk2pQgK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2iuR7/btqJRmkxWoL/gfqkLW6xRZhuPMyZk2pQgK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2iuR7%2FbtqJRmkxWoL%2FgfqkLW6xRZhuPMyZk2pQgK%2Fimg.png&quot; data-origin-width=&quot;1518&quot; data-origin-height=&quot;1022&quot; data-filename=&quot;스크린샷 2020-09-28 오후 11.19.56.png&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 파일을 로드하여 뷰를 사용하면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1601304543126&quot; class=&quot;swift&quot; style=&quot;display: block; overflow: auto; padding: 15px; color: #383a42; background: #f6f7f8; font-size: 14px; border-radius: 3px; font-family: Menlo, Consolas, Monaco, monospace; border: 1px solid #dddddd; margin: 20px auto 0px; cursor: default; z-index: 1; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let xibView = XibTestViewController(nibName: &quot;XibTestViewController&quot;, bundle: nil)
navigationController?.pushViewController(viewController: xibView, animated: true, completion: nil)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;UIViewController를 확장하여 조금 더 편하게 사용할 수도 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1601304563187&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;extension UIViewController {
    static func loadFromNib() -&amp;gt; Self {
        func instantiateFromNib&amp;lt;T: UIViewController&amp;gt;() -&amp;gt; T {
            return T.init(nibName: String(describing: T.self), bundle: nil)
        }
                
        return instantiateFromNib()
    }
}
        
let xibView = XibTestViewController.loadFromNib()
navigationController?.pushViewController(viewController: xibView, animated: true, completion: nil)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Swift</category>
      <category>NIB</category>
      <category>Swift</category>
      <category>xib</category>
      <author>진태우</author>
      <guid isPermaLink="true">https://jintaewoo.tistory.com/55</guid>
      <comments>https://jintaewoo.tistory.com/55#entry55comment</comments>
      <pubDate>Mon, 28 Sep 2020 23:56:25 +0900</pubDate>
    </item>
    <item>
      <title>[swift] xib를 이용한 alertView 만들기</title>
      <link>https://jintaewoo.tistory.com/54</link>
      <description>&lt;p&gt;가끔씩 커스텀 AlertView를 사용할 때가 있어서 xib 파일과 함께 사용하는 법에 대해 정리합니다!!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그럼 바로 xib와 swift 파일을 만들어 주겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-07-18 오후 10.39.27.png&quot; data-origin-width=&quot;1456&quot; data-origin-height=&quot;1056&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cR2xIr/btqFNFQawnl/iRC3RUSOhAx4GJC1ZD7250/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cR2xIr/btqFNFQawnl/iRC3RUSOhAx4GJC1ZD7250/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cR2xIr/btqFNFQawnl/iRC3RUSOhAx4GJC1ZD7250/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcR2xIr%2FbtqFNFQawnl%2FiRC3RUSOhAx4GJC1ZD7250%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-07-18 오후 10.39.27.png&quot; data-origin-width=&quot;1456&quot; data-origin-height=&quot;1056&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XVSXH/btqFN3bVJvi/8VZD0p8dDYiSpk4Tizw2bK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XVSXH/btqFN3bVJvi/8VZD0p8dDYiSpk4Tizw2bK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XVSXH/btqFN3bVJvi/8VZD0p8dDYiSpk4Tizw2bK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXVSXH%2FbtqFN3bVJvi%2F8VZD0p8dDYiSpk4Tizw2bK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;저는 헷갈리지 않게 같은 이름으로 파일을 생성했습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;먼저 xib 파일에 커스텀 alertView를 만들겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-07-18 오후 10.49.15.png&quot; data-origin-width=&quot;2342&quot; data-origin-height=&quot;1454&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Zm5z9/btqFN16bnrD/ug6EYYPdrmlif5pW34FU01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Zm5z9/btqFN16bnrD/ug6EYYPdrmlif5pW34FU01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Zm5z9/btqFN16bnrD/ug6EYYPdrmlif5pW34FU01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZm5z9%2FbtqFN16bnrD%2Fug6EYYPdrmlif5pW34FU01%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-07-18 오후 10.49.15.png&quot; data-origin-width=&quot;2342&quot; data-origin-height=&quot;1454&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;뒷 배경을 위한 뷰를 추가하고 검은색 배경에 Alpha 값을 수정하여 원하는 투명도를 조정합니다.&lt;/p&gt;
&lt;p&gt;주의할 점은 Top, Bottom 제약 조건을 설정할 때, Safe Area로 설정하면 화면과 같이 모두 덮지 못하니 Superview로 설정해 주세요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;배경 화면을 만들었으니 팝업 내용을 위한 ContentView를 만들면 됩니다!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2338&quot; data-origin-height=&quot;1368&quot; data-filename=&quot;스크린샷 2020-07-18 오후 10.57.15.png&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3Y33p/btqFMHnJURR/fOJWtzf2iA8NzPdK5RXZ2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3Y33p/btqFMHnJURR/fOJWtzf2iA8NzPdK5RXZ2K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3Y33p/btqFMHnJURR/fOJWtzf2iA8NzPdK5RXZ2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3Y33p%2FbtqFMHnJURR%2FfOJWtzf2iA8NzPdK5RXZ2K%2Fimg.png&quot; data-origin-width=&quot;2338&quot; data-origin-height=&quot;1368&quot; data-filename=&quot;스크린샷 2020-07-18 오후 10.57.15.png&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;ContentView는 추가해서 원하는 화면에 맞게 만들면 됩니다!!&lt;/p&gt;
&lt;p&gt;여기서 주의할 점은 배경 화면 내부가 아닌 같은 레벨에 만들어야 합니다.&lt;/p&gt;
&lt;p&gt;배경 화면에 추가하면 설정한 Alpha 값에 영향을 받아서 같이 투명도가 조절 될 것입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;xib에 뷰를 만들었으니 이것을 로드하는 작업을 해보겠습니다.&lt;/p&gt;
&lt;p&gt;CustomAlertView 클래스를 아래와 같이 생성했습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1595084327439&quot; class=&quot;swift&quot; style=&quot;display: block; overflow: auto; padding: 15px; color: #383a42; background: #f6f7f8; font-size: 14px; border-radius: 3px; font-family: Menlo, Consolas, Monaco, monospace; border: 1px solid #dddddd; margin: 20px auto 0px; cursor: default; z-index: 1; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class CustomAlertView: UIView {

    let xibName = &quot;CustomAlertView&quot;
    
    @IBOutlet weak var rootView: UIView!
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        Bundle.main.loadNibNamed(xibName, owner: self, options: nil)
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
    }

    @IBAction func confirm(_ sender: UIButton) {
        rootView.removeFromSuperview()
    }
    
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;xibName은 xib파일의 이름이고, loadNibNamed 메소드를 사용하여 xib 파일을 로드할 수 있습니다.&lt;/p&gt;
&lt;p&gt;xib 파일을 로드하면 뷰와 연결된 outlet 변수를 사용할 수 있습니다.&lt;/p&gt;
&lt;p&gt;rootView는 최상위 뷰를 outlet 변수로 연결한 값 입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-07-18 오후 11.17.32.png&quot; data-origin-width=&quot;2334&quot; data-origin-height=&quot;934&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgSmNp/btqFNaQP0wD/yVhKImBGp6xcDnn8vli690/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgSmNp/btqFNaQP0wD/yVhKImBGp6xcDnn8vli690/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgSmNp/btqFNaQP0wD/yVhKImBGp6xcDnn8vli690/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgSmNp%2FbtqFNaQP0wD%2FyVhKImBGp6xcDnn8vli690%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-07-18 오후 11.17.32.png&quot; data-origin-width=&quot;2334&quot; data-origin-height=&quot;934&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 xib와 클래스를 연결해야 합니다. 아래 그림에서 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2342&quot; data-origin-height=&quot;1052&quot; data-filename=&quot;스크린샷 2020-07-18 오후 11.08.58.png&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/o8LxC/btqFNba5lui/3F9IUKDFdTq9JgWnBxXHFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/o8LxC/btqFNba5lui/3F9IUKDFdTq9JgWnBxXHFK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/o8LxC/btqFNba5lui/3F9IUKDFdTq9JgWnBxXHFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo8LxC%2FbtqFNba5lui%2F3F9IUKDFdTq9JgWnBxXHFK%2Fimg.png&quot; data-origin-width=&quot;2342&quot; data-origin-height=&quot;1052&quot; data-filename=&quot;스크린샷 2020-07-18 오후 11.08.58.png&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;여기서 주의해야 할 점은 File's Owner에 클래스를 연결해야 합니다.&lt;/p&gt;
&lt;p&gt;파일을 로드해서 이 안에 정의한 뷰들을 사용하는 것이기 때문입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 모든 작업이 끝났습니다.&lt;/p&gt;
&lt;p&gt;CustomAlertView를 출력해보겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1595084412603&quot; class=&quot;swift&quot; style=&quot;display: block; overflow: auto; padding: 15px; color: #383a42; background: #f6f7f8; font-size: 14px; border-radius: 3px; font-family: Menlo, Consolas, Monaco, monospace; border: 1px solid #dddddd; margin: 20px auto 0px; cursor: default; z-index: 1; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class ViewController: UIViewController {

    let alertView = CustomAlertView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }

    @IBAction func touchButton(_ sender: UIButton) {
        UIApplication.shared.windows.first?.addSubview(alertView.rootView)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위에서 언급한 내용처럼 xib를 로드해서 그 안에 정의한 뷰를 사용하기 때문에, CustomAlertView에 정의한 rootView를 추가해서 출력해 줍니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래 그림을 보면 이해하는데 도움이 될 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-07-18 오후 11.34.48.png&quot; data-origin-width=&quot;1401&quot; data-origin-height=&quot;836&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/s8F5r/btqFNaDhcsw/NX2AKvKCIXeAKKWW7cL0VK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/s8F5r/btqFNaDhcsw/NX2AKvKCIXeAKKWW7cL0VK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/s8F5r/btqFNaDhcsw/NX2AKvKCIXeAKKWW7cL0VK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fs8F5r%2FbtqFNaDhcsw%2FNX2AKvKCIXeAKKWW7cL0VK%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-07-18 오후 11.34.48.png&quot; data-origin-width=&quot;1401&quot; data-origin-height=&quot;836&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;loadNibNamed 메소드로 파일을 로드하면 내부에 정의된 뷰의 배열을 리턴합니다.&lt;/p&gt;
&lt;p&gt;그 값을 보면 1개가 정의되어 있다고 나옵니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-07-18 오후 11.36.13.png&quot; data-origin-width=&quot;1463&quot; data-origin-height=&quot;844&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/teCxB/btqFNg4kZg1/EF7unokeOF6dbSghXKPJV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/teCxB/btqFNg4kZg1/EF7unokeOF6dbSghXKPJV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/teCxB/btqFNg4kZg1/EF7unokeOF6dbSghXKPJV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FteCxB%2FbtqFNg4kZg1%2FEF7unokeOF6dbSghXKPJV1%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-07-18 오후 11.36.13.png&quot; data-origin-width=&quot;1463&quot; data-origin-height=&quot;844&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;이렇게 하나를 더 추가하면 2개의 뷰가 리턴되는 것을 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 자신이 원하는 방식으로 뷰를 정의해서 관리하면 될 것 같습니다!!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;연습한 코드도 업데이트 해놨으니 참고하셔도 좋습니다!!  &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1595084555618&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;object&quot; data-og-title=&quot;taewoojin/CustomAlertViewPractice&quot; data-og-description=&quot;Contribute to taewoojin/CustomAlertViewPractice development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/taewoojin/CustomAlertViewPractice&quot; data-og-url=&quot;https://github.com/taewoojin/CustomAlertViewPractice&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b870fq/hyGNMGQNwS/PAAKeEVsif8NXGkrdGmKj0/img.png?width=420&amp;amp;height=420&amp;amp;face=0_0_420_420&quot;&gt;&lt;a href=&quot;https://github.com/taewoojin/CustomAlertViewPractice&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/taewoojin/CustomAlertViewPractice&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b870fq/hyGNMGQNwS/PAAKeEVsif8NXGkrdGmKj0/img.png?width=420&amp;amp;height=420&amp;amp;face=0_0_420_420');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;taewoojin/CustomAlertViewPractice&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;Contribute to taewoojin/CustomAlertViewPractice development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Swift</category>
      <category>customAlertView</category>
      <category>Swift</category>
      <category>xib</category>
      <author>진태우</author>
      <guid isPermaLink="true">https://jintaewoo.tistory.com/54</guid>
      <comments>https://jintaewoo.tistory.com/54#entry54comment</comments>
      <pubDate>Sun, 19 Jul 2020 00:04:21 +0900</pubDate>
    </item>
    <item>
      <title>[swift] UIButton에 클로저를 추가해 보았습니다.</title>
      <link>https://jintaewoo.tistory.com/53</link>
      <description>&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;동적으로 버튼을 생성해야 할 경우, selector를 미리 선언해줘야 하기 때문에 이벤트를 추가하는데 어려움이 있을 수 있습니다.&lt;/p&gt;
&lt;p&gt;이를 위해 AssociatedObject 를 이용하여 UIButton에 closure를 추가하는 방법에 대해 정리해 보았습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;아래는 AssociatedObject에 대해 간단히 정리한 내용입니다~  &lt;span style=&quot;color: #333333;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1594739302214&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[swift] AssociatedObject&quot; data-og-description=&quot;AssociatedObject에 대해 간단히 정리해 보았습니다!! AssociatedObject는 런타임시 기존 클래스에 SubClassing 없이 사용자 정의 속성을 연결(추가) 할 수 있습니다. 아래는 AssociatedObject 관련 함수입니다. /..&quot; data-og-host=&quot;jintaewoo.tistory.com&quot; data-og-source-url=&quot;https://jintaewoo.tistory.com/52&quot; data-og-url=&quot;https://jintaewoo.tistory.com/52&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/uhdvy/hyGModccS8/hd09aFkYZaeaNFoWb52GS1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bqN0y0/hyGLcZKADi/quNmV6MJA9j99i9g9Ku6q1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://jintaewoo.tistory.com/52&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://jintaewoo.tistory.com/52&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/uhdvy/hyGModccS8/hd09aFkYZaeaNFoWb52GS1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bqN0y0/hyGLcZKADi/quNmV6MJA9j99i9g9Ku6q1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;[swift] AssociatedObject&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;AssociatedObject에 대해 간단히 정리해 보았습니다!! AssociatedObject는 런타임시 기존 클래스에 SubClassing 없이 사용자 정의 속성을 연결(추가) 할 수 있습니다. 아래는 AssociatedObject 관련 함수입니다. /..&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;jintaewoo.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;다음 예제는 &lt;span data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;UIButton&lt;/span&gt;을 확장하여 Action으로 closure를 추가할 수 있게 만드는 코드입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1594739092253&quot; class=&quot;swift&quot; style=&quot;display: block; overflow: auto; padding: 15px; color: #383a42; background: #f6f7f8; font-size: 14px; border-radius: 3px; font-family: Menlo, Consolas, Monaco, monospace; border: 1px solid #dddddd; margin: 20px auto 0px; cursor: default; z-index: 1; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;extension UIBUtton {
    public typealias UIBUttonTargetClosure = (UIBUtton) -&amp;gt; ()
    
    // 1
    private class UIBUttonClosureWrapper: NSObject {
        let closure: UIBUttonTargetClosure
        init(_ closure: @escaping UIBUttonTargetClosure) {
            self.closure = closure
        }
    }
    
    private struct AssociatedKeys {
        static var targetClosure = &quot;targetClosure&quot;    // 2
    }
    
    private var targetClosure: UIBUttonTargetClosure? {
        get {
            // 3
            guard let closureWrapper = objc_getAssociatedObject(self, &amp;amp;AssociatedKeys.targetClosure) as? UIBUttonClosureWrapper else { return nil }
            return closureWrapper.closure
        }
        set(newValue) {
            guard let newValue = newValue else { return }
            // 4
            objc_setAssociatedObject(self,
                                     &amp;amp;AssociatedKeys.targetClosure,
                                     UIBUttonClosureWrapper(newValue),
                                     objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
    
    // 5
    @objc func closureAction() {
        guard let targetClosure = targetClosure else { return }
        targetClosure(self)
    }
    
    // 6
    public func addAction(for event: UIButton.Event, closure: @escaping UIButtonTargetClosure) {
        targetClosure = closure
        addTarget(self, action: #selector(UIButton.closureAction), for: event)
    }
}


// How To Use
let button = UIButton()
button.addAction(for: .touchUpInside) { button in
    // Do Something
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;번호 순서대로 간략히 설명하면,&amp;nbsp;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;closure를 저장할 wrapper 클래스 입니다.&lt;/li&gt;
&lt;li&gt;값을 저장하기 위해 선언된 키입니다.&lt;/li&gt;
&lt;li&gt;objc_getAssociatedObject 메소드를 사용하여 키에 저장된 값을 불러 옵니다.&lt;/li&gt;
&lt;li&gt;objc_setAssociatedObject 메소드를 사용해 키에 값을 저장합니다.&lt;/li&gt;
&lt;li&gt;저장된 closure를 가져오는 메소드 입니다.&lt;/li&gt;
&lt;li&gt;addTarget를 wrapping하여 버튼에 closure를 설정하는 메소드 입니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;UIButton에 대해 한정적이기 보다, 상속받고 있는 UIControl를 확장하여 여러 곳에서 사용할 수 있게 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;UIControl은 UIButton, UIPageControl, UISlider 등 여러 곳에서 상속받는 클래스이기 때문에, UIControl을 확장하면 조금 더 효율적으로 사용할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래 링크에서 &lt;span data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;UIControl&lt;/span&gt;을 상속받는 클래스들을 볼 수 있습니다.&lt;/p&gt;
&lt;figure id=&quot;og_1594738273083&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Apple Developer Documentation&quot; data-og-description=&quot;&quot; data-og-host=&quot;developer.apple.com&quot; data-og-source-url=&quot;https://developer.apple.com/documentation/uikit/uicontrol#see-also&quot; data-og-url=&quot;https://developer.apple.com/documentation/uikit/uicontrol#see-also&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/uikit/uicontrol#see-also&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.apple.com/documentation/uikit/uicontrol#see-also&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Apple Developer Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;developer.apple.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;아래는 UIButton&lt;/span&gt;을 확장한 코드에서 &lt;span data-token-index=&quot;2&quot; data-reactroot=&quot;&quot;&gt;UIButton&lt;/span&gt; 대신 &lt;span data-token-index=&quot;4&quot; data-reactroot=&quot;&quot;&gt;UIControl&lt;/span&gt;로 변경만 해준 코드입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1594739096635&quot; class=&quot;swift&quot; style=&quot;display: block; overflow: auto; padding: 15px; color: #383a42; background: #f6f7f8; font-size: 14px; border-radius: 3px; font-family: Menlo, Consolas, Monaco, monospace; border: 1px solid #dddddd; margin: 20px auto 0px; cursor: default; z-index: 1; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;extension UIControl {
    public typealias UIControlTargetClosure = (UIControl) -&amp;gt; ()
    
    private class UIControlClosureWrapper: NSObject {
        let closure: UIControlTargetClosure
        init(_ closure: @escaping UIControlTargetClosure) {
            self.closure = closure
        }
    }
    
    private struct AssociatedKeys {
        static var targetClosure = &quot;targetClosure&quot;
    }
    
    private var targetClosure: UIControlTargetClosure? {
        get {
            guard let closureWrapper = objc_getAssociatedObject(self, &amp;amp;AssociatedKeys.targetClosure) as? UIControlClosureWrapper else { return nil }
            return closureWrapper.closure
        }
        set(newValue) {
            guard let newValue = newValue else { return }
            objc_setAssociatedObject(self,
                                     &amp;amp;AssociatedKeys.targetClosure,
                                     UIControlClosureWrapper(newValue),
                                     objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
	        }
    }
    
    @objc func closureAction() {
        guard let targetClosure = targetClosure else { return }
        targetClosure(self)
    }
    
    public func addAction(for event: UIControl.Event, closure: @escaping UIControlTargetClosure) {
        targetClosure = closure
        addTarget(self, action: #selector(UIControl.closureAction), for: event)
    }
    
}


// How To Use
let button = UIButton()
button.addAction(for: .touchUpInside) { button in
    // Do Something
}

let slider = UISlider()
slider.addAction(for: .valueChanged) { slider in
    // Do Something
}

let pageControl = UIPageControl()
pageControl.addAction(for: .valueChanged) { pageControl in
    // Do Something    
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;마지막으로 UIBarButtonItem에 closure를 추가하는 코드입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1594739108708&quot; class=&quot;swift&quot; style=&quot;display: block; overflow: auto; padding: 15px; color: #383a42; background: #f6f7f8; font-size: 14px; border-radius: 3px; font-family: Menlo, Consolas, Monaco, monospace; border: 1px solid #dddddd; margin: 20px auto 0px; cursor: default; z-index: 1; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;extension UIBarButtonItem {
    public typealias UIBarButtonItemTargetClosure = (UIBarButtonItem) -&amp;gt; ()

    private class UIBarButtonItemClosureWrapper: NSObject {
        let closure: UIBarButtonItemTargetClosure
        init(_ closure: @escaping UIBarButtonItemTargetClosure) {
            self.closure = closure
        }
    }
    
    private struct AssociatedKeys {
        static var targetClosure = &quot;targetClosure&quot;
    }
    
    private var targetClosure: UIBarButtonItemTargetClosure? {
        get {
            guard let closureWrapper = objc_getAssociatedObject(self, &amp;amp;AssociatedKeys.targetClosure) as? UIBarButtonItemClosureWrapper else { return nil }
            return closureWrapper.closure
        }
        set(newValue) {
            guard let newValue = newValue else { return }
            objc_setAssociatedObject(self,
                                     &amp;amp;AssociatedKeys.targetClosure,
                                     UIBarButtonItemClosureWrapper(newValue),
                                     objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }

    @objc func closureAction() {
        guard let targetClosure = targetClosure else { return }
        targetClosure(self)
    }
    
    convenience init(title: String?, style: UIBarButtonItem.Style, closure: @escaping UIBarButtonItemTargetClosure) {
        self.init(title: title, style: style, target: nil, action: nil)
        targetClosure = closure
        action = #selector(UIBarButtonItem.closureAction)
    }
    
    convenience init(image: UIImage, style: UIBarButtonItem.Style, closure: @escaping UIBarButtonItemTargetClosure) {
        self.init(image: image, style: style, target: nil, action: nil)
        targetClosure = closure
        action = #selector(UIBarButtonItem.closureAction)
    }
}


// How To Use
let imageBarButton = UIBarButtonItem(image: UIImage(), style: .plain) { barButtonItem in
    // Do Something    
}

let confirmBarButton = UIBarButtonItem(title: &quot;확인&quot;, style: .plain) { barButtonItem in
    // Do Something    
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;UIBarButtonItem&lt;/span&gt;은 초기화할 때 &lt;span data-token-index=&quot;2&quot; data-reactroot=&quot;&quot;&gt;action&lt;/span&gt;을 추가하기 때문에 초기화 메소드를 wrapping한 메소드를 추가하였고, 나머지는 UIControl을 확장한 코드와 같습니다.  &lt;span style=&quot;color: #333333;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;- Reference&lt;/h2&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1594738546104&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Adding Closures to Buttons in Swift&quot; data-og-description=&quot;One of the things I still find a little backward and unnatural to use in Swift is the Target-Action pattern.&quot; data-og-host=&quot;getswifty.dev&quot; data-og-source-url=&quot;https://getswifty.dev/adding-closures-to-buttons-in-swift/&quot; data-og-url=&quot;https://getswifty.dev/adding-closures-to-buttons-in-swift/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ccP8VO/hyGK1RswG3/vyuu6rdjTSPXi31zb2kOk0/img.jpg?width=2000&amp;amp;height=1333&amp;amp;face=0_0_2000_1333,https://scrap.kakaocdn.net/dn/U3E1z/hyGLcyE53z/Z6XocAQGv10AZdZdVF2hyK/img.jpg?width=2000&amp;amp;height=1333&amp;amp;face=0_0_2000_1333,https://scrap.kakaocdn.net/dn/Piehf/hyGMpwnnyP/9RXoWrPxrXfPp4552OHkKK/img.jpg?width=2000&amp;amp;height=1333&amp;amp;face=0_0_2000_1333&quot;&gt;&lt;a href=&quot;https://getswifty.dev/adding-closures-to-buttons-in-swift/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://getswifty.dev/adding-closures-to-buttons-in-swift/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ccP8VO/hyGK1RswG3/vyuu6rdjTSPXi31zb2kOk0/img.jpg?width=2000&amp;amp;height=1333&amp;amp;face=0_0_2000_1333,https://scrap.kakaocdn.net/dn/U3E1z/hyGLcyE53z/Z6XocAQGv10AZdZdVF2hyK/img.jpg?width=2000&amp;amp;height=1333&amp;amp;face=0_0_2000_1333,https://scrap.kakaocdn.net/dn/Piehf/hyGMpwnnyP/9RXoWrPxrXfPp4552OHkKK/img.jpg?width=2000&amp;amp;height=1333&amp;amp;face=0_0_2000_1333');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Adding Closures to Buttons in Swift&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;One of the things I still find a little backward and unnatural to use in Swift is the Target-Action pattern.&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;getswifty.dev&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Swift</category>
      <category>AssociatedObject</category>
      <category>Closure</category>
      <category>Swift</category>
      <category>UIBarButtonItem</category>
      <category>UIButton</category>
      <category>UIControl</category>
      <author>진태우</author>
      <guid isPermaLink="true">https://jintaewoo.tistory.com/53</guid>
      <comments>https://jintaewoo.tistory.com/53#entry53comment</comments>
      <pubDate>Tue, 14 Jul 2020 23:58:10 +0900</pubDate>
    </item>
    <item>
      <title>[swift] AssociatedObject</title>
      <link>https://jintaewoo.tistory.com/52</link>
      <description>&lt;p&gt;AssociatedObject에 대해 간단히 정리해 보았습니다!!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;AssociatedObject는 런타임시 기존 클래스에 SubClassing 없이 사용자 정의 속성을 연결(추가) 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래는 AssociatedObject 관련 함수입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1594472203462&quot; class=&quot;swift&quot; style=&quot;display: block; overflow: auto; padding: 15px; color: #383a42; background: #f6f7f8; font-size: 14px; border-radius: 3px; font-family: Menlo, Consolas, Monaco, monospace; border: 1px solid #dddddd; margin: 20px auto 0px; cursor: default; z-index: 1; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 객체에 속성 값을 연결
public func objc_setAssociatedObject(_ object: Any,
                                     _ key: UnsafeRawPointer,
                                     _ value: Any?,
                                     _ policy: objc_AssociationPolicy)

// 연결된 속성 값을 반환
public func objc_getAssociatedObject(_ object: Any,
                                     _ key: UnsafeRawPointer) -&amp;gt; Any?

// 객체에 연결된 모든 속성 값을 제거
// 제거하면 안되는 속성까지 제거될 수 있기 때문에, 
// 일반적으로 objc_setAssociatedObject을 nil로 설정하여 필요한 속성만 제거합니다.
public func objc_removeAssociatedObjects(_ object: Any)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- parameters&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;b&gt;object&lt;/b&gt; - 속성을 연결하려는 객체&lt;/p&gt;
&lt;p&gt;&lt;b&gt;key&lt;/b&gt;&amp;nbsp;- 속성에 대한 키 값&lt;/p&gt;
&lt;p&gt;&lt;b&gt;value&lt;/b&gt; - 객체와 연결하려는 속성 값&lt;/p&gt;
&lt;p&gt;&lt;b&gt;policy&lt;/b&gt; - 속성 연결 시 참조 유형을 정의하는 값&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- objc_AssociationPolicy 속성 종류&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1594471508754&quot; class=&quot;swift&quot; style=&quot;display: block; overflow: auto; padding: 15px; color: #383a42; background: #f6f7f8; font-size: 14px; border-radius: 3px; font-family: Menlo, Consolas, Monaco, monospace; border: 1px solid #dddddd; margin: 20px auto 0px; cursor: default; z-index: 1; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 연관된 오브젝트에 대한 약한 참조를 지정합니다.
OBJC_ASSOCIATION_ASSIGN  

// 연관된 오브젝트가 복사되고 ATOMIC으로 설정합니다.
OBJC_ASSOCIATION_COPY  

// 연관된 오브젝트가 복사되고 NONATOMIC으로 설정합니다.
OBJC_ASSOCIATION_COPY_NONATOMIC  

// 연관된 오브젝트에 대한 강력한 참조를 지정하고 ATOMIC으로 설정합니다.
OBJC_ASSOCIATION_RETAIN  

// 연관된 오브젝트에 대한 강력한 참조를 지정하고 NONATOMIC으로 설정합니다.
OBJC_ASSOCIATION_RETAIN_NONATOMIC  &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;런타임에서 클래스에 고유 속성을 선언할 경우 사용되는 속성이라고 합니다.... 잘 이해는 되지 않습니다...&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;How to use&lt;/h2&gt;
&lt;p&gt;사용법을 간단히 설명하면, 미리 선언한 키에 원하는 값을 저장하여 사용하도록 합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래는 키에 String 값을 저장하여 출력하는 코드입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1594471912121&quot; class=&quot;swift&quot; style=&quot;display: block; overflow: auto; padding: 15px; color: #383a42; background: #f6f7f8; font-size: 14px; border-radius: 3px; font-family: Menlo, Consolas, Monaco, monospace; border: 1px solid #dddddd; margin: 20px auto 0px; cursor: default; z-index: 1; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;extension UIButton {
    private struct AssociatedKeys {
        static var tempString = &quot;abc&quot;    // 1
    }
    
    var tempString: String? {
        get {
            // 2
            return (objc_getAssociatedObject(self, &amp;amp;AssociatedKeys.tempString) as? String)
        }
        set(newValue) {
            // 3
            objc_setAssociatedObject(self,
                                     &amp;amp;AssociatedKeys.tempString,
                                     newValue,
                                     .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
}
 
let button = UIButton()
button.tempString = &quot;ABC&quot;
print(button.tempString)  // &quot;ABC&quot;

button.tempString = nil   // 4
print(button.tempString)  // nil&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;값을 저장하기 위해 선언된 키입니다.&lt;/li&gt;
&lt;li&gt;objc_getAssociatedObject 메소드를 사용하여 키에 저장된 값을 불러 옵니다.&lt;/li&gt;
&lt;li&gt;objc_setAssociatedObject 메소드를 사용해 키에 값을 저장합니다.&lt;/li&gt;
&lt;li&gt;해당 키의 값을 nil로 설정하여 해당 키에 연결된 값만 제거합니다. &lt;br /&gt;objc_removeAssociatedObjects 메소드를 사용하면 객체와 연결된 모든 값들이 제거되기 때문에 조심해야 합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;어떤 방식으로 키에 값을 연결하여 사용하는지 간단히 정리해 보았습니다.&lt;/p&gt;
&lt;p&gt;&lt;s&gt;다음 포스팅에는 클로저를 저장하여 사용하는 내용에 대해 정리해보겠습니다&lt;/s&gt;~  &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;AssociatedObject를 이용하여 클로저를 저장하는 내용을 정리한 포스팅입니당~&amp;nbsp;  &lt;span style=&quot;color: #333333;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt; &lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1594774958245&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[swift] UIButton에 클로저를 추가해 보았습니다.&quot; data-og-description=&quot;동적으로 버튼을 생성해야 할 경우, selector를 미리 선언해줘야 하기 때문에 이벤트를 추가하는데 어려움이 있을 수 있습니다. 이를 위해 AssociatedObject 를 이용하여 UIButton에 closure를 추가하는 방��&quot; data-og-host=&quot;jintaewoo.tistory.com&quot; data-og-source-url=&quot;https://jintaewoo.tistory.com/53&quot; data-og-url=&quot;https://jintaewoo.tistory.com/53&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Xl0v8/hyGK7YAn1s/KYN0RN6yubWTPN2zwofg5K/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/G7S3F/hyGMyfYMyt/rIBG1s0dYhqvM8VWAtGOXk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://jintaewoo.tistory.com/53&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://jintaewoo.tistory.com/53&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Xl0v8/hyGK7YAn1s/KYN0RN6yubWTPN2zwofg5K/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/G7S3F/hyGMyfYMyt/rIBG1s0dYhqvM8VWAtGOXk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;[swift] UIButton에 클로저를 추가해 보았습니다.&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;동적으로 버튼을 생성해야 할 경우, selector를 미리 선언해줘야 하기 때문에 이벤트를 추가하는데 어려움이 있을 수 있습니다. 이를 위해 AssociatedObject 를 이용하여 UIButton에 closure를 추가하는 방��&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;jintaewoo.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;- Reference&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1594472102098&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Associated Objects&quot; data-og-description=&quot;Associated Objects is a feature of the Objective-C 2.0 runtime, which allows objects to associate arbitrary values for keys at runtime. It&amp;rsquo;s dark juju, to be handled with as much caution as any other function from objc/runtime.h&quot; data-og-host=&quot;nshipster.com&quot; data-og-source-url=&quot;https://nshipster.com/associated-objects/&quot; data-og-url=&quot;https://nshipster.com/associated-objects/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/QUyCu/hyGJH5hApO/ckVKwja03aVyoeROYBs5kK/img.png?width=500&amp;amp;height=500&amp;amp;face=0_0_500_500,https://scrap.kakaocdn.net/dn/bz3pNU/hyGJTq6w90/NgX0xh7o7PLgFtr6PPaPy1/img.jpg?width=300&amp;amp;height=300&amp;amp;face=34_103_160_241&quot;&gt;&lt;a href=&quot;https://nshipster.com/associated-objects/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://nshipster.com/associated-objects/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/QUyCu/hyGJH5hApO/ckVKwja03aVyoeROYBs5kK/img.png?width=500&amp;amp;height=500&amp;amp;face=0_0_500_500,https://scrap.kakaocdn.net/dn/bz3pNU/hyGJTq6w90/NgX0xh7o7PLgFtr6PPaPy1/img.jpg?width=300&amp;amp;height=300&amp;amp;face=34_103_160_241');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Associated Objects&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;Associated Objects is a feature of the Objective-C 2.0 runtime, which allows objects to associate arbitrary values for keys at runtime. It&amp;rsquo;s dark juju, to be handled with as much caution as any other function from objc/runtime.h&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;nshipster.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Swift</category>
      <category>AssociatedObject</category>
      <category>Swift</category>
      <author>진태우</author>
      <guid isPermaLink="true">https://jintaewoo.tistory.com/52</guid>
      <comments>https://jintaewoo.tistory.com/52#entry52comment</comments>
      <pubDate>Sat, 11 Jul 2020 21:59:27 +0900</pubDate>
    </item>
    <item>
      <title>[Swift] HTTP Live Streaming을 위한 재생 목록 (m3u8)</title>
      <link>https://jintaewoo.tistory.com/51</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;p&gt;HLS(HTTP&amp;nbsp;Live&amp;nbsp;Streaming)는 일반적으로 미디어 세그먼트 파일 이라고하는 일련의 작은 파일로 오디오 및 비디오를 보냅니다.&lt;/p&gt;
&lt;p&gt;인덱스 파일 또는 재생 목록은 미디어 세그먼트 파일의 URL을 순서대로 나열한 내용입니다.&lt;/p&gt;
&lt;p&gt;HLS 용 인덱스 파일은 MP3 재생 목록에 사용되는 M3U 형식의 확장 인 M3U8 재생 목록으로 저장됩니다.&lt;/p&gt;
&lt;p&gt;클라이언트에서는 순서대로 엑세스하여 인덱스 파일의 URL을 요청합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;HLS를 하기 위해서는 용도에 맞게 재생 목록을 만들어 주어야 합니다.&lt;/p&gt;
&lt;p&gt;기본적인 4가지 재생 목록에 대해 정리해 보았습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;Master Playlist&lt;/b&gt;&lt;/h1&gt;
&lt;p&gt;동일한 컨텐츠에 대해 다른 인코딩 버전을 제공하기 위한 재생 목록입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Master Playlist에 명시된 스트림은 특정 비트 전송률에 따라 변형된 재생 목록입니다.&lt;/p&gt;
&lt;p&gt;클라이언트는 측정된 네트워크 비트 전송률에 따라 가장 적합한 스트림으로 전환합니다.&lt;/p&gt;
&lt;p&gt;이를 통해 플레이어는 재생 중단을 최소화하여 사용자에게 좋은 스트리밍 환경을 제공합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-filename=&quot;eea52604-3fad-468c-ad8e-82345a40f4a5.png&quot; data-origin-width=&quot;689&quot; data-origin-height=&quot;760&quot; width=&quot;350&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mBSVR/btqEG80XpCz/wxHH1bR622EIQCZWMFIlOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mBSVR/btqEG80XpCz/wxHH1bR622EIQCZWMFIlOk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mBSVR/btqEG80XpCz/wxHH1bR622EIQCZWMFIlOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmBSVR%2FbtqEG80XpCz%2FwxHH1bR622EIQCZWMFIlOk%2Fimg.png&quot; data-filename=&quot;eea52604-3fad-468c-ad8e-82345a40f4a5.png&quot; data-origin-width=&quot;689&quot; data-origin-height=&quot;760&quot; width=&quot;350&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래 예시는 비트 전송률에 따라 5가지로 변형된 재생 목록을 정의한 내용입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1591678828149&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#EXTM3U
#EXT-X-STREAM-INF:BANDWIDTH=150000,RESOLUTION=416x234,CODECS=&quot;avc1.42e00a,mp4a.40.2&quot;
http://example.com/low/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=240000,RESOLUTION=416x234,CODECS=&quot;avc1.42e00a,mp4a.40.2&quot;
http://example.com/lo_mid/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=440000,RESOLUTION=416x234,CODECS=&quot;avc1.42e00a,mp4a.40.2&quot;
http://example.com/hi_mid/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=640000,RESOLUTION=640x360,CODECS=&quot;avc1.42e00a,mp4a.40.2&quot;
http://example.com/high/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=64000,CODECS=&quot;mp4a.40.5&quot;
http://example.com/audio/index.m3u8&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;재생 목록에서 사용되는 태그에 대해 알아보겠습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;EXTM3U&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;재생 목록이 확장 &lt;a href=&quot;https://en.wikipedia.org/wiki/M3U&quot;&gt;M3U&lt;/a&gt; 파일임을 나타냅니다.&amp;nbsp;이 유형의 파일은 첫 번째 행의 태그를 EXTM3U로 변경하여 기본 M3U 파일과 구별됩니다.&amp;nbsp;모든 HLS 재생 목록은이 태그로 시작해야합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;EXT-X-STREAM-INF&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;재생 목록 파일의 다음 URL이 다른 재생 목록 파일을 식별 함을 나타냅니다.&lt;/p&gt;
&lt;p&gt;EXT-X-STREAM-INF태그는 다음과 같은 매개 변수가 있습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;b&gt;BANDWIDTH&lt;/b&gt;&amp;nbsp;(필수)&lt;/p&gt;
&lt;p&gt;각 미디어 파일에 대한 전체 비트 전송률의 상한을 초 단위로 나타낸 정수입니다.&lt;/p&gt;
&lt;p&gt;상한값은 재생 목록에 표시되거나 표시 될 컨테이너 오버 헤드를 포함하도록 계산됩니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;b&gt;AVERAGE-BANDWIDTH&lt;/b&gt;&amp;nbsp;(선택이지만 권장)&lt;/p&gt;
&lt;p&gt;변형 스트림의 평균 비트 전송률을 나타내는 정수입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;b&gt;FRAME-RATE&lt;/b&gt;&amp;nbsp;(선택이지만 권장)&lt;/p&gt;
&lt;p&gt;변형 스트림의 최대 프레임 속도를 설명하는 부동 소수점 값 입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;b&gt;HDCP-LEVEL&lt;/b&gt;&amp;nbsp;(선택 사항)&lt;/p&gt;
&lt;p&gt;사용 된 암호화 유형을 나타냅니다.&amp;nbsp;유효한 값은&amp;nbsp;TYPE-0및&amp;nbsp;NONE입니다.&amp;nbsp;TYPE-0출력이 HDCP에 의해 보호되지 않으면 스트림이 재생되지 않을 경우&amp;nbsp;사용하십시오&amp;nbsp;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;b&gt;RESOLUTION&lt;/b&gt;&amp;nbsp;(선택이지만 권장)&lt;/p&gt;
&lt;p&gt;재생 목록의 모든 비디오를 표시 할 선택적 표시 크기 크기 (픽셀)입니다.&amp;nbsp;이 매개 변수는 비디오를 포함하는 모든 스트림에 포함되어야합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;b&gt;VIDEO-RANGE&lt;/b&gt;&amp;nbsp;(인코딩에 따라 필요)&lt;/p&gt;
&lt;p&gt;유효한 값이&amp;nbsp;SDR또는 인&amp;nbsp;문자열&amp;nbsp;PQ.&amp;nbsp;전송 특성 코드 1, 16 또는 18을 지정하지 않으면이 매개 변수를 생략해야합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;b&gt;CODECS&lt;/b&gt;&amp;nbsp;(선택이지만 권장)&lt;/p&gt;
&lt;p&gt;미디어 세그먼트의 미디어 샘플 유형을 명시합니다.&lt;/p&gt;
&lt;p&gt;각 유형은 쉼표로 구분된 문자열로 설정합니다.&lt;/p&gt;
&lt;p&gt;이 속성은 선택이지만 거의 필수로 명시해야 합니다. 이 속성을 통해 클라이언트에서 오디오 전용과 오디오 및 비디오를 모두 포함하는 구분할 수 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h1&gt;&lt;b&gt;Live Playlist&lt;/b&gt;&lt;/h1&gt;
&lt;p&gt;라이브 세션에서는 새 미디어 파일이 생성되고 사용 가능하게 될 때, 파일에서 미디어 URI를 제거하여 인덱스 파일을 업데이트 합니다.&lt;/p&gt;
&lt;p&gt;EXT-X-ENDLIST 태그가 Live Playlist에 없으므로 새 미디어 파일이 사용 가능해지면 인덱스 파일에 추가됨을 나타냅니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래 예시는 세션 시작시 나타나는 Live Playlist 입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1591678931401&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#EXTM3U
#EXT-X-TARGETDURATION:10
#EXT-X-VERSION:4
#EXT-X-MEDIA-SEQUENCE:1
#EXTINF:10.0,
fileSequence1.ts
#EXTINF:10.0,
fileSequence2.ts
#EXTINF:10.0,
fileSequence3.ts
#EXTINF:10.0,
fileSequence4.ts
#EXTINF:10.0,
fileSequence5.ts&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;재생 목록에서 사용되는 태그에 대해 알아보겠습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;EXTM3U&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;재생 목록이 확장 M3U 파일임을 나타냅니다.&amp;nbsp;이 유형의 파일은 첫 번째 행의 태그를 EXTM3U로 변경하여 기본 M3U 파일과 구별됩니다.&amp;nbsp;모든 HLS 재생 목록은이 태그로 시작해야합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;EXT-X-TARGETDURATION&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;미디어 파일 duration의 최대값을 명시합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;EXT-X-VERSION&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;재생 목록 파일의 호환 버전을 나타냅니다.&lt;/p&gt;
&lt;p&gt;호환 버전에 따라 재생 목록에 사용할 수 있는 태그 종류가 달라지는 것 같습니다...&lt;/p&gt;
&lt;p&gt;예를 들어 EXTINF 태그를 사용하려면 최소 버전은 3이고, EXT-X-I-FRAME-STREAM-INF 태그를 사용하려면 최소 4 버전이 되야 합니다.&lt;/p&gt;
&lt;p&gt;이 &lt;a href=&quot;https://developer.apple.com/documentation/http_live_streaming/about_the_ext-x-version_tag&quot;&gt;문서&lt;/a&gt;에서 자세한 내용을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;EXT-X-MEDIA-SEQUENCE&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;재생 목록 파일에 나타나는 첫 번째 URL의 시퀀스 번호를 나타냅니다.&lt;/p&gt;
&lt;p&gt;재생 목록의 각 미디어 파일 URL에는 고유 한 정수 시퀀스 번호가 있습니다.&amp;nbsp;URL의 시퀀스 번호가 앞에 오는 URL의 시퀀스 번호보다 1만큼 높습니다.&amp;nbsp;미디어 시퀀스 번호는 파일 이름과 관련이 없습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;재생 목록의 순서대로 재생 목록 파일에서 미디어 URI를 제거되며, 제거될 때마다 EXT-X-MEDIA-SEQUENCE 태그 값은 1씩 증가해야 합니다. 런 유형의 세션은 Live 방송에 적합합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;EXTINF&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;뒤에 오는 URL로 식별되는 미디어 파일을 설명하는 레코드 마커입니다.&lt;/p&gt;
&lt;p&gt;각 미디어 파일 URL 앞에는 EXTINF 태그가 있어야합니다 . 이 태그에는 미디어 세그먼트의 Duration을 초 단위로 지정하는 정수 또는 부동 소수점 타입의 속성이 명시됩니다.&lt;/p&gt;
&lt;p&gt;이 값은 TARGETDURATION보다 작거나 같아야합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;다음 예시는 미디어 URI이 생성/제거되면서 업데이트되는 재생 목록을 보여줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1591679009160&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#EXTM3U
#EXT-X-TARGETDURATION:10
#EXT-X-VERSION:4
#EXT-X-MEDIA-SEQUENCE:2
#EXTINF:10.0,
fileSequence2.ts
#EXTINF:10.0,
fileSequence3.ts
#EXTINF:10.00,
fileSequence4.ts
#EXTINF:10.00,
fileSequence5.ts
#EXTINF:10.0,
fileSequence6.ts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;계속해서 지나간 미디어 URI은 제거되고, 새로 생성된 미디어 URI는 추가됩니다.&lt;/p&gt;
&lt;p&gt;미디어 URI가 제거됨에 따라 #EXT-X-MEDIA-SEQUENCE 태그의 값은 증가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1591679039941&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#EXTM3U
#EXT-X-TARGETDURATION:10
#EXT-X-VERSION:4
#EXT-X-MEDIA-SEQUENCE:4
#EXTINF:10.00,
fileSequence4.ts
#EXTINF:10.00,
fileSequence5.ts
#EXTINF:10.0,
fileSequence6.ts,
#EXTINF:10.0,
fileSequence7.ts,
#EXTINF:10.0,
fileSequence8.ts,
#EXTINF:10.0,
fileSequence9.ts&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h1&gt;&lt;b&gt;Video on Demand(VOD) Playlist&lt;/b&gt;&lt;/h1&gt;
&lt;p&gt;주문형 비디오(VOD) 세션의 경우, 프리젠테이션의 전체 Duration이 명시되어 있습니다.&lt;/p&gt;
&lt;p&gt;인덱스 파일에 생성된 모든 미디어 파일의 전체 URI 목록을 포함하고 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1591679063486&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#EXTM3U
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-TARGETDURATION:10
#EXT-X-VERSION:4
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:10.0,
http://example.com/movie1/fileSequenceA.ts    // 절대 경로
#EXTINF:10.0,
http://example.com/movie1/fileSequenceB.ts
#EXTINF:10.0,
http://example.com/movie1/fileSequenceC.ts
#EXTINF:9.0,
http://example.com/movie1/fileSequenceD.ts
#EXT-X-ENDLIST&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;새로운 보이는 태그에 대해서만 알아보겠습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;EXT-X-PLAYLIST-TYPE&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;전체 재생 목록 파일에 적용되는 변경 정보를 제공합니다. 이 태그의 값으로는 VOD 또는 EVENT가 있습니다.&lt;/p&gt;
&lt;p&gt;태그의 값이 VOD인 경우 재생 목록의 파일이 변경되지 않아야 하고, 값이 EVENT인 경우 서버의 재생 목록 파일의 일부를 변경하거나 삭제하지 않아야 합니다.(추가는 될 수 있음)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;EXT-X-ENDLIST&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;더 이상 미디어 파일이 재생 목록 파일에 추가되지 않음을 나타냅니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래 예시는 상대 경로를 사용하여 미디어 재생 목록을 나타냅니다.&lt;/p&gt;
&lt;p&gt;상대 경로를 사용하면 이식성이 좋고, 텍스트도 적어서 알아보기 쉽습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1591679101928&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#EXTM3U
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-TARGETDURATION:10
#EXT-X-VERSION:4
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:10.0,
fileSequenceA.ts    // 상대 경로
#EXTINF:10.0,
fileSequenceB.ts
#EXTINF:10.0,
fileSequenceC.ts
#EXTINF:9.0,
fileSequenceD.ts
#EXT-X-ENDLIST&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h1&gt;&lt;b&gt;Event Playlist&lt;/b&gt;&lt;/h1&gt;
&lt;p&gt;이벤트 재생 목록은 EXT-X-PLAYLIST-TYPE 태그 값이 EVENT로 지정됩니다.&lt;/p&gt;
&lt;p&gt;처음에는 EXT-X-ENDLIST태그 가 없으므로 새 미디어 파일이 재생 목록에 추가됨을 나타냅니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래 예시는 세션 시작시 나타나는 이벤트 재생 목록입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1591679131616&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#EXTM3U
#EXT-X-PLAYLIST-TYPE:EVENT
#EXT-X-TARGETDURATION:10
#EXT-X-VERSION:4
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:10.00,
fileSequence0.ts
#EXTINF:10.0,
fileSequence1.ts
#EXTINF:10.0,
fileSequence2.ts
#EXTINF:10.0,
fileSequence3.ts
#EXTINF:10.0,
fileSequence4.ts&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;EVENT 태그를 사용할 때는 재생 목록에서 어떤 것도 제거할 수 없으며, 파일 끝에 새 세그먼트만 추가할 수 있습니다.&lt;/p&gt;
&lt;p&gt;새 세그먼트는 이벤트가 종료될 때까지 파일 끝에 추가되며, 마지막에 EXT-X-ENDLIST 태그가 추가되면서 이벤트가 종료됩니다.&lt;/p&gt;
&lt;p&gt;다음 예제는 새 미디어 URI가 업데이트되고 이벤트가 종료된 후의 재생 목록입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1591679159899&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#EXTM3U
#EXT-X-PLAYLIST-TYPE:EVENT
#EXT-X-TARGETDURATION:10
#EXT-X-VERSION:4
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:10.0,
fileSequence0.ts
#EXTINF:10.0,
fileSequence1.ts
#EXTINF:10.0,
fileSequence2.ts
#EXTINF:10.0,
fileSequence3.ts
#EXTINF:10.0,
fileSequence4.ts

// List of files between 4 and 120 go here.

#EXTINF:10.0,
fileSequence120.ts
#EXTINF:10.0,
fileSequence121.ts
#EXT-X-ENDLIST&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이벤트 재생 목록은 일반적으로 콘서트, 스포츠 이벤트와 같이 이벤트의 특정 지점을 시간과 상관없이 볼수 있도록 해야하는 경우에 사용됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;b&gt;Reference&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1591680668825&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Example Playlists for HTTP Live Streaming | Apple Developer Documentation&quot; data-og-description=&quot;Example Playlists for HTTP Live Streaming View and compare playlists for different HLS applications. OverviewHTTP Live Streaming sends audio and video as a series of small files, typically of about 6 seconds duration, called media segment files. An index f&quot; data-og-host=&quot;developer.apple.com&quot; data-og-source-url=&quot;https://developer.apple.com/documentation/http_live_streaming/example_playlists_for_http_live_streaming&quot; data-og-url=&quot;https://developer.apple.com/documentation/http_live_streaming/example_playlists_for_http_live_streaming&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/http_live_streaming/example_playlists_for_http_live_streaming&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.apple.com/documentation/http_live_streaming/example_playlists_for_http_live_streaming&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Example Playlists for HTTP Live Streaming | Apple Developer Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;Example Playlists for HTTP Live Streaming View and compare playlists for different HLS applications. OverviewHTTP Live Streaming sends audio and video as a series of small files, typically of about 6 seconds duration, called media segment files. An index f&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;developer.apple.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Swift</category>
      <category>AVFoundation</category>
      <category>AVKit</category>
      <category>HLS</category>
      <category>http live streaming</category>
      <category>IOS</category>
      <category>m3u8</category>
      <category>Swift</category>
      <author>진태우</author>
      <guid isPermaLink="true">https://jintaewoo.tistory.com/51</guid>
      <comments>https://jintaewoo.tistory.com/51#entry51comment</comments>
      <pubDate>Tue, 9 Jun 2020 14:35:38 +0900</pubDate>
    </item>
    <item>
      <title>[Swift] Documentation Comments</title>
      <link>https://jintaewoo.tistory.com/50</link>
      <description>&lt;p&gt;문서화를 위해 주석을 잘 작성하는 법에 대해 알아보았습니다.&lt;/p&gt;
&lt;p&gt;애플의 주석은 마크다운의 문법과 비슷해서 누구든 쉽게 사용할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2&gt;기본 주석 규칙&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;한 줄 주석에는 3개의 슬래시(///)를 사용합니다.&lt;/li&gt;
&lt;li&gt;여러 줄 주석에는 구분 기호(/** ... */)를 사용합니다.&lt;/li&gt;
&lt;li&gt;단락은 빈 줄로 구분됩니다.&lt;/li&gt;
&lt;li&gt;순서 없는 목록은 기호 문자(-, +, *)로 표시합니다.&lt;/li&gt;
&lt;li&gt;정렬된 목록은 숫자 뒤에 마침표( 1. ) 또는 괄호( 1) )를 사용합니다.&lt;/li&gt;
&lt;li&gt;Header 앞에는 기호( # )를 사용합니다.&lt;/li&gt;
&lt;li&gt;링크와 이미지도 사용 가능합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;swift&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;/**
    # Lists

    You can apply *italic*, **bold**, or `code` inline styles.

    ## Unordered Lists

    - Unordered item
    - Unordered item with sublist
      - Unordered subitem

    ## Ordered Lists

    1. Ordered item
        1. Ordered subitem
        2. Ordered subitem
    2. Ordered item
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;작성한 주석은 아래와 같이 도움말에 추가됩니다.&lt;/p&gt;
&lt;p&gt;도움말은 함수 및 변수 이름을 &lt;code&gt;option + click&lt;/code&gt; 하면 나타납니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-filename=&quot;스크린샷 2020-05-28 오전 11.05.36.png&quot; data-origin-width=&quot;499&quot; data-origin-height=&quot;369&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kwIR3/btqEttqzzCg/qqLOHfJG7lyB22J7RrTYi1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kwIR3/btqEttqzzCg/qqLOHfJG7lyB22J7RrTYi1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kwIR3/btqEttqzzCg/qqLOHfJG7lyB22J7RrTYi1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkwIR3%2FbtqEttqzzCg%2FqqLOHfJG7lyB22J7RrTYi1%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-05-28 오전 11.05.36.png&quot; data-origin-width=&quot;499&quot; data-origin-height=&quot;369&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2&gt;문서 항목&lt;/h2&gt;
&lt;p&gt;각 항목에 대해 도움말을 정의할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;요약(Summary)&lt;/h3&gt;
&lt;p&gt;주석의 첫 단락은 문서의 요약으로 정의됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;Parameter, Return and Throws&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Parameter value&lt;br /&gt;&lt;code&gt;- &amp;lt;Parameter name&amp;gt;:&lt;/code&gt; 필드를 시작으로 매개 변수에 대한 설명을 정의합니다. 매개 변수가 여러 개일 경우에는 &lt;code&gt;- Parameters:&lt;/code&gt;의 하위 항목으로 정의합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Return value&lt;br /&gt;&lt;code&gt;- Returns:&lt;/code&gt; 필드를 시작으로 반환 값에 대한 설명을 정의합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Throws&lt;br /&gt;&lt;code&gt;- Throws:&lt;/code&gt; 필드를 시작으로 발생 가능한 오류에 대한 설명을 정의합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;/**
 Method to open the door

 - Parameter with: Password for open the door

 - Throws: `MyError.invalidPassword`

 - Returns: Success message that the door has been opened
 */
func unlockDoor(with password: String) throws -&amp;gt; String {
    guard password != &quot;1234&quot; else {
        throw MyError.invalidPassword
    }

    return &quot;The door is open.&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;501&quot; data-origin-height=&quot;301&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NDbn2/btqEt53zPSu/WVIPKaDD46mJfEjIMIYJfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NDbn2/btqEt53zPSu/WVIPKaDD46mJfEjIMIYJfk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NDbn2/btqEt53zPSu/WVIPKaDD46mJfEjIMIYJfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNDbn2%2FbtqEt53zPSu%2FWVIPKaDD46mJfEjIMIYJfk%2Fimg.png&quot; data-origin-width=&quot;501&quot; data-origin-height=&quot;301&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;swift&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;/**
  Create UIColor from RGB values with optional transparency.

  - Parameters:
      - red: red component.
      - green: green component.
      - blue: blue component.
      - transparency: optional transparency value (default is 1)
 */
convenience init(red: Int, green: Int, blue: Int, transparency: CGFloat = 1) {
    self.init(red: CGFloat(red) / 255.0, 
              green: CGFloat(green) / 255.0, 
              blue: CGFloat(blue) / 255.0, alpha: trans)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-filename=&quot;스크린샷 2020-05-28 오후 12.54.53.png&quot; data-origin-width=&quot;501&quot; data-origin-height=&quot;293&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/boDNPC/btqEuzi6yIk/R0jhd9ygYSOtYomYKkYkBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/boDNPC/btqEuzi6yIk/R0jhd9ygYSOtYomYKkYkBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/boDNPC/btqEuzi6yIk/R0jhd9ygYSOtYomYKkYkBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FboDNPC%2FbtqEuzi6yIk%2FR0jhd9ygYSOtYomYKkYkBk%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-05-28 오후 12.54.53.png&quot; data-origin-width=&quot;501&quot; data-origin-height=&quot;293&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h1&gt;추가적인 항목&lt;/h1&gt;
&lt;p&gt;Parameter, Returns, Throws 와 더불어 추가적으로 지원하는 항목이 있습니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Safety Information&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;- Precondition&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;- Postcondition&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;- Requires&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;- Invariant&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;- Complexity&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;- Important&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;- Warning&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Metadata&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&lt;span style=&quot;color: #333333;&quot;&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Author&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&lt;span style=&quot;color: #333333;&quot;&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Authors&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&lt;span style=&quot;color: #333333;&quot;&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Copyright&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&lt;span style=&quot;color: #333333;&quot;&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Date&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&lt;span style=&quot;color: #333333;&quot;&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;SeeAlso&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&lt;span style=&quot;color: #333333;&quot;&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Since&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&lt;span style=&quot;color: #333333;&quot;&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Version&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Notes&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&lt;span style=&quot;color: #333333;&quot;&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Attention&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&lt;span style=&quot;color: #333333;&quot;&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Bug&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&lt;span style=&quot;color: #333333;&quot;&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Experiment&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&lt;span style=&quot;color: #333333;&quot;&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Note&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&lt;span style=&quot;color: #333333;&quot;&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Remark&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&lt;span style=&quot;color: #333333;&quot;&gt;-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;ToDo&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h1&gt;Code Block&lt;/h1&gt;
&lt;p&gt;함수의 사용법을 보여주는 예제 코드 블록을 삽입할 수 있습니다.&lt;/p&gt;
&lt;p&gt;코드 블록 삽입 방법은 2가지가 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;앞에 4개 이상의 공백을 줘서 삽입하는 방법입니다.&lt;/p&gt;
&lt;pre class=&quot;swift&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;/**
 Check if string is valid email format

     &quot;test@test.com&quot;.isValidEmail -&amp;gt; true

 */
var isValidEmail: Bool { get }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;3개의 backticks(`) 또는 tildes(~) 기호로 구분 선을 만들어 줘서 삽입하는 방법입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1590680455104&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
 Check if string is valid email format
 
 ```
 &quot;test@test.com&quot;.isValidEmail -&amp;gt; true
 ```

 */
var isValidEmail: Bool { get }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;499&quot; data-origin-height=&quot;237&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFvpi3/btqEviHE5Xb/7dz8ACjNFh5dJcT6pqOwG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFvpi3/btqEviHE5Xb/7dz8ACjNFh5dJcT6pqOwG0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFvpi3/btqEviHE5Xb/7dz8ACjNFh5dJcT6pqOwG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFvpi3%2FbtqEviHE5Xb%2F7dz8ACjNFh5dJcT6pqOwG0%2Fimg.png&quot; data-origin-width=&quot;499&quot; data-origin-height=&quot;237&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h1&gt;MARK, TODO and FIXME&lt;/h1&gt;
&lt;p&gt;기능을 의미 있고 탐색하기 쉬운 섹션으로 나눌 때 사용됩니다.&lt;/p&gt;
&lt;p&gt;섹션은 소스 탐색기에서 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;2개의 슬래시(&lt;code&gt;//&lt;/code&gt;)를 사용합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;// MARK:&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;// TODO:&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;// FIXME:&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;307&quot; data-origin-height=&quot;254&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d1MoZ2/btqEu6AHklw/wU1MLYginNfWy0khVkvTcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d1MoZ2/btqEu6AHklw/wU1MLYginNfWy0khVkvTcK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d1MoZ2/btqEu6AHklw/wU1MLYginNfWy0khVkvTcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd1MoZ2%2FbtqEu6AHklw%2FwU1MLYginNfWy0khVkvTcK%2Fimg.png&quot; data-origin-width=&quot;307&quot; data-origin-height=&quot;254&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Dash(-) 기호를 사용하면 구분 선이 표시되서 좀 더 보기 편합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;// MARK: -&lt;/li&gt;
&lt;li&gt;// TODO: -&lt;/li&gt;
&lt;li&gt;// FIXME: -&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;307&quot; data-origin-height=&quot;278&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQu1Ye/btqEtuQzxks/xuMfKuf16D0T60t9lxu5OK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQu1Ye/btqEtuQzxks/xuMfKuf16D0T60t9lxu5OK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQu1Ye/btqEtuQzxks/xuMfKuf16D0T60t9lxu5OK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQu1Ye%2FbtqEtuQzxks%2FxuMfKuf16D0T60t9lxu5OK%2Fimg.png&quot; data-origin-width=&quot;307&quot; data-origin-height=&quot;278&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h1&gt;문서화 도구&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;Jazzy&lt;/code&gt; 라는 프로젝트의 주석을 HTML 문서로 변환해 주는 오픈 소스 유틸리티가 있습니다.&lt;/p&gt;
&lt;p&gt;프로젝트의 코드를 문서화할 때 해보면 좋을 것 같았습니다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;gem&lt;/code&gt;으로 &lt;code&gt;jazzy&lt;/code&gt;를 설치하고 프로젝트의 루트 폴더에서 &lt;code&gt;jazzy&lt;/code&gt; 명령어를 실행하면 쉽게 문서가 생성됩니다.&lt;/p&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;$ sudo gem install jazzy
$ jazzy&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h1&gt;Code Snippet&lt;/h1&gt;
&lt;p&gt;자신이 사용하기 편한 형식의 코드 조각을 만들어 보겠습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;직접 만든 형식의 코드 범위를 선택하고 우클릭하면 아래와 같은 화면과 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;597&quot; data-origin-height=&quot;483&quot; data-filename=&quot;스크린샷 2020-05-28 오후 3.03.18.png&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cv5luB/btqEuAPP34Z/nuYsNQro3yADRSxNCWMrE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cv5luB/btqEuAPP34Z/nuYsNQro3yADRSxNCWMrE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cv5luB/btqEuAPP34Z/nuYsNQro3yADRSxNCWMrE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcv5luB%2FbtqEuAPP34Z%2FnuYsNQro3yADRSxNCWMrE1%2Fimg.png&quot; data-origin-width=&quot;597&quot; data-origin-height=&quot;483&quot; data-filename=&quot;스크린샷 2020-05-28 오후 3.03.18.png&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Create Code Snippet&lt;/code&gt; 을 선택하면 아래와 같은 화면이 나옵니다.&lt;/p&gt;
&lt;p&gt;Snippet의 이름을 정해 주고, 아래 Completion 항목에도 이름을 정해 줍니다.&lt;/p&gt;
&lt;p&gt;Completion에 설정한 명령어는 자동 완성 기능을 사용할 수 있도록 만들어 줍니다.&lt;/p&gt;
&lt;p&gt;Snippet에서 하얀 글자로 되어 있는 코드는 언제든 대체할 수 있는 부분을 나타내며 &lt;code&gt;&amp;lt;# ... #&amp;gt;&lt;/code&gt; 으로 텍스트를 감싸면 추가됩니다.&lt;/p&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;&amp;lt;#placeholder#&amp;gt;
&amp;lt;#view:UIView#&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-filename=&quot;스크린샷 2020-05-28 오후 3.11.41.png&quot; data-origin-width=&quot;727&quot; data-origin-height=&quot;542&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TJQwV/btqEt6aqowm/L2hDtWTJw1ISldStdFXQYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TJQwV/btqEt6aqowm/L2hDtWTJw1ISldStdFXQYk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TJQwV/btqEt6aqowm/L2hDtWTJw1ISldStdFXQYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTJQwV%2FbtqEt6aqowm%2FL2hDtWTJw1ISldStdFXQYk%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-05-28 오후 3.11.41.png&quot; data-origin-width=&quot;727&quot; data-origin-height=&quot;542&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Completion 항목에 설정한 명령어를 적으면 자동 완성에 추가된 것을 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-filename=&quot;스크린샷 2020-05-28 오후 3.17.56.png&quot; data-origin-width=&quot;364&quot; data-origin-height=&quot;168&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dUXJAG/btqEuW58Gf7/Ky0k9POcZiwtr7aUDyLeh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dUXJAG/btqEuW58Gf7/Ky0k9POcZiwtr7aUDyLeh0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dUXJAG/btqEuW58Gf7/Ky0k9POcZiwtr7aUDyLeh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdUXJAG%2FbtqEuW58Gf7%2FKy0k9POcZiwtr7aUDyLeh0%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-05-28 오후 3.17.56.png&quot; data-origin-width=&quot;364&quot; data-origin-height=&quot;168&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Reference&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://nshipster.com/swift-documentation/&quot;&gt;https://nshipster.com/swift-documentation/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1590680318260&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Swift Documentation&quot; data-og-description=&quot;Code structure and organization is a matter of pride for developers. Clear and consistent code signifies clear and consistent thought.&quot; data-og-host=&quot;nshipster.com&quot; data-og-source-url=&quot;https://nshipster.com/swift-documentation/&quot; data-og-url=&quot;https://nshipster.com/swift-documentation/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/faDo1/hyGdPinqwV/pUCnrJtjgJbNr73Vdscoik/img.png?width=500&amp;amp;height=500&amp;amp;face=0_0_500_500,https://scrap.kakaocdn.net/dn/3oLqW/hyGdMFXjvi/9EZc0cwDPR8S3Xc69D4G3K/img.png?width=1144&amp;amp;height=808&amp;amp;face=0_0_1144_808,https://scrap.kakaocdn.net/dn/dfmRJT/hyGdORiLLv/PSFuI67fiRAnhlan0BreI0/img.png?width=1144&amp;amp;height=728&amp;amp;face=0_0_1144_728&quot;&gt;&lt;a href=&quot;https://nshipster.com/swift-documentation/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://nshipster.com/swift-documentation/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/faDo1/hyGdPinqwV/pUCnrJtjgJbNr73Vdscoik/img.png?width=500&amp;amp;height=500&amp;amp;face=0_0_500_500,https://scrap.kakaocdn.net/dn/3oLqW/hyGdMFXjvi/9EZc0cwDPR8S3Xc69D4G3K/img.png?width=1144&amp;amp;height=808&amp;amp;face=0_0_1144_808,https://scrap.kakaocdn.net/dn/dfmRJT/hyGdORiLLv/PSFuI67fiRAnhlan0BreI0/img.png?width=1144&amp;amp;height=728&amp;amp;face=0_0_1144_728');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Swift Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;Code structure and organization is a matter of pride for developers. Clear and consistent code signifies clear and consistent thought.&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;nshipster.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Swift</category>
      <author>진태우</author>
      <guid isPermaLink="true">https://jintaewoo.tistory.com/50</guid>
      <comments>https://jintaewoo.tistory.com/50#entry50comment</comments>
      <pubDate>Fri, 29 May 2020 00:49:11 +0900</pubDate>
    </item>
  </channel>
</rss>