Offline MapBox Sideloading Merge - swift

First post to Stacked Overflow
I am having some difficulty with merge the MapBox database off-line content via sideloading. I have tried the examples in GitHub to no avail.
Can someone shed some light on the code snippet below I am using
The file path is correct and writable
The file size is 66MB so there is data in there
When I call the addContents function of the MGLOfflineStorage class the pack result is zero and the content not merged.
Any ideas?
CM
import UIKit
import Mapbox
class ViewController: UIViewController, MGLMapViewDelegate {
var mapView: MGLMapView!
var progressView: UIProgressView!
override func viewDidLoad() {
super.viewDidLoad()
let mapView = MGLMapView(frame: view.bounds, styleURL: MGLStyle.streetsStyleURL)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
mapView.delegate = self
view.addSubview(mapView)
mapView.setCenter(CLLocationCoordinate2D(latitude: 22.27933, longitude: 114.16281),
zoomLevel: 13, animated: false)
testAddFileContent()
NotificationCenter.default.addObserver(self, selector: #selector(offlinePackProgressDidChange), name: NSNotification.Name.MGLOfflinePackProgressChanged, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(offlinePackDidReceiveError), name: NSNotification.Name.MGLOfflinePackError, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(offlinePackDidReceiveMaximumAllowedMapboxTiles), name: NSNotification.Name.MGLOfflinePackMaximumMapboxTilesReached, object: nil)
print(MGLOfflineStorage.shared.packs?.count)
}
func testAddFileContent() {
let documentPaths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let documentDir = documentPaths[0]
let fileManager = FileManager.default
let directoryExists: Bool = fileManager.fileExists(atPath: documentDir)
if !directoryExists {
try? fileManager.createDirectory(atPath: documentDir, withIntermediateDirectories: true, attributes: nil)
}
let bundle = Bundle.main
// Valid database
do {
let resourceURL = bundle.url(forResource: "cache", withExtension: ".db")
let filePath = bundle.path(forResource: "cache", ofType: ".db")
// try? fileManager.moveItem(at: resourceURL! to: filePath!)
let attributes = [FileAttributeKey.posixPermissions: NSNumber(value: 0o777)]
try? fileManager.setAttributes(attributes, ofItemAtPath: filePath!)
var fileSize : UInt64
do {
//return [FileAttributeKey : Any]
let attr = try FileManager.default.attributesOfItem(atPath: filePath ?? "<#default value#>")
fileSize = attr[FileAttributeKey.size] as! UInt64
//if you convert to NSDictionary, you can get file size old way as well.
let dict = attr as NSDictionary
fileSize = dict.fileSize()
print(fileSize)
} catch {
print("Error: \(error)")
}
MGLOfflineStorage.keyPathsForValuesAffectingValue(forKey: "packs")
MGLOfflineStorage.shared.addContents(ofFile: filePath!, withCompletionHandler: nil)
print(MGLOfflineStorage.shared.packs?.count)
// loadOffline()
}
}
Merge of offline sideloaded cache.db for MapBox

I had this same issue and finally found the issue. In your main.storyboard click on the map view you are using, then click on the attribute inspector in the top right. Change the default style URL to the style URL you used when you downloaded your map region. Also change the latitude, longitude, and zoom values to match your offline region.

Related

How do I load a Data object into a SCNScene?

I want to load a 3d usdz blob into a view, but since I only have the data object, I'm trying to initialize the scene with that with no luck.
To that, I initialize the SCNSceneSource() and then open it using .scene().
Now what I don't understand:
If I use a URL and load the scene directly - it works.
If I use a Data object on the same URL it doesn't.
Apple docs says, the data should be of type NSData but that seems wrong.
import SceneKit
let url = URL(string: "file:///Users/thilo/Desktop/Input/UU2.usdz")!
// working
let src_ok = SCNSceneSource(url: url)
let scn_ok = src_ok?.scene(options: nil, statusHandler: {
a,b,c,d in print("OK: \(a) \(b) \(String(describing: c)) \(d) ")
})
print("Ok: \(scn_ok)")
// Not working?
let data = try! Data(contentsOf: url)
let src_bad = SCNSceneSource(data: data)
let scn_bad = src_bad?.scene(options: nil, status handler: {
a,b,c,d in print("BAD: \(a) \(b) \(String(describing: c)) \(d) ")
})
print("Failed: \(scn_bad)")
running on Playground says:
Ok: Optional(<SCNScene: 0x6000038e1200>)
BAD: 0.0 SCNSceneSourceStatus(rawValue: 4) nil 0x000000016fa948bf
BAD: 0.0 SCNSceneSourceStatus(rawValue: 4) nil 0x000000016fa942af
BAD: 0.0 SCNSceneSourceStatus(rawValue: -1) Optional(Error Domain=NSCocoaErrorDomain Code=260 "Could not load the scene" UserInfo={NSLocalizedDescription=Could not load the scene, NSLocalizedRecoverySuggestion=An error occurred while parsing the COLLADA file. Please check that it has not been corrupted.}) 0x000000016fa942af
Failed: nil
What am I missing?
SCNSceneSource doesn't support .usdz in Data context
Official documentation says that SCNSceneSource object supports only .scn, .dae and .abc file formats. But it turns out that SceneKit doesn't support URL-loading of .usdz only in the context of working with Data. Thus, when working with Data, use files in the .scn format.
import SceneKit
import Cocoa
class GameViewController : NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let url = URL(string: "file:///Users/swift/Desktop/ship.scn") {
let data = try! Data(contentsOf: url)
let source = SCNSceneSource(data: data)
let sceneView = self.view as! SCNView
sceneView.scene = source?.scene()
}
}
}
To load .usdz using URL, try SCNSceneSource.init?(url: URL)
class GameViewController : NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let url = URL(string: "file:///Users/swift/Desktop/ship.usdz") {
let source = SCNSceneSource(url: url)
let sceneView = self.view as! SCNView
sceneView.scene = source?.scene()
}
}
}
Or use SCNScene object to load .usdz model
class GameViewController : NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(fileURLWithPath: "/Users/swift/Desktop/ship.usdz")
do {
let scene = try SCNScene(url: url)
let sceneView = self.view as! SCNView
sceneView.scene = scene
sceneView.autoenablesDefaultLighting = true
} catch {
print(error.localizedDescription)
}
}
}
Gathering from the comment "does not support usdz" my solution is:
to create a temporary file ( .usdz) seems to be required by the API...
and then manually remove the temporary file after loading.
First extend FileManager with the below code:
public extension FileManager {
func temporaryFileURL(fileName: String = UUID().uuidString,ext: String) -> URL? {
return URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
.appendingPathComponent(fileName + ext)
}
}
For a limited hard-coded use case:
let fm = FileManager.default
let tempusdz = fm.temporaryFileURL(ext:".usdz")!
fm.createFile(atPath: tempusdz.path(), contents: sceneData)
let src = SCNSceneSource(url: tempusdz)
if let scene = src?.scene(options: nil) {
....
}
try? fm.removeItem(at: tempusdz)
of course this is a hack, because it will only work if the data is in usdz format.
Since usdz is a ZIP archive, maybe testing for a zip and then just doing the below is a better option:
let sceneData:Data? = data
var sceneSrc: SCNSceneSource? = nil
var tempURL:URL? = nil
if let dataStart = sceneData?.subdata(in: 0..<4),
let dataMagic = String(data: dataStart, encoding: String.Encoding.utf8) as String?,
dataMagic == "PK\u{3}\u{4}" {
let fm = FileManager.default
tempURL = fm.temporaryFileURL(ext: ".usdz")
if let tempURL {
fm.createFile(atPath: tempURL.path(), contents: sceneData)
sceneSrc = SCNSceneSource(url: tempURL)
}
} else {
sceneSrc = SCNSceneSource(data: sceneData!)
}
let scene = sceneSrc?.scene()
if let tempURL {
try? FileManager.default.removeItem(at: tempURL)
}
Does anyone knows a better solution?
Is there an easy way to check the type of the Data ?
potential solution could be to verify the format of the data object and ensure that it is a valid COLLADA file.
import Foundation
let url = URL(string: "file:///Users/thilo/Desktop/Input/UU2.usdz")!
let data = try! Data(contentsOf: url)
print("Data size: \(data.count)")
print("Data format: \(data.description)")
you usually get these types of errors when the data wasn't properly formatted

