In Swift, does a protocol extension allow function bodies? - swift

I'm going through a tutorial and I noticed that the author extended their protocol called Activity and wrote the function's body in their code. This does compile however I was under the impression that protocols only show method signatures or if it does implement the body then it'll be a mutating function. The code below doesn't use mutating on one of its functions but it still runs and WORKS! Can someone explain the phenomena or confirm that protocol extensions can have method bodies?
import CareKit
import SwiftyJSON
enum ActivityType: String {
case Intervention
case Assessment
}
enum ScheduleType: String {
case Weekly
case Daily
}
enum StepFormat : String {
case Scale
case Quantity
}
protocol Activity {
var identifier : String { get set}
var groupIdentifier : String { get set}
var title : String { get set}
var colour : UIColor? { get set}
var text : String { get set}
var startDate : Date { get set}
var schedule : [NSNumber] { get set}
var scheduleType : ScheduleType { get set}
var instructions : String? { get set}
var imageURL : NSURL? { get set}
var activityType: ActivityType { get set}
var medication : Medication? { get set}
init()
init(json: JSON)
func createCareKitActivity() -> OCKCarePlanActivity
}
extension Activity {
// A mutating function to allow Acticities or Assessments to intialiser base properties
mutating func parseActivityFields(json: JSON) {
self.identifier = json["identifier"].string!
self.groupIdentifier = json["group_identifier"].string!
self.title = json["title"].string!
self.text = json["text"].string!
let colourString = json["color"].string!
self.colour = UIColor.colorWithString(colourString)
if let instructionString = json["instructions"].string {
self.instructions = instructionString
}
if let imageString = json["imageURL"].string {
let componentsOfString = imageString.components(separatedBy: ".")
if let pathForResource = Bundle.main.path(forResource: componentsOfString[0], ofType: componentsOfString[1]){
self.imageURL = NSURL(fileURLWithPath: pathForResource)
}
}
self.startDate = dateFromString(string: json["startdate"].string!)!
self.scheduleType = ScheduleType(rawValue: json["scheduletype"].string!)!
self.schedule = json["schedule"].string!.components(separatedBy: ",").map ( {
NSNumber(value: Int32($0)!)
})
if let medication = json["medication"].string,
let medicationImageString = json["medicationimage"].string {
let componentsOfString = medicationImageString.components(separatedBy: ".")
let pathForResource = Bundle.main.path(forResource: componentsOfString[0], ofType: componentsOfString[1])
self.medication = Medication.init(medication: medication, imageURL: NSURL.init(fileURLWithPath: pathForResource!))
}
}
init(json: JSON) {
self.init()
self.parseActivityFields(json: json)
}
func createCareKitActivity() -> OCKCarePlanActivity{
//creates a schedule based on the internal values for start and end dates
let startDateComponents = NSDateComponents(date: self.startDate, calendar: NSCalendar(calendarIdentifier: NSCalendar.Identifier.gregorian)! as Calendar)
let activitySchedule: OCKCareSchedule!
switch self.scheduleType {
case .Weekly :
activitySchedule = OCKCareSchedule.weeklySchedule(withStartDate: startDateComponents as DateComponents, occurrencesOnEachDay: self.schedule)
case .Daily:
activitySchedule = OCKCareSchedule.dailySchedule(withStartDate: startDateComponents as DateComponents, occurrencesPerDay: self.schedule[0].uintValue)
}
let activity = OCKCarePlanActivity.intervention(
withIdentifier: identifier,
groupIdentifier: nil,
title: title,
text: text,
tintColor: colour,
instructions: instructions,
imageURL: imageURL as? URL,
schedule: activitySchedule,
userInfo: ["medication": medication], optional: false)
return activity
}
}

In Swift, extensions allow you to provide default implementations for protocols.
According to the Swift documentation on Protocols,
Protocols can be extended to provide method, initializer, subscript,
and computed property implementations to conforming types. This allows
you to define behavior on protocols themselves, rather than in each
type’s individual conformance or in a global function.
Source: Swift Documentation

