How to load a usdz model and textures from a remote server using ARKit? - swift

How to load a usdz model and textures from a remote server using ARKit?
As in the example below:
let myURL = NSURL(string: "https://mywebsite.com/vase.scn")
guard let scene = try? SCNScene(url: myURL! as URL, options: nil) else {
return
}
let node = scene.rootNode.childNode(withName: "vase", recursively: true)
let transform = queryResult.worldTransform
let thirdColumn = transform.columns.3
node!.position = SCNVector3(thirdColumn.x, thirdColumn.y, thirdColumn.z)
self.sceneView.scene.rootNode.addChildNode(node!)

I found solution as in the below:
Download model and save
func downloadModel() {
guard let url = URL(url: originalURL) else { return }
let urlSession = URLSession(configuration: .default, delegate: self, delegateQueue: OperationQueue())
let downloadTask = urlSession.downloadTask(with: url)
downloadTask.resume()
}
extension ARViewController: URLSessionDownloadDelegate {
public func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { print("locationUrl:", location.path)
// create destination URL with the original file name
guard let url = downloadTask.originalRequest?.url else { return }
let documentsPath = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
let destinationURL = documentsPath.appendingPathComponent(url.lastPathComponent)
// delete original copy
try? FileManager.default.removeItem(at: destinationURL)
// copy from temp to Document
do {
try FileManager.default.copyItem(at: location, to: destinationURL)
self.originalURL = destinationURL
print("originalURL:", originalURL!.path)
} catch let error {
print("Copy Error: \(error.localizedDescription)")
}
}
}
Load usdz model and textures
func addItem(queryResult: ARRaycastResult) {
let url = URL(string: originalURL!.path)
let mdlAsset = MDLAsset(url: url!)
mdlAsset.loadTextures()
let scene = SCNScene(mdlAsset: mdlAsset)
let node = scene.rootNode.childNode(withName: "model", recursively: true)
let transform = queryResult.worldTransform
let thirdColumn = transform.columns.3
node!.position = SCNVector3(thirdColumn.x, thirdColumn.y, thirdColumn.z)
self.sceneView.scene.rootNode.addChildNode(node!)
}

Related

Download PDF from API

