GithubHelp home page GithubHelp logo

pointfreeco / episode-code-samples Goto Github PK

View Code? Open in Web Editor NEW
933.0 933.0 287.0 30.03 MB

๐Ÿ’พ Point-Free episode code.

Home Page: https://www.pointfree.co

License: MIT License

Swift 99.75% Objective-C 0.11% Makefile 0.13% Dockerfile 0.01%

episode-code-samples's Introduction

๐ŸŽฌ www.pointfree.co

Swift 5.7 CI @pointfreeco

This repo contains the full source code for the Point-Free website, a video series exploring advanced programming topics in Swift. The codebase is split into 3 pieces:

  • PointFree: This is the core application, and is responsible for routing requests, loading data and rendering HTML and CSS.
  • Styleguide: This library contains functions and data types for creating a consistent style across the entire website.
  • Server: This is the actual executable server. It uses NIO to handle the low-level server responsibilities, and hands everything else over to the PointFree package.

Point-Free Homepage

Getting Started

Interested in a video tour of the code base?

video poster image

The repo contains an extensive test suite and some playgrounds to explore. To get things running:

  • Open up a terminal window and grab the code:

    git clone https://github.com/pointfreeco/pointfreeco.git
    cd pointfreeco
  • Make sure cmark is installed. You can install it with Homebrew:

    brew install cmark # or your preferred installation method
  • Make sure Postgres is installed and running. It's our database of choice. You can install it with Homebrew:

    brew install postgres # or your preferred installation method
    brew services start postgresql # or your preferred launch method
    make db

    (If you use Postgres.app, EnterpriseDB, or another installation method, please follow some additional instructions in the CPostgreSQL README.)

With the project open in Xcode, you can:

  • Run the server locally
    • Select the Server target
    • Run: Command+R
    • Visit http://localhost:8080
  • Explore our playgrounds
    • Select the PointFree target
    • Build: Command+B
    • Open a playground!

Some fun things to explore

There're a lot of fun things to explore in this repo. For example:

  • We develop web pages in playgrounds for a continuous feedback loop. This is made possible by the fact that the entire server stack is composed of pure functions with side-effects pushed to the boundaries of the application. It allows us to load up any request in isolation, including POST requests, all without ever worrying about doing a side-effect. Server side Swift in a playground

  • We use snapshot testing to capture full data structures in order to verify their correctness. Not only do we do this in the traditional way of taking screenshots of web pages at various break points (e.g. on iPhone and desktop), but we can also snapshot any entire request-to-response lifecycle (e.g. the POST to a signup page does the correct redirect).

โ–ฟ Step
  ResponseEnded

โ–ฟ Request
  POST http://localhost:8080/launch-signup

  [email protected]

โ–ฟ Response
  Status 302 FOUND
  Location: /?success=true

Xcode Color Theme

Like the color theme we use in our episodes? Run make colortheme to install locally!

Related projects

Point-Free uses a bunch of interesting open-source software:

  • ๐Ÿ—บ swift-html: A Swift DSL for type-safe, extensible, and transformable HTML documents.
  • ๐Ÿ•ธ swift-web: A collection of types and functions for dealing with common web server concerns, such as HTML render, CSS preprocessing, middleware and more.
  • ๐ŸŽถ swift-prelude: Offers a standard library for experimental functional programming in Swift.
  • ๐Ÿท swift-tagged: Helps us create strong contracts with our data boundaries, like JSON from GitHub and Stripe, and our PostgreSQL data.
  • ๐Ÿ“ธ swift-snapshot-testing: Powers our testing infrastructure by taking snapshots of various data structures to guarantee the correctness of their output. We use this on everything from middleware to ensure requests are correctly transformed into responses, and even entire web pages to make sure the site looks correct at a variety of sizes (e.g. on iPhone and desktop).

Explore more of our open-source on the Point-Free organization.

Learn More

Brandon gave a talk about most of the core ideas that went into this project at Swift Summit 2017.

The two sides of writing testable code

Find this interesting?

Then check out Point-Free!

License

The content of this project itself is licensed under the CC BY-NC-SA 4.0 license, and the underlying source code used to format and display that content is licensed under the MIT license.

episode-code-samples's People

Contributors

alephao avatar alex-ozun avatar cheeseonhead avatar geekanddad avatar haaakon avatar ibrahimkteish avatar ixrevo avatar jasdev avatar jharvey3 avatar kaandedeoglu avatar killectro avatar lacyrhoades avatar mayoff avatar mbrandonw avatar mokagio avatar pietrocaselani avatar rstone4-chwy avatar stephencelis avatar valeriyvan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

episode-code-samples's Issues

modularization folders architecture

tank you for your awesome work

i have a question about architecture all the folders and sub-folders, in modularization project.

what is the differences between this two approaches ?

first approach

we put each modules in separated module like bellow

|- Module-1
  |- Package.swift
  |- Source
    |- Module-1
  |- Tests
    |- Module-1-Tests
|- Module-2
  |- Package.swift
  |- Source
    |- Module-2
  |- Tests
    |- Module-3-Tests
|- Module-3
  |- Package.swift
  |- Source
    |- Module-3
  |- Tests
    |- Module-3-Tests
|- Module-4
  |- Package.swift
  |- Source
    |- Module-4
  |- Tests
    |- Module-4-Tests

we put each modules in one Root module like bellow

|- RootModule
  |- Package.swift
  |- Source
    |- Module-1
    |- Module-2
    |- Module-3
    |- Module-4
  |- Tests
    |- Module-1-Tests
    |- Module-3-Tests
    |- Module-3-Tests
    |- Module-4-Tests

Whats is Pro and Cons between this two approaches

Xcode issue ? when importing the modules ?
Conflict Dependencies if each module use same dependency example (Module1 & Module3) use same dependency (Alamofire) ?
Maintain ?
TimeBuild ?
Git ?

Handling long-running observers

I'm wondering how should we approach representing long-running observers as effects?

For example, NotificationCenter.default.publisher never completes which, I believe, makes it hard to represent as an Effect of composable architecture.

As a workaround, I'm currently creating and observing NotificationCenter's publisher in the SceneDelegate, and then sending an appropriate action into the store directly, since I have access to it from there.

That works, but it doesn't feel right and makes testing very hard. Any input would be greatly appreciated.

Episode 26 missing

Hey there! Just went to have a go at the exercises for episode 26 and noticed that the link to the playground was broken.

