Swift mapkit explanation please [duplicate] - swift

This question already has answers here:
What is "self" used for in Swift?
(10 answers)
Closed 4 years ago.
How this part o code works ? Cand someone explain more about self ?
import MapKit
class Artwork: NSObject, MKAnnotation {
let title: String?
let locationName: String
let discipline: String
let coordinate: CLLocationCoordinate2D
init(title: String, locationName: String, discipline: String, coordinate: CLLocationCoordinate2D) {
self.title = title
self.locationName = locationName
self.discipline = discipline
self.coordinate = coordinate
super.init()
}
}

Self is used when accessing the class that you are writing the code in.
class Cat {
let catName = "My Cat"
func name() {
self.nameCat()
}
func nameCat() {
let catName = "Sam"
print(catName)
print(self.catName)
}
}
in this example when running name(), the terminal would print:
"Sam" and then "My Cat". A variable without the "self" would have the value of the most 'recent' reference of that variable while a variable with "self" will reference the class. This also works with functions. You can run self.nameCat() to access the "nameCat" function inside the Cat class. Basically, "self" returns the instance of the class you are writing code in.

Related

NSObject setValuesForKeys coding-complaint error in Swift 4.0.2

I usually would create an NSObject like the example below make a firebase observe single event call to get data, then I would create an instance of the NSObject and simply call the setValueForKeys method on the NSObject and the values will be passed in then I could easily use an if let statement to get the specific data I required. this has stopped working since I upgraded to swift 4.0.2 from swift 3.1 see code snippet below. I believe I am doing this wrong way, since the new update. As the key value it requires does exist as can be seen in the NSObject declaration and also the print out. Any suggestion will be appreciated.
class BarberMarker: NSObject {
var companyID: String?
var companyName: String?
var companyLogoImageUrl: String?
var companyLogoImage: UIImage?
var companyFormattedAddress: String?
var companyAddressLongitude: String?
var companyAddressLatitude: String?
var distanceFromCurrentUser: Double?
var dateCreated: String?
var timezone: String?
var calendar: String?
var local: String?
var ratingValue: CGFloat?
}
firebase call to get data from database
func getAllMarkers(){
firebaseRef.child("barberMarkers").observeSingleEvent(of: .value, with: { (snapshotssshhh) in
if let dictionary = snapshotssshhh.value as? [String: AnyObject] {
for marker in dictionary {
if let locMarker = marker.value as? [String: AnyObject] {
var markerB = BarberMarker()
print(locMarker)
markerB.setValuesForKeys(locMarker)
self.barbermarkers.append(markerB)
guard let lon = markerB.companyAddressLongitude, let lat = markerB.companyAddressLatitude else {
return
}
let latitude = (lat as NSString).doubleValue
let longitude = (lon as NSString).doubleValue
let locValue:CLLocationCoordinate2D = CLLocationCoordinate2DMake(latitude, longitude)
DispatchQueue.main.async{
let desiredMarker = GMSMarker(position: locValue)
desiredMarker.icon = GMSMarker.markerImage(with: UIColor(r: 118, g: 187, b: 220))
desiredMarker.map = self.mapView
self.bMarkers.append(desiredMarker)
}
}
}
}
}, withCancel: nil)
}
error message I get
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<maizewars.BarberMarker 0x7fb62ff5f5b0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key companyAddressLongitude.'
try this bro.
#objcMembers class BarberMarker: NSObject {
...
}
reason #objcMembers Use in Swift 4
When a Swift class introduces many new methods or properties that require behavior from the Objective-C runtime, use the #objcMembers attribute in the declaration of that class.
Applying the #objcMembers attribute to a class implicitly adds the #objc attribute to all of its Objective-C compatible members.
Because applying the #objc attribute can increase the compiled size of an app and adversely affect performance, only apply the #objcMembers attribute on declarations when each member needs to have the #objc attribute applied.

how do I get NSSortDescriptors to work in Swift 4?

