<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>luke_kong.log</title>
        <link>https://velog.io/</link>
        <description>"안녕하세요👋 춤을 사랑하는 댄서이자,             iOS dev. 공태현(Luke)입니다. 😎"</description>
        <lastBuildDate>Fri, 01 Jul 2022 13:01:54 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>luke_kong.log</title>
            <url>https://velog.velcdn.com/images/luke_kong/profile/57233eb3-ca0a-48c3-88b7-80fab53aa784/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. luke_kong.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/luke_kong" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[StoryBoard 없이 UI 개발하기]]></title>
            <link>https://velog.io/@luke_kong/StoryBoard-%EC%97%86%EC%9D%B4-UI-%EA%B0%9C%EB%B0%9C%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@luke_kong/StoryBoard-%EC%97%86%EC%9D%B4-UI-%EA%B0%9C%EB%B0%9C%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 01 Jul 2022 13:01:54 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요 iOS 개발자 루크입니다.</p>
<p>오늘은 UIKit 프로젝트에서 storyBoard 없이 개발하기 위한 초기 설정하는 방법을 알아보겠습니다.</p>
<h1 id="1-mainstoryboard-삭제">1. Main.storyboard 삭제</h1>
<hr>
<p>먼저 메인 스토리보드 파일을 삭제합니다.</p>
<p><img src="https://s3-us-west-2.amazonaws.com/secure.notion-static.com/99c7b9b2-ab74-4d8c-a3a9-92d483b422fa/Untitled.png" alt="Untitled"></p>
<h1 id="2-infoplist-설정">2. <strong>info.plist 설정</strong></h1>
<hr>
<p>스토리볻 파일이 삭제되었음을 info.plist 에 반영합니다.</p>
<p>Application Scene Manifest &gt; Scene Configuration → Storyboard Name</p>
<p><code>-</code> 버튼 눌러서 삭제.</p>
<h1 id="3-main-interface-수정해주기">3. Main interface 수정해주기</h1>
<hr>
<p><img src="https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d38c1cae-eda4-47c1-b693-b310d8f03823/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2022-02-16_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_4.00.55.png" alt="스크린샷 2022-02-16 오후 4.00.55.png"></p>
<h1 id="4-scenedelegate-설정">4. SceneDelegate 설정</h1>
<p><em>(iOS 13버전 미만 타겟에서는, AppDelegate의 <code>func application(- application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -&gt; Bool</code> 메서드 내부에 작업해주세요!)</em></p>
<p><code>guard let _ = (scene as? UIWindowScene) else { return }</code></p>
<p>의 변수에 이름을 지어줍니다.</p>
<pre><code class="language-swift">guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(windowScene: windowScene) // SceneDelegate의 프로퍼티에 설정해줌
let mainViewController = ViewController() // 맨 처음 보여줄 ViewController

window?.rootViewController = mainViewController
window?.makeKeyAndVisible()</code></pre>
<p>rootViewController  이 Is Initial ViewController 라고 생각하면 편합니다.</p>
<p><img src="https://s3-us-west-2.amazonaws.com/secure.notion-static.com/23fa1a11-a338-45d8-ae6c-f765967149e1/Untitled.png" alt="Untitled"></p>
<p>UI를 코드로 짜게 되면 UIViewController 들의 배경색이 검정색입니다.</p>
<p>viewDidLoad 에 배경색과 확인을 위한 라벨을 넣어줍시다.</p>
<pre><code class="language-swift">let test = UILabel()
view.backgroundColor = .white // 배경색
view.addSubview(test)
test.text = &quot;text&quot; // test를 위해서 출력할 라벨 
test.translatesAutoresizingMaskIntoConstraints = false
test.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
test.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true</code></pre>
<h1 id="5-navigation-controller-추가하기">5. Navigation Controller 추가하기</h1>
<p>네비게이션 컨트롤러를 window 의 rootViewController 로 선언해주면된다.</p>
<pre><code class="language-swift">guard let windowScene = (scene as? UIWindowScene) else { return }
 window = UIWindow(windowScene: windowScene) 

 let mainViewController = ViewController() // 이 뷰컨트롤러를 내비게이션 컨트롤러에 담아볼게요!

 let navigationController = UINavigationController(rootViewController: mainViewController) // 내비게이션 컨트롤러에 처음으로 보여질 화면을 rootView로 지정해주고!

window?.rootViewController = navigationController // 시작을 위에서 만든 내비게이션 컨트롤러로 해주면 끝!
window?.makeKeyAndVisible()</code></pre>
<h3 id="참고">참고</h3>
<hr>
<p><a href="https://velog.io/@lina0322/iOSSwift-%EC%8A%A4%ED%86%A0%EB%A6%AC%EB%B3%B4%EB%93%9C-%EC%97%86%EC%9D%B4-%EC%BD%94%EB%93%9C%EB%A1%9C%EB%A7%8C-UI-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0-SceneDelegate%EC%97%90%EC%84%9C-window%EC%84%A4%EC%A0%95">[iOS][Swift] - 스토리보드 없이 코드로만 UI 구현하기 (SceneDelegate에서 window설정)</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SwiftUI 생명주기 이벤트 핸들링의 모든 것]]></title>
            <link>https://velog.io/@luke_kong/SwiftUI-%EC%83%9D%EB%AA%85%EC%A3%BC%EA%B8%B0-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%ED%95%B8%EB%93%A4%EB%A7%81%EC%9D%98-%EB%AA%A8%EB%93%A0-%EA%B2%83</link>
            <guid>https://velog.io/@luke_kong/SwiftUI-%EC%83%9D%EB%AA%85%EC%A3%BC%EA%B8%B0-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%ED%95%B8%EB%93%A4%EB%A7%81%EC%9D%98-%EB%AA%A8%EB%93%A0-%EA%B2%83</guid>
            <pubDate>Thu, 05 May 2022 04:02:28 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요 iOS dev 루크입니다  😇</p>
