self is not available in swift project - swift

I am trying to run an image comparison in swift. However i am getting an error that states Cannot use instance member 'featureprintObservationForImage' within property initializer; property initializers run before 'self' is available.
the error appears on the first of the let lines.
Why would self not already available within the ViewController?
import UIKit
import Vision
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
func featureprintObservationForImage(atURL url: URL) -> VNFeaturePrintObservation? {
let requestHandler = VNImageRequestHandler(url: url, options: [:])
let request = VNGenerateImageFeaturePrintRequest()
do {
try requestHandler.perform([request])
return request.results?.first as? VNFeaturePrintObservation
} catch {
print("Vision error: \(error)")
return nil
}
}
let apple1 = featureprintObservationForImage(atURL:Bundle.main.url(forResource:"apple1", withExtension: "jpg")!)
let apple2 = featureprintObservationForImage(atURL: Bundle.main.url(forResource:"apple2", withExtension: "jpg")!)
let pear = featureprintObservationForImage(atURL: Bundle.main.url(forResource:"pear", withExtension: "jpg")!)
var distance = Float(0)
try apple1!.computeDistance(&distance, to: apple2!)
var distance2 = Float(0)
try apple1!.computeDistance(&distance2, to: pear!)
}

All the code after the closing brace for your featureprintObservationForImage function is not inside any function or closure. You can't do that. (That code is not just variable decalrations. You have function calls, which is not legal outside of a function.)
You can create something called computed properties, where you provide a closure that gets invoked each time you read a value from the property.

You can't access self in variable declaration.
import UIKit
import Vision
class ViewController: UIViewController {
var testVeriable : Int = 0//hear you cannot access `self`
func test(){
self.testVeriable = 1// In function you can access class variable or function using `self`
}
}

Related

UIImage returns nil on segue push

