How to add Apple Watch support with Flutter

Flutter is Google’s cross platform development framework. It uses the Dart programming language to create apps for iOS, Android, Web, MacOS, Windows and Linux. There are many advantages to using Flutter. This blog post lists some of them. However, one of the perceived disadvantages of using Flutter, is the fact that it does not support building apps for the Apple Watch. While this is true, the team at Google has provided a way to do that. In this blog post, I will go through the steps required to add Apple Watch Support with Flutter. I will go through a scenario where an existing Flutter app is available, and add support for Apple Watch to it.

For this to work, you will need to make changes on the Flutter side in your preferred editor, like VSCode, (Dart code). And, you will also have to make changes on the iOS side in Xcode (Swift). The idea is that the Flutter code will interact with the native iOS code using messages passed back and forth between the two. This communication is possible through the MethodChannel class on the Flutter side, and FlutterAppDelegate class on the Swift side. In this post, I will only discuss sending messages from Flutter to the native code (the watch), but the other side of the communication uses the same concept.

Changes in Flutter

First step on the Flutter side is to add the following code in the class where you want to send data from:

static const channel = MethodChannel('channel_name');

In the above code, replace channel_name with a name that you know will be unique on the user’s phone and watch. Ideally, the bundle ID of the app, which is usually your company’s domain name in reverse order. This name will be used whenever messages are exchanged between the app on the phone and on the watch. Depending on your existing code, you may have to add this import statement as well.

import 'package:flutter/services.dart';

Now that the channel of communication has been established, the next step is to actually send some data from Flutter to the watch. The below line of code does just that:

await channel.invokeMethod("flutterToWatch", { "method": method_name, "data": some_data });

Here, I’m invoking a method with the name method_name, and sending some_data. This is happening on a channel called flutterToWatch, but it can be anything. Whatever name you choose, this will be the name that the iOS code will be looking for.

This is all that is required on the Flutter side. Next, we will talk about the changes that are required on the iOS (Xcode) side.

Changes in Xcode

First, open the workspace in Xcode. We will have to add a new project to the existing workspace. Go to File -> New -> Target

From this dialog, make sure you go to the watchOS tab, and select App. Then click Next.

From the next dialog, enter a Product Name and select your Team (assuming you are deploying the app to the App Store). And, make sure to select “Watch App for Existing iOS App”. After this step, you should have 2 targets in your Xcode project, one for iPhone (Runner) and a new one for the Watch.

Select the Watch target and click on the Info tab. You should have the following key, WKCompanionAppBundleIdentifier with a value that’s equal to the bundle ID of the iPhone app. And, the Bundle ID of the Watch app should be the bundle ID of the iPhone app followed by .watchkitapp exactly.

Next, open the AppDelegate.swift file and make some modifications. Below is how the file should look like after you complete all the modifications:

import UIKit
import Flutter
import WatchConnectivity
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
    var session: WCSession?
    
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?
    ) -> Bool {
        initFlutterChannel()
        if WCSession.isSupported() {
            session = WCSession.default;
            session?.delegate = self;
            session?.activate();
        }
        
        GeneratedPluginRegistrant.register(with: self)
        
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }
    
    private func initFlutterChannel() {
        if let controller = window?.rootViewController as? FlutterViewController {
            let channel = FlutterMethodChannel(
                name: "channel_name",
                binaryMessenger: controller.binaryMessenger)
            
            channel.setMethodCallHandler({ [weak self] (
                call: FlutterMethodCall,
                result: @escaping FlutterResult) -> Void in
                switch call.method {
                case "method_name":
                    guard let watchSession = self?.session, watchSession.isPaired, watchSession.isReachable, let methodData = call.arguments as? [String: Any], let method = methodData["method"], let data = methodData["data"] else {
                        result(false)
                        return
                    }
                    
                    let watchData: [String: Any] = ["method": method, "data": data]
                    
                    // Pass the receiving message to Apple Watch
                    watchSession.sendMessage(watchData, replyHandler: nil, errorHandler: nil)
                    result(true)
                default:
                    result(FlutterMethodNotImplemented)
                }
            })
        }
    }
}
extension AppDelegate: WCSessionDelegate {
    
    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
        
    }
    
    func sessionDidBecomeInactive(_ session: WCSession) {
        
    }
    
    func sessionDidDeactivate(_ session: WCSession) {
        
    }
    
    func session(_ session: WCSession, didReceiveMessage message: [String : Any]) {
        DispatchQueue.main.async {
            if let method = message["method"] as? String, let controller = self.window?.rootViewController as? FlutterViewController {
                let channel = FlutterMethodChannel(
                    name: "channel_name",
                    binaryMessenger: controller.binaryMessenger)
                channel.invokeMethod(method, arguments: message)
            }
        }
    }
}

