Notice: Due to size constraints and loading performance considerations, scripts referenced in blog posts are not attached directly. To request access, please complete the following form: Script Request Form Note: A Google account is required to access the form.
Disclaimer: I do not accept responsibility for any issues arising from scripts being run without adequate understanding. It is the user's responsibility to review and assess any code before execution. More information

Implementing Full-Screen Network Blocking in iOS Apps

We discussed the previous post about using a dialogue box based pop-up, while this particular pop-up method is very irritating, if you are not on the correct network, it does not look as professional as a full-screen blocking interface that prevents app usage until users connect to approved corporate networks.

Visual : Full Screen Block

The full screen block or notification applies immediately on opening the application if you are not on the defined trusted corporate network address range, I have two individual applications as demonstrations for this, so I will be using the one with the orange icon on the left:


The moment the application is launched if you are not on the correct corporate network range, you will immediately be shown the blocked notification as below:


If you then scroll down on the notification, you will see you have the prominent blue retry connection button and the red exit application:


Full Screen Block

When I set out to implement corporate network validation for iOS applications, I needed to decide how to handle unauthorized access attempts. I chose to implement a full-screen blocking (unlike the last post which was dialogue based blocking) interface for several reasons:

  • Complete visual control - The interface takes over the entire screen, making the security requirement immediately clear
  • Professional appearance - A custom-designed screen provides a polished, branded experience
  • Comprehensive instructions - Full-screen space allows detailed, step-by-step guidance for users
  • Clear severity communication - The design immediately conveys this is a security requirement, not a casual notification

The solution I developed uses a full-screen SwiftUI view that completely blocks app access until network validation passes.

Designing the Full-Screen Blocking Interface

The blocking interface completely replaces the app's normal interface when network validation fails. Users cannot access any app functionality until they connect to an approved corporate network.

Creating the Blocking View

I created a new file called NetworkBlockedView.swift that contains a purpose-built SwiftUI interface. The view uses a red gradient background to immediately communicate that access is restricted:

struct NetworkBlockedView: View {
    let onRetry: () -> Void
    let onQuit: () -> Void
    
    var body: some View {
        ZStack {
            LinearGradient(
                gradient: Gradient(colors: [
                    Color(red: 0.8, green: 0.2, blue: 0.2),
                    Color(red: 0.9, green: 0.3, blue: 0.3),
                    Color(red: 0.7, green: 0.1, blue: 0.1)
                ]),
                startPoint: .topLeading,
                endPoint: .bottomTrailing
            )
            .ignoresSafeArea()
            
            // Content goes here
        }
    }
}

The view accepts two closures as parameters: onRetry and onQuit. This design pattern allows the calling code to define what happens when users tap each button, making the component reusable across different contexts.

Visual Hierarchy

The blocking screen includes several key components arranged vertically:

Warning Icon: A large, prominent icon immediately draws attention:

ZStack {
    Circle()
        .fill(Color.white.opacity(0.2))
        .frame(width: 120, height: 120)
    
    Image(systemName: "wifi.exclamationmark")
        .font(.system(size: 50, weight: .medium))
        .foregroundColor(.white)
}

Clear Messaging: The title and description explain the situation without technical jargon:

VStack(spacing: 24) {
    Text("Corporate Connection Required")
        .font(.system(size: 32, weight: .bold, design: .rounded))
        .foregroundColor(.white)
        .multilineTextAlignment(.center)
    
    Text("This application requires a secure connection to the corporate network.")
        .font(.system(size: 18, weight: .medium))
        .foregroundColor(.white.opacity(0.8))
        .multilineTextAlignment(.center)
}

Step-by-Step Instructions: I created a reusable component to display numbered instructions:

struct InstructionRow: View {
    let icon: String
    let text: String
    
    var body: some View {
        HStack(alignment: .top, spacing: 12) {
            Image(systemName: icon)
                .font(.system(size: 16, weight: .semibold))
                .foregroundColor(.white)
                .frame(width: 24)
            
            Text(text)
                .font(.system(size: 15, weight: .medium))
                .foregroundColor(.white.opacity(0.9))
                .multilineTextAlignment(.leading)
        }
    }
}

This component displays instructions like "Connect to corporate WiFi" and "Activate your VPN (Twingate)" in a clear, scannable format.

Action Buttons

The interface provides two distinct buttons with different visual weights:

Button(action: onRetry) {
    HStack {
        Image(systemName: "arrow.clockwise")
        Text("Retry Connection")
    }
    .foregroundColor(.white)
    .frame(maxWidth: .infinity)
    .frame(height: 54)
    .background(
        LinearGradient(
            gradient: Gradient(colors: [
                Color.blue.opacity(0.8),
                Color.blue
            ]),
            startPoint: .top,
            endPoint: .bottom
        )
    )
    .cornerRadius(12)
}

Button(action: onQuit) {
    HStack {
        Image(systemName: "xmark.circle")
        Text("Exit Application")
    }
    .foregroundColor(.white.opacity(0.8))
    .frame(maxWidth: .infinity)
    .frame(height: 44)
    .background(Color.white.opacity(0.1))
    .cornerRadius(10)
}

