Why Are My Default Property Values Still Showing as Parameters in Init()? - swift

I have a protocol that describes a marine water parameter that needs tested:
protocol Parameter {
var name: String { get }
var unit: Unit { get }
var value: Double { get }
}
I have a struct, Calcium, that conforms to Parameter:
struct Calcium: Parameter {
var name: String = "Calcium"
var unit: Unit = UnitDispersion.partsPerMillion
var value: Double
}
Since the name and unit parameters of Calcium have default values, why do I need to provide them in the init method? Shouldn't I only need to provide a value for value?
I am trying to understand protocol-oriented-programming and would really appreciate a little guidance here.

This has nothing whatever to do with protocols.
You do not have to provide an initializer for anything but the value. But you have not provided any initializer. Therefore the only initializer you have is the one provided automatically, and that initializer is the memberwise initializer which wants parameters for all your properties.
If you don't like that, write an initializer yourself:
struct Calcium: Parameter {
var name: String = "Calcium"
var unit: Unit = UnitDispersion.partsPerMillion
var value: Double
init(value:Double) {self.value = value}
}
Now it is legal to say:
let c = Calcium(value:2)

Related

Swift Function Input Generic Conform To Both Class And Protocol

I'm trying to have a function take a generic input that conforms to both a class and a protocol in order to process values where some belong to the class and others belong to the delegate. It works fine for individual types, but not for an array of types (which conform to both the super class and the delegate). Instead the compiler throws the error "Instance method 'printValues(for:)' requires that 'MeasurementClass' conform to 'UnitDelegate'"
I've added the code below, which will make much more sense. Just put it in a Swift Playground. If you run it as is, you can see that it properly prints the users distance and steps individually. If you uncomment the last few lines, the same function cannot be used with an array.
My goal is to be able to pass an array of types that conform to the UnitDelegate and MeasurementClass (aka MeasurementObject typealias) and have the values processed. I want an array, because I will need up having a bunch of different classes that conform to MeasurementObject.
protocol UnitDelegate: class{
var units: [String: String] { get }
}
class MeasurementClass{
var imperial: Double!
var metric: Double!
convenience init(imperial: Double, metric: Double){
self.init()
self.imperial = imperial
self.metric = metric
}
}
typealias MeasurementObject = MeasurementClass & UnitDelegate
class Distance: MeasurementObject{
var units = ["imperial": "miles", "metric":"kms"]
}
class Steps: MeasurementObject{
var units = ["imperial": "steps", "metric":"steps"]
}
class User{
func printValues<T: MeasurementObject>(for type: T) {
print("\(type.imperial!) \(type.units["imperial"]!) = \(type.metric!) \(type.units["metric"]!)")
}
}
//This is what I'm trying to achieve in the for loop below
let distance = Distance(imperial: 30, metric: 48.28)
let steps = Steps(imperial: 30, metric: 30)
let user = User()
user.printValues(for: distance)
user.printValues(for: steps)
//let types = [distance, steps]
//
//for type in types{
// user.printValues(for: type)
//}
Not a direct answer to your question but all you need is to add an enumeration to store the kind of measurement to your class, and a computed property to return the corresponding dictionary. No need to use a protocol, protocol composition, class and/or subclass to accomplish what you are trying to do:
struct AMeasurement {
let imperial: Double
let metric: Double
let kind: Kind
enum Kind { case distance, steps }
var units: [String: String] {
switch kind {
case .distance: return ["imperial": "miles", "metric":"kms"]
case .steps: return ["imperial": "steps", "metric":"steps"]
}
}
}
extension AMeasurement {
func printValues() {
print("\(imperial) \(units["imperial"]!) = \(metric) \(units["metric"]!)")
}
}
let distance = AMeasurement(imperial: 30, metric: 48.28, kind: .distance)
let steps = AMeasurement(imperial: 30, metric: 30, kind: .steps)
let types = [distance, steps]
for type in types {
type.printValues()
}
30.0 miles = 48.28 kms
30.0 steps = 30.0 steps

Stored property without initial value prevents synthesized initializers

I declare a protocol:
protocol FullNameable {
var fullName: String {get set}
}
then adopt the above protocol for class and struct like below:
struct LectureStruct: FullNameable {
var fullName: String
}
class LectureClass: FullNameable {
var fullName: String
}
But for class I am getting error - Stored property 'fullName' without initial value prevents synthesized initializers. Why this error is not for a struct?
According to swift documentation:
All structures have an automatically generated memberwise initializer,
which you can use to initialize the member properties of new structure
instances. Initial values for the properties of the new instance can
be passed to the memberwise initializer by name
let vga = Resolution(width: 640, height: 480)
Unlike structures, class instances don’t receive a default memberwise
initializer.
In your case, you can instantiate LectureStruct with:
let lecture = LectureStruct(fullName: "Match Lecture")
In order to do the same with LectureClass you'll need to define an init method like so:
class LectureClass: FullNameable {
var fullName: String
init(fullName: String) {
self.fullName = fullName
}
}

Swift Protocols: Difference between { get } and { get set } with concrete examples?

