How can I get specific instance when I use static method - ios

I hope to get a specific instance when I use my static method. For example:
class Food: NSObject {
var name: String
class func initFruit() -> Food? {
let fruitName = NSStringFromClass(self).components(separatedBy: ".").last! as String
if "Apple" == fruitName {
return Apple(name: fruitName)
} else if "Orange" == fruitName {
return Orange(name: fruitName)
}
return nil
}
init(name: String) {
self.name = name
}
}
class Apple: Food {
}
class Orange: Food {
}
When I create an Apple instance with the method:
let apple = Apple.initFruit() as? Apple
How can I get the specific instance apple rather than use as? Apple?. I wonder how to modify the method:
static func initFruit() -> Food?

There are a couple of problems with your design, let me try and enumerate them:
base classes should not be aware of their subclasses, it's bad practice and it's not scalable, as adding a new subclass would require maintaining the base class method
the static method is not needed at all, at least in the shape it's written in the question, you could simply directly call the initializers for the subclasses
Leaving all those aside, you can use Self as return type for the static method, this will allow dynamic results.
class Food: NSObject {
var name: String
class func initFruit() -> Self {
let fruitName = NSStringFromClass(self).components(separatedBy: ".").last! as String
return self.init(name: fruitName)
}
required init(name: String) {
self.name = name
}
}
class Apple: Food {
}
class Orange: Food {
}
let apple = Apple.initFruit() // is an Apple, no cast needed

i think it's not good idea because Food is parent class and Apple inherits Food. Apple may know it's parent class cause is extends Food but Food does not.
So, if you want to create instance by some string or some variable. I would like to recommend you to adopt "Factory pattern"
reference here:
https://medium.com/swift-programming/design-patterns-creational-patterns-factory-pattern-in-swift-d049af54235b

Inspired by the question Generics in Swift - "Generic parameter 'T' could not be inferred I find another way to resolve this question. I add a method to infer the specific type.
func ascertainFruitType<T>() -> T {
return self as! T // as! is dangerous
}
Then the method initFruit is changed on below:
class func initFruit() -> Self {
let fruitName = NSStringFromClass(self).components(separatedBy: ".").last! as String
if "Apple" == fruitName {
return Apple(name: fruitName).ascertainFruitType()
} else {
return Orange(name: fruitName).ascertainFruitType()
}
}

Related

Error with retrieve / save custom object in core data

