Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[iOS] Reopening map offline throws exception from SQLite and breaks #703

Open
AAverin opened this issue Oct 2, 2021 · 28 comments
Open

[iOS] Reopening map offline throws exception from SQLite and breaks #703

AAverin opened this issue Oct 2, 2021 · 28 comments
Labels

Comments

@AAverin
Copy link
Contributor

AAverin commented Oct 2, 2021

Neither onMapReady nor onStyleLoaded are not called when device is offline

@AAverin
Copy link
Contributor Author

AAverin commented Oct 3, 2021

I suspect that style files are actually somewhere online and because in the implementation didFinishLoading style and not didFinishLoadingMap is used as a trigger that map is ready, if style is not accessible map is never ready in Flutter.

Is there a way to put styles offline too? Why doesn't this happen on Android?

Some logs that I get

2021-10-03 09:32:23.132188+0200 Runner[1843:2050389] Connection 117: received failure notification
2021-10-03 09:32:23.132224+0200 Runner[1843:2050389] Connection 117: failed to connect 1:50, reason -1
2021-10-03 09:32:23.132238+0200 Runner[1843:2050389] Connection 117: encountered error(1:50)
2021-10-03 09:32:23.132764+0200 Runner[1843:2050389] Task <5684C397-827F-4E36-8BE1-8183B6B5292F>.<11> HTTP load failed, 0/0 bytes (error code: -1009 [1:50])
2021-10-03 09:32:23.133185+0200 Runner[1843:2050391] Task <5684C397-827F-4E36-8BE1-8183B6B5292F>.<11> finished with error [-1009] Error Domain=NSURLErrorDomain Code=-1009 "The Internet connection appears to be offline." UserInfo={_kCFStreamErrorCodeKey=50, NSUnderlyingError=0x283ce7ab0 {Error Domain=kCFErrorDomainCFNetwork Code=-1009 "(null)" UserInfo={_kCFStreamErrorCodeKey=50, _kCFStreamErrorDomainKey=1}}, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <5684C397-827F-4E36-8BE1-8183B6B5292F>.<11>, _NSURLErrorRelatedURLSessionTaskErrorKey=(
    "LocalDataTask <5684C397-827F-4E36-8BE1-8183B6B5292F>.<11>"
), NSLocalizedDescription=The Internet connection appears to be offline., NSErrorFailingURLStringKey=https://events.mapbox.com/events/v2?access_token=s..., NSErrorFailingURLKey=https://events.mapbox.com/events/v2?access_token=..., _kCFStreamErrorDomainKey=1}
2021-10-03 09:32:29.170930+0200 Runner[1843:2050389] Connection 118: received failure notification
2021-10-03 09:32:29.170973+0200 Runner[1843:2050389] Connection 118: failed to connect 1:50, reason -1
2021-10-03 09:32:29.171003+0200 Runner[1843:2050389] Connection 118: encountered error(1:50)
2021-10-03 09:32:29.172025+0200 Runner[1843:2050389] Task <0F420101-5251-48D3-98FE-2F920E68A9A5>.<108> HTTP load failed, 0/0 bytes (error code: -1009 [1:50])
2021-10-03 09:32:29.172229+0200 Runner[1843:2050389] Task <0F420101-5251-48D3-98FE-2F920E68A9A5>.<108> finished with error [-1009] Error Domain=NSURLErrorDomain Code=-1009 "The Internet connection appears to be offline." UserInfo={_kCFStreamErrorCodeKey=50, NSUnderlyingError=0x283ce2730 {Error Domain=kCFErrorDomainCFNetwork Code=-1009 "(null)" UserInfo={_kCFStreamErrorCodeKey=50, _kCFStreamErrorDomainKey=1}}, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <0F420101-5251-48D3-98FE-2F920E68A9A5>.<108>, _NSURLErrorRelatedURLSessionTaskErrorKey=(
    "LocalDataTask <0F420101-5251-48D3-98FE-2F920E68A9A5>.<108>"
), NSLocalizedDescription=The Internet connection appears to be offline., NSErrorFailingURLStringKey=https://api.mapbox.com/styles/v1/mapbox/streets-v11?sku=100RqKiN39k2f64dae1bfc0b11ffe0564e296d139dc&access_token=..., NSErrorFailingURLKey=https://api.mapbox.com/styles/v1/mapbox/streets-v11?sku=100RqKiN39k2f64dae1bfc0b11ffe0564e296d139dc&access_token=..., _kCFStreamErrorDomainKey=1}
2021-10-03 09:32:30.183758+0200 Runner[1843:2050389] Connection 119: received failure notification
2021-10-03 09:32:30.184189+0200 Runner[1843:2050389] Connection 119: failed to connect 1:50, reason -1
2021-10-03 09:32:30.184308+0200 Runner[1843:2050389] Connection 119: encountered error(1:50)
2021-10-03 09:32:30.186569+0200 Runner[1843:2054072] Task <C1B164E1-921A-4EC9-987B-F867E39F8723>.<109> HTTP load failed, 0/0 bytes (error code: -1009 [1:50])
2021-10-03 09:32:30.187461+0200 Runner[1843:2050389] Task <C1B164E1-921A-4EC9-987B-F867E39F8723>.<109> finished with error [-1009] Error Domain=NSURLErrorDomain Code=-1009 "The Internet connection appears to be offline." UserInfo={_kCFStreamErrorCodeKey=50, NSUnderlyingError=0x283ca91d0 {Error Domain=kCFErrorDomainCFNetwork Code=-1009 "(null)" UserInfo={_kCFStreamErrorCodeKey=50, _kCFStreamErrorDomainKey=1}}, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <C1B164E1-921A-4EC9-987B-F867E39F8723>.<109>, _NSURLErrorRelatedURLSessionTaskErrorKey=(
    "LocalDataTask <C1B164E1-921A-4EC9-987B-F867E39F8723>.<109>"
), NSLocalizedDescription=The Internet connection appears to be offline., NSErrorFailingURLStringKey=https://api.mapbox.com/styles/v1/mapbox/streets-v11?sku=100RqKiN39k2f64dae1bfc0b11ffe0564e296d139dc&access_token=..., NSErrorFailingURLKey=https://api.mapbox.com/styles/v1/mapbox/streets-v11?sku=100RqKiN39k2f64dae1bfc0b11ffe0564e296d139dc&access_token=..., _kCFStreamErrorDomainKey=1}
2021-10-03 09:32:32.199082+0200 Runner[1843:2050389] Connection 120: received failure notification
2021-10-03 09:32:32.199245+0200 Runner[1843:2050389] Connection 120: failed to connect 1:50, reason -1
2021-10-03 09:32:32.199347+0200 Runner[1843:2050389] Connection 120: encountered error(1:50)
2021-10-03 09:32:32.202032+0200 Runner[1843:2050389] Task <487B69EE-224B-42B0-8E95-41AFCCA7E7A7>.<110> HTTP load failed, 0/0 bytes (error code: -1009 [1:50])
2021-10-03 09:32:32.202822+0200 Runner[1843:2050389] Task <487B69EE-224B-42B0-8E95-41AFCCA7E7A7>.<110> finished with error [-1009] Error Domain=NSURLErrorDomain Code=-1009 "The Internet connection appears to be offline." UserInfo={_kCFStreamErrorCodeKey=50, NSUnderlyingError=0x283cafdb0 {Error Domain=kCFErrorDomainCFNetwork Code=-1009 "(null)" UserInfo={_kCFStreamErrorCodeKey=50, _kCFStreamErrorDomainKey=1}}, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <487B69EE-224B-42B0-8E95-41AFCCA7E7A7>.<110>, _NSURLErrorRelatedURLSessionTaskErrorKey=(
    "LocalDataTask <487B69EE-224B-42B0-8E95-41AFCCA7E7A7>.<110>"
), NSLocalizedDescription=The Internet connection appears to be offline., NSErrorFailingURLStringKey=https://api.mapbox.com/styles/v1/mapbox/streets-v11?sku=100RqKiN39k2f64dae1bfc0b11ffe0564e296d139dc&access_token=..., NSErrorFailingURLKey=https://api.mapbox.com/styles/v1/mapbox/streets-v11?sku=100RqKiN39k2f64dae1bfc0b11ffe0564e296d139dc&access_token=..., _kCFStreamErrorDomainKey=1}
2021-10-03 09:32:36.212156+0200 Runner[1843:2054071] Connection 121: received failure notification
2021-10-03 09:32:36.212295+0200 Runner[1843:2054071] Connection 121: failed to connect 1:50, reason -1
2021-10-03 09:32:36.212399+0200 Runner[1843:2054071] Connection 121: encountered error(1:50)
2021-10-03 09:32:36.214260+0200 Runner[1843:2054072] Task <9E16A306-93AC-44B2-9C9C-2C5A56FEB2CF>.<111> HTTP load failed, 0/0 bytes (error code: -1009 [1:50])
2021-10-03 09:32:36.215052+0200 Runner[1843:2054072] Task <9E16A306-93AC-44B2-9C9C-2C5A56FEB2CF>.<111> finished with error [-1009] Error Domain=NSURLErrorDomain Code=-1009 "The Internet connection appears to be offline." UserInfo={_kCFStreamErrorCodeKey=50, NSUnderlyingError=0x283caad00 {Error Domain=kCFErrorDomainCFNetwork Code=-1009 "(null)" UserInfo={_kCFStreamErrorCodeKey=50, _kCFStreamErrorDomainKey=1}}, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <9E16A306-93AC-44B2-9C9C-2C5A56FEB2CF>.<111>, _NSURLErrorRelatedURLSessionTaskErrorKey=(
    "LocalDataTask <9E16A306-93AC-44B2-9C9C-2C5A56FEB2CF>.<111>"
), NSLocalizedDescription=The Internet connection appears to be offline., NSErrorFailingURLStringKey=https://api.mapbox.com/styles/v1/mapbox/streets-v11?sku=100RqKiN39k2f64dae1bfc0b11ffe0564e296d139dc&access_token=..., NSErrorFailingURLKey=https://api.mapbox.com/styles/v1/mapbox/streets-v11?sku=100RqKiN39k2f64dae1bfc0b11ffe0564e296d139dc&access_token=..., _kCFStreamErrorDomainKey=1}

@AAverin
Copy link
Contributor Author

AAverin commented Oct 3, 2021

The style itself is, of course, bundled into the offline package and should be used by default instead of loading it from the network and failing.
And it seems to be used the first time map is opened, but every subsequent opening results in error

@AAverin
Copy link
Contributor Author

AAverin commented Oct 3, 2021

The reason behind is a leak. I will continue investigation if it's in my codebase or in mapbox for ios

@AAverin
Copy link
Contributor Author

AAverin commented Oct 4, 2021

#389

@AAverin
Copy link
Contributor Author

AAverin commented Oct 4, 2021

Fixing the leaks, it doesn't seem to be related.

@AAverin
Copy link
Contributor Author

AAverin commented Oct 4, 2021

The first start of the map, when offline map loads correctly, the errors look like

mapViewDidFailLoadingMap Error Domain=MGLErrorDomain Code=-1 "The map failed to load because an unknown error occurred." UserInfo={NSLocalizedDescription=The map failed to load because an unknown error occurred., NSLocalizedFailureReason=Failed to load tile 2/2/0=>2 for source composite: The Internet connection appears to be offline.}

The second start of the screen with the map, when offline map does not display anymore correctly, errors look like:

mapViewDidFailLoadingMap Error Domain=MGLErrorDomain Code=5 "The map failed to load because the style can't be loaded." UserInfo={NSLocalizedDescription=The map failed to load because the style can't be loaded., NSLocalizedFailureReason=loading style failed: The Internet connection appears to be offline.}

Nothing changes between two screens, second screen with the map just doesn't work because it can't load style even though it's already bundled into the map database that was side-loaded.

@m0nac0 Do you have any iOS knowledge or maybe ideas why this could be happening?

@m0nac0
Copy link
Collaborator

m0nac0 commented Oct 4, 2021

Unfortunately, my iOS dev knowledge is very limited, so I'm afraid I don't have an idea of what is going on here.

@TimothySealy wrote a lot of the iOS code for this plugin, maybe he has got an idea?

@AAverin
Copy link
Contributor Author

AAverin commented Oct 7, 2021

I have also noticed this in logs

[logging] BUG IN CLIENT OF libsqlite3.dylib: database integrity compromised by API violation: vnode unlinked while in use: /private/var/mobile/Containers/Data/Application/E9F89FE6-3545-416A-A344-313E5A65A875/Library/Application Support/___/.mapbox/cache.db
[logging] invalidated open fd: 48 (0x11)

@AAverin
Copy link
Contributor Author

AAverin commented Oct 7, 2021

This gets outputed on second call to installOfflineMapTiles method.
@TimothySealy I would really appreciate some help or input on this ticket, offline support is critical for my project release and it's the last thing that needs fixing before I can roll out the app to customers.

@AAverin
Copy link
Contributor Author

AAverin commented Oct 7, 2021

I have also opened mapbox/mapbox-gl-native-ios#637 because it's highly likely issue is somewhere in original Mapbox-iOS SDK

@AAverin
Copy link
Contributor Author

AAverin commented Oct 8, 2021

It looks like original mapbox-gl-native-ios SDK is not in active development for the last 2 years, they have probably been working on new v10 implementation

@raphaaugustosilva
Copy link

@AAverin how did you managed to workaround this situation?
I need to initialize mapbox map when offline too.

@AAverin
Copy link
Contributor Author

AAverin commented Oct 18, 2021

@raphaaugustosilva I didn't.
I created mapbox/mapbox-gl-native-ios#637 for the original mapbox ios SDK because the error is coming from it, so it's likely that cause is also inside original SDK. Didn't get any response there yet.

Another theoretical possibility are memory leaks that are now investigated in
#706 and #710
flutter/flutter#91508 is tracking leaks in flutter engine itself.

All in all, I don't know how to solve this problem yet. Some input of experienced iOS developer is needed here.

@raphaaugustosilva If you will manage to reproduce the issue using original Mapbox iOS SDK sample app and post the example here, it would help greately with investigation

@raphaaugustosilva
Copy link

@AAverin thanks!
Unfortunately, I am only going to use Mapbox using Flutter (Android and iOS), but my core is to work offline.

I also tried to download offline region in background with internet before opening the map for the first time without internet (using flutter mapbox downloadOfflineRegion method), but the error is the same.

Thanks for the inputs, I`ll subscribe here in order to get more updates.

@AAverin
Copy link
Contributor Author

AAverin commented Oct 22, 2021

It seems that database communication is implemented in the C++ layer of Mapbox.
on iOS everything is supposed to correctly destroy in dealloc, including database connections.
This leads me to thinking again that memory leaks in the main culprit. With flutter/flutter#91508 Flutter seems to retain in memory at least one instance of the widget, including MapboxMap. If that widget was not correctly destroyed dealloc was not invoked in the Swift implementation and database connection wasn't correctly teared down.

@AAverin
Copy link
Contributor Author

AAverin commented Oct 22, 2021

Another major flutter issue related to memory leaks: flutter/flutter#79605

@AAverin
Copy link
Contributor Author

AAverin commented Oct 22, 2021

Debugging some more I can confirm that even if there are completely no memory leaks in Flutter code Mapbox on iOS still throws BUG IN CLIENT OF libsqlite3.dylib: database integrity compromised by API violation: vnode unlinked while in use: /private/var/mobile/Containers/Data/Application/E9F89FE6-3545-416A-A344-313E5A65A875/Library/Application Support/___/.mapbox/cache.db
That would mean that database connection is not properly cleaned either by Mapbox iOS SDK or by MapboxGL native implementation.

@tobrun Is there any way to get some insight from Mapbox developers on this topic?

@AAverin AAverin changed the title [iOS] When offline, map doesn't initialise [iOS] Reopening map offline throws exception from SQLite and breaks Oct 22, 2021
@AAverin
Copy link
Contributor Author

AAverin commented Oct 22, 2021

@AAverin
Copy link
Contributor Author

AAverin commented Nov 5, 2021

I can reproduce the bug with the following minimal example.

The problem seems to be that MapboxMap is holding connection to cache.db even when map is no longer displayed on the screen and should have been cleared. Installing offline tiles tries to replace cache.db with a different offline set of tiles and fails with sql error. Same thing doesn't happen on Android, replacing cache.db when app is running works fine.

Because I have eliminated all possible issues in dart code I think problem lies in ios implementation of mapbox sdk.
@tobrun is there any way Mapbox iOS devs could investigate the problem mapbox/mapbox-gl-native-ios#637?

import 'dart:math';

import 'package:flutter/material.dart';
import 'package:mapbox_gl/mapbox_gl.dart';

void main() async {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      initialRoute: "/",
      onGenerateRoute: (settings) {
        switch (settings.name) {
          case "/":
            return MaterialPageRoute(builder: (_) {
              return HomePage();
            });
          case "/map":
            return MaterialPageRoute(builder: (_) {
              return MapPage();
            });
          default:
            return MaterialPageRoute(
                settings: const RouteSettings(name: "error"),
                builder: (_) {
                  return Scaffold(
                    appBar: AppBar(
                      title: const Text("error"),
                    ),
                  );
                });
        }
      },
    );
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Home page"),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () async {
          // uncomment this to install offline tile
          // try {
          //   await installOfflineMapTiles("assets/61.db");
          // } catch (err) {
          //   print(err);
          // }
          await Navigator.of(context).pushNamed("/map");
        },
        child: const Icon(Icons.navigation),
        backgroundColor: Colors.green,
      ),
    );
  }
}

class MapPage extends StatefulWidget {
  @override
  State<MapPage> createState() => _MapPageState();
}

class _MapPageState extends State<MapPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: const Text("Map page"),
        ),
        body: MapboxMap(
          initialCameraPosition:
              const CameraPosition(target: LatLng(52.5200, 13.4050), zoom: 15),
        ));
  }
}

@stale
Copy link

stale bot commented Jan 14, 2022

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the stale label Jan 14, 2022
@AAverin
Copy link
Contributor Author

AAverin commented Jan 14, 2022

Issue is still relevant and not fixed

@stale
Copy link

stale bot commented Mar 15, 2022

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the stale label Mar 15, 2022
@AAverin
Copy link
Contributor Author

AAverin commented Mar 15, 2022

Issue is still relevant and not fixed downstream

@stale stale bot removed the stale label Mar 15, 2022
@BartoszStasiurka
Copy link

Hi, I faced the same problem. Is there any workaround?

@AAverin
Copy link
Contributor Author

AAverin commented Jan 19, 2023

@BartoszStasiurka If issue is still there, then probably it is related to some problem in the underlying mapbox ios sdk and it won't be fixed.
New official Mapbox plugin can be found here: https://pub.dev/packages/mapbox_maps_flutter
Offline is not yet supported as far as I know, but they do accept feature requests

@raphael-bmec-co
Copy link

@AAverin we are using this package in production for Android. Our customer is asking after an iOS port. Is this issue still a known issue? We run mostly offline so if it is likely to crash we need to advise them to wait on mapbox_maps_flutter.

@AAverin
Copy link
Contributor Author

AAverin commented May 8, 2024

@raphael-bmec-co I have removed offline support from my product for now and didn't test since then.
New official mapbox still doesn't support offline and I couldn't get any official feedback from Mapbox on the roadmap and plans. You can try asking in their Discord server.
But for me it doesn't look like Mapbox is investing into Flutter, so if you need offline, best bet would be to use community plugins and serve your own tiles instead of mapbox.

@raphael-bmec-co
Copy link

Thanks @AAverin. Appreciate the feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

6 participants