How to change COLLADA(.dae) file to SceneKit(.scn) file programmatically Swift

COLLADA(.dae) , SceneKit(.scn)
I am working with scenekit and problem is there is an admin who will upload .dae file to server and i'll get that link in app and by using code i have to show it on camera. In running app for .scn file url it is working fine but for .dae file url it is generating an error COLLADA files are not supported on this platform. so i want to convert .dae file to .scn file at run time. If anyone know any other source which can convert .dae file to .scn file kindly mention. By this source admin will upload .scn file to server after converting.
Finally solved thanks stackoverflow by using Model I/O swift concept
Step1#
// first need to download file to local directory
func downloadSceneTask(){
//1. Get The URL Of The SCN File
guard let url = URL(string: "Your_url") else { return }
//2. Create The Download Session
let downloadSession = URLSession(configuration: URLSession.shared.configuration, delegate: self, delegateQueue: nil)
//3. Create The Download Task & Run It
let downloadTask = downloadSession.downloadTask(with: url)
downloadTask.resume()
}
Step2#
Now i have url saved in my local device memory now i'll click anywhere in camera and load that local url in scene.
func addTapGestureToSceneView() {
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(didReceiveTapGesture(_:)))
sceneView.addGestureRecognizer(tapGestureRecognizer)
}
#objc func didReceiveTapGesture(_ sender: UITapGestureRecognizer) {
let location = sender.location(in: sceneView)
guard let hitTestResult = sceneView.hitTest(location, types: [.featurePoint, .estimatedHorizontalPlane]).first
else { return }
let results = self.sceneView.hitTest(location, types: .featurePoint)
// 2
guard let result = results.first else {
return
}
// 3
let translation = result.worldTransform.translation
self.translation = translation
anchor = ARAnchor(transform: hitTestResult.worldTransform)
sceneView.session.add(anchor: anchor!)
}
Step3#
My ViewDidLoad function be like
override func viewDidLoad() {
super.viewDidLoad()
sceneView.delegate = self
addTapGestureToSceneView()
downloadSceneTask()
}
Step4#
Add Protocol ARSCNViewDelegate
// Getting file from local directory and load
extension ViewController: ARSCNViewDelegate {
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
guard !(anchor is ARPlaneAnchor) else { return }
if let droneNode = loadModel() {
DispatchQueue.main.async {
node.addChildNode(droneNode)
}
}
}
func getDocumentsDirectory() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
return documentsDirectory
}
// Loads The SCNFile From The Documents Directory
func loadModel() -> SCNNode? {
//1. Get The Path Of The Downloaded File
let downloadedScenePath = getDocumentsDirectory().appendingPathComponent("table.obj")
let asset = MDLAsset(url: downloadedScenePath)
let object = asset.object(at: 0)
let node = SCNNode(mdlObject: object)
//7. Add It To The Scene
return node
}
}