The API I am using has 3 parameters year month and salaryType.
When I enter those parameters to my URL
if let url = URL(string: "https://rip.rtuk.gov.tr/workplacebackend/api/Employee/GetPayroll?yil=\(year)&ay=\(month)&maasturu=\(salaryType)")
URL becomes
"https://rip.rtuk.gov.tr/workplacebackend/api/Employee/GetPayroll?yil=2020&ay=2&maasturu=1"
When I enter this URL with Auth and Token it returns a pdf file. How should I modify my getPayroll so that I can download pdf after the request?
func getPayroll(year: Int, month: Int, salaryType: Int, completion: #escaping (String) -> ()) {
if let url = URL(string: "https://rip.rtuk.gov.tr/workplacebackend/api/Employee/GetPayroll?yil=\(year)&ay=\(month)&maasturu=\(salaryType)") {
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.addValue("Basic \(authToken)", forHTTPHeaderField: "Authorization")
request.addValue(workingToken, forHTTPHeaderField: "token")
let task = URLSession.shared.downloadTask(with: request) { (localURL, urlResponse, error) in
if let localURL = localURL {
if let string = try? String(contentsOf: localURL) {
completion(string)
}
}
}
task.resume()
}
}
So when I tap on a cell I want to show an alert, download and then open pdf
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
switch cellType(rawValue: selected.title) {
case .salary:
selectedPayment = (payment?[indexPath.row])!
RtukNetworkManager.shared.getPayroll(year: selectedPayment.year, month: selectedPayment.month, salaryType: selectedPayment.paymentType) { filePath in
DispatchQueue.main.async {
self.displayDownloadAlert(filePath)
}
}
func downloadFile(path: String) {
let donwloadManager = FileDownloadManager()
donwloadManager.downloadFile(path: path, viewController: self)
}
When I tap on a cell getting an error as an alert
Error
The document can not be shown
this will save pdf file to url and also check if already downloaded
func getPayroll(year: Int, month: Int, salaryType: Int, completion: #escaping ((URL?) -> ())) {
let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let parameters: [String: Int] = ["yıl": year, "ay": month, "maasturru": salaryType]
if let url = URL(string: "https://rip.rtuk.gov.tr/workplacebackend/api/Employee/GetPayroll?yil=\(year)&ay=\(month)&maasturu=\(salaryType)") {
let destinationUrl = documentsUrl.appendingPathComponent(url.lastPathComponent)
if FileManager().fileExists(atPath: destinationUrl.path) {
print("File already exists [\(destinationUrl.path)]")
completion(destinationUrl)
}
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.addValue("Basic \(authToken)", forHTTPHeaderField: "Authorization")
request.addValue(workingToken, forHTTPHeaderField: "token")
let session = URLSession(configuration: .default)
let task = session.dataTask(with: request) { (data, response, error) in
if error == nil {
if let response = response as? HTTPURLResponse {
if response.statusCode == 200 {
if let data = data {
if let _ = try? data.write(to: destinationUrl, options: Data.WritingOptions.atomic)
{
completion(destinationUrl)
}
else
{
completion(destinationUrl)
}
}
}
}
} else {
print(error?.localizedDescription)
completion(nil)
}
}
task.resume()
}
}
after that call your to download
RtukNetworkManager.shared.getPayroll(year: selectedPayment.year, month: selectedPayment.month, salaryType: selectedPayment.paymentType) { filePath in
if let url = filePath {
self.displayDownloadAlert(url.lastPathComponent)
showPdf(url:url)
}
}
to show pdf
func showPdf(url:URL) {
let pdfView = PDFView(frame: CGRect(x:100,y:100,width:200,height:200))
let pdfDocument = PDFDocument(url: url))
pdfView.autoresizesSubviews = true
pdfView.autoresizingMask = [.flexibleWidth, .flexibleHeight, .flexibleTopMargin, .flexibleLeftMargin]
pdfView.displayDirection = .vertical
pdfView.autoScales = true
pdfView.displayMode = .singlePage
pdfView.displaysPageBreaks = true
pdfView.document = pdfDocument
pdfView.maxScaleFactor = 4.0
pdfView.minScaleFactor = pdfView.scaleFactorForSizeToFit
pdfView.usePageViewController(true, withViewOptions: [:])
view.addSubview(pdfView)
}
also import PDFKit
PDFView use for displaying pdf

AVAudioEngine doesn't playback a sound