In Swift, does a protocol extension allow function bodies?
Yes. It's a really convenient way to add specific functionality only to instances of some protocol. Consider this:
protocol Flying {
func useWings()
}
extension Flying {
func fly() {}
}
class Animal {}
class Bird: Animal {}
extension Bird: Flying {
func useWings() {}
}
let bird = Bird()
bird.fly()
It also makes some logic here. If something can use wings, then it also probably can fly. So when we extend Bird to implement Flying as it has useWings - it also can fly now.
The code below doesn't use mutating on one of its functions but it still runs and WORKS! Can someone explain the phenomena
Mutating keyword says that function will mutate the value it's called onto. You have to say it explicitly if your protocol is not a class (protocol Some: class {}). The createCareKitActivity() doesn't mutate self, so you don't have to specify mutating

Class -> Protocol -> Extension
Swift's protocol extension allows your class operates by additional or default functions

Related

Swift, help iterating over keyPaths and setting values

I've found that it's a little easier to explain what I'm doing by giving too much context for why I'm trying to do it, sorry.
I'm currently trying to add an encryption service to my project. It's nothing that'll get published I think though and I've mostly got it working. The problem that I'm having is that I have my model like this
struct Entity {
// All types are examples though I think they will be all optionals.
var prop1: String?
var prop2: Int?
var prop3: Bool?
var encryptedData: [Keypath:EncryptedData]
static var encryptableKeyPaths: [WritableKeyPath<Entity, Any?>]
}
As an example for what's happening, I can get the encryptionService to take in prop1, create an EncryptedData and put it in the encryptedData dictionary. I can even get the keyPath for the property. I can encrypt all the data and decrypt it just fine and get all the values properly, so I don't need help with that. But I'm struggling with 3 issues.
Getting the KeyPaths to be WritableKeyPaths so I can write to them with the values I need.
Setting the properties to nil once the values are encrypted so I'm not storing extra data.
Setting the properties to their values once their decrypted.
All three of these issues seem to revolve around making the KeyPaths into WritableKeyPaths.
This is the closest attempt I've gotten so far. You can copy the following code right into a playground and run it and it should work. Except it'll crash at the end. There are a couple of issues here, I'm losing the type safety as I have to make all the property types Initializable? which isn't great. Also, see that the values are permanently wrapped. I can't figure out how to prevent that. I had to mark Optional as conforming to Initializable to make this work. Lastly, the variable allStoredProperties doesn't let me write to them. I'm not sure how to properly convert it to WritableKeyPath from PartialKeyPath.
import UIKit
protocol Initializable {}
extension String: Initializable {}
extension Int: Initializable {}
extension Bool: Initializable {}
extension Optional: Initializable {}
protocol KeyPathIterable {
associatedtype Model
init()
static var allKeyPaths: [WritableKeyPath<Model, Initializable?>] { get }
}
extension KeyPathIterable {
var keyPathReadableFormat: [String: Initializable] {
var description: [String: Initializable] = [:]
let mirror = Mirror(reflecting: self)
for case let (label?, value) in mirror.children {
description[label] = (value as! Initializable)
}
return description
}
static var allStoredProperties: [PartialKeyPath<Self>] {
var members: [PartialKeyPath<Self>] = []
let instance = Self()
for (key, _) in instance.keyPathReadableFormat {
members.append(\Self.keyPathReadableFormat[key])
}
return members
}
static func setValue<Self: KeyPathIterable, T: Initializable>(on root: inout Self,
at keyPath: WritableKeyPath<Self, Initializable?>,
withValue value: T?) throws {
root[keyPath: keyPath] = value
}
}
struct Foo: KeyPathIterable {
typealias Model = Foo
var prop1: Initializable? // I want this to be String?
var prop2: Initializable? // I want this to be Int?
var prop3: Initializable? // I want this to be Bool?
init() {
self.prop1 = nil
self.prop2 = nil
self.prop3 = nil
}
static var allKeyPaths: [WritableKeyPath<Foo, Initializable?>] {
return [\Model.prop1, \Model.prop2, \Model.prop3]
}
}
var foo = Foo()
foo.prop1 = "Bar"
foo.prop2 = 1
foo.prop3 = true
print(foo.prop1 as Any)
let keyPath = \Foo.prop1
foo[keyPath: keyPath] = "Baz"
print(foo.prop1 as Any)
for path in Foo.allStoredProperties {
print("-=-=-")
print(path)
}
print("-=-=-=-=-=-=-=-")
do {
try Foo.setValue(on: &foo, at: keyPath, withValue: "BazBar" as Initializable?)
} catch {
print("Should never fail")
}
print(foo.prop1 as Any) // Returns Optional(Optional("BarBaz")) - I want this to be all the way unwrapped.
print("--------------")
let values1: [Initializable] = ["Hello World", 100, false]
do {
for (path, value) in zip(Foo.allKeyPaths, values1) {
try Foo.setValue(on: &foo,
at: path,
withValue: value as Initializable?)
}
} catch {
print("Success?")
}
print(foo.prop1 as Any)
print(foo.prop2 as Any)
print(foo.prop3 as Any)
print("----====----====----")
let values2: [Initializable] = ["Howdy", 0, false]
do {
for (path, value) in zip(Foo.allStoredProperties, values2) {
try Foo.setValue(on: &foo,
at: path as! WritableKeyPath<Foo, Initializable?>,
withValue: value as Initializable?)
}
} catch {
print("Always fails")
}
print("=-=-=-=-=-=-=-=")
print(foo)
I've looked all over google and youtube and everywhere and I can't seem to get this to work. I'm open to a different architecture if that work accomplish my goals better. Just a little frustrated. Thanks for your help.