I am struggling to get NSSortDescriptors to work on my mac tables.
I did a quick proof of concept below and it still shows the same error:
Error ProofSortDescriptors[19446:1951711] Failed to set
(contentViewController) user defined inspected property on (NSWindow):
[<_SwiftValue 0x60400007c180> valueForUndefinedKey:]: this class is
not key value coding-compliant for the key name. ( name is the name of
the property of the struct)
Could it be to do with the change in way that Swift 4 bridges Objective C?
would be great to get this working on Mac Swift
Here is my attempt:
import Cocoa
struct Customer {
var name:String
var value:Int
}
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
let cust1 = Customer(name: "bob", value: 100)
let cust2 = Customer(name: "cat", value: 200)
let cust3 = Customer(name: "dog", value: 300)
var custcol = [cust2, cust3, cust1]
let descrip = NSSortDescriptor(key: "name", ascending: true)
let sorted = (custcol as NSArray).sortedArray(using: [descrip])
var sortedCustomers = sorted as? [Customer]
print(sortedCustomers!)
}
}
You can't use a normal NSSortDescriptor with a Swift struct.
The normal NSSortDescriptor's compareObject:toObject: method uses “key-value coding” to access the properties of the objects. Key-value coding only works with instances of NSObject (including instances of subclasses of NSObject). Swift structs are not subclasses of NSObject (or even classes at all).
Since you're using sort descriptors because that's what NSTableView uses to sort rows, the simplest solution is just to change your model object type from a struct to a class that subclasses NSObject. Then declare each sortable property #objc.
swift 4
For sorting custom class array using NSSortDescriptors in swift 4, you need to declare class as below.
#objcMembers class Customer:NSObject {
var name:String!
var value:Int!
}
Unless you have a particular reason, you should avoid Objective-C relics in Swift code. Bridging back and forth between NSArray is a sign there might be a better, Swifty way of solving the problem.
let sortedCustomers = customers.sorted(by: { $0.name.compare($1.name) == .orderedAscending })
Here was the code I needed to get this working without changing the NSSortDescriptor. To get Customer KVC compliant, it needs to be a class derived from NSObject and any KVC compliant properties need to be marked with #objc.
class Customer: NSObject {
#objc var name: String
var value: Int
init(name: String, value: Int) {
self.name = name
self.value = value
}
}
A Swiftier way of doing this is implement Comparable
struct Customer {
var name: String
var value: Int
}
extension Customer: Comparable {
static func ==(lhs: Customer, rhs: Customer) -> Bool {
return lhs.name == rhs.name
}
static func <(lhs: Customer, rhs: Customer) -> Bool {
return lhs.name < rhs.name
}
}
class MyViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
let cust1 = Customer(name: "bob", value: 100)
let cust2 = Customer(name: "cat", value: 200)
let cust3 = Customer(name: "dog", value: 300)
let custcol = [cust2, cust3, cust1]
let sortedCustomers = custcol.sorted()
print(sortedCustomers)
}
}
or mutate customers directly.
class MyViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
let cust1 = Customer(name: "bob", value: 100)
let cust2 = Customer(name: "cat", value: 200)
let cust3 = Customer(name: "dog", value: 300)
var customers = [cust2, cust3, cust1]
customers.sort()
print(customers)
}
}

How do I create global object in swift?

I think my previous question was too vague. Let me try again.
I'm trying to hold user information by creating a singleton.
After a bit of research, this is what I understood.
Class UserInfo {
var userFirstName: String?
var userLastName: String?
/*I'll add all other variables here*/
}
let sharedInfo = UserInfo()
Am I on right path?
EDIT: For Swift 1.2 and higher (Thanks Will M)
class UserInfo {
static let sharedUserInfo = UserInfo()
var firstName: String?
var lastName: String?
}
For Swift earlier than 1.2
Something along the lines of:
class UserInfo
{
// Singleton Setup
private static var info: UserInfo?
class func sharedUserInfo() -> UserInfo
{
if self.info == nil
{
self.info = UserInfo()
}
return self.info!
}
// Class properties
var firstName: String?
var lastName: String?
}
let user = UserInfo.sharedUserInfo()
user.firstName = "Fred"
user.lastName = "Smith"
I would also recommend making sure that a singleton is really what you want, because depending on how you use it, you could run into some race conditions with threading.

Swift Access variable in the Class [duplicate]

This question already has an answer here:
Swift Access Controll Example
(1 answer)
Closed 8 years ago.
i have this Class in my ViewController.swift for example ...
class Person {
internal let name: String
init(name: String) {
self.name = name
}
}
class ViewController: UIViewController {
}
and in my SecondViewController i have
override func viewDidLoad() {
super.viewDidLoad()
let person = Person(name: "")
println(person.name)
}
How can I assign a value a name from the internal of the view controller class ?
Just create an instance of it:
var person = Person(name: "Person Name")
I don't know what you are going to do with it - it can be either a local variable in a view controller method, or a view controller property.
You can use this to create an instance and it's name property
var personsName = Person(name: "persons name")