From the above code, replace channel_name and method_name with the same values that you used in the Dart code on the Flutter side.

That’s it! Now, you can receive the data that’s sent from the iPhone app and use it in the Watch app.

Running in Simulator

To actually run the project in the simulator, you have to do this from Xcode, which means you will not have support for Flutter hot reloading.

From the top of Xcode, change the target to the Watch app, and then click the Start button (play icon). It should look something like this:

If you have any questions, or face issues with this process, please feel free to contact me or comment here.

Update your apps regularly

Most mobile apps on the app stores include third party plugins and libraries that help the app perform certain features. For example, in order to display ads in a Flutter app, you include a third party plugin called google_mobile_ads. This plugin is developed and maintained by a team at Google. All well-maintained plugins are updated from time to time. Each update may include new features and/or, more importantly, security updates.

Keeping your apps in the app stores updated regularly is a very important habit to acquire. And, just to be clear, I’m not talking about publishing updates with new features to the apps (although, this is not a bad thing either!). However, even if you do not have a new feature to publish, it is likely that one or more of the plugins that your app uses, have been updated. If that is the case, then you should include the updated plugin in your app, and publish the updated app to the app store.

Benefits of an App Update

This will accomplish more than one desirable result. First, your app will remain updated with all the security fixes. Keeping your app secure does not involve your code only, but any code that your app uses, including plugins. Second, both Apple and Google have been clamping down on apps that have not been updated for a long time and appear to be abandoned. These apps are ultimately removed from the app stores. And third, users who look at the date when the app was last updated, will more likely going to be installing your app if they see that it has been updated recently.

At W3 Applications, I try to publish an update to every app at least once a month. Even if there are no new features to publish, it is very likely that at least one plugin has been updated in a one month period. That is why I try to update my apps regularly.

Risks in an App Update

An update to any part of a software inherently includes some risks. The biggest risk is that the update might break the app, and make it unusable. This is the worst outcome possible. However, even if the update doesn’t break the whole app, it make break a feature, or a part of the app that some users find useful.

To mitigate this risk, there are certain steps you can take. First, read the release notes of the third party plugin. In some cases, the notes may identify some “Breaking Changes”. If that is the case, then you have to make sure that the breaking change does not affect your app. And, if it does, then you have to change your code to avoid any issues.

Unfortunately, not all release notes identify breaking changes, even if they exist. In cases like this, the best way to avoid issues is to test the update as thoroughly as possible, before publishing to the app store. Ideally, you would want to involve a small subset of your users in the testing process. But, if this option is not available or feasible, then the second best option is to test the update yourself. Test all the features of the app, not only the parts that have been updated. This is because some parts of the app may involve other parts, even if they are not directly related.

Conclusion

Update your apps regularly to keep them secure, and make them more attractive to install. Also, you may end up avoiding having your app removed from the app store for lack of updates.

Upgrading to Flutter 2.10

The Flutter team at Google has recently released version 2.10 of the popular cross-platform development framework. I wrote about Flutter and the advantages of using it over other frameworks in this post. If you have existing Flutter apps, and you want to upgrade them to the latest version, you may run into issues that you haven’t encountered before (at least that’s what happened with me!). In this blog post, I want to write about a build error that you may encounter after upgrading to the latest version.

The Upgrade

First of all, to actually do the upgrade, you can perform these steps:

  • Make sure you are on the stable channel. To do that, run this command flutter channel in a terminal window to see which channel you’re currently on. If you are not on stable, you can run this command to switch to the stable channel flutter channel stable. Please note that the stable channel is the recommended one because (as the name indicates!) you will get the most stable version of Flutter.
  • Run this command to upgrade to the latest version flutter upgrade.

The Error

After you upgrade, you may get this error when you try to run your app:

Error: Member not found: 'packageRoot'

If you do get this error, then here are a couple of steps you can perform to fix it.

The Fix

I’m going to list the steps that I had to take to fix the error. However, it is very likely that the last step is the only step that you need.

  • Make sure that all of your packages in pubspec.yaml are up to date.
  • In a terminal window, run flutter clean and then flutter pub get. If you still get a build error at this point, try the next step.
  • Run flutter pub upgrade. This will update all your packages, along with their dependancies to the latest version. This step fixed it for me, and I was able to run my app without a build error.

I hope you found this helpful. And, if you have any questions, please don’t hesitate to post them in the comments here, or send me an email through my contact page. Happy coding!

Email invoices and get paid online

