< Back

September 9th, 2024

Our journey to custom iOS frameworks

{ Engineering }

Our journey to custom iOS frameworks


Every iOS app uses frameworks. This article discusses how and why we are transitioning from third-party to custom frameworks.

As our codebase grows, we sometimes install third-party frameworks to create complex UIs or monitor app analytics.

Our iOS app integrates multiple third-party frameworks, each contributing to its functionality and size. Based on their roles and the availability of alternatives, we can categorize these frameworks into compulsory and non-compulsory.

Compulsory third-Party frameworks: These frameworks are essential to the app’s core functionality, and no custom substitute exists. For example, we rely on the Firebase framework for critical services, such as real-time databases, authentication, and analytics. Since the complexity and resources required to develop these features are prohibitively expensive, Firebase can offer robust and scalable infrastructure that would be too cumbersome to replace yourself. In this respect, this framework is considered compulsory and a constituent of our app architecture.

Non-compulsory third-party frameworks: We use these frameworks for very specific reasons. They are not compulsory but can be installed when needed. The convenient ones can be replaced anytime with a custom solution. A good example is the tooltip library, which offers features that could be redeveloped using any custom framework, hence reducing the size of the app and providing complete control over the codebase.

Why transition from third-party to custom frameworks?

  1. Constraint of UI Customization: When you install a third-party framework, you will love what you install, and it will work for you at that moment. In the future, the design team may ask for some customization to the UI components created with that framework, not knowing that it’s a 3rd-party framework with limited customization. At this point, you will get stuck because you need help to customize the UI third-party framework; creating a custom framework allows you to change the UI look and feel at any given time. 
  2. Crash Rage: We all know what it feels like when an app crashes. Third-party frameworks can be buggy. For example, in our case, the Tooltip third-party framework crashed our app too frequently. This is one reason why we created a custom tooltip framework.
  3. Performance Improvements: The custom framework helps with the smooth UX of the app, so we do not experience slow UI transitions.

Use case of a custom framework we created.

We created a framework called ToolTip that provides additional guidance and encourages users to take action. We came up with a custom tooltip framework because the third-party framework we were using before crashed frequently, and we did not have control over it to fix it. Another reason was that we could not customize the design to what we wanted 100%. 

We replaced the tooltip third-party framework with a custom ToolTip framework; this gives us total control over the framework and how we want it displayed anywhere on the app. The custom tooltip has a UIViewController and a UIView.

class ToolTipViewController: UIViewController {
    var id: Int
    var tooltipData: ToolTipView!
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    override func loadView() {
        let containerView = UIView()
        containerView.backgroundColor = UIColor.black.withAlphaComponent(0.5)
        
        tooltipData.translatesAutoresizingMaskIntoConstraints = false
        
        containerView.addSubview(tooltipData)
        tooltipData.topAnchor.constraint(equalTo: containerView.topAnchor).isActive = true
        tooltipData.leftAnchor.constraint(equalTo: containerView.leftAnchor).isActive = true
        tooltipData.rightAnchor.constraint(equalTo: containerView.rightAnchor).isActive = true
        tooltipData.bottomAnchor.constraint(equalTo: containerView.bottomAnchor).isActive = true
        view = containerView
    }
    
    init(
        id: Int = 0,
        tooltipData: ToolTipView
    ) {
        self.id = id
        super.init(nibName: nil, bundle: nil)
        self.tooltipData = tooltipData
        tooltipData.delegate = self
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

  
init?(data: [ToolTipData], currentIndex: Int = 0) {
        super.init(frame: .zero)
        setupView(data: data, currentIndex: curre Hu ntIndex)
        tipData = data
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
 }

It is reused throughout the codebase, as shown below:

let toolTipView = ToolTipView(
            data: [
                .init(
                    image: "yellow-star",
                    title: "Introducing Pods",
                    desc: "Create specific longterm milestones with your partner & work towards them on your way to building lifetime wealth.",
                    callToAction: "GOT IT",
                    view: editPlanCustomView,
                    direction: .bottom,
                    type: .barButton
                )
            ]
        )
        
   let toolTipVC = ToolTipViewController(tooltipData: toolTipView!)
   toolTipVC.modalTransitionStyle = .crossDissolve
   toolTipVC.modalPresentationStyle = .overCurrentContext
   self.present(toolTipVC, animated: true)

How it has been useful to us so far.  

  1. Separation of concerns: After creating the tooltip custom framework, it helps us break our code into distinct components. Now, the Tooltip framework mainly displays the tooltip in our app and doesn’t interfere with any other framework in our application.
  1. Reduced App Size: Replacing the third-party framework with our custom tooltip framework reduced the app size. A smaller app size benefits storage and can also improve performance. With fewer dependencies and less extraneous code, the Cowrywise app can operate more efficiently, providing a better user experience.
  1. Ease of Testing: The tooltip framework can be tested independently, and debugging is much easier. Unit tests can be written for a particular framework, ensuring it works as it should.

In conclusion, transitioning from third-party to custom frameworks has allowed us to enhance control, reduce app size, and improve performance in our iOS app. Custom frameworks offer the flexibility to adapt UI elements to our exact specifications, ensuring a seamless user experience. By focusing on modularity, we have simplified testing and debugging, leading to more efficient maintenance and overall app optimization. This journey has significantly improved our app’s functionality and customer satisfaction.