Why is it not possible to load a UIViewController from a nib into Swift Playground? - uiviewcontroller

Create a new playground
Create an UI file (xib) and add it to the "Resources" folder in playground
In the xib, drag a UIViewController into the canvas. By default it will have a UIView attached to it. Leave it there.
In the terminal, compile the xib into a nib (using ibtool --compile out.nib in.xib). Now there's a nib file that you can use.
Finally in Playground, load the VC from the nib and assign it to playground's live view:
import UIKit
import PlaygroundSupport
class MyVC: UIViewController {
}
let vc = MyVC(nibName: "vc", bundle: nil)
PlaygroundPage.current.liveView = vc
Doesn't matter how I slice it, keep getting "loaded the "vc" nib but the view outlet was not set" error.
I've exhausted all my search results and book reading on this issue in vain. Maybe this is not possible?
I can load a UIView from a nib just fine. I can also create a view controller class in playground and in its loadView method, I can load a UIView from a nib and assign it to the VC's view. But I can't seem to just load the view controller itself from a nib...?
Thanks for any suggestions!

Figured it out. I had to load it as a UINib and instantiate the view controller from there as the first object in the returned array:
let nib = UINib(nibName: "vc", bundle: nil)
let nibArray = nib.instantiate(withOwner: nil, options: nil)
let vc = nibArray.first as! UIViewController
PlaygroundPage.current.liveView = vc

Related

Problem loading JSON resource in SWIFT package

I am trying to load a JSON file that is bundled with a Swift package I am working on.
The JSON file is called config.json and it is located in my project under /Sources/<Target>/data/config.json
UPDATE: I have also added the resource to the target in the Package Description as follows:
...
.target(
name: "MyPackage",
resources: [
.process("data/config.json")
]
),
...
I am trying to load it using the following code:
guard let sourcesURL = Bundle.main.url(forResource: "config", withExtension: "json") else {
fatalError("Could not find config.json")
}
... but I keep getting nil. Has anyone had an issue like this? Any idea what I'm doing wrong?
You should use module
Bundle.module.url(forResource: "config", withExtension: "json")
And you need to make sure the file is included when building by adding below to your target in Package.swift
resources: [
.copy("data/config.json") //This could alo be .process
])
The above assumes that the resource is loaded from within the same package, if you want to load a resource from another package you can use the following general solution.
First define a constant in the package that holds the bundle identifier for the package, for instance in an extension to Bundle
extension Bundle {
public static let myPackageNameBundleIdentifier = Bundle.module.bundleIdentifier!
}
And then create an instance of that Bundle when loading the resource in another package or the app
if let bundle = Bundle(identifier: Bundle.myPackageNameBundleIdentifier) {
let url = bundle.url(forResource: "config", withExtension: "json")
// ...
}

Bitrate in TVMLKit JS media player