Episode #3 Question: Advantages of your approach over a virtual-DOM approach?

First off: I enjoyed episode 3. I really like how you compare different possible approaches to highlight the advantages of function composition.

But watching it, the next thing I would want to try would be to define something like a virtual DOM. (Is there a better term for that concept outside of the JavaScript world?)

struct ButtonStyle {
  var contentEdgeInsets: UIEdgeInsets?

  var backgroundColor: UIColor?
  var tintColor: UIColor?

  var borderColor: UIColor?
  var borderWidth: CGFloat?

  init(contentEdgeInsets: UIEdgeInsets? = nil, โ€ฆ) {
    โ€ฆ
  }
}

This would be a monoid, so you'd retain the same composition with your functions.

There are a few drawbacks to this:

  1. You have to write this for all the properties you use
  2. It could add to your codegen size and memory usage
  3. You have to deal with inheritance (maybe adding a var view: ViewStyle property)

But it also has some potential advantages:

  1. Most testing no longer requires actual views
  2. Fewer code blocks mean fewer opportunities for effects to sneak in

So I'm curious:

  1. Was this on your radar when you made the episode?
  2. Do you think the approach you outlined has other advantages over this?
  3. Do you have any general thoughts on it?

dyld: Library not loaded: @rpath/WeatherClient.framework/WeatherClient 0114-designing-dependencies-pt5

remove framework for view and add the swift package for view and getting this error on a real device

Apple Swift version 5.3.2 (swiftlang-1200.0.45 clang-1200.0.32.28)
Target: x86_64-apple-darwin20.3.0
XCODE: Version 12.4 (12D4e)

dyld: Library not loaded: @rpath/WeatherClient.framework/WeatherClient
  Referenced from: /private/var/containers/Bundle/Application/714B6D18-5451-4ED0-B753-87050889ED83/DesigningDependencies.app/Frameworks/WeatherFeature.framework/WeatherFeature
  Reason: image not found
dyld: launch, loading dependent libraries
DYLD_LIBRARY_PATH=/usr/lib/system/introspection
DYLD_INSERT_LIBRARIES=/Developer/usr/lib/libBacktraceRecording.dylib:/Developer/usr/lib/libMainThreadChecker.dylib:/Developer/Library/PrivateFrameworks/DTDDISupport.framework/libViewDebuggerSupport.dylib

DesigningDependencies.zip

Nesting components in themselves within Composable Architecture

I've been giving the Composable Architecture a try and on the whole it feels great. However, I have run into an issue that I just cannot resolve and it regards components that can contain values of their own type.

In my situation I have a Folder component. A Folder contains a list of items. Item is an enum where one of the cases is Folder. So a folder can contain a sub-folder. So I have something that looks like this:

struct Folder {
   var items: [Item]
}

enum Item {
  case document(Document)
  case folder(Folder)
}

enum FolderAction {
  case addItem
}

enum FolderViewAction {
  case folder(FolderAction)
  case document(DocumentViewAction)
  case subFolder(FolderViewAction)
}

let folderReducer: Reducer<Folder, FolderAction, Void> = { value, action, _ in 
  switch action {
    case .addItem:
      value.items.append(Bool.random() ? .document(Document()) : .folder(Folder()))
      return []
  }
}

let folderViewReducer = compose(
  // these are the exact reducers as I have a special reducer for 'pulling back' 
  // into the array element, but it illustrates the point
  pullback(
    folderReducer,
    value: \.self,
    action: /FolderViewAction.folder,
    environment: { _ in () }
  ),
  pullback(
    folderViewReducer, // *** Variable used within it's own initial value *** //
    value: \.folder,
    action: /FolderViewAction.subFolder,
    environment: { _ in () }
  ),
  pullback(
    documentViewReducer,
    value: \.document
    action: /FolderViewAction.document,
    environment: { _ in () }
  )
)

Seeing as the reducers are all defined at compile time I just cannot see a way define this kind of structure. I'm probably going about this all wrong.

It would be great if someone could shed some light on this. I can imagine this circular reference situation quite common. Another example I can think of from the top of my head would be user profile that lists friends, which would then view their profile, etc, etc.

Store shouldn't store environment

Store.init receives a reducer as an argument, and an environment object appropriate to that reducer. It saves the given environment in a property of type Any, and wraps the given reducer in a new closure that casts an Any-typed environment back to the appropriate type.

Instead, the closure can capture the environment with the appropriate type, and pass it to the given reducer with no casting.

Please see this diff.

UIButton Styling with Functions

Hi,

I enjoyed a lot episode #3 about uikit styling and wanted to introduce this concept in my codebase. While trying to convert from inheritance to functions, I stumbled to one use case.

