Search code examples
iosswiftios-app-extension

"'shared' is unavailable: Use view controller based solutions where appropriate instead" on App Extension


I have a class that makes a network call. I want to be able to use this class in both the main app and App Extension. However, in this class I use: UIApplication.shared.isNetworkActivityIndicatorVisible.

Since shared isn't available in App Extensions, is there a way that I can use something along the lines of #available such that I don't have to duplicate this class just because of that one line of code? (I could just remove the activity indicator but I rather not since I like to let users know when network calls are happening)

I've tried adding a pre-compiled Macro as suggested by an answer I found on another question, but the compile still complains.


Solution

  • Use dependency injection pattern to refactor out the code depending on UIApplication.shared.

    1. Use a protocol to describe the functionality that is currently provided by UIApplication.shared.

    2. Implement that protocol for the base app, where UIApplication.shared is available and use it there. Implement another implementation without using UIApplication.shared, which would be used for app extensions.

    3. Refactor your current class to use the protocol implementing objects and inject the proper implementation at the initialization (dependency injection pattern) - initializer of that class would accept a protocol implementation.

    4. Wire up a proper implementation when creating an instance of the refactored class - in app extension pass it the implementation without UIApplication.shared, in base app the one with UIApplication.shared.

    So e.g., Let's have this big class here:

    class Big {
    
        ... a lot of code ...
    
        func a() {
            ... again some code ...
    
            // here the code that needs shared
            UIApplication.shared.isNetworkActivityIndicatorVisible = true
    
            ... more code ...
        }
    }
    
    1. So first, a protocol:

      protocol NetworkIndicator {
          func showActivity()
      }
      
    2. Then two implementations:

      class AppNetworkIndicator: NetworkIndicator {
           func showActivity() {
               UIApplication.shared.isNetworkActivityIndicatorVisible = true
          }
      }
      
      class ExtensionNetworkIndicator: NetworkIndicator {
          func showActivity() {}
      }
      
    3. Refactor Big to support dependency injection:

      class Big {
          private let networkIndicator: NetworkIndicator
      
          init(networkIndicator: NetworkIndicator) {
              self.networkIndicator = networkIndicator
          }
      
          ... rest of code ...
      
          func a() {
              ... again some code ...
      
              // use injected protocol implementation
              networkIndicator.showActivity()
      
              ... more code ...
          }
      }
      
    4. Finally, in the app, where you use the Big class, initiate it with:

      let big = Big(networkIndicator: AppNetworkIndicator())
      

      And in the extension, use the other implementation:

      let big = Big(networkIndicator: ExtensionNetworkIndicator())