How to get bit rate information from the TVMLKit JS player object?
In apple documentation(https://developer.apple.com/documentation/tvmljs/player), I am not able to identify any attribute/method that returns this particular information. Is this possible to get this information?
Doesn't seem like there is any way to read the player item's access log from JS. At least none that I have been able to find either.
However, if you have access to the native code, as a workaround, you can listen for the AVPlayerItemNewAccessLogEntry notification.
Not the perfect solution, but perhaps it's enough for your use-case.
// Add observer somewhere.
NotificationCenter.default.addObserver(self, selector: #selector(accessLog(notification:)), name: .AVPlayerItemNewAccessLogEntry, object: nil)
#objc func accessLog(notification: Notification) {
guard let playerItem = notification.object as? AVPlayerItem,
let accessLogEvent = playerItem.accessLog()?.events.last
else { return }
_ = accessLogEvent.observedBitrate
}

integrate UISearchController with Async result update in swift

I want to implement UISearchContoller that search from webservice JSON with swifty json, exactly like apple's appStore when you search for an app, it loads without load them into tableView
here is what I have done in updateSearchResultsForSearchController method:
func updateSearchResultsForSearchController(searchController: UISearchController) {
filterContentForSearchText(searchController.searchBar.text!)
}
func filterContentForSearchText(searchText: String) {
filteredContents = myStruct.filter{$0.name.rangeOfString(searchText) != nil
}
Posting more of your code would be nice, like what you are using to get the results from the web service, However, I will try to step you through it anyway.
I have done with before just using a UISearchBar and it's delegate method, one the user pressed the search button or enter, I would use NSURLSession to pass the user's search terms to the API, and parsed the response.
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
let searchText : String = searchBar.text
webService.getRecipe(ingredient: searchText, completionHandler: { (recipeArray) in
self.highProteinArray = recipeArray
dispatch_async(dispatch_get_main_queue(), {
self.collectionView.reloadData()
})
})
}
As you can see I used a callback to handle setting the newly parsed data to a variable for later use, and then reloaded the collectionView. This way your tableView/collectionView will load and set itself up while you are waiting for the response from the web service and then parsing it, once that is complete you just reload to show the new data.
To add a little extra you could even add a fading in animation in your cellForItemAtIndexPath or cellForRowAtIndexPath, whichever you are using.

Parsing JSON file from folder - NSTemporaryDirectory

I have got a file downloaded from AWS S3 to my NSTemporaryDirectory using this code:
let downloadFilePath = (NSTemporaryDirectory() as NSString).stringByAppendingPathComponent("FILENAME")
let downloadingFileURL = NSURL(fileURLWithPath:downloadFilePath)
let downloadRequest: AWSS3TransferManagerDownloadRequest = AWSS3TransferManagerDownloadRequest();
downloadRequest.bucket = "Bucketname";
downloadRequest.key = "FileName";
downloadRequest.downloadingFileURL = downloadingFileURL;
The file = .json file
the apps content is all saved on the JSON file, therefore i need to redirect the file from the NSTemporaryDirectory in order for the content to appear on the application.
Does anyone know the parse function in order to load the data from my JSON file into the application?
Thank you
You cannot modify the main bundle, so this isn't going to work.
There are a few directories under your control, for example the home directory, the application support directory, cache directories, or possibly the document directory.
As gnasher729 notes, you can't modify the main bundle itself, but there's no reason to here. You don't need to move the file in order to display it. You can read from your temporary directory. It's inside your application sandbox, and there's nothing special about it (it doesn't even get cleaned up for you either, so that's your responsibility if you need that).
The temp directory isn't backed up, so if you want that, you should move this to your documents directory. See NSFileManager moveItemAtPath:toPath:error: if you want to do that (or just download to your documents directory in the first place).
I figured it out.
So after downloading the File:
let downloadFilePath = (NSTemporaryDirectory() as NSString).stringByAppendingPathComponent("FILENAME")
let downloadingFileURL = NSURL(fileURLWithPath:downloadFilePath)
let downloadRequest: AWSS3TransferManagerDownloadRequest = AWSS3TransferManagerDownloadRequest();
downloadRequest.bucket = "Bucketname";
downloadRequest.key = "FileName";
downloadRequest.downloadingFileURL = downloadingFileURL;
i have to submit the download request - as seen below:
// submit download request
let transferManager: AWSS3TransferManager = AWSS3TransferManager.defaultS3TransferManager();
print("Downloading started, please wait...");
transferManager.download(downloadRequest).continueWithExecutor(AWSExecutor.defaultExecutor(), block: { (task) -> AnyObject? in
print("TASK:::::: \(task)");
if (task.error != nil){
print("Error Downloading");
}else{
self.readFile()
print("Download complete");
}
return nil;
}, cancellationToken: nil)
}
create a function that will parse your JSON file through the temp directory, using AlamoFire and SwiftyJSON:
func readFile() {
// JSON parsing step (from temporary directory)
let path = (NSTemporaryDirectory() as NSString).stringByAppendingPathComponent("FILENAME")
do {
let readFile:NSString? = try NSString(contentsOfFile: path, encoding: NSUTF8StringEncoding)
let json = JSON.parse(readFile as String!)
for (_, subJson) in json["FILECONTENT"] {
let version = subJson["FILECONTENT"].string
let newsletter = Edition(Version: version!)
self.editions.append(ARRAYNAME!)
}
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
}
//print(readFile)
} catch {
}
}
this will showcase the text on your application when ran.
I am trying to get the images to do the something now.

Why do I unwrap nil in my Swift file?

I have an app (the same one from my previous post about unwrapping nil. I really hate nil now.) that searches the iTunes store and returns data in JSON. I have it working, it gets the song name, artist name, everything! I created an #IBAction button for playing the song's preview. The JSON has a property that is the url to the song preview. When I click the button, it does the following:
let alertSound = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource(url, ofType: "m4a")!)
AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, error: nil)
AVAudioSession.sharedInstance().setActive(true, error: nil)
var error:NSError?
audioPlayer = AVAudioPlayer(contentsOfURL: alertSound, error: &error)
audioPlayer.prepareToPlay()
audioPlayer.play()
The url is this: http://a1993.phobos.apple.com/us/r1000/101/Music/b7/b3/e0/mzm.ooahqslp.aac.p.m4a. I know my setup for playing an audio file works; I have another app I am building that uses the exact same setup. Why does it tell me that I unwrap nil here: http://a1993.phobos.apple.com/us/r1000/101/Music/b7/b3/e0/mzm.ooahqslp.aac.p.m4a? The url is valid and the file plays.
Examine this line of code.
let alertSound = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource(url, ofType: "m4a")!)
fileUrlWithPath is asking for a local path, that is one on your device.
NSBundle.mainBundle().pathForResource(url.....
This method returns the local path for the resource you send to it. You are sending it a web url, which is not in the mainBundle unless you've explicitly put it there. So the path that it returns is nil, because there is no local path that satisfies the arguments you are passing to it.
If you have a local resource you should use a method called URLForResource
This line makes no sense. You should always prefer working with urls and extract the path from it if needed.
Replace this line:
let alertSound = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("fileName", ofType: "m4a")!) // this would crash if not found (nil)
with this block of code
if let alertSoundUrl = NSBundle.mainBundle().URLForResource("fileName", withExtension: "m4a") {
println(true)
} else {
println(false)
}
If it is a web link you need to use NSURL(string:). fileUrlWithPath it is only for local resources.
if let checkedUrl = NSURL(string: "http://a1993.phobos.apple.com/us/r1000/101/Music/b7/b3/e0/mzm.ooahqslp.aac.p.m4‌​") {
println(true)
} else {
println(false)
}