Swift 5 - Sharing location using vCardURL

I am trying to share an address through UIActivityViewController.
This is how I get the URL:
func vCardURL(from coordinate: CLLocationCoordinate2D, with name: String?) -> URL {
let vCardFileURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("vCard.loc.vcf")
let vCardString = [
"BEGIN:VCARD",
"VERSION:4.0",
"FN:\(name ?? "Shared Location")",
"item1.URL;type=pref:http://maps.apple.com/?ll=\(coordinate.latitude),\(coordinate.longitude)",
"item1.X-ABLabel:map url",
"END:VCARD"
].joined(separator: "\n")
do {
try vCardString.write(toFile: vCardFileURL.path, atomically: true, encoding: .utf8)
} catch let error {
print("Error, \(error.localizedDescription), saving vCard: \(vCardString) to file path: \(vCardFileURL.path).")
}
return vCardFileURL
}
func didTapShareButton() {
let coordinate = CLLocationCoordinate2D(latitude: 52.520007, longitude: 13.404954)
let url = self.vCardURL(from: coordinate, with: "Berlin")
let activityViewController = UIActivityViewController(activityItems: [url], applicationActivities: nil)
//Here it opens the UIActivityViewController
present(activityViewController, animated: true, completion: nil)
}
URL Output:
file:///Users/edoardodecal/Library/Developer/CoreSimulator/Devices/E1A8A47C-ABF7-4048-ACB1-0AC91E7E0B5B/data/Containers/Data/Application/F79E6A32-B2E7-4137-A75B-AFDE3294A1C2/tmp/vCard.loc.vcf
This is the result:
What I expect:
Any hints? Thanks
Forget all this stuff. The only thing you need is to share the Apple Maps URL with the coordinates... That's it! Tested on iOS 14 with UIKit and SwiftUI.
if let url = URL(string: "https://maps.apple.com?ll=\(latitude),\(longitude)") {
let activity = UIActivityViewController(activityItems: [url], applicationActivities: nil)
}
And then show the View Controller...

Share pdf on web view to another devices using swift code

