Before you start adding this pattern everywhere in your code, you need to understand that it solves certain issues, so it's helpful to encounter (and suffer from) those first.
The main use of this pattern is to ensure you do not need to write platform-specific code in your application, and that you keep those pesky #available and os() checks (or other compile time or runtime checks) hidden from the consumer.
- First, ensure you have platform-specific code you need to abstract away (iOS vs tvOS, or iOS 9.0 vs iOS 12.0).
- Define a common interface for the platform-specific objects, as we did with PushNotificationService and UserLocationService.
- Implement platform-specific classes for each interface, such as iOSPushNotificationService.
- Define and implement a common service factory interface, such as ServiceFactoryType, and implement platform-specific service factories.
- Define a common and simple way to get the proper serviceFactory for the current context. In our scenario, it was the ServicesFactory.shared singleton, which was instantiated based on #availability APIs; in other contexts, this may be a method.
- Protect the visibility of your services or factories with proper accessors (private, internal, and so on), in order to prevent accidental instantiation. For example, we don't want the iOSPushNotificationService constructor to be used or available outside iOSServiceFactory.
- Replace all occurrences of creating those objects through their (now-unavailable) constructor by creating them through your new abstract factory.
Now that you know everything about the factory method pattern, we can move on to the builder pattern.