I recently looked on some possibilities for implementing something similar and you could now actually implement something like this quite easily without additional libraries. To do that you need countries borders coordinates data and you can get that from Natural Earth (https://www.naturalearthdata.com/). This data is unfortunately in format that cannot be easily read on iOS but you can convert it to a json format or to be precise geojson format with QGIS (https://www.qgis.org/en/site/forusers/download.html) or you can just use geojson that someone else converted. Here is a geojson with countries coordinates: https://github.com/nvkelso/natural-earth-vector/blob/master/geojson/ne_110m_admin_0_countries.geojson that should fit the needs for the task in this question.
We can parse geojson file and draw a map with CAShapeLayers. Below is an example code that draws a map with random colors for shapes from geojson which gave me the result as on the screenshot below which I think is quite close to expected result so modifying it should be quite easy.

Geojson parsing and map drawing code example:
import UIKit
import CoreLocation
struct GeoJson: Codable {
let type: String
let features: [GeoJsonFeature]
}
struct GeoJsonFeature: Codable {
let type: String
let geometry: GeoJsonGeometry
}
struct GeoJsonGeometry: Codable {
let type: String
let coordinates: GeoJsonCoordinates
}
struct GeoJsonCoordinates: Codable {
var point: [Double]?
var line: [[Double]]?
var polygon: [[[Double]]]?
var multiPolygon: [[[[Double]]]]?
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let point = try? container.decode([Double].self) {
self.point = point
return
}
if let line = try? container.decode([[Double]].self) {
self.line = line
return
}
if let polygon = try? container.decode([[[Double]]].self) {
self.polygon = polygon
return
}
if let multiPolygon = try? container.decode([[[[Double]]]].self) {
self.multiPolygon = multiPolygon
return
}
throw DecodingError.valueNotFound(Self.self, .init(codingPath: [], debugDescription: ""))
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
loadGeoJson()
}
func loadGeoJson() {
guard let url = Bundle.main.url(forResource: "ne_110m_admin_0_countries", withExtension: "geojson"),
let data = try? Data(contentsOf: url),
let geoJson = try? JSONDecoder().decode(GeoJson.self, from: data)
else {
return
}
for feature in geoJson.features {
let geometry = feature.geometry
let randomColor = UIColor(hue: Double.random(in: 0...1), saturation: 1, brightness: 1, alpha: 1)
// check https://macwright.com/2015/03/23/geojson-second-bite.html for other types info if needed
// note that below we do not support it exactly as it should (internal cutouts in polygons are ignored)
// but for needed purpose it should not make a big difference
if geometry.type == "Polygon", let coordinates = feature.geometry.coordinates.polygon {
for polygon in coordinates {
addShape(polygon: polygon, color: randomColor)
}
}
if geometry.type == "MultiPolygon", let coordinates = feature.geometry.coordinates.multiPolygon {
for multiPolygon in coordinates {
for polygon in multiPolygon {
addShape(polygon: polygon, color: randomColor)
}
}
}
}
}
func addShape(polygon: [[Double]], color: UIColor) {
let polygonCoordinates: [CLLocationCoordinate2D] = polygon.map { coordinate in
CLLocationCoordinate2D(latitude: coordinate[1], longitude: coordinate[0])
}
let points: [CGPoint] = polygonCoordinates.map { coordinate in
coordinateToPoint(coordinate)
}
let path = UIBezierPath()
path.move(to: points[0])
for point in points {
path.addLine(to: point)
}
path.close()
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.fillColor = color.cgColor
shapeLayer.position = CGPoint(x: 50, y: 200)
view.layer.addSublayer(shapeLayer)
}
func coordinateToPoint(_ coordinate: CLLocationCoordinate2D) -> CGPoint {
let width = 300.0
let height = 200.0
let x = (coordinate.longitude + 180.0) * (width / 360.0)
let latitudeRadians = coordinate.latitude * .pi / 180.0
let n = log(tan((.pi / 4.0) + (latitudeRadians / 2.0)))
let y = (height / 2.0) - (width * n / (2.0 * .pi))
return CGPoint(x: x, y: y)
}
}