I have some trouble in understanding my specific request, in Swift.
There is a class named Origin and multiple its subclasses.
How I can update my block-method written ONLY in Origin class?
class Origin: NSObject {
func mod(_ block: (() throws -> Void)) {
try! block()
}
}
I need use mod from all Origin subclasses, and I need to have this usage effect:
var originSubclassObject = OriginSubclass()
originSubclassObject.mod {
.age = 12 //age is OriginSubclass property
.name = "Bob" //name is OriginSubclass property
}
So, you see, I need extract OriginSubclass properties for using in mod-block. I need to create usage exactly likes in usage effect code (extract mod-caller properties from ".").
Thanks all for help!
You could consider a protocol with a default implementation, e.g.:
protocol Modifiable { }
extension Modifiable {
func mod(_ block: ((Self) throws -> Void)) {
try! block(self)
}
}
class Origin: Modifiable { }
class OriginSubclass: Origin {
var name: String?
var age: Int?
}
And then:
let originSubclassObject = OriginSubclass()
originSubclassObject.mod { object in
object.age = 12
object.name = "Bob"
}
Or
let originSubclassObject = OriginSubclass()
originSubclassObject.mod {
$0.age = 12
$0.name = "Bob"
}
Or, if that base class was only there for the mod method, you could lose it entirely:
protocol Modifiable { }
extension Modifiable {
func mod(_ block: ((Self) throws -> Void)) {
try! block(self)
}
}
class MyObject: Modifiable {
var name: String?
var age: Int?
}
And
let myObject = MyObject()
myObject.mod { object in
object.age = 12
object.name = "Bob"
}
Or
let myObject = MyObject()
myObject.mod {
$0.age = 12
$0.name = "Bob"
}
Related
I need to change this generic class that accepts an array to something that is not an array
class GenericDataSource<T> : NSObject {
var data: DynamicValue<[T]> = DynamicValue([])
//var data: DynamicValue<T>?
}
When I remove the square brackets I get an error
typealias CompletionHandler = (() -> Void)
class DynamicValue<T> {
var value : T {
didSet {
self.notify()
}
}
private var observers = [String: CompletionHandler]()
init(_ value: T) {
self.value = value
}
public func addObserver(_ observer: NSObject, completionHandler: #escaping CompletionHandler) {
observers[observer.description] = completionHandler
}
public func addAndNotify(observer: NSObject, completionHandler: #escaping CompletionHandler) {
self.addObserver(observer, completionHandler: completionHandler)
self.notify()
}
private func notify() {
observers.forEach({ $0.value() })
}
deinit {
observers.removeAll()
}
}
You need an init in GenericDataSource
class GenericDataSource<T> {
var data: DynamicValue<T>
init(_ value: T) {
data = DynamicValue(value)
}
}
or make data optional
class GenericDataSource<T> : NSObject {
var data: DynamicValue<T>?
}
And then use them like
let gds = GenericDataSource("Hello")
or for the optional variant
let gds = GenericDataSource<String>()
gds.data = DynamicValue("Hello")
From within a property wrapper in Swift, can you someone refer back to the instance of the class or struck that owns the property being wrapped? Using self doesn't obviously work, nor does super.
I tried to pass in self to the property wrapper's init() but that doesn't work either because self on Configuration is not yet defined when #propertywrapper is evaluated.
My use case is in a class for managing a large number of settings or configurations. If any property is changed, I just want to notify interested parties that something changed. They don't really need to know which value just, so use something like KVO or a Publisher for each property isn't really necessary.
A property wrapper looks ideal, but I can't figure out how to pass in some sort of reference to the owning instance that the wrapper can call back to.
References:
SE-0258
enum PropertyIdentifier {
case backgroundColor
case textColor
}
#propertyWrapper
struct Recorded<T> {
let identifier:PropertyIdentifier
var _value: T
init(_ identifier:PropertyIdentifier, defaultValue: T) {
self.identifier = identifier
self._value = defaultValue
}
var value: T {
get { _value }
set {
_value = newValue
// How to callback to Configuration.propertyWasSet()?
//
// [self/super/...].propertyWasSet(identifier)
}
}
}
struct Configuration {
#Recorded(.backgroundColor, defaultValue:NSColor.white)
var backgroundColor:NSColor
#Recorded(.textColor, defaultValue:NSColor.black)
var textColor:NSColor
func propertyWasSet(_ identifier:PropertyIdentifier) {
// Do something...
}
}
The answer is no, it's not possible with the current specification.
I wanted to do something similar. The best I could come up with was to use reflection in a function at the end of init(...). At least this way you can annotate your types and only add a single function call in init().
fileprivate protocol BindableObjectPropertySettable {
var didSet: () -> Void { get set }
}
#propertyDelegate
class BindableObjectProperty<T>: BindableObjectPropertySettable {
var value: T {
didSet {
self.didSet()
}
}
var didSet: () -> Void = { }
init(initialValue: T) {
self.value = initialValue
}
}
extension BindableObject {
// Call this at the end of init() after calling super
func bindProperties(_ didSet: #escaping () -> Void) {
let mirror = Mirror(reflecting: self)
for child in mirror.children {
if var child = child.value as? BindableObjectPropertySettable {
child.didSet = didSet
}
}
}
}
You cannot do this out of the box currently.
However, the proposal you refer to discusses this as a future direction in the latest version:
https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-wrappers.md#referencing-the-enclosing-self-in-a-wrapper-type
For now, you would be able to use a projectedValue to assign self to.
You could then use that to trigger some action after setting the wrappedValue.
As an example:
import Foundation
#propertyWrapper
class Wrapper {
let name : String
var value = 0
weak var owner : Owner?
init(_ name: String) {
self.name = name
}
var wrappedValue : Int {
get { value }
set {
value = 0
owner?.wrapperDidSet(name: name)
}
}
var projectedValue : Wrapper {
self
}
}
class Owner {
#Wrapper("a") var a : Int
#Wrapper("b") var b : Int
init() {
$a.owner = self
$b.owner = self
}
func wrapperDidSet(name: String) {
print("WrapperDidSet(\(name))")
}
}
var owner = Owner()
owner.a = 4 // Prints: WrapperDidSet(a)
My experiments based on : https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-wrappers.md#referencing-the-enclosing-self-in-a-wrapper-type
protocol Observer: AnyObject {
func observableValueDidChange<T>(newValue: T)
}
#propertyWrapper
public struct Observable<T: Equatable> {
public var stored: T
weak var observer: Observer?
init(wrappedValue: T, observer: Observer?) {
self.stored = wrappedValue
}
public var wrappedValue: T {
get { return stored }
set {
if newValue != stored {
observer?.observableValueDidChange(newValue: newValue)
}
stored = newValue
}
}
}
class testClass: Observer {
#Observable(observer: nil) var some: Int = 2
func observableValueDidChange<T>(newValue: T) {
print("lol")
}
init(){
_some.observer = self
}
}
let a = testClass()
a.some = 4
a.some = 6
The answer is yes! See this answer
Example code for calling ObservableObject publisher with a UserDefaults wrapper:
import Combine
import Foundation
class LocalSettings: ObservableObject {
static var shared = LocalSettings()
#Setting(key: "TabSelection")
var tabSelection: Int = 0
}
#propertyWrapper
struct Setting<T> {
private let key: String
private let defaultValue: T
init(wrappedValue value: T, key: String) {
self.key = key
self.defaultValue = value
}
var wrappedValue: T {
get {
UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
}
set {
UserDefaults.standard.set(newValue, forKey: key)
}
}
public static subscript<EnclosingSelf: ObservableObject>(
_enclosingInstance object: EnclosingSelf,
wrapped wrappedKeyPath: ReferenceWritableKeyPath<EnclosingSelf, T>,
storage storageKeyPath: ReferenceWritableKeyPath<EnclosingSelf, Setting<T>>
) -> T {
get {
return object[keyPath: storageKeyPath].wrappedValue
}
set {
(object.objectWillChange as? ObservableObjectPublisher)?.send()
UserDefaults.standard.set(newValue, forKey: object[keyPath: storageKeyPath].key)
}
}
}
Using Xcode-8.2.1, Swift-3.0.2, RealmSwift-2.2.0, iOS-Simulator-10:
I try applying the MVVM pattern (explained by Steve Scott here) using Realm.
Everything works until the moment (inside the VIEW-part - see below) where I try to access a viewmodel-property. It says: Realm accessed from incorrect thread
How could I still make the MVVM-pattern do its job of separating model, view-model and view but, on the same time, get thread-safety with realm ?
Is there a way to make Realm-results (i.e. Results<BalancesDataEntry>) being passed across threads ??
Here is my code:
(the issue happens at the very bottom, inside the View-part)
// REALM-OBJECT:
import Foundation
import RealmSwift
class BalancesDataEntry: Object {
dynamic var category: String = ""
dynamic var index: Int = 0
}
MODEL:
import Foundation
import RealmSwift
class MVVMCBalancesModel: BalancesModel
{
fileprivate var entries = [BalancesDataEntry]()
let realm = try! Realm()
init() {
self.createDataEntries()
}
fileprivate func createDataEntries() {
let myBalance = BalancesDataEntry()
myBalance.index = 0
myBalance.category = "Love"
try! self.realm.write {
self.realm.deleteAll()
self.realm.add(myBalance)
}
}
func getEntries(_ completionHandler: #escaping (_ entries: [BalancesDataEntry]) -> Void)
{
// Simulate Aysnchronous data access
DispatchQueue.global().async {
let realmThread = try! Realm()
let returnArray: [BalancesDataEntry] = Array(realmThread.objects(BalancesDataEntry.self))
completionHandler(returnArray)
}
}
}
VIEW-MODEL:
import Foundation
import RealmSwift
class MVVMCBalancesViewModel: BalancesViewModel
{
weak var viewDelegate: BalancesViewModelViewDelegate?
weak var coordinatorDelegate: BalancesViewModelCoordinatorDelegate?
fileprivate var entries: [BalancesDataEntry]? {
didSet {
viewDelegate?.entriesDidChange(viewModel: self)
}
}
var model: BalancesModel? {
didSet {
entries = nil;
model?.getEntries({ (myEntries) in
self.entries = myEntries
})
}
}
var title: String {
return "My Balances"
}
var numberOfEntries: Int {
if let entries = entries {
return entries.count
}
return 0
}
func entryAtIndex(_ index: Int) -> BalancesDataEntry?
{
if let entries = entries , entries.count > index {
return entries[index]
}
return nil
}
func useEntryAtIndex(_ index: Int)
{
if let entries = entries, let coordinatorDelegate = coordinatorDelegate , index < entries.count {
coordinatorDelegate.balancesViewModelDidSelectData(self, data: entries[index])
}
}
}
VIEW:
import UIKit
class MVVMCBalancesViewController: UIViewController {
#IBOutlet weak var label1Outlet: UILabel!
#IBOutlet weak var label2Outlet: UILabel!
var viewModel: BalancesViewModel? {
willSet {
viewModel?.viewDelegate = nil
}
didSet {
viewModel?.viewDelegate = self
refreshDisplay()
}
}
var isLoaded: Bool = false
func refreshDisplay()
{
if let viewModel = viewModel , isLoaded {
// !!!!!!! HERE IS THE ISSUE: Realm accessed from incorrect thread !!!!
self.label1Outlet.text = viewModel.entryAtIndex(0)?.category
self.label2Outlet.text = viewModel.entryAtIndex(1)?.category
} else {
}
}
override func viewDidLoad()
{
super.viewDidLoad()
isLoaded = true
refreshDisplay();
}
}
extension MVVMCBalancesViewController: BalancesViewModelViewDelegate
{
func entriesDidChange(viewModel: BalancesViewModel)
{
}
}
You can use ThreadSafeReference to pass Realm's thread-confined types (Object, Results, List, LinkingObjects) to a different thread. The documentation's section on Passing Instances Across Threads contains this example of passing a single instance of an Object subclass across threads:
let person = Person(name: "Jane")
try! realm.write {
realm.add(person)
}
let personRef = ThreadSafeReference(to: person)
DispatchQueue(label: "background").async {
let realm = try! Realm()
guard let person = realm.resolve(personRef) else {
return // person was deleted
}
try! realm.write {
person.name = "Jane Doe"
}
}
It can be used similarly for Results.
I have found a workaround (see below): Maybe you have better solutions - please let me know!
Here is my github-code realm_mvvm_c on github
After introducing a new protocol and making (pretty much everything) conform to it, things worked out.
Here is the protocol called DataEntry:
import Foundation
protocol DataEntry: class {
var idx: Int { get set }
var category: String { get set }
}
Now, make everything conform to it, such as
--> the realm object (i.e. class BalancesDataEntry: Object, DataEntry {...)
--> the getEntries return value (i.e. func getEntries(_ completionHandler: #escaping (_ entries: [DataEntry]) -> Void))
--> the View-Model's entries (i.e. fileprivate var entries: [DataEntry]? {..)
--> all the corresponding Model- and View-Model protocols also need the DataEntry datatype (see git-repo for complete picture)
After that, it was enough to change the completion-handler return-array of the MODEL's method getEntries(..) to a newly created object-instance (ie. DataEntryDub) that is keept conform to the DataEntry protocol:
func getEntries(_ completionHandler: #escaping (_ entries: [DataEntry]) -> Void)
{
// Simulate Aysnchronous data access
DispatchQueue.global().async {
let realmThread = try! Realm()
class DataEntryDub: DataEntry {
var idx: Int
var category: String
init(idx: Int, category: String) {
self.idx = idx
self.category = category
}
}
var returnArray = [DataEntry]()
for entry in realmThread.objects(BalancesDataEntry.self) {
returnArray.append(DataEntryDub(idx: entry.idx, category: entry.category))
}
completionHandler(returnArray)
}
}
Here is my github-code realm_mvvm_c on github
I am trying to create a couple of objects which are dependent one to each other and they mush have a method to downcast directly the concrete class of the other object. Something like this:
protocol aProt
{
var bVar:bProt! { get set }
}
protocol bProt
{
var aVar:aProt! { get set }
}
class a: aProt
{
var bVar: bProt!
func bConcrete() -> b {
return bVar as! b
}
}
class b: bProt
{
var aVar: aProt!
func aConcrete() -> a {
return aVar as! a
}
Now, the problem is that I want this behavior (func aConcrete(),func bConcrete()) to be inherited by the subclasses of a and b. Then I thought the perfect way of doing this was using generics, but... There's no way of doing this.
class a: aProt
{
var bVar: bProt!
func bConcrete() -> T {
return bVar as! T
}
}
class b: bProt
{
var aVar: aProt!
func aConcrete<T>() -> T {
return aVar as! T
}
You can do it but when you have to use it you must downcast the variable anyway, so there is no way of doing it in a clean manner:
let aObject = a()
let bSubclassObject = a.bConcrete() // The compiler complains it cannot infer the class of T
let bSubclassObject = a.bConcrete() as! bSubclass // this works, but this is exactly which I wanted to avoid... :(
Define the generic function and add where to T:
protocol aProt {
var bVar: bProt! { get set }
}
protocol bProt {
var aVar:aProt! { get set }
}
class a: aProt {
var bVar: bProt!
func bConcrete<T: b>(_ type: T.Type) -> T? {
return bVar as? T
}
}
class b: bProt {
var aVar: aProt!
func aConcrete<T: a>(_ type: T.Type) -> T? {
return aVar as? T
}
}
class a1: a { }
class b1: b {
var fullName: String = "new object"
}
let aObj = a()
aObj.bVar = b1()
let bObj = aObj.bConcrete(b1.self)
bObj?.fullName
According to your requirement, calls bConcrete(b1.self) might still not good enough, but at least you need to know what type of data you are expecting to return.
I have two protocols and two objects that implement them. One object uses name:String as its primary key, the other uses code:Int.
protocol AlphaProtocol{
var name:String {get set}
init(name: String)
}
protocol BetaProtocol{
var code: Int {get set}
init(code:Int)
}
class AlphaObject: AlphaProtocol{
var name: String
required init(name: String){
self.name = name
}
}
class BetaObject: BetaProtocol{
var code: Int
required init(code: Int){
self.code = code
}
}
Right now, to store the these objects I use two different memory stores that implement two different protocols, one for each kind of object.
protocol AlphaStoreProtocol{
func addObject(object anObject: AlphaProtocol)
func getObject(name aName:String)->AlphaProtocol?
func removeObject(name aName: String)
}
protocol BetaStoreProtocol{
func addObject(object anObject: BetaProtocol)
func getObject(code aCode:Int)->BetaProtocol?
func removeObject(code aCode: Int)
}
class AlphaStore{
fileprivate var objects = [AlphaProtocol]()
func addObject(object anObject: AlphaProtocol){
if getObject(name: anObject.name) == nil{
objects.append(anObject)
}
}
func getObject(name aName:String)->AlphaProtocol?{
for o in objects{
if o.name == aName{
return o
}
}
return nil
}
func removeObject(name aName: String){
self.objects = self.objects.filter({$0.name != aName})
}
}
class BetaStore: BetaStoreProtocol{
fileprivate var objects = [BetaProtocol]()
func addObject(object anObject: BetaProtocol){
if getObject(code: anObject.code) == nil{
objects.append(anObject)
}
}
func getObject(code aCode:Int)->BetaProtocol?{
for o in objects{
if o.code == aCode{
return o
}
}
return nil
}
func removeObject(code aCode: Int){
self.objects = self.objects.filter({$0.code != aCode})
}
}
Test Code using two tailor made stores.
let alpha = AlphaObject(name: "Alpha")
let beta = BetaObject(code: 12345)
let alphaStore = AlphaStore()
let betaStore = BetaStore()
alphaStore.addObject(object: alpha)
if (alphaStore.getObject(name: alpha.name) != nil){
print("alpha object has been added to alphaStore")
}
alphaStore.removeObject(name: alpha.name)
if (alphaStore.getObject(name: alpha.name) == nil){
print("alpha object has been removed from alphaStore")
}
betaStore.addObject(object: beta)
if (betaStore.getObject(code: beta.code) != nil){
print("beta object has been added to betaStore")
}
betaStore.removeObject(code: beta.code)
if (betaStore.getObject(code: beta.code) == nil){
print("beta object has been removed from betaStore")
}
The goal: using a single generic class for both the stores but I'm stuck because the two objects use two different primary keys (different type and different name) and I can't simply force a generic "Id" as the primary key in the objects. One has to be named "name" and the other "code".
Is there a way to write the getObject and removeObject methods to accept both kind of objects?
protocol GenericStoreProtocol{
associatedtype T
func addObject(object anObject: T)
// func getObject()->T // One object use a name:String, the other code:Int as its primary key!
// func removeObject() // One object use a name:String, the other code:Int as its primary key!
}
class GenericStore<T>: GenericStoreProtocol{
fileprivate var objects = [T]()
func addObject(object anObject: T){
objects.append(anObject)
}
// ...
}
let genericAlphaStore = GenericStore<AlphaProtocol>()
let genericBetaStore = GenericStore<BetaProtocol>()
To generalize the problem, we need a store that can:
add items of any types (or ones we specify)
look up and delete items by id
use the correct id property for different stored objects
First, I'd create a protocol called Storable which has an identifier computed property. This should be of type Equatable as we will eventually be using equality comparisons when looking up objects by id in our Store.
protocol Storable {
associatedtype Identifier: Equatable
var identifier: Identifier { get }
}
We can now define the classes of the objects we are going to store (AlphaObject and BetaObject). Both of these classes should conform to their own protocol as well as the Stored protocol. Here is where you'd define what property should be used as the identifier. For AlphaObject it's name and for BetaObject it's code. These can be read-only computed properties that return the values of name and code respectively.
protocol AlphaProtocol {
var name: String { get set }
init(name: String)
}
protocol BetaProtocol {
var code: Int { get set }
init(code: Int)
}
class AlphaObject: AlphaProtocol, Storable {
typealias Identifier = String
internal var identifier: Identifier {
return self.name
}
var name: String
required init(name: String) {
self.name = name
}
}
class BetaObject: BetaProtocol, Storable {
typealias Identifier = Int
internal var identifier: Identifier {
return self.code
}
var code: Int
required init(code: Int){
self.code = code
}
}
Finally, our Store will take any objects that are Storable and will access, insert, and delete based on T's specified identifier.
class Store<T: Storable> {
fileprivate var objects = [T]()
func addObject(object: T) {
if getObject(identifier: object.identifier) == nil {
objects.append(object)
}
}
func getObject(identifier: T.Identifier) -> T? {
for o in objects {
if o.identifier == identifier {
return o
}
}
return nil
}
func removeObject(identifier: T.Identifier) {
self.objects = self.objects.filter({$0.identifier != identifier})
}
}
The full code with tests!
protocol Storable {
associatedtype Identifier: Equatable
var identifier: Identifier { get }
}
protocol AlphaProtocol {
var name: String { get set }
init(name: String)
}
protocol BetaProtocol {
var code: Int { get set }
init(code: Int)
}
class AlphaObject: AlphaProtocol, Storable {
typealias Identifier = String
internal var identifier: Identifier {
return self.name
}
var name: String
required init(name: String) {
self.name = name
}
}
class BetaObject: BetaProtocol, Storable {
typealias Identifier = Int
internal var identifier: Identifier {
return self.code
}
var code: Int
required init(code: Int){
self.code = code
}
}
class Store<T: Storable> {
fileprivate var objects = [T]()
func addObject(object: T) {
if getObject(identifier: object.identifier) == nil {
objects.append(object)
}
}
func getObject(identifier: T.Identifier) -> T? {
for o in objects {
if o.identifier == identifier {
return o
}
}
return nil
}
func removeObject(identifier: T.Identifier) {
self.objects = self.objects.filter({$0.identifier != identifier})
}
}
/* Tests */
let alpha = AlphaObject(name: "Alpha")
let beta = BetaObject(code: 12345)
let alphaStore = Store<AlphaObject>()
let betaStore = Store<BetaObject>()
alphaStore.addObject(object: alpha)
if (alphaStore.getObject(identifier: alpha.name) != nil){
print("alpha object has been added to alphaStore")
}
alphaStore.removeObject(identifier: alpha.name)
if (alphaStore.getObject(identifier: alpha.name) == nil){
print("alpha object has been removed from alphaStore")
}
betaStore.addObject(object: beta)
if (betaStore.getObject(identifier: beta.code) != nil){
print("beta object has been added to betaStore")
}
betaStore.removeObject(identifier: beta.code)
if (betaStore.getObject(identifier: beta.code) == nil){
print("beta object has been removed from betaStore")
}
I can't simply force a generic "Id" as the primary key in the objects.
Yep, you totally can if you use a single protocol instead of two unrelated ones (AlphaProtocol and BetaProtocol).
protocol KeyedObject {
associatedtype PrimaryKey : Equatable
var key: PrimaryKey { get }
}
Just make your objects conform to this protocol; they can declare whatever equatable type you require for the key, they just have to provide some way to access it.
class AlphaObject: KeyedObject {
typealias PrimaryKey = String
var name: String
required init(name: String) {
self.name = name
}
var key: String {
return self.name
}
}
Then you can use a straightforward generic class that contains only objects you provided:
class GenericStore<T : KeyedObject> {
fileprivate var objects = [T]()
func addObject(object anObject: T){
objects.append(anObject)
}
func getObject(key: T.PrimaryKey) -> T? {
for o in objects{
if o.key == key {
return o
}
}
return nil
}
...
}