I'm pretty new to Swift, and although I've read Apple's documentation and many topics and threads about this, I still can't understand what's the difference between { get } and { get set }. I mean, I'm looking for an explanation with a concrete example.
Like, for example:
protocol PersonProtocol {
var firstName: String { get }
var lastName: String { get set }
}
What would be the actual difference between these two properties? I tried to play with these properties in a playground:
struct Person: PersonProtocol {
var firstName: String
var lastName: String
}
var p = Person(firstName: "John", lastName: "Lennon")
print(p.firstName) // John
print(p.lastName) // Lennon
p.firstName = "Paul"
p.lastName = "McCartney"
print(p.firstName) // Paul
print(p.lastName) // McCartney
Did not help... Thanks for your help.
You are creating a variable of type Person and there are no restrictions on that struct. If you instead create a variable of type PersonProtocol then firstName will be read only
var p1: PersonProtocol = Person(firstName: "John", lastName: "Lennon")
print(p1.firstName) // John
print(p1.lastName) // Lennon
p1.firstName = "Paul" <== error: cannot assign to property: 'firstName' is a get-only property
protocol — is a requirement of some minimal interface of the type implementing it.
var name: Type { get } requires type to have property with at least a getter (accessible from outside of the type, not private), i.e. outside code should be able to read value of the property. In the implementing type it could be let name: Type, var name: Type, private(set) var name: Type, fileprivate(set) var name: Type, etc.
var name: Type { get set } requires type to have property with both accessible getter and setter, i.e. outside code should be able to read and write to the property. Here only var name: Type would be allowed.
If protocol requires for getter but you also provide a setter — it's not against protocol requirements.
But if protocol requires for both getter and setter — you must provide both, and not having any of them won't be valid implementation.
Your Person class defined both properties as var(with accessible getter and setter) therefore you can change them both. But PersonProtocol haven't required ability to set firstName.
And as #JoakimDanielson shows, if you will use just interface required by protocol you won't be to change the firstName value.

Swift dynamictype initialisation with dynamic protocol type

I have a number of structs which implement a Resource protocol. This defines that they must have a variable extendedInfo which conforms to ExtendedInfo protocol to provide a way to initialise them with json via init(json: [String: AnyObject]. I'm trying to provide a way to dynamically instantiate these, with JSON, providing the right type of ExtendedInfo and assign it to the struct's extendedInfo variable. However, I'm getting a Argument labels '(json:)' do not match any available overloads error when trying to instantiate them via their dynamicType
protocol Resource {
associatedtype ExtendedInfoTypeAlias: ExtendedInfo
var extendedInfo: ExtendedInfoTypeAlias? { get set }
}
protocol ExtendedInfo {
init(json: [String: AnyObject])
}
struct User: Resource {
typealias ExtendedInfoTypeAlias = UserExtendedInfo
let name: String = "Name"
var extendedInfo: UserExtendedInfo?
}
struct UserExtendedInfo: ExtendedInfo {
let age: Int?
init(json: [String: AnyObject]) {
age = json["age"] as? Int
}
}
let user = User()
let sampleJSON = ["age": 50]
let userExtendedInfo = user.extendedInfo.dynamicType.init(json: sampleJSON) // Argument labels '(json:)' do not match any available overloads
user.extendedInfo = userExtendedInfo
Any ideas guys? Thanks
First of all, you don't need to explicitly define the type of ExtendedInfoTypeAlias in your struct implementation – you can just let it be inferred by the type you provide for extendedInfo.
struct User: Resource {
let name: String = "Name"
var extendedInfo: UserExtendedInfo?
}
Second of all, you can just use the protocol's associated type of your given struct's dynamicType in order to use your given initialiser. For example:
user.extendedInfo = user.dynamicType.ExtendedInfoTypeAlias.init(json: sampleJSON)
print(user.extendedInfo) // Optional(Dynamic_Protocols.UserExtendedInfo(age: Optional(50)))
As for why your current code doesn't work, I suspect it's due to the fact that you're getting the dynamicType from an optional – which is preventing you from calling your initialiser on it.
I did find that the following works, even when extendedInfo is nil. (This is a bug).
user.extendedInfo = user.extendedInfo!.dynamicType.init(json: sampleJSON)
Change:
let user = User()
To:
var user = User()
and try this:
user.extendedInfo = UserExtendedInfo(json: sampleJSON)

Change what print(Object) displays in Swift 2.0

I am trying to make my class Digit display the num variable whenever print is called on that object, in Swift 2.0. I thought this might be done with a description variable, but no luck.
class Digit {
var num: Int
var x: Int
var y: Int
var box: Int
var hintList: [Int] = []
var guess: Bool = false
var description: String {
let string = String(num)
return string
}
}
It isn't enough to just add a description variable. You need to also state that your class conforms to CustomStringConvertible (formerly known as Printable in earlier Swift versions).
If you command click the print function, you find the following description.
Writes the textual representation of value, and an optional newline,
into the standard output.
The textual representation is obtained from the value using its protocol
conformances, in the following order of preference: Streamable,
CustomStringConvertible, CustomDebugStringConvertible. If none of
these conformances are found, a default text representation is constructed
in an implementation-defined way, based on the type kind and structure.
The part of which that matters here being that objects passed to print are not checked for whether or not they have a description method, but instead checked for things like whether or not the conform to protocols like CustomStringConvertible which offer data to be printed.
That being said, all you need to do in this case is specify that your class conforms to CustomStringConvertible since you've already added a description variable. If you hadn't already added this, the compiler would complain because this protocol requires that the description variable be implemented.
class Digit: CustomStringConvertible {
var num: Int
var x: Int
var y: Int
var box: Int
var hintList: [Int] = []
var guess: Bool = false
var description: String {
let string = String(num)
return string
}
}