Effect CommunityEC
Effect Community3y ago
19 replies
imagio

Struggling with Platform-Specific Service Implementations

I'm still struggling a bit to find the cleanest pattern for dealing with services that need slightly different implementations when constructed on different platforms. Here's what I'm doing now:

//DeviceStateService.ts -- shared implementation


export interface DeviceStateService {
    _tag: "DeviceStateService"
    isOnline: ObservableSubscriptionRef<boolean>
    isUpdateAvailable: ObservableSubscriptionRef<boolean>
    platform: PlatformType
    isOnReactNative(): boolean
    appStateRef: ObservableSubscriptionRef<AppStateStatus>
}

export const DeviceStateService = Context.Tag<DeviceStateService>()

export interface DeviceStateConfig {
    platform: PlatformType
}

export const make = ({ platform }: DeviceStateConfig) =>
    Effect.gen(function* (_) {
        const isOnline = yield* _(observableSubscriptionRef(`isOnline`, () => true))
        const isUpdateAvailable = yield* _(observableSubscriptionRef(`isUpdateAvailable`, () => false))
        const appStateRef = yield* _(observableSubscriptionRef<AppStateStatus>(`appStateRef`, () => "active"))
        const isOnReactNative = () => platform !== "web" && platform !== "server"

        return {
            _tag: "DeviceStateService" as const,
            isOnline,
            isUpdateAvailable,
            platform,
            isOnReactNative,
            appStateRef,
        }
    })

export const makeLayer = (config: Config.Config.Wrap<DeviceStateConfig>) =>
    Layer.effect(DeviceStateService, Effect.flatMap(Effect.config(Config.unwrap(config)), make))


//AppDeviceStateService.ts -- the app specific version of DeviceStateService
export const makeLayer = () =>
    pipe(
        DeviceStateService.makeLayer(Config.succeed({ platform: getPlatformFromReactNative() })),
        Layer.map(ds => pipe(
            //app specific initialization goes here
            //ex set up a stream from React Native's AppState.addEventListener
            //to keep the appStateRef up to date
      ))
    )


The general pattern is to construct a shared implementation then use Layer.map to modify it with platform specific stuff. Does that seem like a good approach or is there a cleaner way to do "platform specific implementation details" with Layers?
Was this page helpful?