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

iOSDC RejectCon 20180915: Factoryの自動生成によりテストを書きやすくする

Takeshi Ihara
September 17, 2018

iOSDC RejectCon 20180915: Factoryの自動生成によりテストを書きやすくする

Takeshi Ihara

September 17, 2018
Tweet

More Decks by Takeshi Ihara

Other Decks in Programming

Transcript

  1. Factoryͷࣗಈੜ੒ʹΑ
    ΓςετΛॻ͖΍͘͢
    ͢Δ
    1 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  2. Takeshi Ihara
    • AbemaTV
    • Twitter: @nonchalant0303
    • GitHub: Nonchalant
    • Climbing
    !
    • Game
    "
    2 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject
    Conference days1

    View full-size slide

  3. Test
    struct User {
    let age: Int
    var isAdult: Bool {
    return age >= 20
    }
    }
    class UserTests: XCTestCase {
    func test20ࡀҎ্ͳΒ੒ਓͰ͋Δ() {
    let user = User(age: 20)
    XCTAssertTrue(user.isAdult)
    }
    func test20ࡀະຬͳΒ੒ਓͰͳ͍() {
    let user = User(age: 19)
    XCTAssertFalse(user.isAdult)
    }
    }
    3 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  4. New Property
    struct User {
    let name: String
    let age: Int
    var isAdult: Bool {
    return age >= 20
    }
    }
    class UserTests: XCTestCase {
    func test20ࡀҎ্ͳΒ੒ਓͰ͋Δ() {
    let user = User(age: 20)
    XCTAssertTrue(user.isAdult)
    }
    func test20ࡀະຬͳΒ੒ਓͰͳ͍() {
    let user = User(age: 19)
    XCTAssertFalse(user.isAdult)
    }
    }
    4 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  5. Compile Error
    struct User {
    let name: String
    let age: Int
    let birthday: Date
    var isAdult: Bool {
    return age >= 20
    }
    }
    class UserTests: XCTestCase {
    func test20ࡀҎ্ͳΒ੒ਓͰ͋Δ() {
    let user = User(age: 20) // Missing argument for parameter 'name' in call
    XCTAssertTrue(user.isAdult)
    }
    func test20ࡀະຬͳΒ੒ਓͰͳ͍() {
    let user = User(age: 19) // Missing argument for parameter 'name' in call
    XCTAssertFalse(user.isAdult)
    }
    }
    5 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  6. Fragile Test
    struct User {
    let name: String
    let age: Int
    var isAdult: Bool {
    return age >= 20
    }
    }
    class UserTests: XCTestCase {
    func test20ࡀҎ্ͳΒ੒ਓͰ͋Δ() {
    let user = User(name: "Takeshi Ihara", age: 20)
    XCTAssertTrue(user.isAdult)
    }
    func test20ࡀະຬͳΒ੒ਓͰͳ͍() {
    let user = User(name: "Takeshi Ihara", age: 19)
    XCTAssertFalse(user.isAdult)
    }
    }
    6 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  7. Factory Pattern
    ΦϒδΣΫτͷੜ੒ॲཧΛڞ௨Խ͢Δ
    struct UserFactory {
    static func provide(name: String = "", age: Int = 0) -> User {
    return User(name: name, age: age)
    }
    }
    class UserTests: XCTestCase {
    func test20ࡀҎ্ͳΒ੒ਓͰ͋Δ() {
    let user = UserFactory.provide(age: 20)
    XCTAssertTrue(user.isAdult)
    }
    func test20ࡀະຬͳΒ੒ਓͰͳ͍() {
    let user = UserFactory.provide(age: 19)
    XCTAssertFalse(user.isAdult)
    }
    }
    7 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  8. Cost of Factory
    • ςετର৅ͷΦϒδΣΫτͷ਺͚ͩFactory͕ඞཁʹͳΔ
    • ωετ͕ਂ͍ܕͩͱґଘΦϒδΣΫτͷ਺͚ͩඞཁʹͳΔ
    • FactoryͳͲΛ༻ҙ͢Δίετ͕ߴ͍ͱςετΛॻ͔ͳ͘ͳΔ
    8 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  9. FactoryProvider
    https:/
    /github.com/Nonchalant/FactoryProvider
    9 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  10. FactoryProvider
    https:/
    /github.com/Nonchalant/FactoryProvider
    • FactoryΛࣗಈੜ੒͢ΔϥΠϒϥϦ
    • Enum, Struct͕ੜ੒ର৅
    • LensΛαϙʔτ
    • ymlͰઃఆ߲໨Λఆٛ
    • GeneratorΛؚΊΔͨΊʹCocoapodsͰͷΈΠϯετʔϧՄೳ
    10 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  11. Factory (Struct)
    ܕύϥϝʔλʹStructΛࢦఆ͢Δ
    import FactoryProvider
    let user = Factory.provide()
    // User(name: "", age: 0)
    11 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  12. Factory (Enum)
    ܕύϥϝʔλʹEnumΛࢦఆ͢Δ
    import FactoryProvider
    let season = Factory.provide()
    // Season.spring
    enum Season {
    case spring
    case summer
    case automn
    case winter
    }
    12 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  13. Generated Object
    ݻఆ஋Ͱੜ੒͞ΕΔ
    !
    import FactoryProvider
    var user = Factory.provide()
    user.name = "Takeshi Ihara" // Cannot assign to property: 'name' is a 'let' constant
    struct User {
    let name: String
    let age: Int
    var isAdult: Bool {
    return age >= 20
    }
    }
    13 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  14. Lens
    • ෆมੑΛอͪͭͭωετͨ͠σʔλߏ଄ʹର͢ΔΞΫηεΛ
    Lensͷ߹੒ͰදݱͰ͖ΔΑ͏ʹͨ͠΋ͷ
    • ݩʑ͸Haskellͷ֓೦
    • SwiftzͷLens࣮૷Λಠཱͨ͠ϑϨʔϜϫʔΫͱͯ͠੾Γग़ͨ͠
    Focusͱ͍͏ϑϨʔϜϫʔΫ΋ଘࡏ
    • https:/
    /github.com/typelift/Focus
    14 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  15. Lens
    import FactoryProvider
    let user = Factory.provide()
    // User(name: "", age: 0)
    let newUser = user |> User._age *~ 20 // User._ageΛLensͱݺͿ
    // User(name: "", age: 20)
    15 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  16. Lens (Nested)
    import FactoryProvider
    struct User {
    let id: UserId
    let name: String
    let age: Int
    }
    struct UserId {
    let value: String
    }
    let user = Factory.provide() |> User._id * UserId._value *~ "nonchalant0303"
    // User(id: UserId(value: "nonchalant0303"), name: "", age: 0)
    16 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  17. Config
    ymlϑΝΠϧͰઃఆ͢Δ
    includes: # ੜ੒ର৅ͷStruct, EnumΛؚΜͩϑΝΠϧ΁ͷύε (ϑΝΠϧ୯ҐɺσΟϨΫτϦ୯Ґ)
    - Input/SubInput1
    - Input/SubInput2/Source.swift
    excludes: # ੜ੒ର৅ͷStruct, Enumͷྫ֎ΛؚΜͩϑΝΠϧ΁ͷύε
    - Input/SubInput1/SubSubInput
    - Input/SubInput2/Source.swift
    testables: # ςετର৅ͷλʔήοτ
    - target1
    - target2
    output: output/Factories.generated.swift # ࣗಈੜ੒͞Εͨίʔυͷύε
    17 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  18. Build Phases
    ςετͷ࣮ߦ࣌ʹFactoryͷࣗಈੜ੒εΫϦϓτΛݺͼग़͢
    "${PODS_ROOT}/FactoryProvider/generate" --config .factory.yml
    # Factories.generated.swift is generated
    18 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  19. How FactoryProvider Works
    2ͭͷίʔυϕʔε͔Β੒Γཱͭ
    • FactoryProviderͷίʔυ (Fixed)
    • ࣗಈੜ੒͞ΕΔίʔυ (Generated in Build Phases)
    19 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  20. Providable (FactoryProvider)
    Providableʹ४ڌͨ͠ܕ͕FactoryͰΦϒδΣΫτΛऔಘͰ͖Δ
    public protocol Providable {
    static func provide() -> Self
    }
    20 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  21. Primitive Factory (FactoryProvider)
    PrimitiveͳܕͷFactory͕ఆٛ͞Ε͍ͯΔ (Int, Optional, String, ...)
    extension Int: Providable {
    public static func provide() -> Int {
    return 0
    }
    }
    extension Optional: Providable where Wrapped: Providable {
    public static func provide() -> Optional {
    return .some(Wrapped.provide())
    }
    }
    ...
    21 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  22. Factory (FactoryProvider)
    struct Factory {
    static func provide() -> T {
    return T.provide()
    }
    }
    22 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  23. Specified Factory (Generated Code)
    import FactoryProvider
    extension User: Providable {
    static func provide() -> User {
    return User(
    id: Factory.provide(),
    name: Factory.provide(),
    age: Factory.provide()
    )
    }
    }
    extension UserId: Providable {
    static func provide() -> UserId {
    return UserId(
    value: Factory.provide()
    )
    }
    }
    23 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  24. Specified Factory (Generated Code)
    import FactoryProvider
    extension User: Providable {
    static func provide() -> User {
    return User(
    id: Factory.provide(),
    name: Factory.provide(),
    age: Factory.provide()
    )
    }
    }
    extension UserId: Providable {
    static func provide() -> UserId {
    return UserId(
    value: Factory.provide()
    )
    }
    }
    24 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  25. Specified Factory (Generated Code)
    ༿͕͢΂ͯPrimitiveͳܕʹͳΔ·Ͱ໦Λ৳͹͢
    25 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  26. Lens (FactoryProvider)
    public struct Lens {
    private let getter: (Whole) -> Part
    private let setter: (Part, Whole) -> Whole
    public init(getter: @escaping (Whole) -> Part, setter: @escaping (Part, Whole) -> Whole) {
    self.getter = getter
    self.setter = setter
    }
    public func get(_ from: Whole) -> Part {
    return getter(from)
    }
    public func set(_ from: Part, _ to: Whole) -> Whole {
    return setter(from, to)
    }
    }
    26 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  27. Custom Operator (FactoryProvider)
    infix operator *~: MultiplicationPrecedence
    infix operator |>: AdditionPrecedence
    public func * (lhs: Lens, rhs: Lens) -> Lens {
    return Lens(
    getter: { a in
    rhs.get(lhs.get(a))
    },
    setter: { (c, a) in
    lhs.set(rhs.set(c, lhs.get(a)), a)
    }
    )
    }
    public func *~ (lhs: Lens, rhs: B) -> (A) -> A {
    return { a in
    lhs.set(rhs, a)
    }
    }
    public func |> (x: A, f: (A) -> B) -> B {
    return f(x)
    }
    public func |> (f: @escaping (A) -> B, g: @escaping (B) -> C) -> (A) -> C {
    return { g(f($0)) }
    }
    27 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  28. Compose (FactoryProvider)
    public func * (lhs: Lens, rhs: Lens) -> Lens {
    return Lens(
    getter: { a in
    rhs.get(lhs.get(a))
    },
    setter: { (c, a) in
    lhs.set(rhs.set(c, lhs.get(a)), a)
    }
    )
    }
    28 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  29. Set (FactoryProvider)
    infix operator *~: MultiplicationPrecedence
    public func *~ (lhs: Lens, rhs: B) -> (A) -> A {
    return { a in
    lhs.set(rhs, a)
    }
    }
    29 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  30. Modify (FactoryProvider)
    infix operator |>: AdditionPrecedence
    public func |> (x: A, f: (A) -> B) -> B {
    return f(x)
    }
    public func |> (f: @escaping (A) -> B, g: @escaping (B) -> C) -> (A) -> C {
    return { g(f($0)) }
    }
    30 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  31. Lens (Generated Code)
    extension User {
    static var _name: Lens {
    return Lens(
    getter: { $0.name },
    setter: { name, base in
    User(name: name, age: base.age)
    }
    )
    }
    static var _age: Lens {
    return Lens(
    getter: { $0.age },
    setter: { age, base in
    User(name: base.name, age: age)
    }
    )
    }
    }
    31 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  32. Decompose
    let user = Factory.provide() |> User._name *~ "Takeshi Ihara"
    = (User._name *~ "Takeshi Ihara")(Factory.provide())
    = { user in
    Lens(
    getter: { $0.name },
    setter: { name, base in
    User(name: name, age: base.age)
    }
    ).set("Takeshi Ihara", user)
    }(Factory.provide())
    = { name, base in
    User(name: name, age: base.age)
    }("Takeshi Ihara", Factory.provide())
    = User(name: "Takeshi Ihara", age: 0)
    32 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  33. Decompose
    let user = Factory.provide() |> User._name *~ "Takeshi Ihara"
    = (User._name *~ "Takeshi Ihara")(Factory.provide())
    = { user in
    Lens(
    getter: { $0.name },
    setter: { name, base in
    User(name: name, age: base.age)
    }
    ).set("Takeshi Ihara", user)
    }(Factory.provide())
    = { name, base in
    User(name: name, age: base.age)
    }("Takeshi Ihara", Factory.provide())
    = User(name: "Takeshi Ihara", age: 0)
    33 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  34. Decompose (|>)
    let user = Factory.provide() |> User._name *~ "Takeshi Ihara"
    = (User._name *~ "Takeshi Ihara")(Factory.provide())
    = { user in
    Lens(
    getter: { $0.name },
    setter: { name, base in
    User(name: name, age: base.age)
    }
    ).set("Takeshi Ihara", user)
    }(Factory.provide())
    = { name, base in
    User(name: name, age: base.age)
    }("Takeshi Ihara", Factory.provide())
    = User(name: "Takeshi Ihara", age: 0)
    34 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  35. Decompose (Lens, *~)
    let user = Factory.provide() |> User._name *~ "Takeshi Ihara"
    = (User._name *~ "Takeshi Ihara")(Factory.provide())
    = { user in
    Lens(
    getter: { $0.name },
    setter: { name, base in
    User(name: name, age: base.age)
    }
    ).set("Takeshi Ihara", user)
    }(Factory.provide())
    = { name, base in
    User(name: name, age: base.age)
    }("Takeshi Ihara", Factory.provide())
    = User(name: "Takeshi Ihara", age: 0)
    35 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  36. Decompose (Closure)
    let user = Factory.provide() |> User._name *~ "Takeshi Ihara"
    = (User._name *~ "Takeshi Ihara")(Factory.provide())
    = { user in
    Lens(
    getter: { $0.name },
    setter: { name, base in
    User(name: name, age: base.age)
    }
    ).set("Takeshi Ihara", user)
    }(Factory.provide())
    = { name, base in
    User(name: name, age: base.age)
    }("Takeshi Ihara", Factory.provide())
    = User(name: "Takeshi Ihara", age: 0)
    36 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  37. Decompose
    let user = Factory.provide() |> User._name *~ "Takeshi Ihara"
    = (User._name *~ "Takeshi Ihara")(Factory.provide())
    = { user in
    Lens(
    getter: { $0.name },
    setter: { name, base in
    User(name: name, age: base.age)
    }
    ).set("Takeshi Ihara", user)
    }(Factory.provide())
    = { name, base in
    User(name: name, age: base.age)
    }("Takeshi Ihara", Factory.provide())
    = User(name: "Takeshi Ihara", age: 0)
    37 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  38. Result
    struct User {
    let name: String
    let age: Int
    var isAdult: Bool {
    return age >= 20
    }
    }
    import FactoryProvider
    class UserTests: XCTestCase {
    func test20ࡀҎ্ͳΒ੒ਓͰ͋Δ() {
    let user = Factory.provide() |> User._age *~ 20
    XCTAssertTrue(user.isAdult)
    }
    func test20ࡀະຬͳΒ੒ਓͰͳ͍() {
    let user = Factory.provide() |> User._age *~ 19
    XCTAssertFalse(user.isAdult)
    }
    }
    38 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  39. Demo
    39 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  40. Future Work (Protocol)
    Not support protocol
    !
    protocol A {}
    // Type 'A' does not conform to protocol 'Providable'
    let a = Factory.provide()
    40 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  41. Future Work (Protocol)
    Extension of protocol cannot have an inheritance clause
    // Extension of protocol 'A' cannot have an inheritance clause
    extension A: Providable {
    public static func provide() -> Self {
    fatalError()
    }
    }
    let a = Factory.provide()
    41 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  42. Future Work (Protocol)
    Need concrete type
    !
    protocol A: Providable {}
    extension A {
    static func provide() -> Self {
    fatalError()
    }
    }
    // Using 'A' as a concrete type conforming to protocol 'Providable' is not supported
    let a = Factory.provide()
    42 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  43. Future Work (Protocol)
    No problem using concrete type
    !
    protocol A: Providable {}
    extension A {
    static func provide() -> Self {
    fatalError()
    }
    }
    struct B: A {}
    let a = Factory.provide()
    43 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  44. Θ͍Θ͍swiftc
    ڵຯΛ࣋ͬͨํ͸ͥͻ
    44 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  45. Future Work (interface)
    User.provide()ͷΑ͏ʹ௚઀ੜ੒ϝιου͕ݺ΂ͯ͠·͏
    extension User: Providable {
    static func provide() -> Self {
    return User(
    ...
    )
    }
    }
    let user = User.provide() or Factory.provide()
    45 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  46. Future Work (interface)
    fileprivateͳGenericsΛ࣋ͭͳΒͦͷStructࣗ਎΋fileprivateʹ
    ͠ͳͯ͘͸ͳΒͳ͍
    !
    fileprivate Protocol {
    static func provide() -> Self
    }
    fileprivate extension User: Providable {
    ...
    }
    // Generic struct must be declared private or fileprivate because its generic parameter uses a fileprivate type
    struct Factory {
    static func provide() -> T {
    return T.provide()
    }
    }
    46 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  47. Future Work (interface)
    Factoryʹ͢΂ͯهड़͢Δ
    !
    struct Factory {
    static func provide() -> T {
    switch T.self {
    case is User.Type:
    return User(
    name: Factory.provide(),
    age: Factory.provide()
    )
    case is String.Type:
    return ""
    ...
    default:
    fatalError()
    }
    }
    }
    47 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  48. Future Work (interface)
    Inner TypeΛαϙʔτ͕ࠔ೉
    !
    (ύʔε͕େม)
    struct User {
    let id: Id
    struct Id {}
    }
    struct Factory {
    static func provide() -> T {
    switch T.self {
    case is User.Type:
    return User(
    id: Factory.provide() // Use of undeclared type 'Id'
    )
    ...
    }
    }
    }
    48 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  49. FactoryProvider
    https:/
    /github.com/Nonchalant/FactoryProvider
    49 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide

  50. Conclusion
    • ʮߏ଄มԽʹڧ͍ςετʯ͕࣮ݱͰ͖ͨ
    • FactoryΛ༻ҙ͢Δख͕ؒͳ͘ͳͬͨͷͰςετʹूதͰ͖Δ
    • ςετର৅ͷϓϩύςΟ͕෼͔Γ΍͘͢ͳͬͨ
    • Factory.provide() |> User._age *~ 19
    • SourceKitten + StencilΛ࢖ͬͯύʔεͯࣗ͠ಈੜ੒͢Δͷ
    ָ͍͠
    $
    50 Factoryͷࣗಈੜ੒ʹΑΓςετΛॻ͖΍͘͢͢Δ, iOSDC 2018 Reject Conference days1

    View full-size slide