Swift - KeyPath extension - enforce conformance to protocol

ideally, I'd like to get the name of the property referenced by a KeyPath. But this seems not to be possible out-of-the-box in Swift.
So my thinking is that the KeyPath could provide this information based on protocol extension added by a developer. Then I'd like to design an API with an initializer/function that accepts a KeyPath conforming to that protocol (that adds a computed property).
So far I was only able to define the protocol and conditional conformance of the protocol. The following code compiles fine.
protocol KeyPathPropertyNameProviding {
var propertyName: String {get}
}
struct User {
var name: String
var age: Int
}
struct Person {
var name: String
var age: Int
}
extension KeyPath: KeyPathPropertyNameProviding where Root == Person {
var propertyName: String {
switch self {
case \Person.name: return "name"
case \Person.age: return "age"
default: return ""
}
}
}
struct PropertyWrapper<Model> {
var propertyName: String = ""
init<T>(property: KeyPath<Model, T>) {
if let property = property as? KeyPathPropertyNameProviding {
self.propertyName = property.propertyName
}
}
}
let userAge = \User.age as? KeyPathPropertyNameProviding
print(userAge?.propertyName) // "nil"
let personAge = \Person.age as? KeyPathPropertyNameProviding
print(personAge?.propertyName) // "age"
let wrapper = PropertyWrapper<Person>(property: \.age)
print(wrapper.propertyName) // "age"
But I am unable to restrict the API so that initialization parameter property has to be a KeyPath AND must conform to a certain protocol.
For example, the following would result in a compilation error but should work from my understanding (but probably I miss a key detail ;) )
struct PropertyWrapper<Model> {
var propertyName: String = ""
init<T>(property: KeyPath<Model, T> & KeyPathPropertyNameProviding) {
self.propertyName = property.propertyName // compilation error "Property 'propertyName' requires the types 'Model' and 'Person' be equivalent"
}
}
Any tips are highly appreciated!
You are misunderstanding conditional conformance. You seem to want to do this in the future:
extension KeyPath: KeyPathPropertyNameProviding where Root == Person {
var propertyName: String {
switch self {
case \Person.name: return "name"
case \Person.age: return "age"
default: return ""
}
}
}
extension KeyPath: KeyPathPropertyNameProviding where Root == User {
var propertyName: String {
...
}
}
extension KeyPath: KeyPathPropertyNameProviding where Root == AnotherType {
var propertyName: String {
...
}
}
But you can't. You are trying to specify multiple conditions to conform to the same protocol. See here for more info on why this is not in Swift.
Somehow, one part of the compiler thinks that the conformance to KeyPathPropertyNameProviding is not conditional, so KeyPath<Model, T> & KeyPathPropertyNameProviding is actually the same as KeyPath<Model, T>, because somehow KeyPath<Model, T> already "conforms" to KeyPathPropertyNameProviding as far as the compiler is concerned, it's just that the property propertyName will only be available sometimes.
If I rewrite the initialiser this way...
init<T, KeyPathType: KeyPath<Model, T> & KeyPathPropertyNameProviding>(property: KeyPathType) {
self.propertyName = property.propertyName
}
This somehow makes the error disappear and produces a warning:
Redundant conformance constraint 'KeyPathType': 'KeyPathPropertyNameProviding'
Key paths are hashable, so I recommend a dictionary instead. It's especially easy to put it together with strong typing if you're able to use a CodingKey type.
struct Person: Codable {
var name: String
var age: Int
enum CodingKey: Swift.CodingKey {
case name
case age
}
}
extension PartialKeyPath where Root == Person {
var label: String {
[ \Root.name: Root.CodingKey.name,
\Root.age: .age
].mapValues(\.stringValue)[self]!
}
}
Then use parentheses instead of the cast you demonstrated. No need for a protocol so far…
(\Person.name).label // "name"
(\Person.age).label // "age"
This will probably all be cleaner due to built-in support someday. https://forums.swift.org/t/keypaths-and-codable/13945

