Skip to content

Latest commit

 

History

History
1043 lines (668 loc) · 15.2 KB

File metadata and controls

1043 lines (668 loc) · 15.2 KB

class: title

10x Faster App Dev/Verification Cycle

The Survival Guide of Building Huge App

CJ Lin

???

This talk is not about lower overall build time

It's about tips of faster dev/verification cycle

Let's start it!


About

.left-column-80[

struct Profile {

    let name = "CJ Lin"

    var identity = "iOS Nerd"

    var company = "LINE"

    var blog = "https://ejameslin.github.io"

    var github = "https://github.com/eJamesLin"

    var line = "ejameslin"

    var twitter = "@eJamesLin"
}

]

.right-column-20[ ]


class: blank

.vertical-center.center[

Don't Build-and-Run Whole App

]


Outline

LLDB Code Injection

???

Minimize the number of rebuild

--

Develop only at Framework

--

Verify the Framework


Outline

LLDB Code Injection

.grey[

Develop only at Framework

Verify the Framework

]


LLDB Code Injection

  • LLDB and Breakpoints

  • Modify control flow without rebuild


Code Injection Example - Login

.vertical-center.center[ ]


Code Injection Example - Login

.vertical-center.center[ ]


Code Injection - REPL

  • LLDB + REPL

  • Read-Eval-Print-Loop (REPL)

  • Access public functions and global variables

  • Inject new functions


Code Injection Example - Fibonacci

.vertical-center.center[ ]


Code Injection Example - Fibonacci

  • Type repl to enter

.center[ ]


Code Injection Example - Fibonacci

  • Inject new function

.center[ ]


Code Injection Example - Fibonacci

  • Call the function and verify

.center[ ]


Code Injection Example - Fibonacci

  • Prefix : to run LLDB command

.center[ ]


Code Injection Example - Fibonacci

  • Type single colon to return back to LLDB

.center[ ]


Code Injection Example - Fibonacci

  • Modify variable by calling the injected function

.center[ ]


Code Injection Example - API

  • In the code todo
  • Implement a view model to download API json

.center[ ]


Code Injection Example - API

  • Type e in LLDB to enter multi-line expressions

.center[ ]


Code Injection Example - API

  • Inject ViewModel and API code

.center[ ]


Code Injection Example - API

  • Json URL

.center[ ]


Code Injection Example - API

  • Download

.center[ ]


Code Injection Example - API

  • Runloop handling at LLDB

.center[ ]


Code Injection Example - API

  • Call the injected function

.center[ ]


Code Injection Example - API

  • Inspect the result

.center[ ]


Outline

.grey[

LLDB Code Injection

]

Develop only at Framework

.grey[

Verify the Framework

]


Let's do a little survey...

???

App build time > 10 mins

Can't tolerate LONG BUILD TIME?

Let's solve it !!!

.center[ ]


Build Time

Main Target

.center[ ]

???

MacBook Pro (15-inch, 2017)

3.1 GHz Intel Core i7

16 GB 2133 MHz LPDDR3

--

Partitioned Framework Target

.center[ ]

???

Build time quickly disappear for indexing after build at Xcode title bar...


class: blank

.vertical-center.center[

Don't Build-and-Run Whole App

]


Framework Partitioning Tips

.center[ ]

???

Feature could be MVC or MVVM or MVP


Framework Partitioning Tips

.center[ ]


Framework Partitioning Tips

.center[ ]


Let's start from small step

.vertical-center.center[

Ideal partition is too ideal

]


Let's start from small step

.vertical-center.center[

Small Testing-Only Framework

]


Small Testing-Only Framework

  • Build Fast
    • Feature related code only
    • Few compile source count

--

.right-column-20[ ]

  • Testing-Only

    • Code in both targets
    • Not embedded in others
    • Not linked by others --
  • Easy to apply on existing project


Framework Partitioning Tips

  • Coordinator / Router

