Repository: expo/expo
File: packages/expo-modules-core/ios/Core/Records/Formatter.swift (Swift)

Code before:
      shouldSkip: @escaping (RecordType, InputType) -> Bool
    ) -> NextBuilder<InputType> {
      return chainAction { record, value in
        let typedValue = value as! InputType
        if shouldSkip(record, typedValue) {
          return Skip.instance
        }

        return typedValue
      }
    }
  }

  let name: String
  internal var action: (RecordType, Any?) -> Any?

  init (name: String) {
    self.name = name
    self.action = { _, value in value }
  }
}

class Formatter<RecordType: Record> {
  internal var selectors: [PropertySelector<RecordType>] = []

  func property<PropertyType>(
    _ name: String,
    keyPath: KeyPath<RecordType, PropertyType>
  ) -> PropertySelector<RecordType>.ActionBuilder<PropertyType> {
    let selector = PropertySelector<RecordType>(name: name)
    selectors.append(selector)
    return PropertySelector.ActionBuilder(selector: selector)
  }
}


Review comment (suggestion):
```suggestion
public class Formatter<RecordType: Record> {
  public init() {}
  internal var selectors: [PropertySelector<RecordType>] = []

  public func property<PropertyType>(
```
Shouldn't everything be marked as `public`? Because I'm getting
`'Formatter<RecordType>' initializer is inaccessible due to 'internal' protection level` 
`'skip' is inaccessible due to 'internal' protection level`
when trying to recreate this example in an external module:

```swift
let formatter = ExpoModulesCore.Formatter<MyRecord>()
formatter.property("a", keyPath: \.a).map { $0.replacingOccurrences(of: "a", with: "d") }
formatter.property("b", keyPath: \.b).map { $0 ?? "default" }
formatter.property("c", keyPath: \.c).skip()
```

Code after:
        return nextAction(record, previousValue)
      }

      return PropertySelector.ActionBuilder(selector: propertySelector)
    }

    private func chainSkipAction(
      shouldSkip: @escaping (RecordType, InputType) -> Bool
    ) -> NextBuilder<InputType> {
      return chainAction { record, value in
        let typedValue = value as! InputType
        if shouldSkip(record, typedValue) {
          return Skip.instance
        }

        return typedValue
      }
    }
  }

  internal let name: String
  internal var action: (RecordType, Any?) -> Any?

  internal init(name: String) {
    self.name = name
    self.action = { _, value in value }
  }
}

public class Formatter<RecordType: Record> {
  internal var selectors: [PropertySelector<RecordType>] = []

  public func property<PropertyType>(
    _ name: String,
    keyPath: KeyPath<RecordType, PropertyType>
  ) -> PropertySelector<RecordType>.ActionBuilder<PropertyType> {
    let selector = PropertySelector<RecordType>(name: name)
    selectors.append(selector)
    return PropertySelector.ActionBuilder(selector: selector)
  }
}

extension Record {
  public func format(_ body: (Formatter<Self>) -> Void) -> FormattedRecord<Self> {
    let formatter = Formatter<Self>()
    body(formatter)
    return FormattedRecord(record: self, formatter: formatter)
  }

  public func format(formatter: Formatter<Self>) -> FormattedRecord<Self> {
    return FormattedRecord(record: self, formatter: formatter)