Create instance from class using other class instance

So i came across this case, an already published application needed to change all of it's API's & Models.
Now i have created a generic tier to handle the requests and apis and almost mid way into implementing all the services, now i came across this problem, the previous defined models are used widely around the application of course and since its MVC , Massive View Controller. it is going to cost me too much changing everything in each scene to the new model type,
therefore i thought of making an adapter to cast the new models when i get them in my
callback closure to the old ones type.
I have already figured out a way but the problem its pretty much long, long way i am looking for a better approach if existed and a better solution over all for the case if there was a better one.
protocol Parsable {
var time: String { get }
var span: String { get }
init(_ copy: Parsable)
}
class Foo: Parsable {
required init(_ copy: Parsable) {
self.span = copy.span
self.time = copy.time
}
init(time: String, span: String) {
self.time = time
self.span = span
}
var time = ""
var span = ""
}
class Fee: Parsable {
required init(_ copy: Parsable) {
self.span = copy.span
self.time = copy.time
}
init(time: String, span: String, date: String) {
self.time = time
self.span = span
self.date = date // an extra var that is not used in Foo
}
var time = ""
var span = ""
var date = ""
}
var foo = Foo(time: "", span: "")
var fee = Fee(time: "2", span: "ye", date: "123")
// Usage
var deeped = Foo(fee)
As you can tell from the code i've created a protocol that contains the variables and an init() that holds its type, now imagine this to implement a model with +50 variable and +40 model in total, might need an age or two.
I hope i understood the problem, It's not a clean solution but it's quick an flexible:
What about an additional method in the protocol with an implementation in it's extension to perform the all the copies? This is possible since i see that all the properties have an assigned dummy value. Then the only thing to do for each object implementing Parsable is to call such method in the initializer. kind of a commonInit() method.
protocol Parsable {
var time: String { get }
var span: String { get }
init(_ copy: Parsable)
func initParsableProperties(from copy: Parsable)
}
extension Parsable {
func initParsableProperties(from copy: Parsable) {
self.span = copy.span
self.time = copy.time
}
}
class Foo: Parsable {
...
required init(_ copy: Parsable) {
initParsableProperties(from: copy)
}
...
}
This also allows you to add additional properties in the initializers if needded. If you don't need additional properties it could then be directly implemented in the initializer, but it requires some more tricky solutions.
So i Achieved this using Codable, i have created a dummy protocol that is conforming to Codable, and using that in every class, struct that i needed to convert it, and created a generic function extended from that protocol, to encode the object to data then decode it into the new type desired,
With that i don't have to declare any variable or property i needed to copy manually.
check out the code below.
protocol Convertable: Codable {}
class Foo: Convertable {
var foo: String
var fee: String
init(foo: String, fee: String) {
self.foo = foo
self.fee = fee
}
}
class Fee: Convertable{
var fee: String
init( fee: String) {
self.fee = fee
}
}
//Generic function to convert
extension Convertable {
func convert<T: Codable>(_ primary: T.Type) -> T? {
return try? JSONDecoder().decode(primary, from: try! JSONEncoder().encode(self))
}
}
var foo = Foo(foo: "nothing", fee: "nothing")
let fee = foo.convert(Fee.self)
fee?.fee // nothing