I am trying to play with AVAudioEngine to playback the wav file. I tried to do it in a few different ways, but nothing work.
Try 1
...
private var audioEngine: AVAudioEngine = AVAudioEngine()
private var mixer: AVAudioMixerNode = AVAudioMixerNode()
private var audioFilePlayer: AVAudioPlayerNode = AVAudioPlayerNode()
func Play1() {
guard let filePath = Bundle.main.url(forResource: "testwav", withExtension: "wav", subdirectory: "res") else {
print("file not found")
return
}
print("\(filePath)")
guard let audioFile = try? AVAudioFile(forReading: filePath) else{ return }
let audioFormat = audioFile.processingFormat
let audioFrameCount = UInt32(audioFile.length)
guard let audioFileBuffer = AVAudioPCMBuffer(pcmFormat: audioFormat, frameCapacity: audioFrameCount) else{ return }
do{
try audioFile.read(into: audioFileBuffer)
} catch{
print("over")
}
let mainMixer = audioEngine.mainMixerNode
audioEngine.attach(audioFilePlayer)
audioEngine.connect(audioFilePlayer, to:mainMixer, format: audioFileBuffer.format)
audioEngine.connect(mainMixer, to:audioEngine.outputNode, format: audioFileBuffer.format)
try? audioEngine.start()
audioFilePlayer.play()
audioFilePlayer.scheduleBuffer(audioFileBuffer, at: nil, options:AVAudioPlayerNodeBufferOptions.loops)
}
...
Try 2
...
private var audioEngine: AVAudioEngine = AVAudioEngine()
private var mixer: AVAudioMixerNode = AVAudioMixerNode()
private var audioFilePlayer: AVAudioPlayerNode = AVAudioPlayerNode()
func Play2() {
DispatchQueue.global(qos: .background).async {
self.audioEngine.attach(self.mixer)
self.audioEngine.connect(self.mixer, to: self.audioEngine.outputNode, format: nil)
// !important - start the engine *before* setting up the player nodes
try! self.audioEngine.start()
let audioPlayer = AVAudioPlayerNode()
self.audioEngine.attach(audioPlayer)
// Notice the output is the mixer in this case
self.audioEngine.connect(audioPlayer, to: self.mixer, format: nil)
guard let fileUrl = Bundle.main.url(forResource: "testwav", withExtension: "wav", subdirectory: "res") else {
// guard let url = Bundle.main.url(forResource: "audiotest", withExtension: "mp3", subdirectory: "res") else {
print("mp3 not found")
return
}
do {
let file = try AVAudioFile(forReading: fileUrl)
audioPlayer.scheduleFile(file, at: nil, completionHandler: nil)
audioPlayer.play(at: nil)
} catch let error {
print(error.localizedDescription)
}
}
}
...
...
private var audioEngine: AVAudioEngine = AVAudioEngine()
private var mixer: AVAudioMixerNode = AVAudioMixerNode()
private var audioFilePlayer: AVAudioPlayerNode = AVAudioPlayerNode()
func Play3() {
DispatchQueue.global(qos: .background).async {
self.audioEngine = AVAudioEngine()
_ = self.audioEngine.mainMixerNode
self.audioEngine.prepare()
do {
try self.audioEngine.start()
} catch {
print(error)
}
guard let url = Bundle.main.url(forResource: "testwav", withExtension: "wav", subdirectory: "res") else {
// guard let url = Bundle.main.url(forResource: "audiotest", withExtension: "mp3", subdirectory: "res") else {
print("mp3 not found")
return
}
let player = AVAudioPlayerNode()
player.volume = 1.0
do {
let audioFile = try AVAudioFile(forReading: url)
let format = audioFile.processingFormat
print(format)
self.audioEngine.attach(player)
self.audioEngine.connect(player, to: self.audioEngine.mainMixerNode, format: format)
player.scheduleFile(audioFile, at: nil, completionHandler: nil)
} catch let error {
print(error.localizedDescription)
}
player.play()
}
}
...
Also should be mentioned that there are no errors, while debugging I see that all the methods are executed and everything is ok, but I don't hear sound playback...
What am I doing wrong?
Try to activate your audio session with the following method:
func setActive(_ active: Bool, options: AVAudioSession.SetActiveOptions = []) throws.
Please note that if another active audio session has higher priority than yours (for example, a phone call), and neither audio session allows mixing, attempting to activate your audio session fails. Deactivating an audio session that has running audio objects stops them, deactivates the session, and return an AVAudioSession.ErrorCode.isBusy error.

How to download USDZ by URLSession?

I download usdz by urlsession in my code:
let url = URL(string: "download usdz url")
let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let destinationUrl = documentsUrl.appendingPathComponent(url!.lastPathComponent)
let session = URLSession(configuration: URLSessionConfiguration.default, delegate: nil, delegateQueue: nil)
var request = URLRequest(url: url!)
request.httpMethod = "GET"
let downloadTask = session.downloadTask(with: request, completionHandler: { (location:URL?, response:URLResponse?, error:Error?) -> Void in
let fileManager = FileManager.default
try! fileManager.moveItem(atPath: location!.path, toPath: destinationUrl.path)
do {
let testEntity = try Entity.load(contentsOf: destinationUrl) // Error
}
catch {
print("\(error.localizedDescription)")
}
})
downloadTask.resume()
but this code crash for:
let testEntity = try Entity.load(contentsOf: destinationUrl).
Does everybody have this problem?
stack picture
This problem is solved.
let entity call on the main thread.
let url = URL(string: "download usdz url")
let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let destinationUrl = documentsUrl.appendingPathComponent(url!.lastPathComponent)
let session = URLSession(configuration: URLSessionConfiguration.default, delegate: nil, delegateQueue: nil)
var request = URLRequest(url: url!)
request.httpMethod = "GET"
let downloadTask = session.downloadTask(with: request, completionHandler: { (location:URL?, response:URLResponse?, error:Error?) -> Void in
let fileManager = FileManager.default
if fileManager.fileExists(atPath: destinationUrl.path) {
try! fileManager.removeItem(atPath: destinationUrl.path)
}
try! fileManager.moveItem(atPath: location!.path, toPath: destinationUrl.path)
DispatchQueue.main.async {
do {
let object = try Entity.load(contentsOf: destinationUrl) // It is work
let anchor = AnchorEntity(world: [0, 0, 0])
anchor.addChild(object)
self.arView.scene.addAnchor(anchor)
}
catch {
print("Fail load entity: \(error.localizedDescription)")
}
}
})
downloadTask.resume()
Thanks BlackMirrorz