<p>오늘은 SwiftUI 앱의 생명주기에 대해서 알아보려고 합니다.</p>
<p>iOS 14 업데이트와 함께 <code>App</code> 프로토콜이 <code>AppDelegate</code> 를 대체하게 되었습니다.</p>
<p>그에 따라서 생명주기의 활용법 또한 변경이 되었는데요 </p>
<p>SwiftUI 프로젝트를 하게된다면 당연히 바뀐 생명주기도 다룰 줄 알아야겠죠 ㅎ</p>
<p>하나씩 살펴 보겠습니다.</p>
<pre><code class="language-swift">@main
struct MyApp: App //- App protocol 을 채택하는 struct 의 이름은 자유로우나 일반적으로 ~~App 이라고 짓는다.
{
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}</code></pre>
<h1 id="1-main">1. <code>@main</code></h1>
<p>앱의 시작점인 <strong>Entry Points 를 지정하는 역할</strong>을 합니다.</p>
<p>기존 <code>AppDelegate</code> 에서 진행되었던 리소스 초기화는 
SwiftUI 에 들어와서는 <code>init()</code> 을 사용하게 됩니다</p>
<pre><code class="language-swift">@main
struct MyApp: App 
{
        // 앱이 실행됨과 동시에 첫 view 생성전 미리 구성해야하는 즉 loading 과정 구현
    let persistenceController = PersistenceController.preview

        init() {
                //앱 실행 전 초기화 작업 or 필요한 네트워킹
                //앱이 실행되기도 전에 실행해야하는 서비스는 이렇게 구현
                //앱을 실행하기위해 구성해야 하는 항목들은 init() 에 구현
        }

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}</code></pre>
<ul>
<li><code>body</code> 프로퍼티에는 <code>WindowGroup</code> 객체가 자리잡고 있다. 이 객체는 cross-platform struct 로 여러개의 윈도우를 가진 scene 을 나타낸다. macOS iOS 등등에서 사용할 수 있으며, 앱내의 view 들의 계층구조를 담아내는 container 라고 생각하면된다.</li>
<li><code>WindowGroup</code> 내에는 <strong>앱의 첫번째 <code>view</code> 를 선언</strong>해준다. 위 코드에서는 <code>BookList()</code> 가 앱 실행시 처음으로 띄워질 <code>view</code> 가 된다.</li>
</ul>
<h3 id="appdelegate-대체-사항">AppDelegate 대체 사항</h3>
<p><code>AppDelegate</code>에서 사용하던</p>
<ul>
<li><code>applicationDidBecomeActive</code> 백그라운드 → 포그라운드 이동 후</li>
<li><code>applicationWillResignActive</code> 포그라운드 →  백그라운드 이동 전</li>
<li><code>applicationDidEnterBackground</code> 포그라운드 → 백그라운드 이동 후</li>
</ul>
<p>들이 다른 방식으로 대체되었습니다</p>
<hr>
<h1 id="2-swiftui-lifecycle-이벤트에-반응하기-🌟🌟">2. SwiftUI lifecycle 이벤트에 반응하기 🌟🌟</h1>
<p><code>@Environment</code> 프로퍼티 래퍼를 사용해 현재 앱의 생명주기 상태를 가져올 수 있습니다.</p>
<p>이렇게 가져온 값에 대해 <code>.onChange(of:)</code> 메서드를 사용해서 해당 생명주기의 대응할 수 있게 됩니다</p>
<pre><code class="language-swift">import SwiftUI