Binding 2 properties (observe) using keyPath

I am trying to create a routine to simplify binding one property to another, a very common operation. I'm using the block based KVO's in Swift 4 and XCode 9.
I want to be able to write the following to bind two variables using their corresponding keyPath:
self.bind(to: \BindMe.myFirstName, from: \BindMe.person.firstName )
This is a simplified example that is generating various compile errors that I can't get around. It is likely the incorrect passing of the keyPath to func bind, but the setValue using a keyPath also fails to compile. Please see the comments in the code for the compile errors I'm getting.
class Person : NSObject
{
init( firstName:String, lastName:String )
{
self.firstName = firstName
self.lastName = lastName
}
#objc dynamic var firstName:String
#objc dynamic var lastName:String
}
class BindMe : NSObject
{
var observers = [NSKeyValueObservation]()
let person:Person
var myFirstName:String = "<no first name>"
var myLastName:String = "<no last name>"
init( person:Person )
{
self.person = person
self.setupBindings()
}
func setupBindings()
{
self.bind(to: \BindMe.myFirstName, from: \BindMe.person.firstName )
self.bind(to: \BindMe.myLastName, from: \BindMe.person.lastName )
}
// this func declaration is likely incorrect
func bind<T,Value,Value2>( to targetKeyPath:KeyPath<T,Value>, from sourceKeyPath:KeyPath<T,Value2>)
{
// Error: Generic parameter 'Value' could not be inferred
self.observers.append( self.observe(sourceKeyPath, options: [.initial,.new], changeHandler: { (object, change) in
// Error: Cannot convert value of type 'KeyPath<T, Value>' to expected argument type 'String'
self.setValue(change.newValue, forKeyPath: targetKeyPath)
}))
}
}
EDIT
The answer below helps with the initial compile problems. However, for this to be useful at all I need to be able to push the plumbing into a superclass as shown here. This will make the class using it very simple, but I am still struggling with compile errors:
Cannot invoke 'bind' with an argument list of type '(to: WritableKeyPath<PersonWatcher, PersonWatcher>, from: WritableKeyPath<PersonWatcher, PersonWatcher>)'
If I pass a generic type T to the bind routine, I get this error instead:
Type 'BindBase' has no subscript members
class BindBase :NSObject
{
var observers = [NSKeyValueObservation]()
func bind<Value>(to targetKeyPath: ReferenceWritableKeyPath<BindBase, Value>, from sourceKeyPath: KeyPath<BindBase, Value>)
{
self.observers.append(self.observe(sourceKeyPath, options: [.initial, .new], changeHandler: { (object, change) in
self[keyPath: targetKeyPath] = change.newValue!
}))
}
}
class PersonWatcher : BindBase
{
#objc dynamic var person: Person
#objc var myFirstName: String = "<no first name>"
#objc var myLastName: String = "<no last name>"
init(person: Person) {
self.person = person
super.init()
self.bind(to: \PersonWatcher.myFirstName, from: \PersonWatcher.person.firstName)
self.bind(to: \PersonWatcher.myLastName, from: \PersonWatcher.person.lastName)
}
}
According to the accepted proposal SE-0161 Smart KeyPaths: Better Key-Value Coding for Swift, you need to use ReferenceWritableKeyPath to write a value to the key path for an object with reference semantics, using subscript.
(You need to pass a classic String-based key path to setValue(_:forKeyPath:), not KeyPath.)
And some more:
Value and Value2 need to be the same for assignment
T needs to represent the type of self
KVC/KVO target properties need to be #objc
BindMe.init(person:) needs super.init()
So, your BindMe would be something like this:
class BindMe: NSObject {
var observers = [NSKeyValueObservation]()
#objc let person: Person
#objc var myFirstName: String = "<no first name>"
#objc var myLastName: String = "<no last name>"
init(person: Person) {
self.person = person
super.init()
self.setupBindings()
}
func setupBindings() {
self.bind(to: \BindMe.myFirstName, from: \BindMe.person.firstName)
self.bind(to: \BindMe.myLastName, from: \BindMe.person.lastName)
}
func bind<Value>(to targetKeyPath: ReferenceWritableKeyPath<BindMe, Value>, from sourceKeyPath: KeyPath<BindMe, Value>) {
self.observers.append(self.observe(sourceKeyPath, options: [.initial, .new], changeHandler: { (object, change) in
self[keyPath: targetKeyPath] = change.newValue!
}))
}
}
For EDIT:
The demand to make a BindBase like thing seems very reasonable, so I have made some tries.
To fulfill
T needs to represent the type of self
(where T == KeyPath.Root), using Self would be the most instinctive, but unfortunately, its usage is still very restricted in the current version of Swift.
You can move the definition of bind into a protocol extension using Self:
class BindBase: NSObject, Bindable {
var observers = [NSKeyValueObservation]()
}
protocol Bindable: class {
var observers: [NSKeyValueObservation] {get set}
}
extension Bindable {
func bind<Value>(to targetKeyPath: ReferenceWritableKeyPath<Self, Value>, from sourceKeyPath: KeyPath<Self, Value>)
where Self: NSObject
{
let observer = self.observe(sourceKeyPath, options: [.initial, .new]) {object, change in
self[keyPath: targetKeyPath] = change.newValue!
}
self.observers.append(observer)
}
}