How to put image to NSCache in Swift?

I make some code using swift 4 to load image from URL, but every time I add images to server, it took a lot of time to load it in colection view or table view. I want to try store it in NScache but i dont understand to do it. can anyone help me, I'm new in swift :(
#objc func loadPosts() {
let url = URL(string: "http://someURL/Url.php")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
let body = "phomepost=\(homepost)"
request.httpBody = body.data(using: String.Encoding.utf8)
URLSession.shared.dataTask(with: request) { data, response, error in
DispatchQueue.main.async(execute: {
if error == nil {
do{
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
self.comments.removeAll(keepingCapacity: false)
self.images.removeAll(keepingCapacity: false)
self.collectionView?.reloadData()
guard let parseJSON = json else {
print("Error While Parsing")
return
}
guard let posts = parseJSON["posts"] as? [AnyObject] else {
print("Error while parseJSONing")
return
}
self.comments = posts.reversed()
for i in 0 ..< self.comments.count {
let path = self.comments[i]["path"] as? String
if !path!.isEmpty {
let url = NSURL(string: path!)!
let imageData = try? Data(contentsOf: url as URL)
let image = UIImage(data: imageData! as Data)!
self.images.append(image)
} else {
let image = UIImage()
self.images.append(image)
}
}
self.collectionView?.reloadData()
//print(posts.count)
} catch {
print(error)
}
}else{
print(error)
}
})
}.resume()
}
You can use something like this:
private let cache = NSCache<NSString, NSData>()
.....
func downloadImage(url: String, handler: #escaping(Data?, Error?) -> Void){
let cacheID = NSString(string: url)
if let cachedData = cache.object(forKey: cacheID) {
handler((cachedData as Data), nil)
}else{
if let url = URL(string: url) {
let session = URLSession(configuration: urlSessionConfig)
var request = URLRequest(url: url)
request.cachePolicy = .returnCacheDataElseLoad
request.httpMethod = "get"
session.dataTask(with: request) { (data, response, error) in
if let _data = data {
self.cache.setObject(_data as NSData, forKey: cacheID)
handler(_data, nil)
}else{
handler(nil, error)
}
}.resume()
} else {
// NetworkError is a custom error
handler(nil, NetworkError.invalidURL)
}
}
}
}
This will add a small animation while loading using image set.
let imageCache = NSCache<AnyObject, AnyObject>()
extension UIImageView {
func loadImageFromUrl(urlString: String) {
let loader1 = UIImage(named: "loaderImage1.png")
let loader2 = UIImage(named: "loaderImage2.png")
let loader3 = UIImage(named: "loaderImage3.png")
let imageArray = [loader1, loader2, loader3]
let animatedImage = UIImage.animatedImage(with: imageArray as! [UIImage], duration: 1.7)
if let imageFromCache = imageCache.object(forKey: urlString as AnyObject) as? UIImage{
self.image = imageFromCache
return
} else {
self.image = animatedImage
Alamofire.request(urlString, method: .get).response { (responseData) in
if let data = responseData.data {
DispatchQueue.main.async {
if let imageToCache = UIImage(data: data){
imageCache.setObject(imageToCache, forKey: urlString as AnyObject)
self.image = imageToCache
}
}
}
} //alamofire
}
}
}