All freelancers, consultants and self-employed individuals know that the more options you give to a customer to pay your invoice, the faster it will get paid. This is especially true when one of the options is for clients to pay online. With the latest update to our invoicing app (Invoicing 24/7), getting paid faster is possible. Simply email invoices and get paid online via PayPal, a credit card or a debit card.

Here’s what you need to do to get started. First, download the free app from the Apple App Store for your iPhone, or Google Play Store for you Android phone. Then, create your free account right from within the app. Then you can go to the Account section and enter your PayPal.me username. If you do not have a PayPal.me username, you can set one up by going to PayPal.com.

After you complete all the steps above, and after you create an invoice, you will have the option to receive payments on your PayPal account. Just before sending the invoice by email from within the app, you can decide whether to allow payments through PayPal or not. If you enable the option, then the client can pay the invoice by using their PayPal credit, a credit card or a debit card.

I need to mention here that this feature was added as a result of direct feedback from one of our users of the app! If you have a feature that you want us to add to the app or service, you can let us know without leaving the app itself. Simply go to the “Request a feature” page in the app and leave your feedback there. If you haven’t downloaded the app yet, and you have a question or comment, you can use the contact page, or leave a comment here.

This is what you need to learn to become a mobile developer

Mobile app developers are paid very well in the software development industry. According to ZipRecruiter, the average salary of a mobile app developer is more than $100K per year, or $50 per hour. In this post, I will list some of the technologies that you need to learn to become a mobile app developer.

The first thing that we need to decide, is what platform we want to develop apps for. Of course, iPhones and Android phones are the most popular phone operating systems in the world, and any time you see a person carrying a phone, it will most likely be running one of the two. So, you can choose to develop apps for iOS, Android or both.

iOS

Even though iOS does not have the most global market share in terms of number of phones, it is the most profitable app store for developers.

There are multiple options available for developing mobile applications for iOS. However, the official language that is Apple recommends is Swift. This programming language was first released in 2014 as a replacement for Objective-C. It includes a lot of modern programming language features, such as null safety and more.

You can still create iOS apps using Objective-C, and there is still a large number of applications developed by Objective-C live in the App Store. However, if you are going to start learning how to create iOS apps, I highly recommend learning Swift, because it’s the official language that’s recommended by Apple.

Android

About 87% of the mobile phones in the world are running Android. This makes it an attractive option for making your apps available for as many users as possible.

Just like iOS, there are multiple options for developing Android mobile applications. The first official language that Google recommended was Java. But, as of 2019, Google now prefers Kotlin for Android app development. So, even though you can still develop apps using Java, if you are just starting with Android development, I would highly recommend starting with Kotlin.

Both (iOS and Android)

If you want to be able to create mobile apps for both iOS and Android phones, there are options for that as well. This type of development is call cross-platform. This is where you write the code one time and have it run on iOS and Android without making any changes to the code (or very little).

The option that I recommend for this type of development is Flutter. I have already wrote about Flutter in this blog post. In short, it is a platform developed by Google, and uses Dart as the programming language. It allows you to write code once and run it (natively) on iOS and Android.

Another popular option for cross-platform app development is React Native. Using React Native, you can write the app using JavaScript, which will be translated at run-time into native code. This is a very popular option, but apps developed in React Native will not run as fast as those developed with Flutter.

Conclusion

You have a lot of options for creating mobile apps for iOS, Android or both. The first thing you need to decide is, which platform you want to create apps for. Once you do that, you can look at the different options you have to learn to become a mobile app developer. If you have any questions about any of the options for developing mobile applications, feel free to leave a comment here or send me an email using the contact form.

Upgrading your Flutter app to use Null Safety

Null Safety is a feature in some programming languages which guarantees that no variable in your code can contain a null value, unless you explicitly allow it. In other words, it makes your program less likely to encounter a Null Reference error during run-time. Null Safety has been added to the Dart programming language in version 2.12.0. This means that you can upgrade your Flutter apps to use Null Safety by following a few steps. I will outline the steps needed for upgrading your Flutter app to use Null Safety in this blog post.

Before upgrading

Before you upgrade your code to use Null Safety, there are some implications that you need to consider. The first implication is that the way you declare your variables will be different. For example, the below statement would not compile in a null-safe program:

int i;

Instead, you have to initialize the variable at the same time you declare it. So, the above line would have to look something like this:

int i = 0;

This will ensure that the variable i will never be null.

However, there may be cases when it’s not possible to set an initial value for a variable during declaration. Or, you may need your variable to contain a null value in certain cases. In such scenarios, you can declare a variable as nullable. Meaning, the variable is able to hold a null value. This can be done by declaring the variable like this:

int? i;

The ? after the type of the variable indicates that the variable is nullable.

Caveat

The caveat here is that nullable variables can potentially cause a Null Reference error during run-time. So, you have to be very careful when dealing with these variables. In fact, the compiler does not let you access these variables unless you explicitly tell it that you’re sure that the value is not null. For example, after declaring a variable like this int? i; the below statement would not compile:

int x = i + 5;

The above statement would not compile because the value of i could be null during run-time. If you are sure that at this point in the code, the variable i cannot be null, you can force the above statement to compile like this:

int x = i! + 5;

Adding the ! symbol tells the compiler to skip the null safety check and allow the code to run. Again, you have to be very careful with the above statement, because if the value of i is null at run-time, the program would encounter a Null Reference error.

The second, and perhaps more important, implication to consider before upgrading your Flutter app to use Null Safety, is that all of the packages that you use in your app must also be Null Safe. If any of the packages does not use Null Safety, then you cannot upgrade your code.

Checking your packages

To check if all the packages that you use in your app use support Null Safety, you can run this command:

dart pub outdated --mode=null-safety

The above command will list all of your dependency packages and show you if the current version of the package supports Null Safety or not. Also, if there’s a newer version of the package that you’re not using, this command will let you know whether that version supports Null Safety or not.

Once you confirm that all your packages support Null Safety, or upgrade to versions that do, you can start the process of upgrading your app. If even the latest version of any of your dependency packages does not support Null Safety, you can always write to the developers of the package and ask them to upgrade.

Upgrading your app

The first step is to make sure that you’re using Dart version 2.12.0 or later. To know which version of Dart you’re using, run this command:

dart --version

If you see that you’re running an older version, you can upgrade to version 2.12.0 by going to your pubspec.yaml and changing the environment entry to look something like this:

environment:
  sdk: ">=2.12.0 <3.0.0"

After you change the version and complete your first build, it is very likely that you will receive a lot of compilation errors. Don’t worry, this is very normal when first making the upgrade. The next step would be to fix all of these errors. Make sure that all of your variables are initialized during declaration (recommended) or declared with the ? symbol if initialization is not possible.

Conclusion

Null Safety allows you to write code that is less likely to run into errors during run-time. Dart version 2.12.0 and later is now one of the programming languages that support Null Safety. To take advantage of this feature in your Flutter apps, make sure you upgrade to version 2.12.0 and then modify your code to comply with the rules of Null Safety. You can find more information about the upgrade process in this article. If you have any questions, or you run into any issues while upgrading, please feel free to leave a comment here or send me your question through the contact page.

Making HTTP requests from a Flutter mobile app

Most mobile applications need to make requests to an external service, whether it’s an API, or any other service that’s hosted on an external server and can be accessed by making a request over HTTP. So, how do you make an HTTP call from a Flutter mobile app?

As discussed in a previous blog post, there are a lot of advantages to using an API. In this post, I will outline the steps needed to make a call to an external API, using HTTP, from a Flutter mobile app. The same concepts apply to the different types of Flutter apps as well.

There’s a package for that

To start off, you will need to include the http package in your application. Just add it to your pubspec.yaml file, and run the flutter pub get command. For more details on this package, check out its page on pub.dev.

After including the package in your app, you can start using it and making HTTP requests. To use it in your code, you have to add an import statement like this: import 'package:http/http.dart' as http.

GET requests

The easiest type of HTTP request to make, is a GET request. Here’s an example of how to make a GET request to an external server from your Flutter application: http.Response response = await http.get('https://example.com/api');

The above example uses the await keyword, which means that this line of code has to be inside a function that’s declared with the async keyword. This will make the call, and store the response in an object called response.

You can then read the response by using the below code: final Map<String, dynamic> responseData = json.decode(response.body);

This assumes that the external service that we’re calling returns data in JSON format. Otherwise, you will have to declare the variable responseData with the appropriate data type returned. To use the json.decode method, you have to import the built-in convert package like this (import 'dart:convert').

We are reading the contents of the response, by calling the body property. The Response object has another useful property called statusCode. This property contains the HTTP status code that was returned as a result of making the HTTP call. You can get a list of valid HTTP status code from here.

If we want to make sure that the call was successful, we can check the value of the statusCode property like this: if (response.statusCode == 200).

Here’s a complete example of making an HTTP GET request:

Future<void> getFamily() async {
    http.Response response = await http.get('https://example.com/api');
    final Map<String, dynamic> responseData = json.decode(response.body);
    if (response.statusCode == 200 || response.statusCode == 201) {
        // SUCCESS!
    } else {
        // THE CALL FAILED!
    }
    return;
}