What is the proper way to reference a static variable on a Swift Protocol?

Assume a protocol defined below:
protocol Identifiable {
static var identifier: String { get }
}
extension Identifiable {
static var identifier: String { return "Default Id" }
}
What is the best way to reference the static variable? The example below illustrates two ways to access the variable. What is the difference, and is the type(of:) better?
func work<I: Identifiable>(on identifiable: I) {
let identifier: String = I.identifier
print("from Protocol: \(identifier)")
let identiferFromType: String = type(of: identifiable).identifier
print("using type(of:): \(identiferFromType)")
}
struct Thing: Identifiable {
static var identifier: String { return "Thing" }
}
work(on: Thing())
In the example you show, there is no difference. Because identifier is a protocol requirement, it will be dynamically dispatched to in both cases, therefore you don't need to worry about the wrong implementation being called.
However, one difference arises when you consider the value of self inside the static computed property when classes conform to your protocol.
self in a static method/computed property is the metatype value that it's is called on. Therefore when called on I, self will be I.self – which is the static type that the compiler infers the generic placeholder I to be. When called on type(of: identifiable), self will be the dynamic metatype value for the identifiable instance.
In order to illustrate this difference, consider the following example:
protocol Identifiable {
static var identifier: String { get }
}
extension Identifiable {
static var identifier: String { return "\(self)" }
}
func work<I : Identifiable>(on identifiable: I) {
let identifier = I.identifier
print("from Protocol: \(identifier)")
let identiferFromType = type(of: identifiable).identifier
print("using type(of:): \(identiferFromType)")
}
class C : Identifiable {}
class D : C {}
let d: C = D()
// 'I' inferred to be 'C', 'type(of: d)' is 'D.self'.
work(on: d)
// from Protocol: C
// using type(of:): D
In this case, "which is better" completely depends on the behaviour you want – static or dynamic.