Coordinator / Router

How Coordinator eliminate dependency?

  • Example
    • In Profile, Click Edit button, Show EditProfile

.center[ ]

--

  • Profile is depends on EditProfile --

  • Difficult to move into framework


Coordinator / Router

.vertical-center.center[ ]


Coordinator / Router

.vertical-center.center[ ]


Coordinator / Router

.vertical-center.center[ ]


Coordinator / Router

protocol ProfileViewControllerDelegate: class {
	viewControllerDidSelectEdit(_ vc: ProfileViewController) {}
}

class ProfileViewController: UIViewController {
	weak var delegate: ProfileViewControllerDelegate?
}

--

class Coordinator: ProfileViewControllerDelegate {
	viewControllerDidSelectEdit(_ vc: ProfileViewController) {
		// show EditProfileViewController...
	}
}

Framework Partitioning Tips

  • .grey[Coordinator / Router]

  • Dependency Injection


Dependency Injection Example

.center[ ]

.center[ ]

.center[ ]


Dependency Injection Example

--

protocol AnalyticsProtocol {
    func track(screen: AnalyticsScreen)
}

???

Start from AnalyticsProtocol definition

class Analytics: AnalyticsProtocol {
    func track(screen: AnalyticsScreen) {
        GA.track(screen: screen)
    }
}

???

Analytics conforms to the protocol

class FeedController: UIViewController {
    let analytics: AnalyticsProtocol
    init(analytics: AnalyticsProtocol) {
        self.analytics = analytics
        super.init(nibName: nil, bundle: nil)
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        analytics.track(screen: .feed)
    }

???

Controller is now depends on only the protocol, not the Analytics, nor the implementation detail GA.


Framework Partitioning Tips

  • Ready for framework partitioning

.center[ ]


Outline

.grey[

Code Injection to minimize rebuild

Develop only at Framework

]

Verify the Framework


Framework + Live View

SwiftUI Canvas Preview

Playground


Framework + Live View

SwiftUI Canvas Preview

.grey[

Playground

]


SwiftUI Canvas Preview Advantage

--

.left-column[ .font-large[Live Preview] ]

.right-column[

  • Interactable ] --

.right-column[

  • Can also preview UIViewController / UIView

  • Even if the minimum target is iOS 12 and below ]


SwiftUI Preview at iOS 12 and below

Though SwiftUI cannot be used at iOS 12 and below

Still could preview existing screens

The `@available(iOS 13.0, *)` is not enough...

dyld: Library not loaded: /System/Library/Frameworks/SwiftUI.framework/SwiftUI

--

Known Issues and Workaround: -weak_framework
Apps containing SwiftUI inside a Swift package might not run on versions of iOS earlier than iOS 13. (53706729)

.center[

.footnote[iOS 13 Release Notes] ]


SwiftUI Canvas Preview Advantage

.left-column[ .font-large.grey[Live Preview]

.font-large[Fast Access] ]

.right-column[

  • Render the page directly

  • No need following steps

  1. Select A
  2. Scroll
  3. Click B
  4. ... ]

SwiftUI Canvas Preview Advantage

.left-column[ .font-large.grey[Live Preview]

.font-large.grey[Fast Access]

.font-large[Preview device size] ]

.right-column[

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
        	.previewDevice("iPhone 11")
    }
}

]


SwiftUI Canvas Preview Advantage

.left-column[ .font-large.grey[Live Preview]

.font-large.grey[Fast Access]

.font-large.grey[Preview device size]

.font-large[Group Preview] ]

.right-column[

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            ContentView()
            	.previewDevice("iPhone SE")

            ContentView()
            	.previewDevice("iPhone 11")
        }
    }
}

]


SwiftUI Canvas Preview Advantage

.left-column[ .font-large.grey[Live Preview]

.font-large.grey[Fast Access]

.font-large.grey[Preview device size]

.font-large.grey[Group Preview]

.font-large[Vary Language] ]

