On-Demand Resources download but not accessible. Swif 4 - swift4

In my iOS app I have a bunch of mp4 videos that I download at a certain time on the app using On Demand Resources. Using this tutorial:
https://www.raywenderlich.com/520-on-demand-resources-in-ios-tutorial
I download the resources like this at the start of the app, in a previous view controller:
func requestSceneWith(tag: String,
onSuccess: #escaping () -> Void,
onFailure: #escaping (NSError) -> Void) {
// 2
currentRequest = NSBundleResourceRequest(tags: [tag])
// 3
guard let request = currentRequest else { return }
request.beginAccessingResources { (error: Error?) in
// 4
if let error = error {
onFailure(error as NSError)
return
}
// 5
onSuccess()
}
The resource seem to download fine, and I know that they have been downloaded, by looking in the disk report in xcode.
However, when the videos are supposed to be played in the app, the app just shows a black screen. Here is my code to play the videos:
let videoURL = Bundle.main.url(forResource: "cow2", withExtension: "mp4", subdirectory: "Videos/Animals")
self.player = AVPlayer(url: videoURL!)
self.myPlayerController.player = self.player
self.myPlayerController.player?.play()
Now, when the resources are not tagged, and they come with the app and not downloaded later, they work fine. And the console prints the file name, like I did (print(videoURL.absoluteString). But after they are tagged and downloaded later, they dont work, and nothing prints in the console. Just a black screen appears in the app.
I've been stuck on this for ages, and help with really help.
Thanks

I think you didn't request the on-demand resources before accessing the video file. Maybe you can try to declare the NSBundleResourceRequest instance as a global variable in AppDelegate.

Related

Implementing "Open file with" in Swift Cocoa App

I'm working on a macOS cocoa-app in Swift where I import several different file types into the app for the user to interact with.
I'm currently trying to determine if it's possible to implement the "Open file with" feature, so that the user could open those files in a different program if they wanted to:
I've found a few different SO questions that seem tangentially related to what I'm trying to do:
Swift: How to open file with associated application?
Launch OSX Finder window with specific files selected
...but so far nothing to indicate if it's possible to implement right-click Finder/file (?) access in the way I had in mind.
Apologies if this is too vague of a question; any help / guidance appreciated!
Without going into details, it's pretty straight forward:
Get the list of all known applications that can open a specific file type (see LSCopyApplicationURLsForURL, a Core Foundation C function).
Build the menu. You can use NSWorkspace (and probably URL) to get the application icons.
Use NSWorkspace.openFile(_:withApplication:) to tell the application to open the given document.
2022, Swift 5
Get app list associated with local file:
func getAppsAssociatedWith(_ url: URL?) {
guard let url = localFileURL,
let retainedArr = LSCopyApplicationURLsForURL( url as CFURL, .all)?.takeRetainedValue(),
let listOfRelatedApps = retainedArr as? Array<URL>
else {
return []
}
return listOfRelatedApps
}
Getting thumbnail for app:
let singleAppIcon = NSWorkspace.shared
.icon(forFile: appUrl.path)
.scaledCopy(sizeOfLargerSide: 17)
Open url with app:
#available(macOS 10.15, iOS 9.0, *)
public class func openUrlWithApp(_ urls: [URL], appUrl: URL) {
NSWorkspace.shared.open(urls, withApplicationAt: appUrl, configuration: NSWorkspace.OpenConfiguration())
}
In my app I'm cashing all apps icons in dictionary.
[someFile localURL : app icon]
If I have already got icon earlier - no need to get it once more
var relatedAppsThumbnails: [URL: Image] = [:]
func updateRelatedApps() {
guard let url = currImgUrl, // file url to get icons from related apps
let retainedArr = LSCopyApplicationURLsForURL( url as CFURL, .all)?.takeRetainedValue(),
let listOfRelatedApps = retainedArr as? Array<URL>
else {
relatedApps = []
return
}
self.relatedApps = listOfRelatedApps
// add app icon in case of it wasn't added yet
for appUrl in listOfRelatedApps {
if relatedAppsThumbnails[appUrl] == nil {
let nsImg = NSWorkspace.shared.icon(forFile: appUrl.path)
.scaledCopy(sizeOfLargerSide: 17)
relatedAppsThumbnails[appUrl] = Image(nsImage: nsImg)
}
}
}
LSCopyApplicationURLsForURL is deprecated. You can use this alternative:
func getListOfExternalApps(forURL url: URL) -> [(URL, Image)] {
let listOfExternalApps = NSWorkspace.shared.urlsForApplications(toOpen: url)
let icons = listOfExternalApps.map {
let nsimage = NSWorkspace.shared.icon(forFile: $0.path())
nsimage.size = CGSize(width: .s16, height: .s16)
return Image(nsImage: nsimage)
}
return Array(zip(listOfExternalApps, icons))
}

Swift 3/4 AVAudioPlayer:contentsOf: fails when loading same URL many times

I am working on a simple game in Swift 4, Xcode 9 Beta 6, and I get an error when attempting to load an Audio File from the same URL many times. Let me show you what I mean; I have a code that looks a little like this.
guard let url = Bundle.main.url(forResource: soundName, withExtension: ext) else {
return nil
}
do {
player = try AVAudioPlayer(contentsOf: url)
player.prepareToPlay()
} catch let error {
print("Could not create AVAudioPlayer with url: \(url.absoluteString)")
return nil
print("Error: \(error.localizedDescription")
}
Note: soundName and ext are String objects composing the name of the local audio file. player is a property of the type AVAudioPlayer.
The game creates a sound using this code, which works perfectly at first, but after a while, it kind of gets lazy and the player = try AVAudioPlayer(contentsOf: url) fails and prints:
Could not create AVAudioPlayer with url: url here
Error: The operation couldn't be completed. (NSOSStatusErrorDomain error -42.)
Anyone has any ideas?
EDIT 1:
I loaded the content of the URL into a Data object by using let data = try Data(contentsOf: url) and that seems to have fix the issue. I am not making it as resolved because I still wish to know what is wrong with using just a URL and why it stops working after loading the same URL several times. Additionally, loading the contents into a binary Data object first might take more resources and time?

Loading Data from Bundled Realm Database in Swift

I’m relatively new to Realm. My task is to bundle a RealmDB and make it writable. Thus far I have copied the bundled realm file into the project and implemented the following code in the app delegate. Above the "func application(application: UIApplication, didFinishLaunchingWithOptions” I used the following function:
func bundleURL(name: String) -> NSURL? {
return NSBundle.mainBundle().URLForResource("data", withExtension: "realm") }
And below didFinishLaunchingWithOptions, I used the following:
if let v0URL = bundleURL("data.realm") {
do {
try NSFileManager.defaultManager().removeItemAtURL(defaultURL)
try NSFileManager.defaultManager().copyItemAtURL(v0URL, toURL: defaultURL)
} catch {}
The issue is that I have to load the app twice to get the data to show up in a MapViewController, which is the first controller upon launch. In this case, I want map pins in the MapViewController to automatically appear upon build. I tried to implement a notification in the MapViewController using the following:
let results = try! Realm().objects(Spaces)
notificationToken = results.addNotificationBlock {[weak self](changes: RealmCollectionChange<Results<Sapces>>) in
self!.populateMap()
I also tried to implement a Database Manager:
func getDBItems() -> [Spaces] {
let dbItemsFromRealm = try! Realm().objects(Spaces)
var bathroom = [Spaces]()
if dbItemsFromRealm.count > 0 {
for dbItemsInRealm in dbItemsFromRealm {
let spaces = dbItemsInRealm as Spaces
space.append(space)
}
}
return space
}
}
However, I can’t get the pins to load upon launch. Any help would be much appreciated.
The behavior you describe is what I'd expect to see if you've already opened the Realm at the target path prior to copying the bundled Realm over to that location. You can confirm this by putting a breakpoint on the Realm initializer and on your code that calls removeItemAtURL and seeing which is hit first.

Play audio file using AVAudioPlayer swift

I am downloading the audio message first then play it using Avaudioplayer
downloadTask = NSURLSession.sharedSession().downloadTaskWithURL(urlStr, completionHandler: { (URL, response, error) -> Void in
self.play(URL!)
})
downloadTask
This works fine, but if I call play(URL) method after saving the response URL that I have stored in NSUserDefaults. The URL is same in both cases. I have checked it. its something like:
file:///Users/mymac/Library/Developer/CoreSimulator/Devices/X-CD91-XXXXXXX-XXX-XXXXXX/data/Containers/Data/Application/XXXXXXX-XXXX-XXXXXXXXXX/tmp/CFNetworkDownload_InUTA3.tmp
Passing this file url to:
func play(url : NSURL) {
do {
player = try AVAudioPlayer(contentsOfURL: url)
player.prepareToPlay()
player.play()
} catch let error as NSError {
//self.player = nil
print(error.localizedDescription)
} catch {
print("AVAudioPlayer init failed")
}
}
Url path is same, but the audioPlayer is unable to play the sound, it shows the following error:
The operation couldn’t be completed. (OSStatus error 2003334207.)
Can anyone guide me in this regard?
Thanks.
As can be seen from the comments above the problem was that the downloaded file was not moved before completing the completionHandler of the downloadTask method.
As it says in the description of the location parameter
The location of a temporary file where the server’s response is stored. You must move this file or open it for reading before your completion handler returns. Otherwise, the file is deleted, and the data is lost.
Therefore, in your completionHandler you must move the file to another location that you control before ending the completionHandler.
This probably also explains the crash. If the "temp" URL is saved to UserDefaults in the completionHandler then that URL is no longer valid when the completionHandler has completed. So when you then later call
play(URL!)
with a force unwrapped URL that is now invalid, you get a crash.
Therefore, as an extra safety measure, try unwrapping the URL before trying to use it:
if let url = urlFromUserDefaults {
play(url)
}
Hope that helps you.
You can simply write this...
do
{
self.player = try AVAudioPlayer(contentsOf:Url)
self.player?.numberOfLoops = 0
self.player?.prepareToPlay()
self.player?.volume = 1.0
self.player?.play()
self.player?.delegate=self
}
catch let error as NSError
{
print(error)
}

CNContact(s) created locally are not synced to iCloud account

Recently some of our users were complaining about contacts not syncing to their iCloud account. It was working in iOS 8 and mysteriously stopped in one of the updates in iOS 9. With iOS 10 around the corner I thought it might be linked to deprecation of AddressBook.framework to Contacts.framework. However, even moving to new Contacts.framework it didn't fix the problem.
There are no error logs on console of the device and neither of the frameworks generate any errors when contacts are being created/updated on the device.
Contacts are visible and available on the device just not syncing to iCloud and other devices attached to the iCloud account.
After a lot of debugging what I was able to isolate the issue to imageData property. Contacts which had imageData populated were not synced and the few which had no images were synced. This lead me to look at the code for imageData. Turns out I had been using UIImagePNGRepresentation to convert UIImage to NSData for imageData. Moving to UIImageJPEGRepresentation fixed the issue. The day was saved and iCloud accounts are synced.
Thank you Apple for not documenting this change. (it might be the image size even that is not documented)
CNContact(s) created locally are not synced to Third party application.
I have crated contacts in iphone contacts page.Those contacts are not synching inside my application.I have used below method to fetch contacs from my contacts framework.
There are no error logs on console of the device and neither of the frameworks generate any errors when contacts are being created/updated on the device.
Contacts are visible and available on the device just not syncing to my app.I have given permission to my application to access my contacts.
#available(iOS 10.0, *)
func retrieveContacts(_ completion: (_ success: Bool, _ contacts: [ContactEntry]?) -> Void) {
var contacts = [ContactEntry]()
do {
let contactsFetchRequest = CNContactFetchRequest(keysToFetch: [CNContactGivenNameKey as CNKeyDescriptor, CNContactFamilyNameKey as CNKeyDescriptor, CNContactImageDataKey as CNKeyDescriptor, CNContactImageDataAvailableKey as CNKeyDescriptor, CNContactPhoneNumbersKey as CNKeyDescriptor, CNContactEmailAddressesKey as CNKeyDescriptor])
try contactStore.enumerateContacts(with: contactsFetchRequest, usingBlock: { (cnContact, error) in
if let contact = ContactEntry(cnContact: cnContact) {
contacts.append(contact)
print(contact,contacts.count)
}
})
completion(true, contacts)
} catch {
completion(false, nil)
}
let keysToFetch = [CNContactFormatter.descriptorForRequiredKeys(for: .fullName),CNContactPhoneNumbersKey] as [Any]
let fetchRequest = CNContactFetchRequest( keysToFetch: keysToFetch as! [CNKeyDescriptor])
var contacts1 = [CNContact]()
if #available(iOS 10.0, *) {
fetchRequest.mutableObjects = false
} else {
// Fallback on earlier versions
}
fetchRequest.unifyResults = true
fetchRequest.sortOrder = .userDefault
let contactStoreID = CNContactStore().defaultContainerIdentifier()
print("\(contactStoreID)")
do {
try CNContactStore().enumerateContacts(with: fetchRequest) { (contact, stop) -> Void in
contacts1.append(contact)
print(contact)
}
} catch let e as NSError {
print(e.localizedDescription)
}