The "Retry Connection" button uses a prominent blue gradient, while the "Exit Application" button has a more subtle appearance, guiding users toward the preferred action.

Integrating with Network Validation

The next step was connecting this full-screen interface to the network validation logic through the CorporateNetworkValidator.

Blocked Presentation Logic

The validator presents the blocking screen when network validation fails:

private func showNetworkBlockedScreen() {
    guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
          let window = windowScene.windows.first else { return }
    
    let blockedView = NetworkBlockedView(
        onRetry: {
            CorporateNetworkValidator.validateAndBlockIfNeeded()
        },
        onQuit: {
            exit(0)
        }
    )
    
    let hostingController = UIHostingController(rootView: blockedView)
    hostingController.modalPresentationStyle = .fullScreen
    hostingController.modalTransitionStyle = .crossDissolve
    
    window.rootViewController?.present(hostingController, animated: true)
}

This approach uses UIHostingController to wrap the SwiftUI view and present it as a full-screen modal. The .fullScreen presentation style is crucial—it ensures users cannot dismiss the view through standard gestures.

Application Flow

When network validation fails, the sequence of events is:

  1. Validation detects unauthorized network → Calls showNetworkBlockedScreen()
  2. Creates NetworkBlockedView → Passes retry and quit closures
  3. Wraps in UIHostingController → Enables presentation in UIKit environment
  4. Presents full-screen modal → Completely blocks underlying interface
  5. User taps Retry → Re-runs validation check
  6. If validation passes → Modal dismisses automatically
  7. User taps Quit → Application exits cleanly

Implementation

To implement this system in your application, you need three main files working together.

File 1: NetworkBlockedView.swift

Create a new Swift file with the complete blocking interface:

import SwiftUI

struct NetworkBlockedView: View {
    let onRetry: () -> Void
    let onQuit: () -> Void
    
    var body: some View {
        ZStack {
            // Background gradient
            LinearGradient(
                gradient: Gradient(colors: [
                    Color(red: 0.8, green: 0.2, blue: 0.2),
                    Color(red: 0.9, green: 0.3, blue: 0.3),
                    Color(red: 0.7, green: 0.1, blue: 0.1)
                ]),
                startPoint: .topLeading,
                endPoint: .bottomTrailing
            )
            .ignoresSafeArea()
            
            ScrollView {
                VStack(spacing: 40) {
                    Spacer(minLength: 60)
                    
                    // Warning Icon
                    ZStack {
                        Circle()
                            .fill(Color.white.opacity(0.2))
                            .frame(width: 120, height: 120)
                        
                        Image(systemName: "wifi.exclamationmark")
                            .font(.system(size: 50, weight: .medium))
                            .foregroundColor(.white)
                    }
                    
                    // Title and Message
                    VStack(spacing: 24) {
                        Text("Corporate Connection Required")
                            .font(.system(size: 32, weight: .bold, design: .rounded))
                            .foregroundColor(.white)
                            .multilineTextAlignment(.center)
                        
                        Text("This application requires a secure connection to the corporate network.")
                            .font(.system(size: 18, weight: .medium))
                            .foregroundColor(.white.opacity(0.8))
                            .multilineTextAlignment(.center)
                    }
                    
                    // Instructions
                    VStack(spacing: 20) {
                        Text("To continue, please:")
                            .font(.system(size: 16, weight: .semibold))
                            .foregroundColor(.white.opacity(0.9))
                        
                        VStack(alignment: .leading, spacing: 12) {
                            InstructionRow(
                                icon: "1.circle.fill",
                                text: "Connect to your corporate WiFi network"
                            )
                            
                            InstructionRow(
                                icon: "2.circle.fill", 
                                text: "Or activate your corporate VPN (Twingate)"
                            )
                            
                            InstructionRow(
                                icon: "3.circle.fill",
                                text: "Ensure your connection is active and configured"
                            )
                        }
                    }
                    
                    // Action Buttons
                    VStack(spacing: 16) {
                        Button(action: onRetry) {
                            HStack {
                                Image(systemName: "arrow.clockwise")
                                Text("Retry Connection")
                            }
                            .frame(maxWidth: .infinity)
                            .frame(height: 54)
                            .background(Color.blue)
                            .foregroundColor(.white)
                            .cornerRadius(12)
                        }
                        
                        Button(action: onQuit) {
                            HStack {
                                Image(systemName: "xmark.circle")
                                Text("Exit Application")
                            }
                            .frame(maxWidth: .infinity)
                            .frame(height: 44)
                            .foregroundColor(.white.opacity(0.8))
                        }
                    }
                    
                    Text("Contact IT Support if issues persist")
                        .font(.system(size: 14))
                        .foregroundColor(.white.opacity(0.6))
                }
            }
        }
    }
}

struct InstructionRow: View {
    let icon: String
    let text: String
    
    var body: some View {
        HStack(alignment: .top, spacing: 12) {
            Image(systemName: icon)
                .foregroundColor(.white)
            
            Text(text)
                .foregroundColor(.white.opacity(0.9))
        }
    }
}

