Pro Yearly is on sale from $80 to $50! »

Xcode Previews - IB없이 개발하기

6a11050c8147e4f5fbf2637907c27964?s=47 VCNC
November 12, 2019

Xcode Previews - IB없이 개발하기

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

6a11050c8147e4f5fbf2637907c27964?s=128

VCNC

November 12, 2019
Tweet

Transcript

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

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

  3. Xcode Previews Xcode Previews in UIKit Problems with Storyboard, XIB

    UI programming using Xcode Previews $POUFOUT
  4. Xcode Previews

  5. • Xcode 11 • MacOS Catalina 9DPEF1SFWJFXT

  6. #BDLHSPVOE ` Mastering Xcode Previews @WWDC 2019

  7. #BDLHSPVOE Mastering Xcode Previews @WWDC 2019

  8. #BDLHSPVOE Mastering Xcode Previews @WWDC 2019

  9. • Configuring • Building • Running • Navigating • Getting

    to the state #BDLHSPVOE
  10. #BDLHSPVOE Mastering Xcode Previews @WWDC 2019

  11. #BDLHSPVOE Mastering Xcode Previews @WWDC 2019

  12. #BDLHSPVOE Mastering Xcode Previews @WWDC 2019

  13. Time consuming

  14. 9DPEF1SFWJFXT Mastering Xcode Previews @WWDC 2019

  15. struct PriceView_Preview: PreviewProvider { static var previews: some View {

    PriceView(price:…) .previewLayout(.sizeThatFits) .environment(\.colorScheme, .dark) } }
  16. 4BNQMF

  17. Demo

  18. Previews ز੘ ਗܻ

  19. • 2 Main Components • A preview build • Xcode

    extension ( live view ) 9DPEF1SFWJFXT
  20. • Normal Build • Xcode scheme, build settings ਸ ٮܰח

    ੌ߈੸ੋ ࠽٘ • Preview Build • ୶о compile optionҗ ೣԋ normal buildܳ ഛ੢ೠ ߹ب੄ ࠽٘ 9DPEF1SFWJFXT
  21. • Xcode build artifact ী normal ࠽٘৬ ೣԋ ઓ੤ •

    Object file, artifact ҕਬ • Additional Options • Interactive UI ࣻ੿ / dynamic behavior ೲਊ • ਗࠄ ౵ੌ ৻ ୶о੸ੋ ࣗझ ౵ੌ ࢤࢿ • ਗࠄ ࣗझ ௏٘ܳ ࠗ࠙੸ਵ۽ UIܳ ࣻ੿ೡ ࣻ ੓ח ௏٘۽ ߸҃ by method swizzling 1SFWJFX#VJME
  22. 1SFWJFX#VJME

  23. struct ContentView_Previews: PreviewProvider { static var previews: some View {

    ContentView() } }
  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
  25. • Xcodeо пࢤࢿػ ౵ੌਸ ة݀੸ੋ dynamic library۽ ࠽٘ • library

    -> Preview applicationਵ۽ ۽٘ 9DPEF1SFWJFXT
  26. Xcode Previews in UIKit

  27. • UIViewController ب Previews ࢎਊ оמ • UIViewControllerRepresentable protocol 9DPEF1SFWJFXTJO6*,JU

    func makeUIViewController(context: Self.Context) -> Self.UIViewControllerType func updateUIViewController( _ uiViewController: Self.UIViewControllerType, context: Self.Context )
  28. • UIView ب Previews ࢎਊ оמ • UIViewRepresentable protocol 9DPEF1SFWJFXTJO6*,JU

    func makeUIView(context: Self.Context) -> Self.UIViewType func updateUIView( _ uiView: Self.UIViewType, context: Self.Context )
  29. • Previews ীࢲ ࢎਊೞӝ ਤೠ protocol • UIKit -> SwiftUI

    • SwiftUI ܳ ࢎਊೡ ࣻ ੓ח ജ҃ীࢲ݅ ੉ਊ оמ 6*7JFX3FQSFTFOUBCMF
  30. • Debug ࠽٘ ࢸ੿ ߸҃ • iOS Deployment target :

    iOS13 • canImport, @available 9DPEF1SFWJFXTJ04
  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
  32. Demo

  33. Problems with Storyboard, XIB

  34. • XML • ੍Ѣա ࣻ੿ೡ ࣻ হח XML • ࣻ

    ߔ઴ ੉࢚੄ ௾ ౵ੌ 3FBEBCJMJUZ
  35. 3FBEBCJMJUZ

  36. • XML • ੍Ѣա ࣻ੿ೡ ࣻ হח XML • ࣻ

    ߔ઴ ੉࢚੄ ௾ ౵ੌ • э਷ Viewܳ ࣻ੿ೞݶ? • PR 3FBEBCJMJUZ
  37. None
  38. • ౵ੌ੉ ੼੼ ழ૗ • যו࢜ ࣻभ ѐ੄ झ௼ܽ੉ ೞա੄

    ౵ੌী.. • Source control ߄Ե ٸ݃׮ 4UPSZCPBSET
  39. • IBח Swift ௏٘ܳ ੜ ݽܴ(vice versa) • IBOutlet, IBAction

    ਸ ా೧ োѾ • IBীࢲ Viewܳ ૑ਕب ௏٘ח Ӓ؀۽… • Cell in TableView ীࢲ identifier۽ оઉয়ӝ (Typecast ೙ࣻ ) 4XJGU*OUFSGBDFCVJMEFS
  40. • Color asset ਵ۽ ࢸ੿ೠ ࢝੉ viewDidLoad, viewWillAppear, awakeFromNib ࣻ੿

    ࠛо • Callstack • viewDidLoad(viewWillAppear) -> traitCollectionDidChanged ->
 ੿੄ػ asset color۽ trait collection ੸ਊ *OUFSGBDFCVJMEFSCVHT
  41. UI programming using Xcode Previews

  42. • Visualization • Image > Text • Views, Constraints ҳઑ

    ౵ঈ 1SPHSBNNBUJDBMMZ6*
  43. • Verbose • Complex • Difficult to read 1SPHSBNNBUJDBMMZ6*

  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])
  45. • Layout libraries • PureLayout, Cartogrphy, Snapkit … 1SPHSBNNBUJDBMMZ6*

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

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

  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))
  49. • ૒ҙ੸ਵ۽ ചݶਸ ੉೧ೞӝ য۰਑ • View ҳઑܳ ౵ঈೞӝ ਤ೧

    ੹୓ ௏٘ ౵ঈ ೙ਃ • View Hierarchy ↑ ➡ Time ↑ • Layout library੄ ೠ҅ 1SPHSBNNBUJDBMMZ6*
  50. • makeConstraints: э਷ ࠗݽܳ ыח Views݅ оמ • addSubview /

    makeConstraints ௏٘ ܻ࠙ • Nested ҳઑੌ ٸ children ਸ ݢ੷ ੘ࢿ 4OBQ,JU
  51. IBKit https://github.com/glwithu06/IBKit

  52. • Visualization • ૒ҙ੸ੋ ௏٘ ੉೧ *#,JU

  53. • Visualization -> Xcode Previews • ૒ҙ੸ੋ ௏٘ ੉೧ ->

    Declarative झఋੌ *#,JU
  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)
  55. Demo

  56. • UIViewRepresentable • UIViewControllerRepresentable • Preview(root:) 9DPEF1SFWJFXT

  57. Preview sample

  58. • @_functionBuilder • Swift5.1 private feature %FDMBSBUJWFTUZMF

  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) } … }
  60. • SnapKit ӝ߈ Layout
 • addSubview -> SnapKit.makeConstraints • Dynamic

    member lookup -BZPVU func makeConstraints(_ closure: @escaping (ConstraintMaker) -> Void) -> Self
  61. .makeConstraints { $0.left.right.equalToSuperview().inset(50) $0.top.equalTo($0.views.description.bottom).offset(30) $0.height.equalTo(30).priority(ConstraintPriority.low) }

  62. • KeyPath Binding "TTJHOWJFXT func assign<Root>( to keyPath: ReferenceWritableKeyPath<Root, Self>,

    on object: Root ) -> Self
  63. Wrap up

  64. • PreviewProvider protocol • Group / ForEach • previewLayout /

    previewDevice / environment • Development Assets • Pinning 9DPEF1SFWJFXT
  65. • Preview Build • Derived source code • Preview library

    9DPEF1SFWJFXT
  66. • UIViewControllerRepresentable • UIViewRepresentable • makeUIView / updateUIView • canImport(SwiftUI)

    9DPEF1SFWJFXTJO6*,JU
  67. • Readability • Maintenance (Storyboard) • Swift <-> Interface Builder

    • Interface Builder bugs 4UPSZ#PBSE 9*#
  68. • Visualization • Intuitive • IBKit 1SPHSBNNBUJDBMMZ6*

  69. 8FsSFIJSJOH

  70. None