From 2a46adf6e16abe46775902fe14b8eb0e9c3b4570 Mon Sep 17 00:00:00 2001 From: Steven Grosmark <steven.grosmark@weightwatchers.com> Date: Fri, 21 Feb 2020 09:36:21 -0500 Subject: [PATCH 1/7] Incorporate final Flow article edits --- README.md | 2 +- docs/Lasso-FlowsIntro.md | 42 +++++++++++++++++++++------------------- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index fe32687..6071a7a 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ To run the example project ## Learn more - [Lasso: Introducing a new architectural framework for iOS](docs/Lasso-Introduction-part1.md) - article that introduces Lasso, with a concrete example of creating a `Screen` - +- [Lasso: An introduction to Flows](docs/Lasso-FlowsIntro.md) - article that shows how to orchestrate a number of `Screens` with a `Flow` - [Lasso coding style guilde](docs/style-guide.md) - tips for writing Swifty Lasso diff --git a/docs/Lasso-FlowsIntro.md b/docs/Lasso-FlowsIntro.md index 62aeed9..c3cb723 100644 --- a/docs/Lasso-FlowsIntro.md +++ b/docs/Lasso-FlowsIntro.md @@ -1,7 +1,7 @@ # Lasso: An introduction to Flows -Most all iOS applications have some sense of navigation - a user will progress from screen to screen as they utilize the features of an app. In iOS development, the class `UIViewController` is primarily responsible for such navigation. Transitions from screen to screen are realized as side effects in the view controller hierarchy (navigation hierarchy). +Most iOS applications have some sense of navigation — a user will progress from screen to screen as they utilize the features of an app. In iOS development, the class `UIViewController` is primarily responsible for such navigation. Transitions from screen to screen are realized as side effects in the view controller hierarchy (navigation hierarchy). -Typically, view controllers are responsible for creating and presenting other controllers. This results in lots of coupling between controllers. As a result, it becomes very difficult to: +Typically, view controllers are responsible for creating and presenting other controllers. This results in lots of coupling between controllers. As a result, it becomes difficult to: * Reuse similar sequences of screens in varying contexts * Easily modify an app's high level navigation structure @@ -16,22 +16,22 @@ A `Flow` represents a feature - or area - of an app, and is commonly composed of <img src="images/flow.svg" width="80%" min-width="300" /> -A `Flow` is instantiated and started within an appropriate context of a navigation hierarchy (e.g., a "sign up" flow might be presented on a menu screen, or a "welcome" flow might be pushed onto a navigation stack). The `Flow` starts by creating its initial `Screen`, and listens for `Output` signals. As `Outputs` arrive, the `Flow` decides what to do with them - it can create and place another `Screen` into the navigation hierarchy, emit its own `Output` (when an event occurs that is more appropriately handled elsewhere), or whatever is appropriate for the `Flow`. +A `Flow` is instantiated and started within an appropriate context of a navigation hierarchy (e.g., a "sign up" flow might be presented on a menu screen, or a "welcome" flow might be pushed onto a navigation stack). The `Flow` starts by creating its initial `Screen`, and listens for `Output` signals. As `Outputs` arrive, the `Flow` decides what to do with them. It can create and place another `Screen` into the navigation hierarchy, emit its own `Output` (when an event occurs that is more appropriately handled elsewhere), or do whatever else is appropriate for the `Flow`. Since `Screens` and `Flows` are encapsulated modules with discrete entry and exit points, it's quite easy and common for a `Flow` to manage both `Screens` _and_ `Flows`. <img src="images/flow-flow-screen.svg" /> -From a functional perspective, `Flows` are a mechanism for composition - they aggregate smaller independent units of behavior into larger ones. `Flows` themselves may also be composed. This makes it possible to define the views that constitute an application as a hierarchy of `Flows`. Describing an app's features in this way drastically reduces complexity and increases maintainability. +From a functional perspective, `Flows` are a mechanism for composition — they aggregate smaller independent units of behavior into larger ones. `Flows` themselves may also be composed. This makes it possible to define the views that constitute an application as a hierarchy of `Flows`. Describing an app's features in this way drastically reduces complexity and increases maintainability. ## Implementing a Flow -Business has crunched the numbers, and they've decided that we need to provide a short tutorial to our members to enhance new member onboarding. The feature calls for a series of views with images and text describing the program. A user completes the tutorial by progressing forward through all the views. +Let's say that business has crunched the numbers, and they've decided that we need to provide a short tutorial to our members to enhance new member onboarding. The feature calls for a series of views with images and text describing the program. A user completes the tutorial by progressing forward through all of the views. <img src="images/TutorialFlowDiagram.svg" /> How can we implement this the Lasso way? First, we must define the structural types of our `Flow`. -The types that constitute a `Flow's` structure are defined in a `FlowModule`. This is simply a convenience for grouping the member types of a specific `Flow` - namely, its `Output` and `RequiredContext`. +The types that constitute a `Flow's` structure are defined in a `FlowModule`. This is simply a convenience for grouping the member types of a specific `Flow`, namely its `Output` and `RequiredContext`. `Output` defines the messages that a `Flow` can propagate outward to some unknown higher level object. These `Output` messages constitute the modular boundary of the `Flow`. @@ -49,11 +49,11 @@ enum TutorialFlowModule: FlowModule { } ``` -There are several user actions our tutorial `Flow` must respond to. On the first screen, we see a "next" and a "skip" button. Our `Flow` must execute some logic in response to the user pressing these buttons. It will handle the "next" button press by creating and showing the second screen. However, it is not responsible for handling the "skip" button press - some higher level object will be responsible for driving the app in response to this event. This logical design is realized by including `didPressSkip` as an `Output` case. When the user presses "skip", our `Flow` will simply emit `didPressSkip` as an `Output`. +There are several user actions our tutorial `Flow` must respond to. On the first screen, we see a "next" and a "skip" button. Our `Flow` must execute some logic in response to the user pressing these buttons. It will handle the "next" button press by creating and showing the second screen. However, it is not responsible for handling the "skip" button press; some higher level object will be responsible for driving the app in response to this event. This logical design is realized by including `didPressSkip` as an `Output` case. When the user presses "skip", our `Flow` will simply emit `didPressSkip` as an `Output`. -On the second screen, we see a "done" button. When this button is pressed, our `Flow` is finished - it has no further screens to show. This is embodied by the `didFinish` case of `Output`. Our `Flow` will emit this message when the "OK" button is pressed. As before, some higher level object will need to respond to this message, progressing the application according to its broader business logic. +On the second screen, we see a "done" button. When this button is pressed, our `Flow` is finished — it has no further screens to show. This is embodied by the `didFinish` case of `Output`. Our `Flow` will emit this message when the "OK" button is pressed. As before, some higher level object will need to respond to this message, progressing the application according to its broader business logic. -We would like to create the effect that the user is progressing forwards through the tutorial screens. To satisfy this, we will use the "push" API on `UINavigationController`. Our `Flow` needs a way to specify this requirement - our `Flow` must be _started_ in a navigation controller and be able to make "push" calls on that navigation controller. This is achieved by stating that the `RequiredContext` is of type `UINavigationController`. +We would like to create the effect that the user is progressing forward through the tutorial screens. To satisfy this, we will use the "push" API on `UINavigationController`. Our `Flow` needs a way to specify this requirement — our `Flow` must be _started_ in a navigation controller and be able to make "push" calls on that navigation controller. This is achieved by stating that the `RequiredContext` is of type `UINavigationController`. We are now ready to implement our `Flow`: @@ -151,7 +151,7 @@ class TutorialFlow: Flow<TutorialFlowModule> { } ``` -`TutorialFlow` observes the `Output` of the "welcome" screen, capturing `self` weakly in order to avoid retain cycles. When `TutorialFlow` receives the `didPressButton` message from that screen, it must evaluate the associated `index` value to determine which button was tapped. We know that this screen has two buttons, "skip" and "next". The indices of these buttons correspond to the `buttonTitles` property on `State` - "skip" corresponds to an index value of 0, and "next" to a value of 1. +`TutorialFlow` observes the `Output` of the "welcome" screen, capturing `self` weakly in order to avoid retain cycles. When `TutorialFlow` receives the `didPressButton` message from that screen, it must evaluate the associated `index` value to determine which button was tapped. We know that this screen has two buttons: "skip" and "next." The indices of these buttons correspond to the `buttonTitles` property on `State` — "skip" corresponds to an index value of 0, and "next" to a value of 1. The "finish" screen is constructed in the same manner as the welcome screen. It is placed into the navigation hierarchy by calling `pushViewController(:animated:)` on the local `context: UINavigationController?`. By definition, the `context` property always reflects the `RequiredContext` type. It is an optional because it is weakly owned by the `Flow`, which is necessary to avoid retain cycles. @@ -272,13 +272,13 @@ let tutorialFlow = TutorialFlow() tutorialFlow.start(with: presented(on: viewController)) // ERROR ``` -Here, `presented(on:)` returns a `ScreenPlacer<UIViewController>`. This means that `TutorialFlow` will be provided a context object of type `UIViewController`. This is inadequate - we know that `TutorialFlow` needs to be able to push controllers onto a navigation stack, requiring a context object of type `UINavigationController`. +Here, `presented(on:)` returns a `ScreenPlacer<UIViewController>`. This means that `TutorialFlow` will be provided a context object of type `UIViewController`. This is inadequate. We know that `TutorialFlow` needs to be able to push controllers onto a navigation stack, requiring a context object of type `UINavigationController`. -This compilation failure is incredibly valuable - our code will not compile if we violate the explicit requirements of a `Flow`. `Flows` can thus be chained together with confidence - the compiler will tell us when we have broken a screen sequence. +This compilation failure is incredibly valuable. Our code will not compile if we violate the explicit requirements of a `Flow`. `Flows` can thus be chained together with confidence — the compiler will tell us when we have broken a screen sequence. ### ScreenPlacers and UIViewController Embeddings -`ScreenPlacers` also support view controller embeddings. In Lasso, view controller embeddings are composable with respect to `ScreenPlacers` - an embedding can be "chained along" to any `ScreenPlacer` instance. Intuitively, if I have some `ScreenPlacer` instance, I can place some container view controller with that placer. I can then place some other view controller into that container. This is precisely how `ScreenPlacer` embeddings work. +`ScreenPlacers` also support view controller embeddings. In Lasso, view controller embeddings are composable with respect to `ScreenPlacers` — an embedding can be "chained along" to any `ScreenPlacer` instance. Intuitively, if I have some `ScreenPlacer` instance, I can place some container view controller with that placer. I can then place some other view controller into that container. This is precisely how `ScreenPlacer` embeddings work. We could start `TutorialFlow` in a modally presented navigation controller. @@ -295,7 +295,7 @@ let tabPlacers: [ScreenPlacer<UITabBarController>] = tabBarEmbedding(UITabBarCon tutorialFlow.start(with: tabPlacers[1].withNavigationEmbedding()) ``` -We have seen many built-in `ScreenPlacer` conveniences. Lasso contians many such conveniences covering typical UIKit use cases. Lasso clients are not limited to this built-in set of placers - the `ScreenPlacer` API is completely extensible. Clients can create custom extensions as desired in support of custom containers and less common use cases. +We have seen many built-in `ScreenPlacer` conveniences. Lasso contains many such conveniences covering typical UIKit use cases. Lasso clients are not limited to this built-in set of placers — the `ScreenPlacer` API is completely extensible. Clients can create custom extensions as desired in support of custom containers and less common use cases. ## What's the Point? In concert, `Flows` and `ScreenPlacers` are the ultimate tools for describing and implementing an application's sequences of screens. @@ -306,12 +306,14 @@ In Lasso, `Flows` encapsulate sequences of `Screens`. Thanks to `ScreenPlacers` Thus, Lasso moves us from a world with inherent coupling to one with inherent modularity. This quality incurs many benefits: -* Reusing related collections of `Screens` is trivial -* Related collections of `Screens` can be effectively tested in isolation -* Construction and mocking is clean -* Composition is at our fingertips: we can create all types of new behaviors by aggregating smaller, existing ones -* Top level application code is much more expressive / maintainable: where we used to have references to low level controllers, we now have references to higher level abstractions that encapsulate feature sets +* Reusing related collections of `Screens` is trivial. +* Related collections of `Screens` can be effectively tested in isolation. +* Construction and mocking is clean. +* Composition is at our fingertips. We can create all types of new behaviors by aggregating smaller, existing ones. +* Top level application code is much more expressive and maintainable. Where we used to have references to low level controllers, we now have references to higher level abstractions that encapsulate feature sets. ### Is that All? -We have only begun to scratch the surface: `Stores`, `Screens`, and `Flows` can be wielded together in a surprising number of ways, giving the developer precision control over the logical design of their application. What's more, Lasso's companion library, `LassoTestUtilities`, provides a mechanism for writing expressive and succinct `Flow` unit tests. These topics will be treated in future articles. \ No newline at end of file +We have only begun to scratch the surface: `Stores`, `Screens`, and `Flows` can be wielded in a surprising number of ways, giving the developer precision control over the logical design of their application. What's more, Lasso's companion library, `LassoTestUtilities`, provides a mechanism for writing expressive and succinct `Flow` unit tests. These topics will be addressed in future articles. + +Interested in joining the WW team? Check out the careers page to view technology job listings as well as open positions on other teams. \ No newline at end of file From d5d655822166e0b4a183ca9624548757e13c51d0 Mon Sep 17 00:00:00 2001 From: Ross Freeman <ross.freeman@ww.com> Date: Fri, 21 Feb 2020 10:30:33 -0500 Subject: [PATCH 2/7] Update readme to swift 5.2 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6071a7a..21f73aa 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ <h1 align="center"><img src="docs/images/Lasso_Logo.svg" width="75%" alt="Lasso logo" /></h1> <p align="center"> - <img src="https://img.shields.io/badge/Swift-4.2%20--%205.1-b63bdb.svg?style=flat" /> + <img src="https://img.shields.io/badge/Swift-4.2%20--%205.2-b63bdb.svg?style=flat" /> <img src="https://img.shields.io/badge/iOS-10.3%20--%2013.x-208eff.svg?style=flat" /> </p> From 398f2090ac9ca9a1282a8c684d9f2fb3f7d96653 Mon Sep 17 00:00:00 2001 From: Steven Grosmark <steven.grosmark@weightwatchers.com> Date: Fri, 21 Feb 2020 11:33:59 -0500 Subject: [PATCH 3/7] Update codeowners file --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 468cb18..d37b363 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,7 +4,7 @@ # the repo. Unless a later match takes precedence, # the following people will be requested for review # when someone opens a pull request. -* @g-mark @trevor-beasty @yuichi-kuroda-ww +* @g-mark @trevor-beasty # Order is important; the last matching pattern takes the most # precedence. When someone opens a pull request that only From ee6c3b42c855c6389e3f03d542f1bd0ed24fde7f Mon Sep 17 00:00:00 2001 From: Steven Grosmark <steven.grosmark@weightwatchers.com> Date: Fri, 21 Feb 2020 13:07:51 -0500 Subject: [PATCH 4/7] Remove unused file --- docs/Dependency Injection case study.md | 120 ------------------------ 1 file changed, 120 deletions(-) delete mode 100644 docs/Dependency Injection case study.md diff --git a/docs/Dependency Injection case study.md b/docs/Dependency Injection case study.md deleted file mode 100644 index 29b1251..0000000 --- a/docs/Dependency Injection case study.md +++ /dev/null @@ -1,120 +0,0 @@ -## Dependency Injection case study - - - -How to deal with legacy classes - -That's a great question. - -#### Push.Manager - -The `Push.Manager` is set up for mocking, but not in a way that would be very useful to you. - -What you really want to mock is your interface with the manager, rather than the manager's interface with an HTTP service and data model. - -Since that manager is a class with no protocol, I think you have two options: -1. make a mock subclass, and override the functions you need in your store -2. make a protocol that represents what you need in the store, and then create a mock that conforms to that protocol. - -There's pros and cons to both approaches, but personally I would lean towards a protocol. Since the manager is a pretty big class that does a lot, if you overrode a small subset of those functions, there's a risk that some other weird side-effects will happen. In other words, the mock shouldn't really have any functionality in it, except for what is needed to pretend to be a service for your store. - -So, what you want to do in my opinion, is to collect all methods you need, and copy/paste those function signatures into a new protocol. - -Here's an example with just the `setupReminder` function - this would all go in your reminder store file: -```swift -// set up a base protocol for the reminder store -protocol RemiderServiceProtocol { - func setupReminder(service: HTTPProtocol, for date: Date, weekday: Weekday, notificationType: String, completion: (() -> Void)?) -} - -// set up an extension to handle default parameters -extension RemiderServiceProtocol { - func setupReminder(for date: Date, weekday: Weekday, notificationType: String, completion: (() -> Void)?) { - setupReminder(service: Services.notifications, for: date, weekday: weekday, notificationType: notificationType, completion: completion) - } -} - -// make the push manager conform to the protocol: -extension Push.Manager: RemiderServiceProtocol { } - -// in the store, just use the protocol -public final class ReminderStore: LassoStore<ReminderScreenModule> { - var service: RemiderServiceProtocol = Push.shared - ... -} -``` - -Then, create your mock: -```swift -final class MockReminderService: RemiderServiceProtocol { - - // capture the completion handler: - var setupReminderCompletion: (() -> Void)? - - // implement the protocol function, and just grab the completion: - func setupReminder(service: HTTPProtocol, for date: Date, weekday: Weekday, notificationType: String, completion: (() -> Void)?) { - setupReminderCompletion = completion - } -} -``` - -Inject it when creating your store: -```swift -mockService = MockReminderService() -store.service = mockService -``` - -Then, in your tests: -```swift -// when you run your unit test, you can then make sure the service function was called: -store.dispatchAction(.submit) -XCTAssertNotNil(mockService.setupReminderCompletion) - -// and then call the completion, and test for how the store handles it: -mockService.setupReminderCompletion?() -XCTAssertOutputs([.dismiss]) -``` - -#### PreAuthorizationAlert - -Similar to the Push manager, you just want to test your interface with the pre-auth alert, and ignore what happens with UIKit. - -Existing: - -```swift -public enum PreAuthorizationAlert { - - public static func presentPreAuthorizationAlert() { ... } - -} -``` - - - -This is a little easier to deal with. Since it's a case-less enum, I would change it to be a protocol / struct - something like this (only the public funcs would go into the protocol): - -```swift -protocol PreAuthorizationAlertProtocol { - public func presentPreAuthorizationAlert() -} - -struct PreAuthorizationAlert: PreAuthorizationAlertProtocol { - - public static let shared: PreAuthorizationAlertProtocol = PreAuthorizationAlert() - - public func presentPreAuthorizationAlert() { - ... - } -} -``` - -You can do the same mocking as described above for the Push manager. -```swift -public final class ReminderStore: LassoStore<ReminderScreenModule> { - var preAuthAlert: PreAuthorizationAlertProtocol = PreAuthorizationAlert.shared - ... -} -``` -etc. - -I think this is the longest review comment I've ever written. \ No newline at end of file From fa9b4de1d392b17df4fa22d93ebd066795605fcc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 26 Feb 2020 12:45:58 +0000 Subject: [PATCH 5/7] Bump nokogiri from 1.10.7 to 1.10.8 Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.10.7 to 1.10.8. - [Release notes](https://github.com/sparklemotion/nokogiri/releases) - [Changelog](https://github.com/sparklemotion/nokogiri/blob/master/CHANGELOG.md) - [Commits](https://github.com/sparklemotion/nokogiri/compare/v1.10.7...v1.10.8) Signed-off-by: dependabot[bot] <support@github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 7683d97..2fc3bf0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -162,7 +162,7 @@ GEM nap (1.1.0) naturally (2.2.0) netrc (0.11.0) - nokogiri (1.10.7) + nokogiri (1.10.8) mini_portile2 (~> 2.4.0) os (1.0.1) plist (3.5.0) From 5ee59b95728235f5b7ce0f7615192b3a46d222c9 Mon Sep 17 00:00:00 2001 From: Steven Grosmark <steven.grosmark@weightwatchers.com> Date: Thu, 27 Feb 2020 08:53:14 -0500 Subject: [PATCH 6/7] Remove fastlane and slather from Gemfile These weren't being used --- Gemfile | 2 - Gemfile.lock | 168 +++++---------------------------------------------- 2 files changed, 14 insertions(+), 156 deletions(-) diff --git a/Gemfile b/Gemfile index 7453bf6..dc1c3b7 100755 --- a/Gemfile +++ b/Gemfile @@ -2,5 +2,3 @@ source 'https://rubygems.org' gem 'cocoapods', '>= 1.8.4' -gem 'fastlane', '>= 2.129.0' -gem 'slather', '>= 2.4.7' diff --git a/Gemfile.lock b/Gemfile.lock index 2fc3bf0..68b60c7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -7,19 +7,15 @@ GEM minitest (~> 5.1) thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) - addressable (2.7.0) - public_suffix (>= 2.0.2, < 5.0) algoliasearch (1.27.1) httpclient (~> 2.8, >= 2.8.3) json (>= 1.5.1) atomos (0.1.3) - babosa (1.0.3) claide (1.0.3) - clamp (1.3.1) - cocoapods (1.8.4) + cocoapods (1.9.0) activesupport (>= 4.0.2, < 5) claide (>= 1.0.2, < 2.0) - cocoapods-core (= 1.8.4) + cocoapods-core (= 1.9.0) cocoapods-deintegrate (>= 1.0.3, < 2.0) cocoapods-downloader (>= 1.2.2, < 2.0) cocoapods-plugins (>= 1.0.0, < 2.0) @@ -34,13 +30,15 @@ GEM molinillo (~> 0.6.6) nap (~> 1.0) ruby-macho (~> 1.4) - xcodeproj (>= 1.11.1, < 2.0) - cocoapods-core (1.8.4) + xcodeproj (>= 1.14.0, < 2.0) + cocoapods-core (1.9.0) activesupport (>= 4.0.2, < 6) algoliasearch (~> 1.0) concurrent-ruby (~> 1.1) fuzzy_match (~> 2.0.4) nap (~> 1.0) + netrc (~> 0.11) + typhoeus (~> 1.0) cocoapods-deintegrate (1.0.4) cocoapods-downloader (1.3.0) cocoapods-plugins (1.0.0) @@ -51,180 +49,42 @@ GEM nap (>= 0.8, < 2.0) netrc (~> 0.11) cocoapods-try (1.1.0) - colored (1.2) colored2 (3.1.2) - commander-fastlane (4.4.6) - highline (~> 1.7.2) - concurrent-ruby (1.1.5) - declarative (0.0.10) - declarative-option (0.1.0) - digest-crc (0.4.1) - domain_name (0.5.20190701) - unf (>= 0.0.5, < 1.0.0) - dotenv (2.7.5) - emoji_regex (1.0.1) + concurrent-ruby (1.1.6) escape (0.0.4) - excon (0.72.0) - faraday (0.17.3) - multipart-post (>= 1.2, < 3) - faraday-cookie_jar (0.0.6) - faraday (>= 0.7.4) - http-cookie (~> 1.0.0) - faraday_middleware (0.13.1) - faraday (>= 0.7.4, < 1.0) - fastimage (2.1.7) - fastlane (2.140.0) - CFPropertyList (>= 2.3, < 4.0.0) - addressable (>= 2.3, < 3.0.0) - babosa (>= 1.0.2, < 2.0.0) - bundler (>= 1.12.0, < 3.0.0) - colored - commander-fastlane (>= 4.4.6, < 5.0.0) - dotenv (>= 2.1.1, < 3.0.0) - emoji_regex (>= 0.1, < 2.0) - excon (>= 0.71.0, < 1.0.0) - faraday (~> 0.17) - faraday-cookie_jar (~> 0.0.6) - faraday_middleware (~> 0.13.1) - fastimage (>= 2.1.0, < 3.0.0) - gh_inspector (>= 1.1.2, < 2.0.0) - google-api-client (>= 0.29.2, < 0.37.0) - google-cloud-storage (>= 1.15.0, < 2.0.0) - highline (>= 1.7.2, < 2.0.0) - json (< 3.0.0) - jwt (~> 2.1.0) - mini_magick (>= 4.9.4, < 5.0.0) - multi_xml (~> 0.5) - multipart-post (~> 2.0.0) - plist (>= 3.1.0, < 4.0.0) - public_suffix (~> 2.0.0) - rubyzip (>= 1.3.0, < 2.0.0) - security (= 0.1.3) - simctl (~> 1.6.3) - slack-notifier (>= 2.0.0, < 3.0.0) - terminal-notifier (>= 2.0.0, < 3.0.0) - terminal-table (>= 1.4.5, < 2.0.0) - tty-screen (>= 0.6.3, < 1.0.0) - tty-spinner (>= 0.8.0, < 1.0.0) - word_wrap (~> 1.0.0) - xcodeproj (>= 1.13.0, < 2.0.0) - xcpretty (~> 0.3.0) - xcpretty-travis-formatter (>= 0.0.3) + ethon (0.12.0) + ffi (>= 1.3.0) + ffi (1.12.2) fourflusher (2.3.1) fuzzy_match (2.0.4) gh_inspector (1.1.3) - google-api-client (0.36.4) - addressable (~> 2.5, >= 2.5.1) - googleauth (~> 0.9) - httpclient (>= 2.8.1, < 3.0) - mini_mime (~> 1.0) - representable (~> 3.0) - retriable (>= 2.0, < 4.0) - signet (~> 0.12) - google-cloud-core (1.5.0) - google-cloud-env (~> 1.0) - google-cloud-errors (~> 1.0) - google-cloud-env (1.3.0) - faraday (~> 0.11) - google-cloud-errors (1.0.0) - google-cloud-storage (1.25.1) - addressable (~> 2.5) - digest-crc (~> 0.4) - google-api-client (~> 0.33) - google-cloud-core (~> 1.2) - googleauth (~> 0.9) - mini_mime (~> 1.0) - googleauth (0.10.0) - faraday (~> 0.12) - jwt (>= 1.4, < 3.0) - memoist (~> 0.16) - multi_json (~> 1.11) - os (>= 0.9, < 2.0) - signet (~> 0.12) - highline (1.7.10) - http-cookie (1.0.3) - domain_name (~> 0.5) httpclient (2.8.3) i18n (0.9.5) concurrent-ruby (~> 1.0) json (2.3.0) - jwt (2.1.0) - memoist (0.16.2) - mini_magick (4.10.1) - mini_mime (1.0.2) - mini_portile2 (2.4.0) minitest (5.14.0) molinillo (0.6.6) - multi_json (1.14.1) - multi_xml (0.6.0) - multipart-post (2.0.0) nanaimo (0.2.6) nap (1.1.0) - naturally (2.2.0) netrc (0.11.0) - nokogiri (1.10.8) - mini_portile2 (~> 2.4.0) - os (1.0.1) - plist (3.5.0) - public_suffix (2.0.5) - representable (3.0.4) - declarative (< 0.1.0) - declarative-option (< 0.2.0) - uber (< 0.2.0) - retriable (3.1.2) - rouge (2.0.7) ruby-macho (1.4.0) - rubyzip (1.3.0) - security (0.1.3) - signet (0.12.0) - addressable (~> 2.3) - faraday (~> 0.9) - jwt (>= 1.5, < 3.0) - multi_json (~> 1.10) - simctl (1.6.7) - CFPropertyList - naturally - slack-notifier (2.3.2) - slather (2.4.7) - CFPropertyList (>= 2.2, < 4) - activesupport (>= 4.0.2, < 5) - clamp (~> 1.3) - nokogiri (~> 1.8) - xcodeproj (~> 1.7) - terminal-notifier (2.0.0) - terminal-table (1.8.0) - unicode-display_width (~> 1.1, >= 1.1.1) thread_safe (0.3.6) - tty-cursor (0.7.1) - tty-screen (0.7.0) - tty-spinner (0.9.3) - tty-cursor (~> 0.7) + typhoeus (1.3.1) + ethon (>= 0.9.0) tzinfo (1.2.6) thread_safe (~> 0.1) - uber (0.1.0) - unf (0.1.4) - unf_ext - unf_ext (0.0.7.6) - unicode-display_width (1.6.1) - word_wrap (1.0.0) - xcodeproj (1.14.0) + xcodeproj (1.15.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) colored2 (~> 3.1) nanaimo (~> 0.2.6) - xcpretty (0.3.0) - rouge (~> 2.0.7) - xcpretty-travis-formatter (1.0.0) - xcpretty (~> 0.2, >= 0.0.7) PLATFORMS ruby DEPENDENCIES cocoapods (>= 1.8.4) - fastlane (>= 2.129.0) - slather (>= 2.4.7) BUNDLED WITH - 2.0.2 + 2.1.4 From 2b6dc7a0ee5787bf177ff0ffef67e93d9cf6e779 Mon Sep 17 00:00:00 2001 From: Steven Grosmark <steven.grosmark@weightwatchers.com> Date: Thu, 27 Feb 2020 09:06:13 -0500 Subject: [PATCH 7/7] Bump podspec version to 1.0.1 --- Lasso.podspec | 4 ++-- LassoTestUtilities.podspec | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Lasso.podspec b/Lasso.podspec index 5228a47..2b37e5a 100644 --- a/Lasso.podspec +++ b/Lasso.podspec @@ -5,7 +5,7 @@ Pod::Spec.new do |s| s.name = 'Lasso' - s.version = '1.0.0' + s.version = '1.0.1' s.summary = 'iOS architectural pattern and framework.' s.description = 'Lasso is an iOS application architecture for building discrete, composable and testable compenents both big and small - from single one-off screens, through complex flows, to high-level application structures.' @@ -16,7 +16,7 @@ Pod::Spec.new do |s| s.source = { :git => 'https://github.com/ww-tech/lasso.git', :tag => s.version.to_s } - s.swift_versions = '4.2', '5', '5.1' + s.swift_versions = '4.2', '5', '5.1', '5.2' s.ios.deployment_target = '10.0' s.source_files = 'Sources/Lasso/**/*' diff --git a/LassoTestUtilities.podspec b/LassoTestUtilities.podspec index 258f2f9..4d4ac5a 100644 --- a/LassoTestUtilities.podspec +++ b/LassoTestUtilities.podspec @@ -5,7 +5,7 @@ Pod::Spec.new do |s| s.name = 'LassoTestUtilities' - s.version = '1.0.0' + s.version = '1.0.1' s.summary = 'Unit test support for the Lasso framework.' s.description = 'Lasso is an iOS application architecture for building discrete, composable and testable compenents both big and small - from single one-off screens, through complex flows, to high-level application structures.' @@ -17,7 +17,7 @@ Pod::Spec.new do |s| s.source = { :git => 'https://github.com/ww-tech/lasso.git', :tag => s.version.to_s } s.pod_target_xcconfig = { 'ENABLE_BITCODE' => 'NO' } - s.swift_versions = '4.2', '5', '5.1' + s.swift_versions = '4.2', '5', '5.1', '5.2' s.ios.deployment_target = '10.0' s.framework = 'XCTest'