Type 'className -> () -> className!' does not conform to protocol - protocols

I'm messing around with Swift. I have a protocol defined as
protocol timerProtocol {
func timerFired()
}
A class who holds a reference to the delegate
class Stopwatch: NSObject {
var delegate: protocol <timerProtocol>
init(delegate: protocol <timerProtocol> ) {
self.delegate = delegate
}
...
}
and a class that implements the protocol
class StopwatchesTableViewController: UITableViewController, timerProtocol {
func timerFired() {
println("timer");
}
let stopwatch = Stopwatch(delegate: self) // Error here
...
}
I get the error when declaring the stopwatch - "Type 'StopwatchesTableViewController -> () -> StopwatchesTableViewController!' does not conform to protocol 'timerProtocol'"
How do I fix this issue?

Change var delegate: protocol <timerProtocol>
To var delegate: timerProtocol?

syntactically and logically that works for me like a charm:
protocol TimerProtocol {
func timerFired()
}
class Stopwatch {
var delegate: protocol <TimerProtocol>? = nil
init() { }
convenience init(delegate: protocol <TimerProtocol> ) {
self.init()
self.delegate = delegate
}
}
class StopwatchesTableViewController: UITableViewController, TimerProtocol {
#lazy var stopwatch: Stopwatch = Stopwatch()
init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
stopwatch.delegate = self
}
func timerFired() {
println("timer");
}
}
NOTE: the protocols' names should start with capital letter.
or
the StopwatchesTableViewController class would look like e.g. this:
class StopwatchesTableViewController: UITableViewController, TimerProtocol {
var stopwatch: Stopwatch? = nil
init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
stopwatch = Stopwatch(delegate: self)
}
func timerFired() {
println("timer");
}
}

Try change,
let stopwatch = Stopwatch(delegate: self) // Error here
to
#lazy var stopwatch: Stopwatch = Stopwatch(delegate: self)

Your code
let stopwatch = Stopwatch(delegate: self)
is in the scope of the class (not inside a func) and hence self refers to the class (not an instance). The class does not conform to the protocol, only its instances.
You need to do
let stopwatch: Stopwatch
func init() {
stopwatch = Stopwatch(delegate: self)
}

Related

Trying to incorporate sound within a beacon region in Swift