I have an image URL that needs to be parsed and displayed. The URL exists, but returns nil.
It successfully parses in the cellForRowAt function by calling cell.recipeImage.downloadImage(from: (self.tableViewDataSource[indexPath.item].image))
With this line the image displays. However, it doesn't exist when calling it in didSelectRowAt
RecipeTableViewController.swift
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let Storyboard = UIStoryboard(name: "Main", bundle: nil)
let resultsVC = Storyboard.instantiateViewController(withIdentifier: "ResultsViewController") as! ResultsViewController
// Information to be passed to ResultsViewController
if (tableViewDataSource[indexPath.item] as? Recipe) != nil {
if isSearching {
resultsVC.getTitle = filteredData[indexPath.row].title
//resultsVC.imageDisplay.downloadImage(from: (self.filteredData[indexPath.row].image))
} else {
resultsVC.getTitle = tableViewDataSource[indexPath.row].title
// Parse images
resultsVC.imageDisplay.downloadImage(from: (self.tableViewDataSource[indexPath.row].image))
}
}
// Push to next view
self.navigationController?.pushViewController(resultsVC, animated: true)
}
extension UIImageView {
func downloadImage(from url: String) {
let urlRequest = URLRequest(url: URL(string: url)!)
let task = URLSession.shared.dataTask(with: urlRequest) { (data,response,error) in
if error != nil {
print(error!)
return
}
DispatchQueue.main.sync {
self.image = UIImage(data: data!)
}
}
task.resume()
}
}
ResultsViewController.swift
class ResultsViewController: UIViewController {
var getTitle = String()
var getImage = String()
#IBOutlet weak var recipeDisplay: UILabel!
#IBOutlet weak var imageDisplay: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
recipeDisplay.text! = getTitle
}
...
}
Returns the error
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
From my understanding, the app is getting crashed at this line:
recipeDisplay.text! = getTitle
If it is, obviously this is not the proper way to do it. Just remove the force unwrapping because the text on the label here is nil by default. Force referencing a nil value will crash the app.
recipeDisplay.text = getTitle
UPDATED:
- Let's make sure that you wired the label and the outlets properly. Connect ti to the VC, not the File Owner.
You're calling view-related code on views that haven't been initialized yet. Remember, IBOutlets are implicitly unwrapped properties, so if you try to access them before they're initialized they'll force-unwrap and crash. So it's not that the UIImage is coming up nil, it's that recipeDisplay is nil and is getting force unwrapped.
The idiomatic iOS thing to do is to hand a view model of some sort (an object or a struct) to the view controller, and then let it do the work with that item once it has finished loading.
So, in you didSelect method, you could create your view model (which you'd need to define) and hand it off like this:
let title = filteredData[indexPath.row].title
let imageURL = self.tableViewDataSource[indexPath.row].image
let viewModel = ViewModel(title: title, imageURL: imageURL)
resultsVC.viewModel = viewModel
And then in your resultsVC, you'd do something like this:
override func viewDidLoad() {
super.viewDidLoad()
if let vm = viewModel {
recipeDisplay.text = vm.title
downloadImage(from: vm.imageURL)
}
}
So in your case all you'd need to do is hand those strings to your VC (you can wrap them up in a view model or hand them off individually) and then in that VC's viewDidLoad() that's where you'd call downloadImage(from:). That way there's no danger of calling a subview before that subview has been loaded.
One last note: Your download method should be a little safer with its use of the data and error variables, and its references to self. Remember, avoid using ! whenever you don't absolutely have to use it (use optional chaining instead), and unless you have a really good reason to do otherwise, always use [weak self] in closures.
I'd recommend doing it like this:
func downloadImage(from url: String) {
let urlRequest = URLRequest(url: URL(string: url)!)
let task = URLSession.shared.dataTask(with: urlRequest) { [weak self] (data,response,error) in
if let error = error {
print(error)
return
}
if let data = data {
DispatchQueue.main.sync {
self?.image = UIImage(data: data)
}
}
}
task.resume()
}
Update: Because the 'view model' concept was a little too much at once, let me explain.
A view model is just an object or struct that represents the presentation data a screen needs to be in a displayable state. It's not the name of a type defined by Apple and isn't defined anywhere in the iOS SDK. It's something you'd need to define yourself. So, in this case, I'd recommend defining it in the same fine where you're going to use it, namely in the same file as ResultsViewController.
You'd do something like this:
struct ResultsViewModel {
let title: String
let imageURL: String
}
and then on the ResultsViewController, you'd create a property like:
var viewModel: ResultsViewModel?
or if you don't like dealing with optionals, you can do:
var viewModel = ResultsViewModel(title: "", imageURL: "")
OR, you can do what you're already doing, but I'd highly recommend renaming those properties. getTitle sounds like it's doing something more besides just holding onto a value. title would be a better name. Same criticism goes for getImage, with the additional criticism that it's also misleading because it sounds like it's storing an image, but it's not. It's storing an image url. imageURL is a better name.

Why isn't my method getting called?

I have a viewController communicating with DownloaderHandler using DownloaderDelegate protocol.
My protocol is defined as:
protocol DownloaderDelegate : class {
func didReceive(data:Data)
}
I have a viewController
class ViewController: UIViewController {
weak var downloadHandler : DownloaderHandler?
override func viewDidLoad() {
super.viewDidLoad()
downloadHandler = DownloaderHandler()
downloadHandler?.delegate = self
changeBackground()
}
func changeBackground (){
let googleURL = URL(fileURLWithPath: "https://www.google.com/doodle4google/images/splashes/featured.png")
print(googleURL)
downloadHandler?.downloadData(url:googleURL) // Line BB
}
}
extension ViewController : DownloaderDelegate{
func didReceive(data: Data) {
let image = UIImage(data: data)
let imageView = UIImageView(image: image!)
view.insertSubview(imageView, at: 0)
}
}
And I have a Delegating class as :
class DownloaderHandler : NSObject, URLSessionDelegate{
weak var delegate :DownloaderDelegate?
var downloadsSession: URLSession = {
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration)
return session // Line AA
}()
func downloadData(url: URL){ // Line CC
downloadsSession.dataTask(with: url){ data, response, error in
print("error is \(error), data is \(data) and response is \(response)") // Line DD
if let badError = error {
print(" the url didn't succeeed error is \(badError.localizedDescription)")
}
else if let someResponse = response as? HTTPURLResponse {
if someResponse.statusCode == 200{
self.delegate?.didReceive(data: data!)
}
}
}
}
}
Using breakpoints: Line AA, gets loaded. Line BB calls. Line CC never gets called. Why? What am I doing wrong?!
You have declared:
weak var downloadHandler : DownloaderHandler?
Then you say:
downloadHandler = DownloaderHandler()
downloadHandler is a weak reference, and nothing else retains this DownloaderHandler instance, so it vanishes in a puff of smoke after it is created. Your logging shows it being created, but if you were to log on its deinit you would also see it vanish immediately afterward. By the time you say downloadHandler?.downloadData(url:googleURL), your downloadHandler reference is nil and so nothing happens; you are talking to nobody at that point.
[You are probably slavishly following a mental rule that delegate references should be weak. But that rule is predicated on the assumption that the delegate has an independent existence, and thus should not be "owned" by the referrer. This object, however, has no independent existence; it is more a decorator object (what I would call a Helper). Thus, the reference needs to be strong. The back-reference is still weak, so you won't get a circular retain cycle.]
Remove the "weak" qualifier from the downloadHandler property on your view controller.
As it is the only reference to the downloadHandler object, it will be removed from memory as soon as the viewDidLoad method finishes executing.
You can make a small test; add a breakpoint to line BB and check if downloadHandler has a value. I suspect it will be "nil", because it is a weak property.

Create a CFRunLoopSourceRef using IOPSNotificationCreateRunLoopSource in Swift

I am trying to subscribe to changes in power state on macOS. I discovered there is a way using IOKit, though it is a bit convoluted. I need to import it using #import <IOKit/ps/IOPowerSources.h> in an ObjC Bridging header. Then I get access to the function IOPSNotificationCreateRunLoopSource, which has the signature:
IOPSNotificationCreateRunLoopSource(_ callback: IOPowerSourceCallbackType!, _ context: UnsafeMutablePointer<Void>!) -> Unmanaged<CFRunLoopSource>!
I got some help from the answer in Callback method to Apple run loop, but still doesn't manage to create a function of type IOPowerSourceCallbackType in Swift. What is the missing piece to have this compile?
The issue is that IOPowerSourceCallbackType is a C function.
According to Apple's documentation these functions are available as closures:
C function pointers are imported into Swift as closures with C function pointer calling convention
https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithCAPIs.html#//apple_ref/doc/uid/TP40014216-CH8-ID148
So the easiest way is to use a closure:
IOPSNotificationCreateRunLoopSource({ (context: UnsafeMutableRawPointer?) in
debugPrint("Power source changed")
}, &context)
A second option is to use a top-level function:
func powerSourceChanged(arg: UnsafeMutableRawPointer?) {
debugPrint("Power source changed")
}
IOPSNotificationCreateRunLoopSource(powerSourceChanged, &context)
For reference the complete implementation of how I'm using this:
class WindowController: NSWindowController {
static var context = 0
override func windowDidLoad() {
super.windowDidLoad()
let loop: CFRunLoopSource = IOPSNotificationCreateRunLoopSource({ (context: UnsafeMutableRawPointer?) in
debugPrint("Power source changed")
}, &WindowController.context).takeRetainedValue() as CFRunLoopSource
CFRunLoopAddSource(CFRunLoopGetCurrent(), loop, CFRunLoopMode.defaultMode)
}
}
UPDATE
To let it interact with the instance the loop was setup from, you have to pass self as context, however self isn't a pointer.
When you try to pass self as pointer by prepending it with & (&self), you'll get an error that self is immutable.
To convert it a to an opaque pointer you can use the Unmanaged class:
let opaque = Unmanaged.passRetained(self).toOpaque()
Which then can be used as an UnsafeMutableRawPointer:
let context = UnsafeMutableRawPointer(opaque)
What we can use as the context for IOPSNotificationCreateRunLoopSource.
And then in the callback, by using the Unmanaged class again, we can resolve this pointer back to its initiating instance:
let opaque = Unmanaged<WindowController>.fromOpaque(context!)
let _self = opaque.takeRetainedValue()
Full example:
func PowerSourceChanged(context: UnsafeMutableRawPointer?) {
let opaque = Unmanaged<WindowController>.fromOpaque(context!)
let _self = opaque.takeRetainedValue()
_self.powerSourceChanged()
}
class WindowController: NSWindowController {
override func windowDidLoad() {
super.windowDidLoad()
let opaque = Unmanaged.passRetained(self).toOpaque()
let context = UnsafeMutableRawPointer(opaque)
let loop: CFRunLoopSource = IOPSNotificationCreateRunLoopSource(
PowerSourceChanged,
context
).takeRetainedValue() as CFRunLoopSource
CFRunLoopAddSource(CFRunLoopGetCurrent(), loop, CFRunLoopMode.defaultMode)
}
func powerSourceChanged() {
debugLog("Power source changed")
}
}
Bonus
A related article about CFunction pointers

Cannot load view using NSNib

Anyone knows the correct way to instantiate a view using NSNib in Swift ?
The code below crashes.
import Cocoa
class ViewController: NSViewController {
var myview:NSView!
override func viewDidLoad() {
super.viewDidLoad()
let obj = AutoreleasingUnsafeMutablePointer<NSArray?>()
if let success = NSNib(nibNamed: "View", bundle: NSBundle.mainBundle())?.instantiateWithOwner(self, topLevelObjects: obj) where success {
if let m = obj.memory { // fatal error:
myview = m[0] as! NSView
}
}
}
}
You are seeing the fatal error there because the value you are trying to access is nil and you are forcefully trying to unwrap the optional. You should safely unwrap optionals this is what they are meant for. So, always use if statement when you try to access optionals, then you won't get this error.

SwiftyJSON how to correctly access variables?

I try to figure out SwiftyJSON but I'm facing a problem
The code shown below works fine
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let url = NSURL(string: "https://api.whitehouse.gov/v1/petitions.json")
var request = NSURLRequest(URL: url!)
var data = NSURLConnection.sendSynchronousRequest(request, returningResponse: nil, error: nil)
if data != nil {
let hoge = JSON(data: data!)
let count = hoge["results"][0]["body"]
println(count)
}
}}
but when i try to add a method which accesses the hoge it returns nothing
code looks like this
func res() {
dump(hoge)
}
I tried to declare let hoge and let count in the header of ViewController, but it always gives errors.
How to do it correctly, so i can access array thorough all the code ?
Thanks in advance
If you declare a variable inside a function, like you do here in viewDidLoad, this variable is only available in the same scope, meaning that variable doesn't exist outside viewDidLoad. Actually it is even deallocated (destroyed) when the function execution finishes.
The solution is to create var hoge: JSON? at the root of your class, outside any function, then only assign the JSON value to this variable when it is available:
class ViewController: UIViewController {
var hoge: JSON?
override func viewDidLoad() {
super.viewDidLoad()
let url = NSURL(string: "https://api.whitehouse.gov/v1/petitions.json")
var request = NSURLRequest(URL: url!)
var data = NSURLConnection.sendSynchronousRequest(request, returningResponse: nil, error: nil)
if data != nil {
hoge = JSON(data: data!)
let count = hoge!["results"][0]["body"]
println(count)
}
}}
That way you can also create other methods that can access hoge outside of viewDidLoad.