Question: how would one go about initializing objects using default property values? Can one override an init within a class to create the objects?
I want to avoid using the view controller to input the object's property data. I am not pulling in data from an external source. Just want to keep the data in a separate file, like a class file, because the application involves a ton of text.
The objects I have in mind would be something that looks like a typical class object with a number of properties: object1(prop1:"string", prop2: [string], prop3: [string] and so on ...) but created in the same class to be inserted into an array of objects.
Use a caseless enum, kind of like a name space, to store your text. Then just reference those in the default initializers.
enum StaticText {
static let A = "A"
static let B = "B"
static let C = "C"
}
class SomeClass {
let A = StaticText.A
let B = StaticText.B
let C = StaticText.C
}
Make an extension that uses a convenience initializer:
public class ToolBoardView: UIVisualEffectView {
public var closeButton = ToolBoardCloseButton()
public var imageSegments = UISegmentedControl (items: ["Subject","Frame"])
internal var sliderBackgrounds:[UILabel] = []
convenience internal init(
_ tag :Int,
_ p: inout [NSLayoutConstraint],
_ l: inout [NSLayoutConstraint],
_ target :Any,
_ hideAction :Selector
)
{
self.init(frame: CGRect.zero)
self.effect = UIBlurEffect(style: .light)
self.tag = tag
switch tag {
case 0:
break
case 1:
addFilterControls(&p, &l)
default:
break
}
closeButton = ToolBoardCloseButton(tag: tag, target: target, action: hideAction)
self.addSubview(closeButton)
turnOffAutoResizing()
makeControlsHidden(true)
}
}
That's just part of one I have, but it should get you going.
Here is a very good description of what I just posted.
Related
I would like to make my class in Swift iterable.
My goal is to be able to create a class called Contact that holds properties such as the givenName, familyName, and middleName, like iOS CNContact. I would like to be able to have a class function that compares two instances of the class Contact, and finds which property the two contact objects have that match, so that say if both contacts have the same value for the givenName property, then the class function returns the result.
Here is a sample code:
class Contact {
static func compare(left: Contact, right: Contact) {
for property in left.properties {
if property == right.property {
// match is found
}
}
}
var givenName: String = ""
var familyName: String = ""
var middleName: String = ""
private var properties = [givenName, familyName, middleName]
}
let left = Contact()
let right = Contact()
Contact.compare(left: left, right: right)
I found posts that used mirroring and reflection, but I want to use Sequence and IteratorProtocol. I suspect there is already the ability to do exactly what I want to do. It seems to be a logical need that would arise.
What is the way to do this that has a balance between simplicity and the ability to address common needs to iterate through the instance properties of a class. An enumeration can be declared with given has values. Is there a way to make that work for this purpose? Is there a protocol that a class can use that assigns a hash value or other identifiable value that would allow for a sequential order to iterate through the properties of a class?
I was able to find posts and documentation that allowed me to get as far as the following code in playground that generated the following in debug window:
struct Name: Sequence {
typealias Iterator = NameIterator
typealias Element = Name
typealias Name = String
var name = "<name>"
func makeIterator() -> NameIterator {
return NameIterator()
}
}
struct NameIterator: IteratorProtocol {
typealias Iterator = String
typealias Element = Name
typealias Name = String
mutating func next() -> Name? {
let nextName = Name()
return nextName
}
}
let nameStrings = ["Debbie", "Harper", "Indivan", "Juniella"]
for nameString in nameStrings {
print(nameString)
}
Debbie
Harper
Indivan
Juniella
If you really don't want to use mirror, a straightforward way is to cycle through a list of key paths. This is particularly easy in your case because the properties are all strings:
class Contact {
static let properties = [\Contact.givenName, \Contact.familyName, \Contact.middleName]
static func compare(left: Contact, right: Contact) {
for property in properties {
if left[keyPath: property] == right[keyPath: property] {
print("got a match"); return
}
}
print("no match")
}
var givenName: String = ""
var familyName: String = ""
var middleName: String = ""
}
I think there's some confusion going on here.
The Sequence protocol and friends (IteratorProtocol, Collection, etc.) exist for you to be able to define custom sequences/collections that can leverage the existing collection algorithms (e.g. if you conform to Sequence, your type gets map "for free"). It has absolutely nothing to do with accessing object properties. If you want to do that, the only official reflection API in Swift is Mirror.
It's possible to...
...just Mirror, to create a standard collection (e.g. Array) of properties of an object
...just Sequence/Collection, to create a custom collection object that lists the property values of an object from hard-coded keypaths
...or you can use both, together, to create a custom collection object that uses Mirror to automatically list the properties of an object and their values
for example we have simple enum
public enum CXActionSheetToolBarButtonItem {
case cancel
case done
case now
private static var titles: [CXActionSheetToolBarButtonItem: String] = [
.cancel: "Cancel",
.done: "Done",
.now: "Now",
]
public var title: String {
get { return CXActionSheetToolBarButtonItem.titles[self] ?? String(describing: self) }
// what am I want to do
set(value) { CXActionSheetToolBarButtonItem.titles[self] = value }
}
// what am I forced to do
public static func setTitle(_ title: String, for item: CXActionSheetToolBarButtonItem) {
CXActionSheetToolBarButtonItem.titles[item] = title
}
}
why I don't can set new title like this
CXActionSheetToolBarButtonItem.cancel.title = "asd"
compiler responded error - Cannot assign to property: 'cancel' is not settable, but I can set title with function
CXActionSheetToolBarButtonItem.setTitle("asd", for: .cancel)
what should I change for worked my var as settable? .cancel.title = "asd"
Using an enum for this seems inappropriate, but I'll address the porblem at face value. You need to mark your setter as nonmutating, so that it can be called on non-var instances of your enum:
public enum CXActionSheetToolBarButtonItem {
// ...
public var title: String {
get { return CXActionSheetToolBarButtonItem.titles[self] ?? String(describing: self) }
nonmutating set(value) { CXActionSheetToolBarButtonItem.titles[self] = value }
}
}
CXActionSheetToolBarButtonItem.cancel.title = "foo" // Works... but why would you want this?!
I think the main reason why you can't do that is because the Swift compiler sees enum cases as immutable by default (similar to an immutable struct declared with let), and here you are trying to mutate it.
A good way to see this is if you try to add a mutating function to this enum
mutating public func setTitle2(_ newValue: String) {
CXActionSheetToolBarButtonItem.titles[self] = newValue
}
You will then receive the error message
error: cannot use mutating member on immutable value: 'cancel' returns immutable value
How to work around this
One way to have a similar behavior is by changing this enum into a set of static variables (which is more consistent with what you are trying to achieve).
public struct CXActionSheetToolBarButtonItem {
var title: String
static var cancel = CXActionSheetToolBarButtonItem(title: "Cancel")
static var done = CXActionSheetToolBarButtonItem(title: "Done")
static var now = CXActionSheetToolBarButtonItem(title: "Now")
}
Now you can use the following
CXActionSheetToolBarButtonItem.cancel.title = "asd"
Also, you still have the ability to use the dot-syntax
let buttonItem: CXActionSheetToolBarButtonItem = .cancel // it works
Hope that helps!
TD;DR: Because enum is an immutable object.
First of all: Whatever you're trying to do here – it's certainly not a good approach and pretty dangerous. As Craig pointed out in his comment, you're messing around with instance properties and static properties. You can have multiple instances of an enum – but when you want to change the title of a particular instance, you also change the title for all other instances. That's unexpected behavior and you should really think of another solution.
That being said, your code actually does work – with a little modification: Instead of
CXActionSheetToolBarButtonItem.cancel.title = "asd"
you can write
var item = CXActionSheetToolBarButtonItem.cancel
item.title = "asd"
This will compile.
The reason behind it is that an enum is a value type. Everytime you create an instance of an enum type, e.g. var item = CXActionSheetToolBarButtonItem.cancel, the enum's value is copied into the new variable item. You can choose if you want that value to be mutable or not by using either var or let. That's how Swift enums are intended to be used.
A single enum case like CXActionSheetToolBarButtonItem.cancel is immutable by definition.
CXActionSheetToolBarButtonItem.cancel.title = "asd"
wouldn't have any meaning because there is no instance to which the title can be assigned. The enum case .cancel is not bound to a variable.
I'm using structs instead of classes to store data in my iOS app because of the obvious advantage of value vs reference types. However, I'm trying to figure out how to architect groups of similar content. User posts may consist of images, text, and/or titles. If I were using classes the approach I would use is having a common Post superclass with different subclasses representing different types of posts. That way I could pass Post data around and cast as needed. However, structs don't allow for inheritance, so how could I architect something similar?
In Swift with struct you can create protocol for common task and also implement default implementation using protocol extension.
protocol Vehicle {
var model: String { get set }
var color: String { get set }
}
//Common Implementation using protocol extension
extension Vehicle {
static func parseVehicleFields(jsonDict: [String:Any]) -> (String, String) {
let model = jsonDict["model"] as! String
let color = jsonDict["color"] as! String
return (model, color)
}
}
struct Car : Vehicle {
var model:String
var color:String
let horsepower: Double
let license_plate: String
init(jsonDict: [String:Any]) {
(model, color) = Car.parseVehicleFields(jsonDict: jsonDict)
horsepower = jsonDict["horsepower"] as! Double
license_plate = jsonDict["license_plate"] as! String
}
}
struct Bicycle : Vehicle {
var model:String
var color:String
let chainrings: Int
let sprockets: Int
init(jsonDict: [String:Any]) {
(model, color) = Bicycle.parseVehicleFields(jsonDict: jsonDict)
chainrings = jsonDict["chainrings"] as! Int
sprockets = jsonDict["sprockets"] as! Int
}
}
There is a detailed answer at following Gist, with all possible approaches. I do not like any of them as I am fan of Classes. But structs are future of Swift, you have to understand, adopt and like :( it .
Link: https://gist.github.com/AliSoftware/9e4946c8b6038572d678
Is there anyway to use conversion using a variable? I am using object stacking using type of "AnyObject" and I've been able to take the class type and populate a variable. Now I need to populate an array using conversion.
var myString = "Hello World"
var objectStack = [AnyObject]()
objectStack.append(myString)
let currentObject = String(describing: objectStack.last!)
var objectType = String()
let range: Range<String.Index> = currentObject.range(of: ":")!
objectType = currentObject.substring(to: range.lowerBound)
let range2: Range<String.Index> = objectType.range(of: ".")!
objectType = objectType.substring(from: range2.upperBound)
The code above will evaluate the class and set the value of "objectType" to "String". Now I'm trying to go the other way. Something like this:
for obj in objectStack{
obj = newObject as! objectType //this doesn't work
}
Is something like this possible?
There is a simpler, safer way to get the type:
let type = type(of: objectStack.last!) // String.Type
let typeString = String(describing: type) // "String"
The other way around is not possible because the type of the object is not known at compile time. Do you have a number of known types you want to try to cast to? In that case, use optional binding to check if the cast is successful:
let object = objectStack.last!
if let string = object as? String {
// do String stuff
}
else if let i = object as? Int {
// do Int stuff
}
// and so on
If you have a large number of possible types that share some common functionality: Use Protocols. See Swift Documentation for a nice introduction.
You define a protocol for some common functionality that different types can implement:
protocol Stackable {
func doStuff()
// (more methods or properties if necessary)
}
The protocol provides a contract that all types conforming to this protocol have to fulfill by providing implementations for all declared methods and properties. Let's create a struct that conforms to Stackable:
struct Foo: Stackable {
func doStuff() {
print("Foo is doing stuff.")
}
}
You can also extend existing types to make them conform to a protocol. Let's make String Stackable:
extension String: Stackable {
func doStuff() {
print("'\(self)' is pretending to do stuff.")
}
}
Let's try it out:
let stack: [Stackable] = [Foo(), "Cat"]
for item in stack {
item.doStuff()
}
/*
prints the following:
Foo is doing stuff.
'Cat' is pretending to do stuff.
*/
This worked for me:
var instance: AnyObject! = nil
let classInst = NSClassFromString(objectType) as! NSObject.Type
instance = classInst.init()
I have this class named Meal
class Meal {
var name : String = ""
var cnt : Int = 0
var price : String = ""
var img : String = ""
var id : String = ""
init(name:String , cnt : Int, price : String, img : String, id : String) {
self.name = name
self.cnt = cnt
self.price = price
self.img = img
self.id = id
}
}
and I have an array of Meal :
var ordered = [Meal]()
I want to duplicate that array and then do some changes to the Meal instances in one of them without changing the Meal instances in the second one, how would I make a deep copy of it?
This search result didn't help me
How do I make a exact duplicate copy of an array?
Since ordered is a swift array, the statement
var orderedCopy = ordered
will effectively make a copy of the original array.
However, since Meal is a class, the new array will contain references
to the same meals referred in the original one.
If you want to copy the meals content too, so that changing a meal in one array will not change a meal in the other array, then you must define Meal as a struct, not as a class:
struct Meal {
...
From the Apple book:
Use struct to create a structure. Structures support many of the same behaviors as classes, including methods and initializers. One of the most important differences between structures and classes is that structures are always copied when they are passed around in your code, but classes are passed by reference.
To improve on #Kametrixom answer check this:
For normal objects what can be done is to implement a protocol that supports copying, and make the object class implements this protocol like this:
protocol Copying {
init(original: Self)
}
extension Copying {
func copy() -> Self {
return Self.init(original: self)
}
}
And then the Array extension for cloning:
extension Array where Element: Copying {
func clone() -> Array {
var copiedArray = Array<Element>()
for element in self {
copiedArray.append(element.copy())
}
return copiedArray
}
}
and that is pretty much it, to view code and a sample check this gist
You either have to, as #MarioZannone mentioned, make it a struct, because structs get copied automatically, or you may not want a struct and need a class. For this you have to define how to copy your class. There is the NSCopying protocol which unifies that on the ObjC world, but that makes your Swift code "unpure" in that you have to inherit from NSObject. I suggest however to define your own copying protocol like this:
protocol Copying {
init(original: Self)
}
extension Copying {
func copy() -> Self {
return Self.init(original: self)
}
}
which you can implement like this:
class Test : Copying {
var x : Int
init() {
x = 0
}
// required initializer for the Copying protocol
required init(original: Test) {
x = original.x
}
}
Within the initializer you have to copy all the state from the passed original Test on to self. Now that you implemented the protocol correctly, you can do something like this:
let original = Test()
let stillOriginal = original
let copyOriginal = original.copy()
original.x = 10
original.x // 10
stillOriginal.x // 10
copyOriginal.x // 0
This is basically the same as NSCopying just without ObjC
EDIT: Sadly this yet so beautiful protocol works very poorly with subclassing...
A simple and quick way is to map the original array into the new copy:
let copyOfPersons: [Person] = allPersons.map({(originalPerson) -> Person in
let newPerson = Person(name: originalPerson.name, age: originalPerson.age)
return newPerson
})
The new Persons will have different pointers but same values.
Based on previous answer here
If you have nested objects, i.e. subclasses to a class then what you want is True Deep Copy.
//Example
var dogsForAdoption: Array<Dog>
class Dog{
var breed: String
var owner: Person
}
So this means implementing NSCopying in every class(Dog, Person etc).
Would you do that for say 20 of your classes? what about 30..50..100? You get it right? We need native "it just works!" way. But nope we don't have one. Yet.
As of now, Feb 2021, there is no proper solution of this issue. We have many workarounds though.
Here is the one I have been using, and one with less limitations in my opinion.
Make your class conforms to codable
class Dog: Codable{
var breed : String = "JustAnyDog"
var owner: Person
}
Create this helper class
class DeepCopier {
//Used to expose generic
static func Copy<T:Codable>(of object:T) -> T?{
do{
let json = try JSONEncoder().encode(object)
return try JSONDecoder().decode(T.self, from: json)
}
catch let error{
print(error)
return nil
}
}
}
Call this method whenever you need true deep copy of your object, like this:
//Now suppose
let dog = Dog()
guard let clonedDog = DeepCopier.Copy(of: dog) else{
print("Could not detach Dog")
return
}
//Change/mutate object properties as you want
clonedDog.breed = "rottweiler"
//Also clonedDog.owner != dog.owner, as both the owner : Person have dfferent memory allocations
As you can see we are piggy backing on Swift's JSONEncoder and JSONDecoder, using power of Codable, making true deep copy no matter how many nested objects are there under our object. Just make sure all your Classes conform to Codable.
Though its NOT an ideal solution, but its one of the most effective workaround.