Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Xcode Previews - IB없이 개발하기

VCNC
November 12, 2019

Xcode Previews - IB없이 개발하기

모바일 개발자에게 화면을 예쁘게 만드는 것은 매우 중요한 업무 중 하나이다. 하지만 이를 달성하기 위해서는 매번 구현, 빌드, 전달, 해당화면 띄우기 등 너무나 많은 시간과 노력이 요구된다. 그뿐만 아니라 잘 동작하는 앱을 만들기 위해서는 dynamic content size, 다크모드 등 다양한 환경과 여러 디바이스에서 확인해야 한다. 이러한 문제를 해결하기 위해 Xcode11에서 Previews 라는 기능이 추가되었다. 이번 세션에서는 Xcode Previews 에 대한 소개와 동작 원리를 파악하고 간단한 데모를 통해 해당 기능을 이해해보려고 한다. 더 나아가 SwiftUI 를 사용하지 않는 기존의 서비스에서 Previews 기능을 사용하는 방법을 알아보고, 이를 이용해 타다가 어떻게 UI 작업을 진행하는지 공유하고자 한다.

VCNC

November 12, 2019
Tweet

More Decks by VCNC

Other Decks in Programming

Transcript

  1. 7$/$
    ӣթഅ
    9DPEF1SFWJFXT
    ఋ׮о*#ܳߡܽ੉ਬ

    View Slide

  2. 7$/$
    ӣթഅ
    9DPEF1SFWJFXT
    ఋ׮о*#ܳߡܽ੉ਬ*#হ੉ѐߊೞӝ

    View Slide

  3. Xcode Previews

    Xcode Previews in UIKit

    Problems with Storyboard, XIB

    UI programming using Xcode Previews
    $POUFOUT

    View Slide

  4. Xcode Previews

    View Slide

  5. • Xcode 11

    • MacOS Catalina
    9DPEF1SFWJFXT

    View Slide

  6. #BDLHSPVOE
    `
    Mastering Xcode Previews @WWDC 2019

    View Slide

  7. #BDLHSPVOE
    Mastering Xcode Previews @WWDC 2019

    View Slide

  8. #BDLHSPVOE
    Mastering Xcode Previews @WWDC 2019

    View Slide

  9. • Configuring

    • Building

    • Running

    • Navigating

    • Getting to the state
    #BDLHSPVOE

    View Slide

  10. #BDLHSPVOE
    Mastering Xcode Previews @WWDC 2019

    View Slide

  11. #BDLHSPVOE
    Mastering Xcode Previews @WWDC 2019

    View Slide

  12. #BDLHSPVOE
    Mastering Xcode Previews @WWDC 2019

    View Slide

  13. Time consuming

    View Slide

  14. 9DPEF1SFWJFXT
    Mastering Xcode Previews @WWDC 2019

    View Slide

  15. struct PriceView_Preview: PreviewProvider {
    static var previews: some View {
    PriceView(price:…)
    .previewLayout(.sizeThatFits)
    .environment(\.colorScheme, .dark)
    }
    }

    View Slide

  16. 4BNQMF

    View Slide

  17. Demo

    View Slide

  18. Previews ز੘ ਗܻ

    View Slide

  19. • 2 Main Components

    • A preview build

    • Xcode extension ( live view )
    9DPEF1SFWJFXT

    View Slide

  20. • Normal Build

    • Xcode scheme, build settings ਸ ٮܰח ੌ߈੸ੋ ࠽٘

    • Preview Build

    • ୶о compile optionҗ ೣԋ normal buildܳ ഛ੢ೠ ߹ب੄
    ࠽٘

    9DPEF1SFWJFXT

    View Slide

  21. • Xcode build artifact ী normal ࠽٘৬ ೣԋ ઓ੤

    • Object file, artifact ҕਬ

    • Additional Options

    • Interactive UI ࣻ੿ / dynamic behavior ೲਊ

    • ਗࠄ ౵ੌ ৻ ୶о੸ੋ ࣗझ ౵ੌ ࢤࢿ

    • ਗࠄ ࣗझ ௏٘ܳ ࠗ࠙੸ਵ۽ UIܳ ࣻ੿ೡ ࣻ ੓ח ௏٘۽ ߸҃
    by method swizzling
    1SFWJFX#VJME

    View Slide

  22. 1SFWJFX#VJME

    View Slide

  23. struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
    ContentView()
    }
    }

    View Slide

  24. @_private(sourceFile: "ContentView.swift") import LetSwiftPreview
    import SwiftUI
    import SwiftUI
    extension ContentView_Previews {
    @_dynamicReplacement(for: previews) private static var
    __preview__previews: some View {
    #sourceLocation(file: "/Users/nate/Documents/samples/SwiftUI/
    LetSwiftPreview/LetSwiftPreview/LetSwiftPreview/ContentView.swift", line: 19)
    AnyView(__designTimeSelection(ContentView(), "#40077.[2].[0].property.[0].
    [0]"))
    #sourceLocation()
    }
    }
    typealias ContentView = LetSwiftPreview.ContentView
    typealias ContentView_Previews = LetSwiftPreview.ContentView_Previews

    View Slide

  25. • Xcodeо пࢤࢿػ ౵ੌਸ ة݀੸ੋ dynamic
    library۽ ࠽٘

    • library -> Preview applicationਵ۽ ۽٘
    9DPEF1SFWJFXT

    View Slide

  26. Xcode Previews

    in UIKit

    View Slide

  27. • UIViewController ب Previews ࢎਊ оמ

    • UIViewControllerRepresentable protocol
    9DPEF1SFWJFXTJO6*,JU
    func makeUIViewController(context: Self.Context)
    -> Self.UIViewControllerType
    func updateUIViewController(
    _ uiViewController: Self.UIViewControllerType,
    context: Self.Context
    )

    View Slide

  28. • UIView ب Previews ࢎਊ оמ

    • UIViewRepresentable protocol
    9DPEF1SFWJFXTJO6*,JU
    func makeUIView(context: Self.Context)
    -> Self.UIViewType
    func updateUIView(
    _ uiView: Self.UIViewType, context: Self.Context
    )

    View Slide

  29. • Previews ীࢲ ࢎਊೞӝ ਤೠ protocol

    • UIKit -> SwiftUI

    • SwiftUI ܳ ࢎਊೡ ࣻ ੓ח ജ҃ীࢲ݅ ੉ਊ оמ
    6*7JFX3FQSFTFOUBCMF

    View Slide

  30. • Debug ࠽٘ ࢸ੿ ߸҃

    • iOS Deployment target : iOS13

    • canImport, @available
    9DPEF1SFWJFXTJ04

    View Slide

  31. #if canImport(SwiftUI) && DEBUG
    import SwiftUI
    @available(iOS 13.0, *)
    struct PriceView_Preview: UIViewRepresentable,
    PreviewProvider {
    let price: Price
    func makeUIView(context: Self.Context) -> PriceView {
    PriceView().also { $0.price = price }
    }
    func updateUIView(_ view: PriceView,
    context: Self.Context) {

    }
    static var previews: some View {
    PriceView_Preview(price: …)
    }
    }
    #endif

    View Slide

  32. Demo

    View Slide

  33. Problems with
    Storyboard, XIB

    View Slide

  34. • XML

    • ੍Ѣա ࣻ੿ೡ ࣻ হח XML

    • ࣻ ߔ઴ ੉࢚੄ ௾ ౵ੌ
    3FBEBCJMJUZ

    View Slide

  35. 3FBEBCJMJUZ

    View Slide

  36. • XML

    • ੍Ѣա ࣻ੿ೡ ࣻ হח XML

    • ࣻ ߔ઴ ੉࢚੄ ௾ ౵ੌ

    • э਷ Viewܳ ࣻ੿ೞݶ?

    • PR
    3FBEBCJMJUZ

    View Slide

  37. View Slide

  38. • ౵ੌ੉ ੼੼ ழ૗

    • যו࢜ ࣻभ ѐ੄ झ௼ܽ੉ ೞա੄ ౵ੌী..

    • Source control ߄Ե ٸ݃׮
    4UPSZCPBSET

    View Slide

  39. • IBח Swift ௏٘ܳ ੜ ݽܴ(vice versa)

    • IBOutlet, IBAction ਸ ా೧ োѾ

    • IBীࢲ Viewܳ ૑ਕب ௏٘ח Ӓ؀۽…

    • Cell in TableView ীࢲ identifier۽ оઉয়ӝ
    (Typecast ೙ࣻ )
    4XJGU*OUFSGBDFCVJMEFS

    View Slide

  40. • Color asset ਵ۽ ࢸ੿ೠ ࢝੉ viewDidLoad,
    viewWillAppear, awakeFromNib ࣻ੿ ࠛо

    • Callstack

    • viewDidLoad(viewWillAppear) ->
    traitCollectionDidChanged ->

    ੿੄ػ asset color۽ trait collection ੸ਊ
    *OUFSGBDFCVJMEFSCVHT

    View Slide

  41. UI programming

    using Xcode Previews

    View Slide

  42. • Visualization

    • Image > Text

    • Views, Constraints ҳઑ ౵ঈ
    1SPHSBNNBUJDBMMZ6*

    View Slide

  43. • Verbose

    • Complex

    • Difficult to read
    1SPHSBNNBUJDBMMZ6*

    View Slide

  44. header.translatesAutoresizingMaskIntoConstraints = false
    let cn1 = NSLayoutConstraint(item: header,
    attribute: .leading, relatedBy: .equal, toItem: view,
    attribute: .leading, multiplier: 1.0, constant: 20)
    let cn2 = NSLayoutConstraint(item: header,
    attribute: .trailing, relatedBy: .equal, toItem: view,
    attribute: .trailing, multiplier: 1.0, constant: -20)
    let cn3 = NSLayoutConstraint(item: header,
    attribute: .height, relatedBy: .equal, toItem: nil,
    attribute: .notAnAttribute, multiplier: 1.0, constant: 60)
    let cn4 = NSLayoutConstraint(item: header, attribute: .top,
    relatedBy: .equal, toItem: view, attribute: .top,
    multiplier: 1.0, constant: 20)
    view.addConstraints([cn1, cn2, cn3, cn4])

    View Slide

  45. • Layout libraries

    • PureLayout, Cartogrphy, Snapkit …
    1SPHSBNNBUJDBMMZ6*

    View Slide

  46. header.snp.makeConstraints { maker in
    maker.leading.trailing.top.equalToSuperview().inset(20)
    maker.height.equalTo(60)
    }

    View Slide

  47. • ૒ҙ੸ਵ۽ ചݶਸ ੉೧ೞӝ য۰਑
    1SPHSBNNBUJDBMMZ6*

    View Slide

  48. blackView.snp.makeConstraints { make in
    make.center.equalTo(view)
    make.size.equalTo(CGSize(width: 100, height: 100))
    }
    redView.snp.makeConstraints { make in
    make.top.equalTo(blackView.snp.bottom).offset(20.0)
    make.right.equalTo(blackView.snp.left).offset(-20.0)
    make.size.equalTo(CGSize(width: 100, height: 100))
    }
    yellowView.snp.makeConstraints { make in
    make.top.equalTo(blackView.snp.bottom).offset(20.0)
    make.left.equalTo(blackView.snp.right).offset(20.0)
    make.size.equalTo(CGSize(width: 100, height: 100))
    }
    blueView.snp.makeConstraints { make in
    make.bottom.equalTo(blackView.snp.top).offset(-20.0)
    make.left.equalTo(blackView.snp.right).offset(20.0)
    make.size.equalTo(CGSize(width: 100, height: 100))
    }
    greenView.snp.makeConstraints { make in
    make.bottom.equalTo(blackView.snp.top).offset(-20.0)
    make.right.equalTo(blackView.snp.left).offset(-20.0)
    make.size.equalTo(CGSize(width: 100, height: 100))

    View Slide

  49. • ૒ҙ੸ਵ۽ ചݶਸ ੉೧ೞӝ য۰਑

    • View ҳઑܳ ౵ঈೞӝ ਤ೧ ੹୓ ௏٘ ౵ঈ ೙ਃ

    • View Hierarchy ↑ ➡ Time ↑

    • Layout library੄ ೠ҅
    1SPHSBNNBUJDBMMZ6*

    View Slide

  50. • makeConstraints: э਷ ࠗݽܳ ыח Views݅ оמ

    • addSubview / makeConstraints ௏٘ ܻ࠙

    • Nested ҳઑੌ ٸ children ਸ ݢ੷ ੘ࢿ
    4OBQ,JU

    View Slide

  51. IBKit

    https://github.com/glwithu06/IBKit

    View Slide

  52. • Visualization

    • ૒ҙ੸ੋ ௏٘ ੉೧

    *#,JU

    View Slide

  53. • Visualization -> Xcode Previews

    • ૒ҙ੸ੋ ௏٘ ੉೧ -> Declarative झఋੌ

    *#,JU

    View Slide

  54. UIView() {
    UIView().identifier(“upper")
    .backgroundColor(.cyan)
    .makeConstraints {
    $0.top.left.right.equalToSuperview()
    $0.height.equalTo(30)
    }
    UIView().identifier("lower")
    .backgroundColor(.blue)
    .makeConstraints {
    $0.top.equalTo($0.views.upper.bottom)
    $0.bottom.left.right.equalToSuperview()
    $0.height.equalTo(30)
    }
    }
    .identifier(“yellow")
    .assign(to: \.container, on: self)

    View Slide

  55. Demo

    View Slide

  56. • UIViewRepresentable

    • UIViewControllerRepresentable

    • Preview(root:)
    9DPEF1SFWJFXT

    View Slide

  57. Preview sample

    View Slide

  58. • @_functionBuilder

    • Swift5.1 private feature
    %FDMBSBUJWFTUZMF

    View Slide

  59. @_functionBuilder struct ViewBulder {
    static func buildBlock() -> Builder {
    BuilderImpl(children: [])
    }
    static func buildEither(first: UIView) -> Builder {
    BuilderImpl(children: [first])
    }
    static func buildEither(second: UIView) -> Builder {
    BuilderImpl(children: [second])
    }
    static func buildIf(_ view: UIView?) -> Builder {
    BuilderImpl(children: view.map { [$0] } ?? [])
    }
    static func buildBlock(_ views: UIView...) -> Builder {
    BuilderImpl(children: views)
    }

    }

    View Slide

  60. • SnapKit ӝ߈ Layout

    • addSubview -> SnapKit.makeConstraints

    • Dynamic member lookup
    -BZPVU
    func makeConstraints(_ closure:
    @escaping (ConstraintMaker) -> Void) -> Self

    View Slide

  61. .makeConstraints {
    $0.left.right.equalToSuperview().inset(50)
    $0.top.equalTo($0.views.description.bottom).offset(30)
    $0.height.equalTo(30).priority(ConstraintPriority.low)
    }

    View Slide

  62. • KeyPath Binding
    "TTJHOWJFXT
    func assign(
    to keyPath: ReferenceWritableKeyPath,
    on object: Root
    ) -> Self

    View Slide

  63. Wrap up

    View Slide

  64. • PreviewProvider protocol

    • Group / ForEach

    • previewLayout / previewDevice / environment

    • Development Assets

    • Pinning
    9DPEF1SFWJFXT

    View Slide

  65. • Preview Build

    • Derived source code

    • Preview library
    9DPEF1SFWJFXT

    View Slide

  66. • UIViewControllerRepresentable

    • UIViewRepresentable

    • makeUIView / updateUIView
    • canImport(SwiftUI)
    9DPEF1SFWJFXTJO6*,JU

    View Slide

  67. • Readability

    • Maintenance (Storyboard)

    • Swift <-> Interface Builder

    • Interface Builder bugs
    4UPSZ#PBSE 9*#

    View Slide

  68. • Visualization

    • Intuitive

    • IBKit
    1SPHSBNNBUJDBMMZ6*

    View Slide

  69. 8FsSFIJSJOH

    View Slide

  70. View Slide