I have developed an ios application that allow users to edit a musical score sheet and now i'd like to implement data persistence to prevent the discarding of changes.
Reading on ios documentation i've noticed that exists different ways to improve data persistence and I believe that the best way for my application is Core Data.
Considering that my application use a lot of custom object i met a lot of problems.
I'm trying to use core data to save an entity, referred to a score sheet, composed by two attributes:
name: String
score: Array of another custom object (Measure), composed by other custom object (Score Element)
According to documentation and other q/a I've decided to use a Trasformable type on the model:
So I've declared a generic class used as trasformer for score attribute:
public class NSSecureCodingValueTransformer<T: NSSecureCoding & NSObject>: ValueTransformer {
public override class func transformedValueClass() -> AnyClass { T.self }
public override class func allowsReverseTransformation() -> Bool { true }
public override func transformedValue(_ value: Any?) -> Any? {
guard let value = value as? T else { return nil }
return try? NSKeyedArchiver.archivedData(withRootObject: value, requiringSecureCoding: true)
}
public override func reverseTransformedValue(_ value: Any?) -> Any? {
guard let data = value as? NSData else { return nil }
let result = try? NSKeyedUnarchiver.unarchivedObject(
ofClass: T.self,
from: data as Data
)
return result
}
/// The name of this transformer. This is the name used to register the transformer using `ValueTransformer.setValueTransformer(_:forName:)`
public static var transformerName: NSValueTransformerName {
let className = NSStringFromClass(T.self)
return NSValueTransformerName("DHC\(className)ValueTransformer")
}
/// Registers the transformer by calling `ValueTransformer.setValueTransformer(_:forName:)`.
public static func registerTransformer() {
let transformer = NSSecureCodingValueTransformer<T>()
ValueTransformer.setValueTransformer(transformer, forName: transformerName)
}
}
Using in this way a DHCMeasureValueTransformer as trasformer in DataModel file.
The problem is that when i save, no error occurs but when i fetch data for a new restart of application, i can fetch just the name of score sheet, while the score array it's empty, like if no elements it's been put inside (clearly, before of save, i've try to print array content, that prove that i'm working with a non empty array)
Here is the code of the save:
static func saveContext() {
let context = getContext()
do {
try context.save()
} catch {
print("error during the save.")
}
}
And here is the code of two classes of the entity object:
// DataClass
#objc(ScoreSheet)
public class ScoreSheet: NSManagedObject {
static var supportsSecureCoding: Bool {
return true
}
}
//DataProperties
extension ScoreSheet {
#nonobjc public class func fetchRequest() -> NSFetchRequest<ScoreSheet> {
return NSFetchRequest<ScoreSheet>(entityName: "ScoreSheet")
}
#NSManaged public var name: String
#NSManaged public var score: [Measure]
}
Clearly Measure class implements NSSecureCoding and method for decode and encode the object.
Here is Measure class implementation:
import Foundation
class Measure: NSObject, NSCoding, NSSecureCoding {
var elements : [ScoreElement] = []
var timeSig : TimeSignature
var clef : Clef
static var supportsSecureCoding = true
init(time : TimeSignature, clef : Clef) {
self.timeSig = time
self.clef = clef
}
func encode(with encoder: NSCoder) {
encoder.encode(self.elements, forKey: "elements")
encoder.encode(self.timeSig, forKey: "timeSig")
encoder.encode(self.clef, forKey: "clef")
}
required convenience init? (coder decoder: NSCoder) {
let elements = decoder.decodeObject(forKey: "elements") as! [ScoreElement]
let timeSig = decoder.decodeObject(forKey: "timeSig") as! TimeSignature
let clef = decoder.decodeObject(forKey: "clef") as! Clef
self.init(time: timeSig, clef: clef)
self.elements = elements
}
}
I'm not sure what's going wrong but there are a couple of things that need fixing that might affect your results.
Firstly, the computed transformer name is not the same as the one you're trying to use. When this line executes, and T is Measure,
let className = NSStringFromClass(T.self)
Then className is going to be something like MyProjectName.Measure. The computed transformer name ends up as something like NSValueTransformerName(_rawValue: DHCMyProjectName.MeasureValueTransformer), which doesn't match what you're using in the data model. All of which means that your transformer isn't getting used.
But that probably doesn't matter because if Measure conforms to NSSecureCoding and all of Measure's properties (ScoreElement, TimeSignature, Clef) also conform to NSSecureCoding (which seems to be the case since your code isn't throwing exceptions), then you don't need a custom transformer at all. If a transformable property type conforms to NSSecureCoding then Core Data will automatically use NSSecureCoding. You don't need a custom transformer unless you don't want to or can't conform to NSSecureCoding for some reason. Because of this, it doesn't matter that your transformer isn't being used.
As for why Measure isn't surviving the encode/decode process, I don't know, but you may help clear things up by removing the distraction of the unnecessary encode/decode class. I'd also suggest putting a breakpoint in Measure in the encode(with:) and init(coder:) methods. You should hit those breakpoints when saving and fetching data.

Generic class type doesn't conform to Any

I have a problem with storing my generic classes in an array. How should I format the type for my array while keeping the reference to the original type (I know I could do var myClasses: [Any] = [] but that wouldn't be helpful when retrieving the variable from my array :(
Example is below:
import UIKit
protocol Reusable { }
extension UITableViewCell: Reusable { }
extension UICollectionViewCell: Reusable { }
class SomeClass<T> where T: Reusable {
init() { }
}
var myClasses: [SomeClass<Reusable>] = []
myClasses.append(SomeClass<UITableViewCell>())
myClasses.append(SomeClass<UICollectionViewCell>())
myClasses.append(SomeClass<UITableViewCell>())
myClasses.append(SomeClass<UICollectionViewCell>())
Edit: Just to clarify, I have used the collection and table cells as an example, I am not actually planning on storing them together :)
Edit 2 var myClasses: [SomeClass<Reusable>] = [] generates error: using 'Reusable' as a concrete type conforming to protocol 'Reusable' is not supported
Edit 3 var myClasses: [SomeClass<AnyObject>] = [] generates error: type 'AnyObject' does not conform to protocol 'Reusable'
I think you can create some sort of Holder class that can accept and retrieve your object:
class Holder {
lazy var classes: [Any] = []
func get<T>(_ type: T.Type) -> [T]? {
return classes.filter({ $0 is T }) as? [T]
}
}
And the main part:
let holder = Holder()
holder.classes.append(SomeClass<UITableViewCell>())
if let someTableViewCells = holder.get(SomeClass<UITableViewCell>.self)?.first {
// or filter out again to get specific SomeClass of UITableViewCell
print(someTableViewCells)
}
Or without holder class:
var classes: [Any] = []
classes.append(SomeClass<UITableViewCell>())
if let someTableViewCell = classes.filter({ $0 is SomeClass<UITableViewCell> }).first as? SomeClass<UITableViewCell> {
print(someTableViewCell)
}
You should use array of AnyObject in your case. Because as you know swift is strong typed language and for example
SomeClass<UITableViewCell>
and
SomeClass<UICollectionViewCell>
are different types of objects. As for example Array< Int > and Array< String >, they are both arrays, but still it's a different types of objects. So in this case you'll have to use declaration:
var myClasses: [AnyObject] = []
and check type of object or typecast them every time you'll need.
if (myClasses[0] is SomeClass<UICollectionViewCell>) { do something }
or
if let cell = myClasses[0] as? SomeClass<UICollectionViewCell> { do something }
My suggestion is adding parent protocol SomeClass Container for your SomeClass generic. Then put an array of SomeClass objects inside SomeClass.
protocol Reusable { func cakePlease() }
extension UITableViewCell: Reusable {
func cakePlease() { }
}
extension UICollectionViewCell: Reusable {
func cakePlease() { }
}
protocol SomeClassContainer {
func teaPlease()
func afternoonPlease()
}
class SomeClass<T: Reusable>: SomeClassContainer {
var item: T?
init() { }
func teaPlease() { }
func afternoonPlease() {
teaPlease()
item?.cakePlease()
}
}
var myClasses = [SomeClassContainer]()
myClasses.append(SomeClass<UITableViewCell>())
myClasses.append(SomeClass<UICollectionViewCell>())
myClasses.append(SomeClass<UITableViewCell>())
myClasses.append(SomeClass<UICollectionViewCell>())
myClasses[0].teaPlease()
if let item = (myClasses[0] as? SomeClass<UITableViewCell>)?.item {
item.cakePlease()
}
for myClass in myClasses {
if let tableCell = (myClass as? SomeClass<UITableViewCell>)?.item {
tableCell.cakePlease()
} else if let collectionCell = (myClass as SomeClass<UICollectionViewCell>)?.item {
collectionCell.cakePlease()
}
}
myClasses.forEach({ $0.afternoonPlease() })
Generally the way to type your array would be to go as specific as possible whilst still covering all bases.
What I mean by this for example is storing an array of UIViewController objects, even though each will actually be a different type. You wouldn't use Any here because you really don't need to be that general.
In your case, why not use Reusable? Since all your generic classes conform to it anyway, it is as general as you can go whilst still maintaining convenience.

How to refactor swift code to take protocol and struct types as method arguments

I have 2 functions that have a lot in common, and I want to re-factor my code to remove the repeated logic, however the things that are different are types, specifically a protocol, and and a struct type. The way I can think about it now is that to re-factor this I'd have 1 common method that would take one protocol type as an argument, and one struct type with the restriction that the struct type must implement the protocol 'DataDictionaryStore'. And would return an array of the protocol type passed in as argument 1
I've tried to implement this with generics, but from how I understand it, you still pass an instance as an argument when using generics, not the actual type itself.
The methods I'd like to re-factor in the code below are 'articles()', and 'authors()'
Here's the code (which can be copied to a playground Xcode 7+):
import Foundation
protocol Article {
var headline: NSString? { get }
}
protocol Author {
var firstName: NSString? { get }
}
protocol DataDictionaryStore {
init(dataDictionary: NSDictionary)
}
struct CollectionStruct {
let arrayOfModels: [NSDictionary]
//This function is identical to authors() except for return type [Article], and 'ArticleStruct'
func articles() -> [Article] {
var articlesArray = [Article]()
for articleDict in arrayOfModels {
let articleStruct = ArticleStruct(dataDictionary: articleDict)
articlesArray.append(articleStruct)
}
return articlesArray
}
func authors() -> [Author] {
var authorsArray = [Author]()
for authorDict in arrayOfModels {
let authorStruct = AuthorStruct(dataDictionary: authorDict)
authorsArray.append(authorStruct)
}
return authorsArray
}
}
struct ArticleStruct : Article, DataDictionaryStore {
var internalDataDictionary: NSDictionary
init(dataDictionary: NSDictionary) {
internalDataDictionary = dataDictionary
}
var headline: NSString? { return (internalDataDictionary["headline"] as? NSString) }
}
struct AuthorStruct : Author, DataDictionaryStore {
var internalDataDictionary: NSDictionary
init(dataDictionary: NSDictionary) {
internalDataDictionary = dataDictionary
}
var firstName: NSString? { return (internalDataDictionary["firstName"] as? NSString) }
}
var collStruct = CollectionStruct(arrayOfModels: [NSDictionary(objects: ["object1", "object2"], forKeys: ["key1", "headline"])])
print(collStruct)
var articles = collStruct.articles()
print(articles)
for article in articles {
print(article.headline)
}
If there is another way to re-factor this to remove the repeated logic, all suggestions welcome.
It's not exactly an answer to your question, but this might simplify it enough for you to be happy:
func articles() -> [Article] {
return arrayOfModels.map(ArticleStruct.init)
}
func authors() -> [Author] {
return arrayOfModels.map(AuthorStruct.init)
}
Based on PEEJWEEJ's answer, this refactor is also worth a shot. Instead of returning a single array, you can return a tuple of authors and articles. If you aren't going to be processing both the authors and articles arrays at once, this method is more expensive. But the syntax is much nicer than the previous solution using generics below.
func allObjects() -> (authors: [AuthorStruct], articles: [ArticleStruct]) {
let authors = arrayOfModels.map(AuthorStruct.init)
let articles = arrayOfModels.map(ArticleStruct.init)
return(authors, articles)
}
You would then call the method like this:
let objects = collection.allObjects()
let authors = objects.authors
let articles = objects.articles
I'm not a huge fan of the clarity here but maybe you can refactor it a bit. It seems to work at least.
func allObjectsOfType<T>(type: T.Type) -> [T] {
var objectArray = [T]()
for objectDict in arrayOfModels {
var objectStruct: T?
if type == Author.self {
objectStruct = AuthorStruct(dataDictionary: objectDict) as? T
} else if type == Article.self {
objectStruct = ArticleStruct(dataDictionary: objectDict) as? T
}
guard objectStruct != nil else {
continue
}
objectArray.append(objectStruct!)
}
return objectArray
}
You can then call it like this...
collection.allObjectsOfType(Author)
collection.allObjectsOfType(Article)

Swift Generic Unknown Member with Protocol Extension

If I have the following code:
protocol ObjectType {
var title: String { get set }
}
extension ObjectType {
var objectTypeString: String {
let mirror = Mirror(reflecting: self)
return "\(mirror.subjectType)"
}
}
class Object: ObjectType {
var title = ""
}
class SomeOtherClass {
private func someFunc<T: Object>(object: T) {
print(object.objectTypeString)
}
}
where Object conforms to ObjectType, you would expect that you can access objectTypeString on any ObjectInstance. But the compiler says that Type T has no member objectTypeString when that member is accessed on some generic type that inherits from Object, as shown in the code above. When the function is non-generic and just passes in an Object parameter, there's no issue. So why does have the parameter be generic make it so I can't access a member of the protocol that the conforming class should have access to?
I came across this question here but I'm not interested in workarounds, I'd just like to understand what it is about the generic system that makes my example not work. (Simple workaround is to do <T: ObjectType>)
Maybe I'm wrong or i didn't understand your question completely, but i think you might be missing initiating "object".
your willing code maybe the code below:
protocol ObjectType {
var title: String { get set }
}
extension ObjectType {
var objectTypeString: String {
let mirror = Mirror(reflecting: self)
return "\(mirror.subjectType)"
}
}
class Object: ObjectType {
var title = ""
}
class SomeOtherClass {
private func someFunc<T: Object>(object: T) {
let object = Object()
print(object.objectTypeString)
}
}
but the thing is, even if we dont initiate the object, the auto complete brings the objectTypeString up! that's what i don't understand, and as you said maybe its where the bug happens!
hope it helps <3

How to call static method provided by protocol in Swift

How to access to static protocol method within a instance
I have a list of Contact, the contact can be a FamilyContact that inherit from Contact and the GroupStatus protocol
I want to call the static method from GroupStatus but in vain...
Here is my code
protocol GroupStatus {
static func isPrivate() -> Bool // static method that indicates the status
}
protocol IsBusy {
func wizzIt()
}
class AdresseBook {
private var contacts = [Contact]()
func addOne(c: Contact) {
contacts.append(c)
}
func listNonPrivated() -> [Contact]? {
var nonPrivateContact = [Contact]()
for contact in contacts {
// here is I should call the static method provided by the protocol
if self is GroupStatus {
let isPrivate = contact.dynamicType.isPrivate()
if !isPrivate {
nonPrivateContact.append(contact)
}
}
nonPrivateContact.append(contact)
}
return nonPrivateContact
}
}
class Contact : Printable {
var name: String
init(name: String) {
self.name = name
}
func wizz() -> Bool {
if let obj = self as? IsBusy {
obj.wizzIt()
return true
}
return false
}
var description: String {
return self.name
}
}
class FamilyContact: Contact, GroupStatus {
static func isPrivate() -> Bool {
return true
}
}
I can't compile Contact.Type does not have a member named 'isPrivate'
How can I call it ? It works if I delete the static keyword, but I think is more logical to define it static.
If I replace
let isPrivate = contact.dynamicType.isPrivate()
by
let isPrivate = FamilyContact.isPrivate()
It works, but I can have more than 1 subclasses
If I remove the static keywork I can do it by this way :
if let c = contact as? GroupStatus {
if !c.isPrivate() {
nonPrivateContact.append(contact)
}
}
But I want to keep the static keyword
This looks like a bug or a non-supported feature. I would expect that
the following works:
if let gsType = contact.dynamicType as? GroupStatus.Type {
if gsType.isPrivate() {
// ...
}
}
However, it does not compile:
error: accessing members of protocol type value 'GroupStatus.Type' is unimplemented
It does compile with FamilyContact.Type instead of GroupStatus.Type. A similar problem is reported here:
Swift 1.1 and 1.2: accessing members of protocol type value XXX.Type' is unimplemented
Making isPrivate() an instance method instead of a class method is
the only workaround that I currently can think of, maybe someone comes
with a better solution ...
Update for Swift 2 / Xcode 7: As #Tankista noted below, this has
been fixed. The above code compiles and works as expected in Xcode 7 beta 3.
type(of: contact).isPrivate()
This should work in recent Swift.

Resources