I have a WKWebView which displayed pdf. I want to share the file to another devices such as iPad, iPhone.... using the share button. I tried to display the pdf in preview so it will have the iOS share button the code below.
import UIKit
import WebKit
class ShowPDFView: UIViewController, UIDocumentInteractionControllerDelegate {
#IBAction func SharePDFFile(_ sender: Any) {
let fileName = "testPDF"
guard let urlPath = Bundle.main.url(forResource: fileName, withExtension: "pdf") else {return}
let controller = UIDocumentInteractionController(url: urlPath)
controller.delegate = self
controller.presentPreview(animated: true)
}
func documentInteractionControllerViewControllerForPreview(controller: UIDocumentInteractionController!) -> UIViewController! {
return self
}
func documentInteractionControllerViewForPreview(controller: UIDocumentInteractionController!) -> UIView! {
return self.view
}
func documentInteractionControllerRectForPreview(controller: UIDocumentInteractionController!) -> CGRect{
return self.view.frame
}
I got runtime error.
[MC] Reading from private effective user settings.
The preview does not loaded. Does anyone know why?
This function works for me. In Swift 4
#IBAction func SharePDFFile(_ sender: Any) {
let fm = FileManager.default
var pdfURL = (fm.urls(for: .documentDirectory, in: .userDomainMask)).last! as URL
pdfURL = pdfURL.appendingPathComponent("johnMilky.pdf") as URL
//Rename document name to "myFile.pdf"
let url = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("johnMilkyFile.pdf") as NSURL
do {
let data = try Data(contentsOf: pdfURL)
try data.write(to: url as URL)
let activitycontroller = UIActivityViewController(activityItems: [url], applicationActivities: nil)
if activitycontroller.responds(to: #selector(getter: activitycontroller.completionWithItemsHandler))
{
activitycontroller.completionWithItemsHandler = {(type, isCompleted, items, error) in
if isCompleted
{
print("completed")
}
}
}
//activitycontroller.excludedActivityTypes = [UIActivity.ActivityType.airDrop]
activitycontroller.popoverPresentationController?.sourceView = self.view
self.present(activitycontroller, animated: true, completion: nil)
}
catch {
print(error)
}
}

open PDF URL in iOS 8?

I have write this code to show pdf using UIDocumentInteractionController.But,I don't know how to search pdf at local directory and open in iOS 8 and below..Any help?
let filename = history.invoiceLongDate // 01223642
if !filename.isEmpty{
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
let docs = paths[0]
let pathURL = NSURL(fileURLWithPath: docs, isDirectory: true)
if #available(iOS 9.0, *) {
let fileURL = NSURL(fileURLWithPath: "\(filename)_my_invoice.pdf", isDirectory: false, relativeToURL: pathURL)
self.docController = UIDocumentInteractionController(URL: fileURL)
self.docController?.delegate = self
self.docController?.presentOptionsMenuFromRect(sender.frame, inView: self.view, animated: true)
} else {
// Fallback on earlier versions
// Any Help with that?
}
}
You can view PDF in iOS 8 by using webview. Try below code,
if let pdf = NSBundle.mainBundle().URLForResource("myPDF", withExtension: "pdf", subdirectory: nil, localization: nil) {
let req = NSURLRequest(URL: pdf)
let webView = UIWebView(frame: CGRectMake(0,0,self.view.frame.size.width,self.view.frame.size.height))
webView.loadRequest(req)
self.view.addSubview(webView)
}
OR
if let baseUrl = NSURL.fileURLWithPath(pathURL) {
let fileURL = baseUrl.URLByAppendingPathComponent(NFConstants.NFCoreDataStringIdentifiers.CoreDataStoresPathComponent.rawValue)
}
Hope this will be helpful to you.
UIDocumentInteractionController is available with (iOS 3.2, *).
For Viewing PDF file:
var documentInteractionController: UIDocumentInteractionController!
#IBAction func openDocument(sender: UIButton) {
let URL: NSURL = NSBundle.mainBundle().URLForResource("pdf-sample", withExtension: "pdf")!
if (URL != "") {
// Initialize Document Interaction Controller
self.documentInteractionController = UIDocumentInteractionController(URL: URL)
// Configure Document Interaction Controller
self.documentInteractionController.delegate = self
// Present Open In Menu
self.documentInteractionController.presentOptionsMenuFromRect(sender.frame, inView: self.view, animated: true)
//presentOpenInMenuFromRect(button.frame, inView: self.view, animated: true)
}
}
// MARK: UIDocumentInteractionControllerDelegate
func documentInteractionControllerViewControllerForPreview(controller: UIDocumentInteractionController) -> UIViewController {
return self
}