@IBDesignable final public class ActionButton: BaseButton {
    override public func commonInit() {
        super.commonInit()
        //Some styling
        addTarget(self, action: #selector(addFeedback), for: .touchDown)
    }
    
    @objc private func addFeedback() {
        let feedback = UISelectionFeedbackGenerator()
        feedback.selectionChanged()
    }
}

To convert styling is easy (basically everywhere where it's used ActionButton, changing to UIButton and apply specified style), but I couldn't think of a nice solution which will allow me with one function to enable haptic feedback on pressing button

Do you have any ideas how this could be implemented in a functional way?

Episode 21 missing

Hi guys, I tried to download the playground driven development example, but it's missing, so the url in the video details is broken.

@FocusState not working with new @BindableState API

In episode 155, the new @FocusState API was explored and an approach using the now deprecated .binding(action:) higher order reducer was demonstrated. Below is the working code snippet:

struct LoginState: Equatable {
  var focusedField: Field? = nil
  var password: String = ""
  var username: String = ""

  enum Field: String, Hashable {
    case username, password
  }
}

enum LoginAction {
  case binding(BindingAction<LoginState>)
  case signInButtonTapped
}

struct LoginEnvironment {
}

let loginReducer = Reducer<
  LoginState,
  LoginAction,
  LoginEnvironment
> { state, action, environment in
  switch action {
  case .binding:
    return .none

  case .signInButtonTapped:
    if state.username.isEmpty {
      state.focusedField = .username
    } else if state.password.isEmpty {
      state.focusedField = .password
    }
    return .none
  }
}
.binding(action: /LoginAction.binding)

struct TcaLoginView: View {
  @FocusState var focusedField: LoginState.Field?
  let store: Store<LoginState, LoginAction>

  var body: some View {
    WithViewStore(self.store) { viewStore in
      VStack {
        TextField(
          "Username",
          text: viewStore.binding(keyPath: \.username, send: LoginAction.binding)
        )
          .focused($focusedField, equals: .username)

        SecureField(
          "Password",
          text: viewStore.binding(keyPath: \.password, send: LoginAction.binding)
        )
          .focused($focusedField, equals: .password)

        Button("Sign In") {
          viewStore.send(.signInButtonTapped)
        }

        Text("\(String(describing: viewStore.focusedField))")
      }
      .synchronize(
        viewStore.binding(keyPath: \.focusedField, send: LoginAction.binding),
        self.$focusedField
      )
    }
  }
}

Upgrading the code to use the new @BindableState, BindableAction API's, we get the following:

struct LoginState: Equatable {
  @BindableState var focusedField: Field? = nil
  @BindableState var password: String = ""
  @BindableState var username: String = ""
  
  enum Field: String, Hashable {
    case username, password
  }
}

enum LoginAction: BindableAction {
  case binding(BindingAction<LoginState>)
  case signInButtonTapped
}

struct LoginEnvironment {
}

let loginReducer = Reducer<
  LoginState,
  LoginAction,
  LoginEnvironment
> { state, action, environment in
  switch action {
    
  case .binding(\.$focusedField):
    return .none
    
  case .binding:
    return .none
    
  case .signInButtonTapped:
    if state.username.isEmpty {
      state.focusedField = .username
    } else if state.password.isEmpty {
      state.focusedField = .password
    }
    return .none
  }
}

struct TcaLoginView: View {
  @FocusState var focusedField: LoginState.Field?
  let store: Store<LoginState, LoginAction>
  
  var body: some View {
    WithViewStore(self.store) { viewStore in
      VStack {
        TextField(
          "Username",
          text: viewStore.binding(\.$username)
        )
          .focused($focusedField, equals: .username)
        
        SecureField(
          "Password",
          text: viewStore.binding(\.$password)
        )
          .focused($focusedField, equals: .password)
        
        Button("Sign In") {
          viewStore.send(.signInButtonTapped)
        }
        
        Text("\(String(describing: viewStore.focusedField))")
      }
      .synchronize(viewStore.binding(\.$focusedField), self.$focusedField)
    }
  }
}

When running the application, tapping on a TextField no longer updates the Text view that displays the name of the focused field.

Episode 20: there might be an error in the implementation of subscript using an `Int` index.

When a Collection's Index is of type Int, there's no guarantee that the valid indices of this object starts with 0.
So, I think in the following implementation:

extension NonEmpty where C: MutableCollection, C.Index == Int {
  subscript(position: Int) -> C.Element {
    get {
      return self[position == 0 ? .head : .tail(position - 1)]
    }
    set(newValue) {
      self[position == 0 ? .head : .tail(position - 1)] = newValue
    }
  }
}

codes like .tail(position - 1) does not make sense in some situations.

The correct way to do it, I believe, is to restrict C to be conforming to RandomAccessCollection, and rely on index(_:offsetBy:) to get the actual valid index in self.tail.

Parallel should be named Promise

Hey guys! I just stumbled upon your content and I love the ideas you are proposing - they really get my gears turning with some new ways to think about code. I noticed watching your episode you had trouble thinking of a name for the concept of F3 and I just wanted to point out that the Promise type from other languages like Javascript is eerily similar to this concept of Parallel, or F3 with the only difference being that promises separate a success and a failure response into a formal construct while the parallel type guarantees a reply unless the inner type is optional. Even with this slight syntactic difference, I think the construct is identical.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

Wrong git modules config

I tried using "git submodule update --init" as the instructions said, but I got this error:

fatal: No url found for submodule path '../0017-styling-pt2/Vendor/swift-overture' in .gitmodules

I had to change the content of .gitmodules to fix it:

[submodule "xyzw-templating-languages/Vendor/Stencil"]
	path = xyzw-templating-languages/Vendor/Stencil
	url = https://github.com/stencilproject/Stencil.git

[submodule "0017-styling-pt2/Vendor/swift-overture"]
	path = 0017-styling-pt2/Vendor/swift-overture
	url = https://github.com/pointfreeco/swift-overture.git
	
[submodule "0018-environment-pt2/Vendor/swift-overture"]
	path = 0018-environment-pt2/Vendor/swift-overture
	url = https://github.com/pointfreeco/swift-overture.git
	
[submodule "0021-playground-driven-development"]
	path = 0021-playground-driven-development/Vendor/swift-overture
	url = https://github.com/pointfreeco/swift-overture.git
	
[submodule "0024-zip-pt2/Vendor/swift-overture"]
	path = 0024-zip-pt2/Vendor/swift-overture
	url = https://github.com/pointfreeco/swift-overture.git

	
[submodule "0024-zip-pt2/Vendor/swift-nonempty"]
	path = 0024-zip-pt2/Vendor/swift-nonempty
	url = https://github.com/pointfreeco/swift-nonempty.git

[submodule "0025-zip-pt3/Vendor/swift-nonempty"]
	path = 0025-zip-pt3/Vendor/swift-nonempty
	url = https://github.com/pointfreeco/swift-nonempty.git

[Modular State Management] Best way to "view" into an element of an array?

Learning a lot from the modular state management series. I have a question with regards to "viewing" into State and Action discussed in episode #74.

What I'm trying to do is this:
I have an list of projects (ProjectsView), that I can click on and get more details about a single project.
And I want to achieve so that the ProjectView only have access to that one project (store: Store<Project, ProjectAction>).

struct AppState {
  var projects: [Project] = []
}

I can do this to pass down the value.

struct ProjectsView: View {
  @ObservedObject var store: Store<[Project], ProjectsViewAction>
  
  public var body: some View {
    NavigationView {
      List {
        ForEach(store.value.indices) { index in
          NavigationLink(
            destination: ProjectView(
              store: self.store.view(
                value: { $0[index] },
                action: { .project($0) }
              )
            )
          ) { Text(self.store.value[index].name) }
        }
      }
    }
  }
}

But then we don't have enough information to pullback from a single project to the projects array. pullback(projectReducer, value: \.project, action: \.project).

My solution so far is to add a project property in AppState and update the corresponding element in the array when this element is updated:

struct AppState {
  var project: Project? = nil {
    didSet {
      guard
        let index = projects.firstIndex(where: { $0.id == oldValue?.id }),
        let project = oldValue else { return }
        projects[index] = project
    }
  }
  var projects: [Project] = []
}

But this doesn't feel quite right. Is there any better solutions to achieve this? It seems like a common case to go from a list of things and view into one single element of that list. So would appreciate some input. ๐Ÿ™๐Ÿผ

[Question] Why Actions are nested?

Hi!
I watched a few episodes regarding app architecture so I probably miss something. But my question is why actions in your architecture are nested? JS Redux uses global actions and they are plain for a reason.

Here is an example from your code:
.offlineCounterView(.primeModal(.saveFavoritePrimeTapped))

This is breaking the Law of Demeter which leads to tight coupling and makes refactoring pretty hard (you will always have to refactor N-1 and N+1 levels).
Also how it will be possible to handle some action on more than one reducer? Let's say logOutAction - it should probably be handled by different reducers.

Thanks in advance!

[Suggestion] Separate Side Effects from reducer

Hi guys! It's hard to underestimate your impact on the community. However, I still didn't get the idea of managing side effects inside the reducer. Of course, it has some pros. However, for the bigger projects than the example from videos, it looks like more pros would have separation of side effects from a reducer.

Before watching your videos, I've already started to work with the unidirectional approach as rxfeedback and it shows for IMHO better way for dealing with side effects.

So, what separation can do for the architecture:

  • Less code in one place. Simple changes for the state over reducer and one service call in side effects
  • Bigger separation from the state itself and actions (If we introduce query-based side effects, the state should always reflect "state" of the app) Right now it's possible to perform a side effect only based on action.
  • Better testability. It's possible to test reducer separately from actions + actions are tests like a store (system) of all blocks together
  • Migration between architectures. When all pieces are separate it's much easier to go from rxfeedback to redux or even mvvm if needed
  • Reducer doesn't know of any dependencies via the Environment approach or any other. It seems more legit to call reducer a pure function without any dependencies around, which could influence result of reducer as an array of side effects.

Below, I'll show some examples from my project written with rxfeedback. This is User Page (I removed some parts for simplification):

Reducer + State + Events (Actions)

struct UserProfileState: Equatable {
    var user: User?
    var teams: [FollowedTeam]?
    var tab: Tab = .following
    var fieldsForUpdate = Set<EditableField>()
    var eventsToLog = Set<EventTracker.Event>()
    var outputEvent: OutputEvent?
}

enum UserProfileEvent: Equatable {
    case eventLogged(EventTracker.Event)
    case fieldWasUpdated(UserProfileState.EditableField)
    case followStatusChanged(forTeamId: String)
    case inputEvent(InputEvent)
    case sceneShown
    case tabWasChanged(UserProfileState.Tab)
    case teamSelected(id: String)
    case teamsLoaded([Team])
    case userLoaded(User?)
}

// MARK: - Queries
extension UserProfileState {
    var queryLoadUser: Void? {
        (user == nil) ? () : nil
    }

    var queryUnfollowTeams: Set<String> {
        guard let teams = teams else { return [] }
        return teams
            .filter((!) <<< ^\.isFollowed)
            .map(^\.team.id)
            |> Set.init
    }

    var queryFollowTeams: Set<String> {
        guard let teams = teams else { return [] }
        return teams
            .filter(^\.isFollowed)
            .map(^\.team.id)
            |> Set.init
    }
}

// MARK: - Reducer
extension UserProfileState {
    static func reduce(state: inout UserProfileState, event: UserProfileEvent) {
        switch event {
        case .eventLogged(let event):
            state.eventsToLog.remove(event)
        case .fieldWasUpdated(let field):
       // some work
        case .followStatusChanged(let teamId):
       // some work
        case .sceneShown:
            state.eventsToLog.insert(EventTracker.Event.UserProfileScreen.pageView())
        case .tabWasChanged(let tab):
            state.tab = tab
        case .teamsLoaded(let teams):
            state.teams = teams.map { FollowedTeam(team: $0, isFollowed: true) }
        case .userLoaded(let user):
            state.user = user
        }
    }
}

SideEffects

struct UserProfileSideEffects {
    private let follow: (_ id: String) -> Completable
    private let loadFollowedTeams: (_ userId: String) -> Single<[Core.Team]>
    private let logEvent: EventTracker.LogEvent
    private let unfollow: (_ id: String) -> Completable
    private let update: (_ userField: User.EditableField) -> Completable
    private let userDownloadService: () -> Single<User?>
}

extension UserProfileSideEffects {
    var feedbackLoops: [SideEffect<UserProfileState, UserProfileEvent>] {
        return [
            react(request: ^\.queryLoadUser, effects: loadUser),
            react(request: ^\.user?.id, effects: loadFollowedTeams),
            react(requests: ^\.eventsToLog, effects: logEvents),
            react(requests: ^\.fieldsForUpdate, effects: updateUserField),
            react(requests: ^\.queryFollowTeams, effects: followTeam),
            react(requests: ^\.queryUnfollowTeams, effects: unfollowTeam)
        ]
    }
}

private extension UserProfileSideEffects {

    func followTeam(id: String) -> Observable<UserProfileEvent> {
        follow(id)
            .asObservable()
            .flatMap { _ in Observable<UserProfileEvent>.empty() }
    }

    func loadFollowedTeams(userId: String) -> Observable<UserProfileEvent> {
        loadFollowedTeams(userId)
            .map(UserProfileEvent.teamsLoaded)
            .asObservable()
    }

    func logEvents(event: EventTracker.Event) -> Observable<UserProfileEvent> {
        logEvent(event)
        return .just(.eventLogged(event))
    }

    func unfollowTeam(id: String) -> Observable<UserProfileEvent> {
        unfollow(id)
            .asObservable()
            .flatMap { _ in Observable<UserProfileEvent>.empty() }
    }

    func loadUser() -> Observable<UserProfileEvent> {
        userDownloadService()
            .map(UserProfileEvent.userLoaded)
            .asObservable()
    }

    func updateUserField(field: UserProfileState.EditableField) -> Observable<UserProfileEvent> {
        update(field |> Core.User.EditableField.init)
            .asObservable()
            .flatMap { _ in Observable<UserProfileEvent>.empty() }
    }
}

I think that reducer testing is obvious. Also, for side effects and all store systems, I've been using similar test approach as point free. I will show, that testing store with side effects separation as the same easy, as your custom assert.

Store test example

    func testThatAfterUserLoadedWillTeamsLoad() {
        // Given
        let user = UserBuilder().build()
        let team = TeamBuilder().build()
        // When
        let result = StateStore(
            scheduler: scheduler,
            disposeBag: disposeBag,
            feedback: [.userLoaded(user)],
            loadFollowedTeams: { userId in
                XCTAssertEqual(userId, user.id)
                return .just([team])
        }
        )
            .start()
            .map(^\.value.element?.teams)
        // Then
        XCTAssertEqual(result, [nil, nil, [.init(team: team, isFollowed: true)]])
    }

Optional Substate

Hi,

i've really enjoyed the series about the composable architecture. Right now i'm applying it to a project and got stuck with a problem regarding optional substates.
Considering the following example:

struct AppState {
    var modalState: ModalState? = nil
}

func modalReducer(state: inout ModalState, action: AppAction) -> Effect<AppAction> {
    fatalError("unimplemented")
}

func optionalize<State, Action>(
    reducer: @escaping (inout State, Action) -> Effect<Action>
) -> (inout State?, Action) -> Effect<Action> {
    fatalError("Unimplemented")
}

let appReducer = pullback(optionalize(reducer: modalReducer), value: \AppState.modalState)

The above modalReducer is now able to work with non-optional state which is great. But as soon i try to view into the store to present a modal dialog the following problem occurs:

let localStore = store.view { $0.modalState } // Store<ModalState?, AppAction>

My goal is to have a local store that works on a non-optional state (Imagine the ModalState / modalReducer are living in their own module and should therefore not have to deal with optionals).

It would be great if you have any input about this kind of problem.

[suggestion] Add scope to ViewState

Adding the scope function allows views to deal exclusively with ViewState objects. This simplifies usage because Views are passed the object they actually use rather than having to transform the Store into a ViewStore. This also simplifies ownership.

I have also added utility methods to ViewStore for bindings that allow snapshot tests to report full coverage. For example in Counter.swift line 183

    Button("+") { self.viewStore.send(.incrTapped) } 

would not be reported as full coverage because of the closure while this is

    Button("+", action: self.viewStore.curry(.incrTapped)) 

The same with CounterView_macO line 57

.popover(
isPresented: Binding(
get: { self.viewStore.value.isPrimePopoverShown },
set: { _ in self.viewStore.send(.primePopoverDismissed) }
)

becomes

.popover( isPresented:
  self.viewStore.bind(/.isPrimePopoverShown,\.primePopoverDismissed)
) {

I have a fork with the changes here,

https://github.com/toddwbates/episode-code-samples

Issue with running TCA on an M1 device.

I am building an app that uses TCA, and I recently purchased the new iMac with the M1. I can successfully build and run the project on my MacBook Pro, but when I try to run the same code, same branch, same device (all updated to the same versions), I get a build error on the M1.
macOS Big Sur 11.3.1
Xcode 12.5
iOS: 14.5.1
Code

Error message on line 23:
Cannot convert value of type 'Store<IdentifiedArray<UUID, HotlistSelection>, HotlistSelector.ListActionData>' (aka 'Store<IdentifiedArray<UUID, HotlistSelection>, (id: UUID, action: HotlistManagementAction.List)>') to expected argument type 'Store<IdentifiedArray<UUID, HotlistSelection>, (UUID, HotlistManagementAction.List)>'

I suspect this has to do with building the library on ARM. Does anyone have a workaround?

Episode 84 crash on iOS 13

Hi,

The test helper introduced in this week episode crash on iOS 13.3 (today Xcode update) while running tests on the Counter module.
This is due to the subscription being discarded, saving it to a let solves the issue but still raises an unused warning.

Recursions in Parsers

Hello there,

I know TCA is where most of your efforts are these days, but I've been rewatching your parsing collection and have run into a block. I'm hoping you can get me past that. I'm curious, how should one handle mutually recursive parsers? For example:

let value = oneOf([
    double.map(Value.number),
    array.map { _, elements, _ in Value.array(elements) }
])

let array = zip(
    literal("["),
    zeroOrMore(value, separatedBy: literal(",")),
    literal("]")
)

(Think of this as a simplified JSON.) Arrays have zero or more elements and those elements can also be arrays. The compiler (correctly) complains about a circular reference here. How does one typically get around this type of issue? It's sure to come up frequently even with relatively simple DSLs, like expressions.

Thanks for any pointers on this.

[Modular State Management] Best way to "view" into an element of an array?

Learning a lot from the modular state management series. I have a question with regards to "viewing" into State and Action discussed in episode #74.

What I'm trying to do is this:
I have an list of projects (ProjectsView), that I can click on and get more details about a single project.
And I want to achieve so that the ProjectView only have access to that one project (store: Store<Project, ProjectAction>).

struct AppState {
  var projects: [Project] = []
}

I can do this to pass down the value.

struct ProjectsView: View {
  @ObservedObject var store: Store<[Project], ProjectsViewAction>
  
  public var body: some View {
    NavigationView {
      List {
        ForEach(store.value.indices) { index in
          NavigationLink(
            destination: ProjectView(
              store: self.store.view(
                value: { $0[index] },
                action: { .project($0) }
              )
            )
          ) { Text(self.store.value[index].name) }
        }
      }
    }
  }
}

But then we don't have enough information to pullback from a single project to the projects array. pullback(projectReducer, value: \.project, action: \.project).

My solution so far is to add a project property in AppState and update the corresponding element in the array when this element is updated:

struct AppState {
  var project: Project? = nil {
    didSet {
      guard
        let index = projects.firstIndex(where: { $0.id == oldValue?.id }),
        let project = oldValue else { return }
        projects[index] = project
    }
  }
  var projects: [Project] = []
}

But this doesn't feel quite right. Is there any better solutions to achieve this? It seems like a common case to go from a list of things and view into one single element of that list. So would appreciate some input. ๐Ÿ™๐Ÿผ

Episode Structs and Enums

let choice: (id: Int | param: String) = .param("Blob")

How can this compile? Am I missing something? Swift doesn't support this right?

episode #64: oneOf can also use variadic argument

Just a small improvement...

func oneOf<A>(_ ps: Parser<A>...) -> Parser<A> {
    return Parser<A> { str -> A? in
        for p in ps {
            if let match = p.run(&str) {
                return match
            }
        }
        return nil
    }
}

let accounting = oneOf(money, moneyLoss)

Unit test don't compile

I am unable to run the unit tests from the PrimeTime target because CounterTests and PrimeTime tests don't compile.

[Suggestion] pipe and compose operator for mutator function

I just subscribe your episodes. It's great!

In Episode #2 and Episode #3, you introduce |> and <> to pipe and compose for mutator functions.

I have some thoughts about it. This is just suggestion and leaving for discussion.

1. Using |!> and >!> to instead of |> and <> for mutator functions

// from this
config |> inoutDecimalStyle <> inoutCurrencyStyle

// to this
config |!> inoutDecimalStyle >!> inoutCurrencyStyle
  • |!> is mutator version of |>
  • >!> is mutator version of >>>

The operators ending with !> (e.g. |!> and >!>) indicate that they are mutator operators which introduce mutations (or side effects). When you need to find those mutator operators in the project, this design would be much useful and helpful by searching with regex.

The ! in the operators indicates:

  • It will handle a mutator function which return type is Void;
  • it makes the mutator functions return their mutated input values, and
  • it still is pipeable/chainable with following |>

2. Make |!> really pipeable/chainable with following |>

In the original implementation, the following code doesn't work:

let button = config 
  |!> inoutDecimalStyle >!> inoutCurrencyStyle
  |> buildButtonWithStyle      // |!> doesn't return any value

To make |!> pipeable/chainable just like fluent API:

// from this
func |!> <A>(a: inout A, f: (inout A) -> Void) -> Void {
  f(&a)
}

// to this
func |!> <A>(a: inout A, f: (inout A) -> Void) -> A {
  f(&a)
  return a  // return the mutated input value
}

Now you can chain up the |!> with |>

let button = config 
  |!> inoutDecimalStyle >!> inoutCurrencyStyle
  |> buildButtonWithStyle

Note: Yon can't chain up multiple |!> with value type mutators, because the return value of the first |!> is immutable which can't pipe into the inout mutator again. You need to compose those value type mutators first and pipe:

config |!> inoutDecimalStyle >!> inoutCurrencyStyle  // OK
config |!> inoutDecimalStyle |!> inoutCurrencyStyle  // Error: left side of mutating operator isn't mutable: '|!>' returns immutable value

3. Make |!> support ref type mutators

Overload the mutator operators to support ref types:

func |!> <A: AnyObject>(a: A, f: (A) -> Void) -> A {
  f(a)
  return a
}
func >!> <A: AnyObject>(
  f: @escaping (A) -> Void,
  g: @escaping (A) -> Void)
-> ((A) -> Void) {

  return { a in
    f(a)
    g(a)
  }
}

It makes you possibly chain the ref type mutator functions as following:

// from this
let emailField = UITextField()
emailTextFieldStyle(emailField)

let passwordField = UITextField()
passwordTextFieldStyle(passwordField)

// to this
let emailField = UITextField()
  |!> emailTextFieldStyle

let passwordField = UITextField()
  |!> passwordTextFieldStyle
  |!> otherRefTypeMutatorIfYouWant
  |> yesYouCanPipeWithPureFunctions

It's possible chaining up multiple |!> with ref type mutator functions, because the mutator functions don't use inout.

Conclusion

Using mutator operators indicates that it will pipe or compose mutator functions with Void return type into the point-free style, including:

  • A value type mutator function which is (inout A) -> Void
  • A ref type mutator function which is (A: AnyObject) -> Void

Using different symbols for mutator operators would be more readable and maintainable. Leave |> and <> for its original meaning (pure pipe and semigroup concat).

For best practice, you should name the mutator functions with prefix set (mutate the state of the data object):

// from this
let button = config 
  |!> inoutDecimalStyle >!> inoutCurrencyStyle

let emailField = UITextField()
  |!> emailTextFieldStyle

// to this
let button = config
  |!> setDecimalStyle >!> setCurrencyStyle   // value type setter/mutator

let emailField = UITextField()
  |!> setEmailTextFieldStyle   // ref type setter/mutator

LocalStore has action that only GlobalStore can handle

I am wondering if it is possible for a localStore have an action that it does not know what to do with, but passes it up to the globalStore / reducer.

image a grocery list 'app'

AppState {
  let groceryItems: [Item]
  var shoppingCart: [Item] = []
}

GroceryItemState {
  let item: [Item]
}

GroceryItemAction {
  case addItemToCart
}

Selecting one of these items in the AppState brings up a detail view for the grocery item. Now in this detail view, there is an 'add to cart button' that should add the item to the AppState's shoppingCart. The only way I can think of doing this is to add the shoppingCart to the GroceryItemState, but this does not seem right to me? Am I missing something?

[0171-modularization-pt1] Setting Up

I came back to this exciting episode to review how to actually perform the magic of getting an existing project involved with Swift packages. However, I'm a bit confused about the "existing" part. From what I saw, you had an existing project "SwiftUINavigation" that resided in a new directory called "Inventory", and it is in that directory that you ran swift package init. I too did this and it worked, but I'm left with the package files outside of git management from my existing project. How did you reconcile that? Is there some magic here I am missing (git or otherwise)?

I tried moving my project into a new directory and then doing the steps, but I failed to change the parent directory name (so I had Foo/Foo) which at first appeared to work, but I think there is some funky name collision stuff happening now that is causing me all sorts of grief that I did not have on my first attempt. My third attempt will be to again do the move, but this time making sure to change the name of the parent directory.

Curious about your thoughts on how this is working and should work. How safe do you think this kind of setup will be in the future? (Additional magic is the Package.swift file used to hide the duplicated original project tree).

Composable Architecture - Returning errors in Effects for Unhappy Cases

How do I return errors from failed Futures?

The nthPrime function returns an Effect<Int?>, the optional signaling the unhappy case, but what if we want to return the error received from the API call itself? Currently wolframAlpha replaces errors with nil. I think I want to return the error from my API callback, but currently as it is I have to switch on the API's failure case and then call .success(nil), which isn't ideal. What's the recommended way of dealing with this scenario?

refreshVideos: {
    Future { callback in
        Network.shared.apollo.fetch(query: AllVideosQuery()) { result in
            switch result {
            case .success(let query):
                if let videos = query.data?.videosList.items.compactMap({ videoFrom($0.fragments.videoDetails) }) {
                    callback(.success(videos))
                } else {
                    callback(.success([]))
                }
            case .failure(_):
                // properly error?
                callback(.success([]))
            }
        }
    }.eraseToEffect()

Sorry if I'm missing something obvious!

Thread 1: Simultaneous accesses to 0x600003a707e0, but modification requires exclusive access

Following Episode 102 I've tried to replicate .concatenate operator but once I try to check a second todo item app crashes and the issue is directly related with cancel.

Screen Shot 2020-08-08 at 8 12 04 PM

here is my reducer:


let appReducer = Reducer<AppState, AppAction, AppEnvironment>.combine(
    todoReducer.forEach(state: \AppState.todos,
                        action: /AppAction.todo(index:action:),
                        environment: { _ in TodoEnvironment() }
    ),
    Reducer { state, action, environment in
        switch action {
        case .addButtonTapped:
            state.todos.insert(Todo(id: environment.uuid()), at: 0)
            return .none
        case .todo(index: _, action: .checkboxTapped):
            struct CancelDelayId: Hashable { }
            
            return .concatenate(
              Effect.cancel(id: "todo completion effect"),
              Effect(value: .todoDelayCompleted)
                .delay(for: 1, scheduler: DispatchQueue.main)
                .eraseToEffect()
                .cancellable(id: "todo completion effect")
            )
        case .todo(index: let index, action: let action):
            return .none
        case .todoDelayCompleted:
            state.todos = state.todos
              .enumerated()
              .sorted(by: { lhs, rhs in
                (rhs.element.isComplete && !lhs.element.isComplete) || lhs.offset < rhs.offset
              })
              .map(\.element)
            return .none
        }
        
    }
)

Capturing effectful callbacks in an Effect

I hit a case that I can't figure out, and for which I'm not sure there is a good answer.

If I have an effect which captures a callback, (A -> B) -> Effect<Never>, which will run continuously and repeatedly invoke the callback, then this functionality is now outside the reducer world.

This is okay if my callback is a pure function and I write lots of tests for it. However, if the computation depends on side effects, it poses a number of problems. In particular, the callback may depend on the state from the reducer, and also any effects that it does run may need to feedback into the reducer.

The only way I see to partially rectify that is to pass a scoped store into the closure such that a getState and send function can be provided. However, not even that helps the case where the effects may be asynchronous and the closure clearly needs to provide synchronous response. That leads to thoughts about promises/futures or other more traditional concurrency tools
that live firmly outside the reducer architecture.

We hit this in the case of a micro HTTP server that we need to host, but it is certainly a more general problem. For our case we were able to make the callback pure. If that were to change, we'd likely still model the HTTP service layer compositionally, but it would represent unattached state in our application.

Any thoughts are welcome. This is the demo code I put together to demonstrate the issue.

func never<A>(_ _: Never) -> A {}

struct Effect<A> { let run: (@escaping (A) -> Void) -> Void }

extension Effect {
    func map<B>(_ f: @escaping (A) -> B) -> Effect<B> {
        Effect<B> { cb in self.run { cb(f($0)) }}
    }
}

struct Reducer<S,A> { let reduce: (inout S, A) -> [Effect<A>] }

struct Store<S,A> {
    let send: (A) -> Void

    init(_ initialState: S, _ reducer: Reducer<S,A>) {
        var state = initialState

        func send(_ action: A) {
            reducer.reduce(&state, action)
                .forEach { $0.run(send) }
        }

        self.send = send
    }
}

// -----------------------------------------------------------

// Vendor API, in this case an HTTP service
struct Service {
    let listen: ((String) -> (Int, String)) -> Void
}

// -----------------------------------------------------------

struct Environment {
    // An opaque handle for mocking purposes
    struct ServiceHandle {
        // An effect to wrap the vendor listen API. This takes
        // a callback that will be invoked repeatedly.
        var listen: (@escaping (String) -> (Int, String)) -> Effect<Never>
    }

    // Start/create to get an instance of the opaque handle
    var start: Effect<ServiceHandle>

    // Some other effect
    var readFile: (String) -> Effect<String>
}

extension Environment.ServiceHandle {
    static let mock = Environment.ServiceHandle(
        listen: { serviceCallback in
            Effect { _ in
                let request = "GET /index.html"
                print("got request: \(request)")
                let response = serviceCallback(request)
                print("sending response: \(response)")
            }
        }
    )
}

extension Environment {
    static let mock = Environment(
        start: Effect { callback in
            callback(.mock)
        },
        readFile: { fname in Effect {
            print("load: \(fname)")
            $0("abkjhckjakjh") }
    })
}

var Current = Environment.mock

struct ServiceState {
    var service: Environment.ServiceHandle? = nil
}

enum ServiceAction {
    case onLoad
    case started(Environment.ServiceHandle)
}

let reducer = Reducer<ServiceState, ServiceAction> { state, action in
    switch action {
    case .onLoad:
        return [Current.start.map(ServiceAction.started)]
    case .started(let handle):
        state.service = handle

        // If requestHandler is pure, this is *okay*...
        let requestHandler = { (request:String) -> (Int, String) in
            // ...but what if we want to run an effect to inform our result?
            var result: String?
            Current.readFile("foo.txt").run({ result = $0 })

            guard let contents = result else {
                return (404, "Not found")
            }
            return (200, contents)
        }

        return [handle.listen(requestHandler).map(never)]
    }
}

let store = Store(ServiceState(), reducer)
store.send(.onLoad)

Episode 7: The provided code is not really composable

let transformation = (first <<< second) { !$0 }
  <> (first <<< first) { $0 + 1 }
  <> second { $0 + "!" }
// ((Int, Bool), String) -> ((Int, Bool), String)

And wherever we want to use it, we just pipe our value through:

nested |> transformation
// ((2, false), "Swift!")

This shows how modeling setters as simple functions can produce something very composable that makes it easy to reuse code.

I might misunderstand the meaning of "very composable", but it seems that this code is not very composable.

You can't remove any of the three parts of the transformation, otherwise the compiler will fail to infer types:

let transformation = (first <<< second) { !$0 }
  <> (first <<< first) { $0 + 1 }
  <> second { $0 + "!" }

This only works if you touch all three elements of the nested tuple. What if you want a transformation that only updates one or two of the tuple's elements? The compiler will fail to infer the type for the remaining element(s).

This seems to contradict "very composable" and "easy to reuse code".

0167 Navigation is weird on iPad

Here is a screen recording of the code running on iPad (it is slightly better in landscape mode):

Simulator.Screen.Recording.-.iPad.Air.3rd.generation.-.2021-11-10.at.10.22.31.mp4

A real app will require separate container views for iPad and iPhone. I am not sure if the leaf views need to be tweaked as well. Please plan an episode/blog post focusing on addressing this pain point.

Thanks!

[Modular State Management] Share state/actions between iOS and macOS app?

Hi everyone! ๐Ÿ‘‹๐Ÿผ

I'm working on a side projects where I'm using the architecture from the video series. Currently I've only worked on the iOS part of the app, and it works great.

I recently started on the macOS app as well, and would love some input/discussion how people would structure this.

Question

Would you write entirely new state and reducers for the new platform? Or extend/modify them so that we can share usage with the macOS target? Or other ideas?

The problem

The problem now is that each framework (for instance Counter from the episodes) has the state and reducers coupled with the Views (SwiftUI). And I'm not sharing much of the UI between the platforms. We could refactor this so that state/reducers are separate frameworks from the UI. Like Counter and CounterUI or something. Drawbacks is that we get double the amount if frameworks. Also we will often have state or actions that are only relevant for one of the platforms that will be visible for the other platforms. For instance action: addProjectModalModalDismissed and state isAddProjectModalShown which only is relevant for iOS.

Or we can write separate state and reducers for the macOS app. Like a CounterMacOS framework. Then we can have state/reducers and UI together, and we don't pollute the other platform with state/actions that aren't relevant, but obviously there are a lot of duplicated code to be written. For instance I have actions that load and save data to CloudKit, that obviously is used on both platforms.

Would love some input from anybody on this. What would you do, and why?

๐ŸฆŠ๐Ÿ’š

Please fix the error Episode #76 ~

Your PrimeTime example is not compile.

image

.../PrimeTime/Counter/Counter.swift:21:12: error: cannot convert value of type '(inout Int, CounterAction) -> ()' to expected argument type '(inout Int, CounterAction) -> Effect' (aka '(inout Int, CounterAction) -> () -> ()')
pullback(counterReducer, value: \CounterViewState.count, action: \CounterViewAction.counter),
^
.../PrimeTime/Counter/Counter.swift:22:12: error: cannot convert value of type '(inout PrimeModalState, PrimeModalAction) -> ()' (aka '(inout (count: Int, favoritePrimes: Array), PrimeModalAction) -> ()') to expected argument type '(inout PrimeModalState, PrimeModalAction) -> Effect' (aka '(inout (count: Int, favoritePrimes: Array), PrimeModalAction) -> () -> ()')
pullback(primeModalReducer, value: .self, action: .primeModal)
^

Xcode Version 13.1 (13A1030d)

Also you should check all episode. There are many episode examples that cannot be built.

App Architecture State Question

In your code base you have the following extension on AppState

extension AppState {
  var counterView: CounterViewState {
    get {
      CounterViewState(
        alertNthPrime: self.alertNthPrime,
        count: self.count,
        favoritePrimes: self.favoritePrimes,
        isNthPrimeButtonDisabled: self.isNthPrimeButtonDisabled
      )
    }
    set {
      self.alertNthPrime = newValue.alertNthPrime
      self.count = newValue.count
      self.favoritePrimes = newValue.favoritePrimes
      self.isNthPrimeButtonDisabled = newValue.isNthPrimeButtonDisabled
    }
  }
}

I do not understand the need for the set. Doesn't this set method violate the design in that all state changes go through the reducer? If the CounterViewAction populates back up to the AppState, which it does, I do not see the need for this set.

0148 Derived Behaviour - Animation

Summary:

  • Removal of the last Counter Row Cell adds the new cell automatically (see frame at 04th second) (At least that's how it looks)
  • Removal of one of the middle Counter Row cells results in App Crash (see frame at 07th second)

Root Cause:

"CounterRowAction.removeButtonTapped" was received by a "forEach" reducer at id 332A618F-1D42-4463-9DB0-5839D002FF82 when its state contained no element at this id. This is generally considered an application logic error, and can happen for a few reasons:

* This "forEach" reducer was combined with or run from another reducer that removed the element at this id when it handled this action. To fix this make sure that this "forEach" reducer is run before any other reducers that can move or remove elements from state. This ensures that "forEach" reducers can handle their actions for the element at the intended id.

* An in-flight effect emitted this action while state contained no element at this id. It may be perfectly reasonable to ignore this action, but you also may want to cancel the effect it originated from when removing an element from the identified array, especially if it is a long-living effect.

* This action was sent to the store while its state contained no element at this id. To fix this make sure that actions for this reducer can only be sent to a view store when its state contains an element at this id. In SwiftUI applications, use "ForEachStore".
--- 

Alternative:

  • Action sent with animation viewStore.send(.removeButtonTapped, animation: .default) causes the issue.
    If action is sent with viewStore.send(.removeButtonTapped), app behaves as expected.

File

ep-148-crash.mov

[DesigningDependencies] Unable to run example app on iOS device

Where you able to run the DesigningDependencies app (episode 113) on an actual iOS device? For me, there's the following linker issue (tested in both Xcode 12 beta 4 & 5):

dyld: Library not loaded: @rpath/LocationClient.framework/LocationClient
Referenced from: /private/var/containers/Bundle/Application/F0C8AB02-6BF5-4C73-AE80-C3A291595B2E/DesigningDependencies.app/Frameworks/WeatherFeature.framework/WeatherFeature
Reason: no suitable image found. Did find:
/private/var/containers/Bundle/Application/F0C8AB02-6BF5-4C73-AE80-C3A291595B2E/DesigningDependencies.app/Frameworks/WeatherFeature.framework/Frameworks/LocationClient.framework/LocationClient: code signature in (/private/var/containers/Bundle/Application/F0C8AB02-6BF5-4C73-AE80-C3A291595B2E/DesigningDependencies.app/Frameworks/WeatherFeature.framework/Frameworks/LocationClient.framework/LocationClient) not valid for use in process using Library Validation: mapped file has no cdhash, completely unsigned? Code has to be at least ad-hoc signed.
dyld: launch, loading dependent libraries
DYLD_LIBRARY_PATH=/usr/lib/system/introspection
DYLD_INSERT_LIBRARIES=/Developer/usr/lib/libBacktraceRecording.dylib:/Developer/usr/lib/libMainThreadChecker.dylib:/Developer/Library/PrivateFrameworks/DTDDISupport.framework/libViewDebuggerSupport.dylib

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.