I am getting "Use of unresolved identifier 'player' in my code using beacons and regions. For this particular region, I also want it to play a sound (Siren.wav). Code is below:
import Combine
import CoreLocation
import SwiftUI
import AVFoundation
class BeaconDetector: NSObject, ObservableObject, CLLocationManagerDelegate {
var objectWillChange = ObservableObjectPublisher()
var locationManager: CLLocationManager?
var lastDistance = CLProximity.unknown
var player: AVAudioPlayer?
// var audioPlayer = AVAudioPlayer()
override init() {
super.init()
locationManager = CLLocationManager()
locationManager?.delegate = self
locationManager?.requestWhenInUseAuthorization()
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedWhenInUse {
if CLLocationManager.isMonitoringAvailable(for: CLBeaconRegion.self) {
if CLLocationManager.isRangingAvailable() {
startScanning()
}
}
}
}
func startScanning() {
let uuid = UUID(uuidString: "00000000-0000-0000-0000-000000000000")!
let constraint = CLBeaconIdentityConstraint(uuid: uuid)
let beaconRegion = CLBeaconRegion(beaconIdentityConstraint: constraint, identifier: "MyBeacon")
locationManager?.startMonitoring(for: beaconRegion)
locationManager?.startRangingBeacons(satisfying: constraint)
}
func locationManager(_ manager: CLLocationManager, didRange beacons: [CLBeacon], satisfying beaconConstraint: CLBeaconIdentityConstraint) {
if let beacon = beacons.first {
update(distance: beacon.proximity)
} else {
update(distance: .unknown)
}
}
func update(distance: CLProximity) {
lastDistance = distance
self.objectWillChange.send()
}
}
struct BigText: ViewModifier {
func body(content: Content) -> some View {
content
.font(Font.system(size: 72, design: .rounded))
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
}
}
struct ContentView: View {
#ObservedObject var detector = BeaconDetector()
var body: some View {
if detector.lastDistance == .immediate {
return Text("DANGER TOO CLOSE")
.modifier(BigText())
.background(Color.red)
.edgesIgnoringSafeArea(.all)
func playSound() {
guard let url = Bundle.main.url(forResource: "Siren", withExtension: "wav") else { return }
do {
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default)
try AVAudioSession.sharedInstance().setActive(true)
player = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileType.wav.rawValue)
guard let player = player else { return }
player.play()
}
catch let error {
print(error.localizedDescription)
The reason you get an "unresolved identifier" error is because the variable player is not defined in the playSound() method. In the Swift language, each variable declaration has a specific "scope" and they cannot be accessed outside that scope.
In this case, player is defined as a member variable in the BeaconDetector class. Because the playSound() method is not in the same variable "scope", you get that error when you try to access the variable.
You might want to read this tutorial on how variable scope works in Swift.

How to return nil object in non void function - swift 2.1?

Here is my code which is giving me an error
class Table_Users: NSManagedObject {
class func getUserFromUserID(userID:String)->Table_Users
{
var user:Table_Users? = nil
if let delegate = UIApplication.sharedApplication().delegate as? AppDelegate
{
let moc = delegate.managedObjectContext
}
else
{
}
return user
}
}
It says i can't return a nil value in non-void function
what else can i return. or how should i write this function the other way ?
using
return user!
is not an option for me. because i have read that if value of object is nil, it will crash.
Use “?” with function’s return type instead
replace "class func getUserFromUserID(userID:String)->Table_Users" with "class func getUserFromUserID(userID:String)->Table_Users?"
Updated Code:
class Table_Users: NSManagedObject {
class func getUserFromUserID(userID:String)->Table_Users?
{
var user:Table_Users? = nil
if let delegate = UIApplication.sharedApplication().delegate as? AppDelegate
{
let moc = delegate.managedObjectContext
}
else
{
}
return user
}
}

WatchKit CoreLocation issue

I am trying to read current location coordinates in WatchKitExtension ExtensionDelegate. This does though not return any value.
The very same code used in WatchKitExtension InterfaceController does return the location. (tried this out of desperation as I could not find an error in the code)
I would need to perform this code in ExtensionDelegate as I would like to pass the retrieved location on to a ClockKit Complication.
Here the code in ExtensionDelegate: (after self.locationManager.requestLocation() the delegate functions didUpdateLocation / didFailWithError do not get called)
import WatchKit
import CoreLocation
class ExtensionDelegate: NSObject, WKExtensionDelegate, CLLocationManagerDelegate {
private let locationManager = CLLocationManager()
override init() {
print("ExtensionDelegate: \(NSDate()) - init")
super.init()
self.getLocation()
}
func getLocation(){
print("ExtensionDelegate: \(NSDate()) - getLocation")
locationManager.delegate = self
locationManager.requestAlwaysAuthorization()
locationManager.requestWhenInUseAuthorization()
locationManager.requestLocation()
}
...
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("ExtensionDelegate: \(NSDate()) - locationManager didUpdateLocations")
guard let mostRecentLocation = locations.last else { return }
let place = mostRecentLocation.coordinate
print("\(place)")
manager.stopUpdatingLocation()
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
print("ExtensionDelegate: \(NSDate()) - locationManager didFailWithError")
print("CL failed: \(error)")
}
}
Here the very same code in InterfaceController and it works perfectly (didUpdateLocation does get called):
import WatchKit
import Foundation
import CoreLocation
class InterfaceController: WKInterfaceController, CLLocationManagerDelegate {
private let locationManager = CLLocationManager()
override func awakeWithContext(context: AnyObject?) {
print("InterfaceController: \(NSDate()) - awakeWithContext")
super.awakeWithContext(context)
self.getLocation()
}
func getLocation(){
print("InterfaceController: \(NSDate()) - getLocation")
locationManager.delegate = self
locationManager.requestAlwaysAuthorization()
locationManager.requestWhenInUseAuthorization()
locationManager.requestLocation()
}
...
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("InterfaceController: \(NSDate()) - locationManager didUpdateLocations")
guard let mostRecentLocation = locations.last else { return }
let place = mostRecentLocation.coordinate
print("\(place)")
manager.stopUpdatingLocation()
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
print("InterfaceController: \(NSDate()) - locationManager didFailWithError")
print("CL failed: \(error)")
}
}

Core Data in Swift

Trying to work with Core Data in Swift. Found this one example:
http://www.sep.com/sep-blog/2014/06/23/a-c-developer-learns-swift-part-1-core-data/
Created Entity "Person" with two string fields - lastname and firstname. Created UITableViewController (MainTableViewController) to display records on the screen. Created UIViewController (DetailViewController) to add new records. Created my own class (AddrBook) for entity data.
Does not work display the records contained in the entity in main class - MainTableViewController.
My class AddrBook.swift:
import UIKit
import CoreData
#objc(AddrBook)
class AddrBook: NSManagedObject {
#NSManaged var lastname:String
#NSManaged var firstname:String
}
UIViewController to add new records. DetailViewController.swift:
import UIKit
import CoreData
class DetailViewController: UIViewController {
#IBOutlet var lastNameField : UITextField = nil
#IBOutlet var firstNameField : UITextField = nil
init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
init(coder aDecoder: NSCoder!) {
super.init(coder: aDecoder)
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func saveButtonPressed(sender : AnyObject) {
let appDelegate:AppDelegate = (UIApplication.sharedApplication().delegate as AppDelegate)
let context:NSManagedObjectContext = appDelegate.managedObjectContext
let projectEntity = NSEntityDescription.entityForName("Person", inManagedObjectContext: context)
var newPerson = AddrBook(entity: projectEntity, insertIntoManagedObjectContext: context)
newPerson.lastname = lastNameField.text
newPerson.firstname = firstNameField.text
context.save(nil)
self.dismissViewControllerAnimated(true, completion: nil)
}
}
Theoretically, in this class all goes well. Entry must be added.
The main class MainTableViewController.swift. To display the records. Trying to get them through the NSLog:
import UIKit
import CoreData
class MainTableViewController: UITableViewController {
init(style: UITableViewStyle) {
super.init(style: style)
}
init(coder aDecoder: NSCoder!) {
super.init(coder: aDecoder)
}
override func viewDidLoad() {
super.viewDidLoad()
let request = NSFetchRequest(entityName: "Person")
request.returnsObjectsAsFaults = false
let appDelegate:AppDelegate = (UIApplication.sharedApplication().delegate as AppDelegate)
let context:NSManagedObjectContext = appDelegate.managedObjectContext
var results:NSArray = context.executeFetchRequest(request, error: nil)
for currentPerson in results as AddrBook[] {
NSLog("\(currentPerson.lastname)")
NSLog("\(currentPerson.firstname)")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSectionsInTableView(tableView: UITableView?) -> Int {
return 1
}
override func tableView(tableView: UITableView?, numberOfRowsInSection section: Int) -> Int {
return 0
}
}
Shows that there is an error in the expression
for currentPerson in results as AddrBook[] {
Error:
Cannot convert the expression's type 'AddrBook[]' to type 'AddrBook[]'
What am I doing wrong?
for LombaX:
override func viewDidLoad() {
super.viewDidLoad()
let request = NSFetchRequest(entityName: "Person")
request.returnsObjectsAsFaults = false
let appDelegate:AppDelegate = (UIApplication.sharedApplication().delegate as AppDelegate)
let context:NSManagedObjectContext = appDelegate.managedObjectContext
var results : AddrBook[]? = context.executeFetchRequest(request, error: nil) as? AddrBook[]
NSLog("\(results)")
if let array = results // check for nil and unwrap
{
for currentPerson in array as AddrBook[] {
NSLog("\(currentPerson.lastname)")
NSLog("\(currentPerson.firstname)")
}
}
// var results:NSArray = context.executeFetchRequest(request, error: nil)
/*for currentPerson in results as AddrBook[] {
NSLog("\(currentPerson.lastname)")
NSLog("\(currentPerson.firstname)")
}*/
}
Output NSLog - 2014-06-24 21:25:41.243 lesson-12-swift[1651:136375] nil
Variable results is nil :-(
Link to project in Dropbox: https://www.dropbox.com/s/q42rw5fw470timi/lesson-12-swift.zip
First, check that you filled the class in the data model:
As ProjectName.AddrBook (for swift classes you have to specify even the project name). (NOTE: this is needed only if you haven't used the prefix #objc(AddrBook) before the class, but I see that you used it, so this is not the problem).
or
as AddrBook as in this image in the Class section, top right
Moreover, change your cast like these:
// since executeFetchRequest can return nil, cast it as an optional array of [AddrBook]
// note: the first [AddrBook]? Can be omitted
var results : [AddrBook]? = context.executeFetchRequest(request, error: nil) as? [AddrBook]
if let array = results // check for nil and unwrap
{
for currentPerson in array as [AddrBook] {
// print
}
}
Or, less explicit and no check for nil
var results = context.executeFetchRequest(request, error: nil)
for currentPerson in results as [AddrBook] {
// print
}
let arrayresult = context!.executeFetchRequest(request, error: &error)
var arrayvalues=NSArray(array: arrayresult!)
for obj in arrayvalues as [AddrBook]
{
}

How passing a protocol as parameter in Swift

In Objective-C, I know how passing a protocol as parameter:
- (void)MyMethod:(Protocol *)myparameter
But in Swift there is no more Protocol type.
How can I pass a protocol as parameter without knowing which is ?
In one of your comments you say:
"I want create a method which return an array of type of class which implements a desired protocol."
Have you tried something like the following:
//notice the use of #objc here
#objc protocol AlertProtocol
{
func getMyName()->String
}
class Class1 : AlertProtocol
{
let name = "Object 1"
func getMyName() -> String
{
return name
}
}
class Class2 : AlertProtocol
{
let name = "Object 2"
func getMyName() -> String
{
return name
}
}
//borrowing from and refactoring siLo's answer
func classesConformingToProtocol(proto:Protocol) -> [AnyClass]
{
let availableClasses : [AnyClass] = [ Class1.self, Class2.self ]
var conformingClasses = Array<AnyClass>()
for myClass : AnyClass in availableClasses
{
if myClass.conforms(to: proto)
{
conformingClasses.append(myClass)
}
}
return conformingClasses
}
Then use the above structure like this:
let classes = classesConformingToProtocol(AlertProtocol.self)
The tricky part that does the work is the "#objc" that exposes the protocol to the objective c runtime and allows us to pass any "Protocol Type" as a parameter.
Probably at some point in the future we will be able to do this in a "pure" Swift way.
Here is what I have tried:
#objc protocol Walker
{
func walk()
}
#objc protocol Runner
{
func run()
}
#objc class Zombie : Walker
{
func walk () { println("Brains...") }
}
#objc class Survivor : Runner
{
func run() { println("Aaaah, zombies!") }
}
func classesConformingToProtocol(proto:Protocol) -> AnyClass[]
{
let availableClasses : AnyClass[] = [ Zombie.self, Survivor.self ]
var conformingClasses = Array<AnyClass>()
for myClass : AnyClass in availableClasses
{
if myClass.conformsToProtocol(proto)
{
conformingClasses.append(myClass)
}
}
return conformingClasses
}
// This does not work
let walkers = classesConformingToProtocol(Walker.self)
let runners = classesConformingToProtocol(Runner.self)
I have been unable to convert Swift's Metatype information into a Protocol object.
In swift 2.0, I use it like this before:
classA.conformsToProtocol(XXXProtocol.self as! Protocol)
It doesn't works fine...
Look the definition of Protocol:
// All methods of class Protocol are unavailable.
// Use the functions in objc/runtime.h instead.
#available(iOS 2.0, *)
public class Protocol {
}
All are unavailable...and I don't know which to use instead in objc/runtime.h
So I have to use this method:
if ClassA is protocol<XXXProtocol> {
// do something
}
Currently, it works...
If you don't allow use #objc (because yours protocols have property, for example), the only solution that I found is with closure. Then, you need use a closure to use a protocol and return a value.
protocol Proto { }
protocol Proto2 { }
class Foo: Proto { }
class Bar: Proto, Proto2 { }
class Baz: Proto2 { }
class Qux { }
func printConforms(classList: [AnyClass], protoCond: (AnyClass) -> Any?) {
for i in classList {
print(i, terminator: " -> ")
if protoCond(i) != nil {
print("is subscriber")
} else {
print("NOT IS subscriber")
}
}
}
let myClasses: [AnyClass] = [Foo.self, Bar.self, Baz.self, Qux.self]
printConforms(classList: myClasses, protoCond: { $0 as? Proto.Type })
More complete example: https://gist.github.com/brunomacabeusbr/eea343bb9119b96eed3393e41dcda0c9
Edit
Another better solution is using generics, for example:
protocol Proto { }
class Foo: Proto { }
class Bar: Proto { }
class Baz { }
func filter<T>(classes: [AnyClass], byConformanceTo: T.Type) -> [AnyClass] {
return classes.filter { $0 is T }
}
filter(classes: [Foo.self, Bar.self, Baz.self], byConformanceTo: Proto.Type.self)
// return [Foo.self, Bar.self]
Worked out a way today (Xcode 6.1):
Firstly, the protocol must be marked as #objc for any checking to work.
Then use an "if let" cast to check for conformance.
#objc protocol MyProtocol {
var protocolValue: Int { get set }
}
if let conformingObject = someObject as? MyProtocol {
// conformingObject is now someObject cast to MyProtocol
conformingObject.protocolValue = 3
}

Resources