Set value of read-only stored property during initializing in Swift

I want to implement my custom MKAnnotation. I took a look at MKAnnotation protocol(MKAnnotation.h).
It's as follow:
//
// MKAnnotation.h
// MapKit
//
// Copyright (c) 2009-2014, Apple Inc. All rights reserved.
//
protocol MKAnnotation : NSObjectProtocol {
// Center latitude and longitude of the annotation view.
// The implementation of this property must be KVO compliant.
var coordinate: CLLocationCoordinate2D { get }
// Title and subtitle for use by selection UI.
#optional var title: String! { get }
#optional var subtitle: String! { get }
// Called as a result of dragging an annotation view.
#optional func setCoordinate(newCoordinate: CLLocationCoordinate2D)
}
Please note the coordinate property (which is a read-only stored property).
And here is how I've implemented this protocol:
class RWDefaultPin: NSObject, MKAnnotation {
var title:String = ""
var subtitle:String = ""
var groupTag:String = ""
var coordinate: CLLocationCoordinate2D { get {
return self.coordinate // this is obviously wrong because property's trying to return itself
} };
init(coordinate:CLLocationCoordinate2D) {
super.init()
self.coordinate = coordinate
}
}
But obviously compiler complaints on my init method where I'm trying to assign to my coordinate property Cannot assign to 'coordinate' in 'self' obviously because it's a read-only property.
Previously in Objective-C we could overcome this issue as properties were backed by ivars.
I wish there was access modifier in Swift so I could define a private property in my class and set its value on init, and returning its value on get action of coordinate, but there is no such thing!
I don't quiet know how to fix this issue in Swift, or maybe I need to make it wide open and change my coordinate to be readable/writable?
You should be able to just add a setter to it and store the information in an inner coordinate value. Since you have a getter it is still conforming to the protocol:
var innerCoordinate: CLLocationCoordinate2D
var coordinate: CLLocationCoordinate2D {
get {
return self.innerCoordinate
}
set {
self.innerCoordinate = newValue
}
};
init(coordinate:CLLocationCoordinate2D) {
super.init()
self.innerCoordinate = coordinate
}
This is actually how I implement readonly and private properties (with protocols and the factory pattern). I setup protocols with the public interface and classes with private variables and setters. It is actually super clean way to setup your code (and gets around the lack of protected/private properties in Swift).
Here is a abstracted example of what I am talking about (if you care):
// this is your MKAnnotation in this example
protocol SomeProtocol {
var getterProperty: String { get }
var setterProperty: String { set get }
func publicFunction(someStirng: String) -> ();
}
// setup a function that returns a class conforming to your needed protocol
func SomeClassMaker() -> SomeProtocol {
// your internal class that no one can access unless by calling the maker function
class SomeClassInternal: NSObject, SomeProtocol {
// private and no one can get to me!
var innerSetterProperty = "default setter";
var getterProperty = "default getter"
var setterProperty: String {
get {
return self.innerSetterProperty;
}
set {
"hit"
self.innerSetterProperty = newValue
}
}
func publicFunction(someString: String) -> () {
// anyone get me
self.getterProperty = someString;
}
func privateFunction() -> () {
// no one can get me except internal functions
}
}
return SomeClassInternal();
}
// create the class
var classInstance = SomeClassMaker();
// totally fine!
classInstance.setterProperty = "secret string"
// prints "secret string"
classInstance.setterProperty;
// error! no public setter for "getter"
classInstance.getterProperty = "another secret"
classInstance.publicFunction("try secret again")
// prints "try secret again"
let blahed = classInstance.getterProperty
// error!
classInstance.privateFunction()
Even though the property is { get } in the protocol, that is just establishing a minimum criteria. It's perfectly acceptable to define it as read-write:
class MyAnnotation:NSObject, MKAnnotation
{
var coordinate:CLLocationCoordinate2D
init(coordinate:CLLocationCoordinate2D) {
self.coordinate = coordinate
}
}
Or, if you really want to keep it as read-only, you can use let:
class MyAnnotation:NSObject, MKAnnotation
{
let coordinate:CLLocationCoordinate2D
init(coordinate:CLLocationCoordinate2D) {
self.coordinate = coordinate
}
}