Internet connectivity state management in Firemonkey

It’s a common task for a Firemonkey developer to check Internet connectivity. It might be useful to notify a user that he is going to download a huge amount of data using his mobile connection, or just indicate Online/Offline mode on the UI.

This functionality is missing from RAD Studio classes, so lots of developers out there have made solutions for their needs, although most of them only solve problems of their developer and may not fulfill your needs.

In order to fill that gap I made a solution which follows these guidelines:

  • Android and iOS support
  • ability to retrieve current Internet connectivity state – disconnected, connected to WiFi, connected to mobile data
  • Internet connectivity state listener which fires every time when connectivity changes
  • a cross-platform interface with encapsulated platform-specific solutions

firemonkey-network-state

The completed solution called “firemonkey-network-state” and available on GitHub – https://github.com/Code-Partners/firemonkey-network-state.

How to integrate

  • clone or download the repo and add files from Source folder to your project.
  • For Android build target, enable ACCESS_NETWORK_STATE permission
  • For iOS Delphi project, put libReachability.a and libReachability64.a into the root directory. For iOS C++ project, add libReachability64.a into project (“Project” -> “Add to project” in IDE). These files can be located in “Samples” directory of the repo. Note, that in C++ project you can use libReachability64.a both for x32 and x64 builds
  • You may need to add “SystemConfiguration” framework for the iOS target in RAD Studio SDK Manager. For instructions of how to do it, read this article

I used materials from Dave Nottage as a baseline – http://delphiworlds.com/about-dave-nottage/

How to use

The only required step is to initialize an instance of TNetworkState class using Factory class method:

Factory(AOwner: TComponent; AOnChange: TNetworkStateChangeEvent): TNetworkState;

The second parameter is network stage event handler, if you don’t need it, pass nil.
There is no limitation to the amount of TNetworkState instances, different parts of the program may safely allocate instances if necessary.

Then, access read-only TNetworkState.CurrentValue property to retreive current network state. Return value type is TNetworkStateValue which is an enumeration of defined network states:

TNetworkStateValue = (
   nsUnknown = 0,
   nsConnectedWifi = 1,
   nsConnectedMobileData = 2,
   nsDisconnected = 3
 );

If you passed an event handler to Factory method, it’ll get called every time when connectivity changes. Note that event handler gets called in the context of the main thread, so you can work with UI inside of with without any extra synchronization. Update state passed as an event handler parameter:

procedure TSampleForm.DoOnChange(Sender: TObject; Value: TNetworkStateValue);
begin
  // react to network state change
end;

Git repository contains sample Delphi and C++ project iOS and Android build targets.

That’s it. If you want to know about limitations and implementation details, please see below.

Limitations

  • In C++, libReachability64.a file needs to be added to the project. If you have one project both for iOS and Android, you’ll need to exclude .a file from the build before building Android and enable back before building iOS. Please note that if you’ll forget to enable it back for the iOS target, the project will compile fine but an exception will happen on allocating TNetworkStateValue instance
  • The library does not work in iOS simulator. The reason behind this is that iOS implementation of TNetworkStateValue uses a static Objective-C library (libReachability64.a), and Delphi Linker ignores static libraries while building iOSSimulator target. For convenience, I instantiate dummy TNetworkStateValue on iOSSimulator platform, so the project will compile and run fine, but CurrentValue will always equal nsUnknown and “on change” event will never get called

Android implementation details

Like in Dave’s solution, TNetworkState retrieves current network state using ConnectivityManager class from Android SDK

Although, this class is not capable to detect when connectivity state changes. Android uses another mechanism to report it – Intent Messages. Every time when connectivity changes, Android broadcasts a specific Intent message to all running apps. In order to receive it, we need an instance of BroadcastReceiver

First, we need to instantiate receiver and subscribe to specific messages, for this task we need only android.net.conn.CONNECTIVITY_CHANGE. onReceive callback fires for every inbound message. Note that the callback fires in the context of the Java thread.

BroadcastReceiver can serve many purposes, such as listening to other system-wide events or inter-process communication.

iOS implementation details

Again, Dave’s solution gets the job done with Internet connectivity retrieving. Unfortunately, there is no convenient API in iOS SDK for managing internet connectivity, so a lot of iOS devs use Reachability class from developer.apple.com

libReachability.a contains this class in a static library which then linked into Firemonkey app binaries.

Then, we are able to create headers for Reachability class and instantiate it from Delphi or C++.

Reachability class does not report about connectivity changes by itself. If we look into its sources, connectivity events get posted to NSNotificationCenter class from iOS SDK.

In order to subscribe to this event we need to define delegate interface:

  INetworkChangeDelegate = interface(IObjectiveC)
    ['{BC4EABBE-F21F-4592-93B0-0C40415E4A91}']
    procedure handleNetworkChange(notice: NSNotification); cdecl;
  end;

Then, implement this interface with Delphi class derived from TOCLocal base class:

TNetworkChangeDelegate = class (TOCLocal, INetworkChangeDelegate)
public
  //  (void) handleNetworkChange:(NSNotification *)notice
  procedure handleNetworkChange(notice: NSNotification); cdecl;
end;

With this being done, we can add observer to connectivity change event:

  TNSNotificationCenter.Wrap(TNSNotificationCenter.OCClass.defaultCenter).addObserver(
    FDelegate.GetObjectID,
    sel_getUid('handleNetworkChange:'),
    StringToID('kReachabilityChangedNotification'),
    nil
  );

where FDelegate.GetObjectID is a reference to Objective-C delegate object, sel_getUid('handleNetworkChange:') is an Objective-C method selector and 'kReachabilityChangedNotification' is a name of the notification we want to observe.

Note that we need to hold a reference to FDelegate object, otherwise it will get destroyed by auto reference counting mechanism.

After addObserver was called, TNetworkChangeDelegate.handleNetworkChange will get called for every connectivity change.

2 thoughts on “Internet connectivity state management in Firemonkey”

  1. I have been using your ‘firemonkey-network-state’ code successfully until today. Today, under Delphi Tokyo 10.2.3, I updated my iOS SDK to version 11.3. Since, I did that I am no longer able to use your code.

    Every time I try to compile your ‘Sample’ project, I get the following error:

    [DCC Error] E2597 ld: warning: unknown dwarf DW_FORM_strp (offset=0xFFFFCF2C) is too big in libReachability.a(Reachability.o)

    Would you happen to know how to solve this issue?

    1. Hello.

      Linking of static Objective-C libraries is not very smooth, so sometimes it might break when development environment changes.

      Please validate if both debug and release builds fail to compile, sometimes release builds fine while debug does not.

      Also, these static libraries were generated 3 years ago:
      http://delphiworlds.com/2013/11/checking-for-an-internet-connection-on-mobile-devices-with-delphi-xe5/

      Maybe, it would be helpful to recompile static Objective-C libraries for in the most recent Xcode+iOS SDK environment, but sources for Xcode project to build that libs were not published, so it’d be necessary to make them from scratch.

      I’m sorry that I can’t give you a more specific answer at the moment. If you’ll be able to resolve that issue or have ideas for a workaround, please share your results.

      Cheers.

Leave a Comment

Save up to 30% off Delphi, C++Builder and RAD Studio for anotherShopClose Countdown
Scroll to Top