File 2: CorporateNetworkValidator.swift

The validator handles IP checking and presents the blocking screen:

import Foundation
import UIKit
import SwiftUI

class CorporateNetworkValidator {
    
    // REPLACE THESE WITH YOUR ACTUAL CORPORATE IP RANGES
    private let corporateIPRanges = [
        "203.0.113",      // Example: 203.0.113.x
        "198.51.100",     // Example: 198.51.100.x
        "10.0.0",         // Example: 10.0.0.x
        "172.16"          // Example: 172.16.x.x
    ]
    
    static func validateAndBlockIfNeeded() {
        let validator = CorporateNetworkValidator()
        Task {
            let isOnCorporateNetwork = await validator.validateCorporateConnection()
            
            await MainActor.run {
                if !isOnCorporateNetwork {
                    validator.showNetworkBlockedScreen()
                }
            }
        }
    }
    
    func validateCorporateConnection() async -> Bool {
        guard let publicIP = await getPublicIPAddress() else {
            return false
        }
        
        print("Current IP: \(publicIP)")
        return isIPInCorporateRange(ip: publicIP)
    }
    
    private func getPublicIPAddress() async -> String? {
        do {
            let url = URL(string: "https://api.ipify.org")!
            let (data, _) = try await URLSession.shared.data(from: url)
            return String(data: data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines)
        } catch {
            print("Failed to get IP address: \(error)")
            return nil
        }
    }
    
    private func isIPInCorporateRange(ip: String) -> Bool {
        for range in corporateIPRanges {
            if ip.hasPrefix(range) {
                return true
            }
        }
        return false
    }
    
    private func showNetworkBlockedScreen() {
        guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
              let window = windowScene.windows.first else { return }
        
        let blockedView = NetworkBlockedView(
            onRetry: {
                CorporateNetworkValidator.validateAndBlockIfNeeded()
            },
            onQuit: {
                exit(0)
            }
        )
        
        let hostingController = UIHostingController(rootView: blockedView)
        hostingController.modalPresentationStyle = .fullScreen
        hostingController.modalTransitionStyle = .crossDissolve
        
        window.rootViewController?.present(hostingController, animated: true)
    }
}

File 3: AppDelegate Integration

Add the validation check to your existing AppDelegate:

import UIKit

class AppDelegate: NSObject, UIApplicationDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        
        // Your existing code stays here
        
        // Add corporate network validation
        CorporateNetworkValidator.validateAndBlockIfNeeded()
        
        return true
    }
    
    func applicationDidBecomeActive(_ application: UIApplication) {
        // Your existing code stays here
        
        // Check when app becomes active
        CorporateNetworkValidator.validateAndBlockIfNeeded()
    }
}

Adding to Existing Applications

If you're integrating this into an existing production app, follow these steps carefully to avoid disrupting current functionality.

Step 1: Add NetworkBlockedView.swift

Create a new Swift file in your project called NetworkBlockedView.swift. Copy the complete view implementation from File 1 above. This file is completely self-contained and doesn't modify any existing code.

Step 2: Add CorporateNetworkValidator.swift

Create another new Swift file called CorporateNetworkValidator.swift. Copy the complete validator implementation from File 2 above.

Critical: Update the corporateIPRanges array with your actual corporate IP address prefixes before deploying.

Step 3: Integrate with Your App Lifecycle

For UIKit Apps: Add the validation calls to your existing AppDelegate.swift:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // All your existing code remains unchanged
    
    // Add this single line at the end
    CorporateNetworkValidator.validateAndBlockIfNeeded()
    
    return true
}

func applicationDidBecomeActive(_ application: UIApplication) {
    // All your existing code remains unchanged
    
    // Add this single line
    CorporateNetworkValidator.validateAndBlockIfNeeded()
}

For SwiftUI Apps:

Modify your main App structure:

@main
struct YourExistingApp: App {
    var body: some Scene {
        WindowGroup {
            YourExistingContentView()
                .onAppear {
                    CorporateNetworkValidator.validateAndBlockIfNeeded()
                }
                .onReceive(NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification)) { _ in
                    CorporateNetworkValidator.validateAndBlockIfNeeded()
                }
        }
    }
}

Step 4: Configure Your IP Ranges

The most critical configuration step is setting your corporate IP ranges. To find these:

  1. Connect to your corporate network or VPN
  2. Visit https://api.ipify.org in a browser
  3. Note the displayed IP address (e.g., 203.145.67.123)
  4. Add the prefix to your array (e.g., "203.145.67")

You can add multiple ranges if your organization has multiple network exit points.

Conclusion

Implementing a full-screen blocking interface for corporate network validation provides both robust security and excellent user experience. The approach completely prevents unauthorized access while providing clear guidance to help users resolve connectivity issues independently.

The implementation integrates cleanly into existing applications with minimal code changes. By using SwiftUI for the blocking interface and UIHostingController to bridge into UIKit presentation, the solution works seamlessly across different app architectures

Previous Post Next Post

نموذج الاتصال