Headers

If you need to set any headers before making the request, you can do so by passing a second argument to the http.get method. For example, if you want to set the Content-Type header value, the call would look like this:

http.Response response = await http.get('https://example.com/api', headers: {'Content-Type': 'application/json'});

POST requests

Making HTTP POST requests is very similar. Of course, with POST requests, the request can have data in its body. So, to make a POST request and pass data in the body, you have to use the http.post method. Typically, this method is called by passing 3 arguments. The first argument is the URL of the server you’re calling. The other two arguments are named. The first named argument is headers, which is used to set header values (just like in the get method). And the second named parameter is body, which is used to send data in the body of the request.

If you plan on sending data in JSON format, then you have to use the json.encode method.

Conclusion

In summary, to make HTTP calls from your Flutter mobile app, include the http package in your app and call the appropriate methods depending on the type of service you want to call. For any questions, please leave a comment here or use my contact page.

What is a native mobile app?

Mobile applications have been steadily increasing in popularity ever since Apple opened up its App Store to third-party developers in 2008. Before that time, Apple was the only developer of iPhone apps. So, the selection was not very diverse, to say the least. On the Android side, HTC released the first Android phone in 2008, so Android phones were still not popular. In either case, iPhone or Android, mobile apps at that time were all “native” apps. So, what is a native mobile app anyway?

Different Operating Systems

iPhone apps run on an operating system currently known as iOS, and Android apps run on the Android operating system. For these apps to run on their respective operating system correctly, just like any other computer software, the programming language compiler (or translator) has to convert the code to a language that the operating system understands. The official programming languages that Apple wants developers to use are Swift, and Objective C before that. On the Android side, Google’s official recommendation is to use a programming language called Kotlin, and Java before that. These programming languages are compiled to a “native” language that the operating system can understand without having a middle layer to act as a translator. This means that the applications will potentially run very fast.

Alternatives to native apps

So, application speed is a major advantage to developing mobile apps in a native language. However, there’s one downside to this approach. Namely, if you want to have your application run on both iOS and Android devices, you have to write the application’s code twice. One time in Swift or Objective C to run it on iOS, and another time in Kotlin or Java to run in on Android. For larger companies, this means hiring two people (or potentially two teams) to develop the same app. And for smaller companies, this means looking for a developer who has experience in developing applications for iOS and Android. Finding the right resources is only part of the problem. The other part is time. It will potentially take the company double the time to develop the same app for two operating systems.

To fix this problem, some companies started to develop their own programming languages and platforms to build mobile apps that run on both iOS and Android at the same time. The developer community calls them “cross-platform” frameworks. A very popular example is React native. Facebook created React native to allow its developers to write code in a language called JavaScript, and use that same code to run the app on both iOS and Android.

The problem with traditional cross-platform software

React native is able to achieve this cross-platform feature by having a translator, or a “bridge”, that translates the JavaScript code to native code when the application is running on the device. For example, for a button to be displayed properly when the app is running, the translator will take the JavaScript code and convert it to an iOS button on an iPhone. When running on an Android device, it will convert the same code to an Android button. This introduces a new problem. Apps written in React native will potentially run slower than apps written in a native programming language. Because, when using a native programming language, there’s no need to translate anything during run-time. This disadvantage is not specific to React native only. Any application that needs a translator at run time will suffer from this slow down.

The best of both worlds

To achieve the best of both worlds, Google developed a cross-platform framework called Flutter. Flutter uses a programming language called Dart. Developers use Flutter to develops apps that run on iOS, Android and the web. In fact, you can even use Flutter to develop desktop apps that run natively on macOS, Linux and Windows. And all of that is achieved using a single code-base. In other words, you can write the app code using Dart once, and have it run on all these platforms. And the best part is that there’s no need for a translator, or a bridge, at run-time to convert the code.

Many companies have started to use Flutter when developing mobile applications. Google officially announced the first stable release of Flutter in 2018. However, here at W3 Applications, we have been using Flutter to develop mobile apps since before the official release. Our first Flutter app was published a few months before the first stable release.

What is a native mobile app?

So, to answer the question: what is a native mobile app? It is an app that does not need to be translated during run-time.

Conclusion

For us, having a programming framework like Flutter at our disposal means a couple of things. First, we can develop beautiful, fast and customizable mobile apps that run natively on iOS and Android at half the time it would take otherwise. And, what this ultimately means, is being able to lower the cost of development. So, if we lower the cost of development, we are able to stay competitive in the market and provide more cost-effective solutions to our clients.

If you have any questions about native mobile apps, or mobile apps in general, please send us an email or use our contact form. Thank you.