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

Xcode Previews でUIKitベースのアプリ開発を効率化する - iOSDC Ja...

kenmaz
September 21, 2020
13k

Xcode Previews でUIKitベースのアプリ開発を効率化する - iOSDC Japan 2020

iOSDC Japan 2020 で発表した内容です。
https://fortee.jp/iosdc-japan-2020/proposal/a88be712-b87a-4d87-bc6d-2579c2ce9b35

動画はこちら
https://www.youtube.com/watch?v=NnqGmeR0uqE

発表者ノート付きPDFは以下からダウンロードできます。
https://drive.google.com/file/d/1GSLyYTSxxW5Q-9C7JFUlYBRvbNdTmUml/view?usp=sharing

kenmaz

September 21, 2020
Tweet

More Decks by kenmaz

Transcript

  1. 6*,JUϕʔεͷΞϓϦ։ൃϑϩʔ 4JNVMBUPSىಈ TUPSZCPBSEYJC 6*7JFX 6*7JFX$POUSPMMFS Start Developing iOS Apps (Swift)

    https://developer.apple.com/library/archive/referencelibrary/GettingStarted/DevelopiOSAppsSwift/BuildABasicUI.html#//apple_ref/doc/uid/TP40015214-CH5-SW1
  2. 6*,JUϕʔεͷΞϓϦ։ൃϑϩʔ 4JNVMBUPSىಈ ίʔυमਖ਼ TUPSZCPBSEYJC 6*7JFX 6*7JFX$POUSPMMFS Start Developing iOS Apps

    (Swift) https://developer.apple.com/library/archive/referencelibrary/GettingStarted/DevelopiOSAppsSwift/BuildABasicUI.html#//apple_ref/doc/uid/TP40015214-CH5-SW1
  3. 6*,JUϕʔεͷΞϓϦ։ൃϑϩʔ 4JNVMBUPSىಈ ίʔυमਖ਼ TUPSZCPBSEYJC 6*7JFX 6*7JFX$POUSPMMFS Start Developing iOS Apps

    (Swift) https://developer.apple.com/library/archive/referencelibrary/GettingStarted/DevelopiOSAppsSwift/BuildABasicUI.html#//apple_ref/doc/uid/TP40015214-CH5-SW1
  4. 6*,JUϕʔεͷΞϓϦ։ൃϑϩʔ 4JNVMBUPSىಈ ίʔυमਖ਼ TUPSZCPBSEYJC 6*7JFX 6*7JFX$POUSPMMFS Start Developing iOS Apps

    (Swift) https://developer.apple.com/library/archive/referencelibrary/GettingStarted/DevelopiOSAppsSwift/BuildABasicUI.html#//apple_ref/doc/uid/TP40015214-CH5-SW1
  5. 6*,JUϕʔεͷΞϓϦ։ൃϑϩʔ 4JNVMBUPSىಈ ίʔυमਖ਼ TUPSZCPBSEYJC 6*7JFX 6*7JFX$POUSPMMFS Start Developing iOS Apps

    (Swift) https://developer.apple.com/library/archive/referencelibrary/GettingStarted/DevelopiOSAppsSwift/BuildABasicUI.html#//apple_ref/doc/uid/TP40015214-CH5-SW1
  6. 6*,JUϕʔεͷΞϓϦ։ൃϑϩʔ 4JNVMBUPSىಈ ίʔυमਖ਼ TUPSZCPBSEYJC 6*7JFX 6*7JFX$POUSPMMFS Start Developing iOS Apps

    (Swift) https://developer.apple.com/library/archive/referencelibrary/GettingStarted/DevelopiOSAppsSwift/BuildABasicUI.html#//apple_ref/doc/uid/TP40015214-CH5-SW1 ɾɾ
  7. 6*7JFX3FQSFTFOUBCMF 6*7JFX$POUSPMMFS3FQSFTFOUBCMF public protocol UIViewControllerRepresentable : View associatedtype UIViewControllerType :

    UIViewController func makeUIViewController(context: Self.Context) -> Self.UIViewControllerType public protocol UIViewRepresentable : View { associatedtype UIViewType : UIView func makeUIView(context: Self.Context) -> Self.UIViewType w 6*7JFX6*7JFX$POUSPMMFSΛ4XJGU6*ͷ7JFXͱͯ͠ѻ͑Δ
  8. 1SFWJFX1SPWJEFS public protocol PreviewProvider { associatedtype Previews : View static

    var previews: Self.Previews { get } w 9DPEFʹϓϨϏϡʔΛදࣔͤ͞ΔͨΊͷಛघͳϓϩτίϧ w 9DPEFͰ1SFWJFX1SPWJEFSͷίʔυΛ։͘ͱɺ9DPEF͕ͦΕΛݕ஌ɺ QSFWJFXTͰࢦఆͨ͠7JFXΛ$BOWBTͰϓϨϏϡʔදࣔ
  9. class HelloView: UIView { private lazy var button: UIButton =

    ... ྫɿ7JFXͷ࣮૷ struct Input { let backgroundColor: UIColor let buttonTitle: String let isButtonEnabled: Bool } func apply(input: Input) { backgroundColor = input.backgroundColor button.setTitle(input.buttonTitle) button.isEnabled = input.isButtonEnabled } }
  10. class HelloView: UIView { private lazy var button: UIButton =

    ... ྫɿ7JFXͷ࣮૷ struct Input { let backgroundColor: UIColor let buttonTitle: String let isButtonEnabled: Bool } func apply(input: Input) { backgroundColor = input.backgroundColor button.setTitle(input.buttonTitle) button.isEnabled = input.isButtonEnabled } } IUUQTEFWFMPQFSBQQMFDPNWJEFPTQMBZXXED 4USVDUVSFZPVSBQQGPS4XJGU6*QSFWJFXT 1SFWJFXBCMFͳ7JFX 1SFWJFX 7JFX 7JFX.PEFM "QQ 7JFXͷঢ়ଶΛ஫ೖ 1SFWJFX࣌ ΞϓϦέʔγϣϯ࣮ߦ࣌
  11. final class HelloViewRepresentable: UIViewRepresentable { let input: HelloView.Input init(input: HelloView.Input)

    { self.input = input } func makeUIView(context: Context) -> HelloView { let view = HelloView() view.apply(input: input) return view } ... } ྫɿ6*7JFX3FQSFTFOUBCMFͷ࣮૷
  12. ྫɿ1SFWJFX1SPWJEFSͷ࣮૷ struct HelloView_Preview: PreviewProvider { static var previews: some View

    { Group { HelloViewRepresentable(input: .init( backgroundColor: .white, buttonTitle: "Click Me", isButtonEnabled: true)) HelloViewRepresentable(input: .init( backgroundColor: .yellow, buttonTitle: "Warning", isButtonEnabled: true)) HelloViewRepresentable(input: .init( backgroundColor: .lightGray, buttonTitle: "Disabled", isButtonEnabled: false)) } } }
  13. 1SFWJFXίʔυͷ഑ஔઌλʔήοτ λʔήοτͷछผ 1SFWJFXͷදࣔ 1SFWJFXͷσόοά "QQMJDBUJPO ✅ ✅ 'SBNFXPSL ✅ ❌

    4UBUJD-JCSBSZ ❌ ❌ 1SFWJFX༻ͷ"QQMJDBUJPO5BSHFUΛ࡞੒ͯ͠1SFWJFXίʔυΛ௥Ճ
  14. + #if DEBUG && canImport(SwiftUI) import SwiftUI class HelloViewRepresentable: UIViewRepresentable

    { ... } + @available(iOS 13, *) struct HelloView_Preview: PreviewProvider { ... } + #endif J043FMFBTF/PUFT4XJGU6*,OPXO*TTVF  ˞ʣ9DPEFͷόά w  9DPEF࢖༻࣌ͷΈ 0UIFS-JOLFS'MBHʹXFBL@GSBNFXPSL4XJGU6*Λ௥Ճ J04ະຬͷ؀ڥͰ͸ w 4XJGU6*ϞδϡʔϧͷΠϯϙʔτ w 1SFWJFXίʔυͷϩʔυ ͕εΩοϓ͞ΕΔ J04ະຬͷαϙʔτ ରԠ ϚΫϩΛ࢖ͬͯ1SFWJFXίʔυΛϏϧυ͔Βআ֎͢Δ
  15. iOSDC2019 / ϞόΠϧܾࡁΞϓϦͷ࡞Γํ / How to develop a mobile payment

    app IUUQTTQFBLFSEFDLDPNLFONB[IPXUPEFWFMPQBNPCJMFQBZNFOUBQQ w ϝϧΧϦΞϓϦ͸ػೳ͝ͱʹ'SBNFXPSLʹ ෼ׂ͞Ε͍ͯΔ w ֤'SBNFXPSLϓϩδΣΫτ͝ͱͷ 4BOECPYΞϓϦΛ࢖ͬͯ௨ৗ͸։ൃ ϝϧϖΠͰͷߏ੒ ˠ1SFWJFXίʔυ͸4BOECPYΞϓϦͷ λʔήοτʹ௥Ճ
  16. %ZOBNJD.FUIPE3FQMBDFNFOU w EZOBNJDΩʔϫʔυΛ෇͚ͨϝϯόʔͷ࣮૷Λɺ@EZOBNJD3FQMBDFNFOUଐੑΛ෇͚ ͨϝϯόʔͷ࣮૷ʹࠩ͠ସ͑Δػೳ 4XJGUd public class Thing { public

    dynamic var someNumber: Int { return 10 } } IUUQTHJUIVCDPNBQQMFTXJGUQVMM extension Thing { @_dynamicReplacement(for: someNumber) var newNumber: Int { return 43 } }
  17. %ZOBNJD.FUIPE3FQMBDFNFOU w EZOBNJDΩʔϫʔυΛ෇͚ͨϝϯόʔͷ࣮૷Λɺ@EZOBNJD3FQMBDFNFOUଐੑΛ෇͚ ͨϝϯόʔͷ࣮૷ʹࠩ͠ସ͑Δػೳ 4XJGUd public class Thing { public

    dynamic var someNumber: Int { return 10 } } IUUQTHJUIVCDPNBQQMFTXJGUQVMM extension Thing { @_dynamicReplacement(for: someNumber) var newNumber: Int { return 43 } } print(Thing().someNumber) //43
  18. // HelloViewPreview.swift struct HelloView_Preview: PreviewProvider { static var previews: some

    View { Group { HelloViewRepresentable( input: .init( backgroundColor: .white, buttonTitle: "Click Me", isButtonEnabled: true ) ) } } } // HelloViewPreview.3.preview-thunk.swift extension HelloView_Preview { @_dynamicReplacement(for: previews) private static var __preview__previews: some View { #sourceLocation(file: "/Users/kenmaz/Projects/iOSDC2020App/iOSDC2020App/HelloViewPreview.swift", line: 26) AnyView(__designTimeSelection(Group { __designTimeSelection(HelloViewRepresentable( input: .init( backgroundColor: .white, buttonTitle: __designTimeString( "#3084.[2].[0].property.[0].[0].arg[0].value.[0].arg[0].value.arg[1].value.[0].value", fallback: "Click Me"), isButtonEnabled: __designTimeBoolean( "#3084.[2].[0].property.[0].[0].arg[0].value.[0].arg[0].value.arg[2].value", fallback: true) ) ), "#3084.[2].[0].property.[0].[0].arg[0].value.[0]") }, "#3084.[2].[0].property.[0].[0]")) #sourceLocation() }
  19. // HelloViewPreview.swift struct HelloView_Preview: PreviewProvider { static var previews: some

    View { Group { HelloViewRepresentable( input: .init( backgroundColor: .white, buttonTitle: "Click Me", isButtonEnabled: true ) ) } } } // HelloViewPreview.3.preview-thunk.swift extension HelloView_Preview { @_dynamicReplacement(for: previews) private static var __preview__previews: some View { #sourceLocation(file: "/Users/kenmaz/Projects/iOSDC2020App/iOSDC2020App/HelloViewPreview.swift", line: 26) AnyView(__designTimeSelection(Group { __designTimeSelection(HelloViewRepresentable( input: .init( backgroundColor: .white, buttonTitle: __designTimeString( "#3084.[2].[0].property.[0].[0].arg[0].value.[0].arg[0].value.arg[1].value.[0].value", fallback: "Click Me"), isButtonEnabled: __designTimeBoolean( "#3084.[2].[0].property.[0].[0].arg[0].value.[0].arg[0].value.arg[2].value", fallback: true) ) ), "#3084.[2].[0].property.[0].[0].arg[0].value.[0]") }, "#3084.[2].[0].property.[0].[0]")) #sourceLocation() } ˞EZOBNJDΩʔϫʔυ͸Ϗϧυ࣌ʹ௥Ճ͞ΕΔʢΆ͍ʣ
  20. // HelloViewPreview.swift struct HelloView_Preview: PreviewProvider { static var previews: some

    View { Group { HelloViewRepresentable( input: .init( backgroundColor: .white, buttonTitle: "Click Me", isButtonEnabled: true ) ) } } } // HelloViewPreview.3.preview-thunk.swift extension HelloView_Preview { @_dynamicReplacement(for: previews) private static var __preview__previews: some View { #sourceLocation(file: "/Users/kenmaz/Projects/iOSDC2020App/iOSDC2020App/HelloViewPreview.swift", line: 26) AnyView(__designTimeSelection(Group { __designTimeSelection(HelloViewRepresentable( input: .init( backgroundColor: .white, buttonTitle: __designTimeString( "#3084.[2].[0].property.[0].[0].arg[0].value.[0].arg[0].value.arg[1].value.[0].value", fallback: "Click Me"), isButtonEnabled: __designTimeBoolean( "#3084.[2].[0].property.[0].[0].arg[0].value.[0].arg[0].value.arg[2].value", fallback: true) ) ), "#3084.[2].[0].property.[0].[0].arg[0].value.[0]") }, "#3084.[2].[0].property.[0].[0]")) #sourceLocation() }
  21. // HelloViewPreview.swift struct HelloView_Preview: PreviewProvider { static var previews: some

    View { Group { HelloViewRepresentable( input: .init( backgroundColor: .white, buttonTitle: "Click Me", isButtonEnabled: true ) ) } } } // HelloViewPreview.3.preview-thunk.swift extension HelloView_Preview { @_dynamicReplacement(for: previews) private static var __preview__previews: some View { #sourceLocation(file: "/Users/kenmaz/Projects/iOSDC2020App/iOSDC2020App/HelloViewPreview.swift", line: 26) AnyView(__designTimeSelection(Group { __designTimeSelection(HelloViewRepresentable( input: .init( backgroundColor: .white, buttonTitle: __designTimeString( "#3084.[2].[0].property.[0].[0].arg[0].value.[0].arg[0].value.arg[1].value.[0].value", fallback: "Click Me"), isButtonEnabled: __designTimeBoolean( "#3084.[2].[0].property.[0].[0].arg[0].value.[0].arg[0].value.arg[2].value", fallback: true) ) ), "#3084.[2].[0].property.[0].[0].arg[0].value.[0]") }, "#3084.[2].[0].property.[0].[0]")) #sourceLocation() }
  22. ࢀߟจݙ w "QQMF%FWFMPQFS4UBSU%FWFMPQJOHJ04"QQT 4XJGU  w 88%$4USVDUVSFZPVSBQQGPS4XJGU6*QSFWJFXT w 88%$.BTUFSJOH9DPEF1SFWJFXT w

    J043FMFBTF/PUFT4XJGU6*,OPXO*TTVF   w (VBSETRVBSF%FWCMPH#FIJOE4XJGU6*1SFWJFXT w LBUFJOPJHBLVLVO*OTJEF4XJGU6* )PX)PUSFMPBEJOHJNQMFNFOUFE  w BQQMFTXJGU%ZOBNJDGVODUJPOSFQMBDFNFOU w J04%$ϞόΠϧܾࡁΞϓϦͷ࡞Γํ)PXUPEFWFMPQBNPCJMFQBZNFOUBQQ w NFSDBSJFOHJOFFSJOH9DPEF1SFWJFXTΛ༻͍ͨ6*,JUϕʔεͷϓϩδΣΫτͷ։ൃޮ཰Խ