Swift3 ios10 Xcode8 saving and loading pdfs to and from device

(this is my first post here, I am sorry for any mistakes i have made)
i have code that works perfectly on the iOS simulator but once i run it on a real device it shows online a white screen and this error: com.apple.WebKit.WebContent: 113: Could not find specified service
this is my code for downloading a pdf file from firebase to the device:
#IBAction func downloadButtonPressed (sender: UIButton) {
print("zzz download button pressed")
let directoryPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) //FileManager().url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
if directoryPath.count > 0 {
let documentsDirectory = directoryPath[0]
let pathURL = URL(fileURLWithPath: documentsDirectory)
let fileURL = pathURL.appendingPathComponent("\(fixture.id).pdf")
let httpsReference = FIRStorage.storage().reference(forURL: fixture.onlineManual)
print("zzz urlManual: \(fixture.onlineManual)")
let downloadTask = httpsReference.write(toFile: fileURL) {(URL, error) in
if error != nil {
print("downloadError with pdf")
print(error?.localizedDescription)
} else {
print("zzz donwload successful: \(URL!)")
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let newManual = NSEntityDescription.insertNewObject(forEntityName: "OfflineManuals", into: context)
newManual.setValue(self.fixture.id, forKey: "id")
newManual.setValue("\(URL!)", forKey: "manual")
do {
try context.save()
print("zzz manual saved to device")
} catch {
print("zzz error saving manual to core data")
}
}
}
}
}
this is the code that loads the file from the device and displays it in a WKWebView
func loadRequest() {
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
if documentsPath.count > 0 {
let documentsDirectory = documentsPath[0]
let restorePath = documentsDirectory + "/\(manualURL!).pdf"
let url = URL(fileURLWithPath: restorePath)
print("zzz url for local file: \(url)")
let req = URLRequest(url: url)
webView.load(req)
}
}
thank you for reading!
I finally found a way to do this. I still have no clou why my code in the question does not work on real devices but this one does and since i found a lot of people with similar problems desperatly looking for an answer use this:
SWIFT3 XCODE 8 IOS 10
import UIKit
import WebKit
class ViewController: UIViewController {
#IBOutlet weak var myView: UIView!
var webView: WKWebView!
let sema = DispatchSemaphore( value: 0 )
override func viewDidLoad() {
super.viewDidLoad()
webView = WKWebView()
myView.addSubview(webView)
savePDFtoDevice()
loadPDF()
}
the LayoutSubviews is not needed but i had problems without it...
override func viewDidLayoutSubviews() {
print("zzz width: \(self.myView.frame.size.width), height: \(myView.frame.size.height)")
print("zzz width screen: \(self.view.frame.width)")
let frame = CGRect(x: 30.0, y: 30.0, width: (self.myView.frame.width - 60), height: 300)
webView.frame = frame
}
func savePDFtoDevice(){
let myURL = URL(string: "http://www.claypaky.it/media/documents/HR_Mythos_Manual_05.2016_EN.pdf")
let req = URLRequest(url: myURL!)
let session = URLSession(configuration: .default)
session.dataTask(with: req) { (data, response, error) in
if error != nil {
print("zzz error got no session")
} else {
if let pdfData = data {
var url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
url = url.appendingPathComponent("file.pdf")
print("zzz url to save: \(url)")
print("zzz got data: \(pdfData.description)")
try? data?.write(to: url)
print("zzz wrote to file")
self.sema.signal()
}
}
}.resume()
sema.wait()
}
func loadPDF() {
let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
print("zzz url to load from: \(url)")
do {
let content = try FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: nil, options: .skipsHiddenFiles)[0]
print("zzz file: \(content)")
webView.loadFileURL(content, allowingReadAccessTo: content)
print("zzz webView loaded")
} catch {
print("zzz error with getting the contents...")
}
}
}
I tried it in my app with changing URLs and filenames etc. and it does the trick.
Finally!!!!