.right-column[

struct SwiftUIText: View {
    var body: some View {
        Text("HelloKey")
    }
}
struct PreviewSwiftUIText: PreviewProvider {
    static var previews: some View {
        Group {
            SwiftUIText()
            .environment(\.locale, 
                		 Locale(identifier: "en"))
            .previewDisplayName("en")

            SwiftUIText()
	        .environment(\.locale, 
	            	     Locale(identifier: "ja"))
	        .previewDisplayName("ja")
        }
        .previewLayout(.sizeThatFits)
    }
}

]

.left-column[ ]


SwiftUI Canvas Preview Advantage

.left-column[ .font-large.grey[Live Preview]

.font-large.grey[Fast Access]

.font-large.grey[Preview device size]

.font-large.grey[Group Preview]

.font-large[Vary Language] ]

.right-column[

Not work at NSLocalizedString

]


SwiftUI Preview + NSLocalizedString

.center[ ]


SwiftUI Canvas Preview + Framework

Known Issue

Previews in packages always perform a full build of the active scheme. (51030302)

.footnote[Xcode 11 Release Notes]

.center[ ]


SwiftUI Canvas Preview + Framework

Previews in packages always perform a full build of the active scheme. (51030302)

Main Scheme

.center[ ]

--

Framework Scheme - Solution

.center[ ]


Framework + Live View

.grey[

SwiftUI Canvas Preview

]

Playground


Playground + Framework

Currently Playground is more stable than SwiftUI canvas preview...

.center[ ]


Playground Advantage

.left-column[ .font-large[Live View] ]

.right-column[

  • Interactable ]

Playground Advantage

.left-column[ .font-large.grey[Live View]

.font-large[Fast Access] ]

.right-column[

  • Render the page directly

  • No need following steps

  1. Select A
  2. Scroll
  3. Click B
  4. ... ]

Playground Advantage

.left-column[ .font-large.grey[Live View]

.font-large.grey[Fast Access]

.font-large[Inline Display] ]

.right-column[ ]


Playground Advantage

.left-column[ .font-large.grey[Live View]

.font-large.grey[Fast Access]

.font-large.grey[Inline Display]

.font-large[Run Step by Step] ]

.right-column[

  • View change on the fly

] --

.right-column[ ]

???

Similar to Scripting Language


Playground Advantage

.left-column[ .font-large.grey[Live View]

.font-large.grey[Fast Access]

.font-large.grey[Inline Display]

.font-large[Run Step by Step] ]

.right-column[

  • View change on the fly ] .right-column[
  • Perfect for network response decoding trial ]

Playground Advantage

.left-column[ .font-large.grey[Live View]

.font-large.grey[Fast Access]

.font-large.grey[Inline Display]

.font-large.grey[Run Step by Step]

.font-large[Vary Screen Size] ]

.right-column[

viewController.preferredContentSize = Some Size

]

???

No safe area...


Playground Advantage

.left-column[ .font-large.grey[Live View]

.font-large.grey[Fast Access]

.font-large.grey[Inline Display]

.font-large.grey[Run Step by Step]

.font-large.grey[Vary Screen Size]

.font-large[Vary Language] ]

.right-column[

  • Use NSLocalizedString with specified language sub-bundle

]

Test Driven Development in Playground

  • To build faster at every iteration
    • SUT (System Under Test) in Framework
    • Start unit test in playground, move to test target for CI after completed
MyUnitTests.defaultTestSuite().run() // in playground

.center[ .font-small[Image Source] ]


Playground Driven Development

  • From Kickstarter Open Source and Speech
  • All code in Framework
  • Every page inspectable in playground

.center[ ]


class: blank

.vertical-center.center[

Don't Build-and-Run Whole App

]


Thank You

  • The slides is made by Markdown powered by remark

  • The markdown and sample code of the slides could be found here

.center[]

  • Happy Coding and Thank You