@main
struct MyApp: App {

    @Environment(\.scenePhase) var scenePhase

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .onChange(of: scenePhase) { newScenePhase in
            switch newScenePhase {
            case .active:
                print(&quot;App is active&quot;)
            case .inactive:
                print(&quot;App is inactive&quot;)
            case .background:
                print(&quot;App is in background&quot;)
            @unknown default:
                print(&quot;unexpected Value&quot;)
            }
        }
    }
}</code></pre>
<p><a href="https://s3-us-west-2.amazonaws.com/secure.notion-static.com/ce1eaf09-c515-48b8-9586-33c75b002528/%E1%84%92%E1%85%AA%E1%84%86%E1%85%A7%E1%86%AB_%E1%84%80%E1%85%B5%E1%84%85%E1%85%A9%E1%86%A8_2022-04-03_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_6.27.06.mov">화면 기록 2022-04-03 오후 6.27.06.mov</a></p>
<h2 id="scenephase-🌟">scenePhase 🌟</h2>
<p><code>ScenePhase</code> enum 은 3개의 상태를 나타냅니다.</p>
<h3 id="active"><code>active</code></h3>
<p>앱이 포그라운로 올라왔을 때</p>
<h3 id="inactive"><code>inactive</code></h3>
<p>앱 스위칭 ing</p>
<blockquote>
<p>디바이스 화면 전경에 앱이 존재하지만 일시정지 상태이며, 이벤트도 받을 수 없는 상태를 의미</p>
</blockquote>
<h3 id="background"><code>background</code></h3>
<p>아이폰 홈 화면으로 나왔을 때</p>
<hr>
<h1 id="3-uikit-패키지-사용을-위해-appdelegate-가-필요할-때">3. UIKit 패키지 사용을 위해 <code>AppDelegate</code> 가 필요할 때</h1>
<h3 id="→--uiapplicationdelegateadaptor-프로퍼티-사용">→  <code>@UIApplicationDelegateAdaptor</code> 프로퍼티 사용</h3>
<p>SwiftUI 에서도 그래도 <code>AppDelegate</code> 를 사용해야 하는 순간이 온다. 바로 <code>UIkit</code> 에 머물러있는 Swift 패키지들을 사용할 때(특히 FireBase🔥) 인데,,, (작성일 기준입니다)</p>
<pre><code class="language-swift">@main 
struct MyApp: App { 
        init() { 
                FirebaseApp.configure() // 🚨 불가능 
        } 
        var body: some Scene { 
                WindowGroup { 
                        ContentView() 
                } 
        } 
}</code></pre>
<p>이는 생각보다 간단히 해결이 가능합니다</p>
<pre><code class="language-swift">//MyApp.Swift 
import SwiftUI

class AppDelegate: NSObject, UIApplicationDelegate
{
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -&gt; Bool
    {
                //패키지 초기화 작업 수행

        FirebaseApp.configure() //👍🏻

        return true
    }   
}

@main
struct MyApp: App
{
        //아래 프로퍼티 래퍼를 활용
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}
</code></pre>
<ol>
<li><code>AppDelegate</code> 클래스 선언<ol>
<li><code>NSObject</code> 프로토콜 준수</li>
<li><code>UIApplicationDelegate</code> 프로토콜 준수 → Delegation 함수 사용가능 </li>
</ol>
</li>
<li><code>@UIApplicationDelegateAdaptor</code> 프로퍼티 래퍼 사용 <code>AppDelegate</code> 클래스 타입 전달</li>
</ol>
<p>이렇게 해서 아직 <code>App</code> protocol 에서 사용이 불가능한 기능들을 사용할 수 있답니다. </p>
<p>Ex. <code>remote push notification</code> </p>
<p>중요한 점은 <code>AppDelegate</code> 를 어떤용도로든 사용할 수 있게 되지만, </p>
<p>이는 어디까지나 <code>App</code> 구조체 내부에서 사용이 불가능한 기능에만 사용하는 것이 현명하겠네요 ㅎ</p>
<pre><code class="language-swift">@main
struct MyApp: App
{
    init() {
        //앱 초기 설정 작업
    }

    var body: some Scene 
}</code></pre>
<p>이렇게 깔끔한데 굳이 <code>AppDelegate</code> 를 사용할 필요가 없지 않을까요? 😃</p>
<p>오늘 포스팅은 여기서 마무리하겠습니다 감사합니다 😊</p>
]]></description>
        </item>
    </channel>
</rss>