Swift Array extension with optionals - arrays

I've created simple extension for Array which will append only unique elements. It works fine until I want to work with an array of optionals. Since that I'm keep getting error
Type 'Event?' does not conform to protocol 'Equatable'
Event class
import RealmSwift
class Event: Object,Equatable {
dynamic var id = ""
}
func ==(lhs: Event, rhs: Event) -> Bool {
return lhs.id == rhs.id
}
Extension
extension Array where Element : Equatable {
mutating func appendUniqueContentOf(elements:[Element]){
for ele in elements {
if (!contains(ele)){
append(ele)
}
}
}
}
Usage
var newEvents:[Event] = someEvents()
var events = [Event?]()
events.appendUniqueContentOf(newEvents)
Question
I don't understand this situation. Event class conform that protocol. I've also tried adding other combination of == function, but without success.
I don't know how to approah this issue. Is it matter of my extension? How I should properly approach it? Could you show me right track?

Event? is syntactic sugar for Optional<Event>. Since Optional does not conform to Equatable, neither will Optional<Event>.
Though possible, I highly discourage implementing Equatable for Optional. This being said, you should probably rethink and try using just Array<Event>.

Related

Swift: Cannot add element in Array from another object

I am struggeling with swift syntax . I want to add objects to an array but I have syntax errors.
The array is located in class Document, and the class that should add objects is in class Viewcontroller.
The array is of type Content:
public class Content: NSObject {
#objc var bankAccSender: String?
#objc var bankAccReceiver: String?
Declaration snippest in Document:
class Document: NSDocument {
var content=[Content]()
override init() {
super.init()
self.content = [Content]()
// force one data record to insert into content
content += [Content (… )] // checked with debugger
The ViewController has assigned the represented Object
contentVC.representedObject = content
But adding data in ViewController gives a compiler error „Type of expression is ambiguous without more context“:
var posting = Content(…)
self.representedObject.append(posting)
Hope you can help..
You can't append an element to an object of type Any. What you need is to replace the existing value with a new collection:
representedObject = (representedObject as? [Content] ?? []) + CollectionOfOne(posting)
representedObject is of type Any?, which is a very difficult type to work with in Swift. Since you already have a content property, I would probably adjust that, and then re-assign it to representedObject.
You can also try this (untested), as long as you are certain that the type is always [Content]:
(self.representedObject as! [Content]).append(posting)
It's possible you'll need some more complex like this:
(self.representedObject as! [Content]?)!.append(posting)
As I said, Any? is an incredibly obnoxious type. You probably want to wrap this up into a function. Or I you can avoid using representedObject, then I would recommend that. In many cases you don't need it. It's often just a convenience (in ObjC; in Swift, it's very hard to use).

how can i pass alot of data from viewController to another on without using segue?

I want to pass a UIimage from viewController to another without using segue and i keep searching for this problem for weeks. Also, i tried to do it using notificationCenter, delegate and protocol but it didn't work with me so help please.
This is a screenShot describe what i want to do exactly.
enter image description here
Also, this is my code for the viewController which i want to pass the thor image from it when the pass Label button is clicked .
import UIKit
protocol UserChosePhoto {
func userHasChosen(image: UIImage)
}
class passingDataViewController: UIViewController {
#IBOutlet var Label: UILabel!
#IBOutlet var image1: UIImageView!
var image: UIImage? = nil
var delegate: UserChosePhoto? = nil
override func viewDidLoad() {
super.viewDidLoad()
image1.image = image
}
#IBAction func passLabel(_ sender: Any) {
func tapped() {
if (delegate != nil) {
self.delegate!.userHasChosen(image: image1.image!)
}
}
}
}
and this is the code for the viewController which i want to display thor image in it .
import UIKit
class receivingDataViewController: UIViewController, UserChosePhoto {
#IBOutlet var label2: UILabel!
#IBOutlet var image2: UIImageView!
var usedImage: UIImage? = nil
override func viewDidLoad() {
super.viewDidLoad()
}
func userHasChosen(image: UIImage) {
usedImage = image
print("delegation: \(image)")
}
}
I'm seeing a few things missing, along with some Swift coding conventions not being followed. So let's start with your UserChosePhoto protocol, which is pretty good. But - why not make your code readable?
protocol PassingDataViewControllerDelegate {
func userHasChosen(image: UIImage)
}
Sure, what you have will work, but adding Delegate to the end of your name (along with which VC you are using this with) make everyone know what you are doing.
Now you need to properly set up your first view controller:
class PassingDataViewController: UIViewController {
var delegate: PassingDataViewControllerDelegate? = nil
func tapped() {
if (delegate != nil) {
self.delegate!.userHasChosen(image: image1.image!)
}
}
}
Again, very close to your code - I just capitalized your view controller's class name. This is how Swift coders do it. Oh, and since I changed the name of the portal, that needed to be changed to.
But there's one more thing - you have a second way to code this:
class PassingDataViewController: UIViewController {
var delegate: PassingDataViewControllerDelegate! = nil
func tapped() {
delegate.userHasChosen(image: image1.image)
}
}
This results in a couple of things. Obviously, the delegate call is simpler. But more importantly, if you don't set up things correctly in your second view controller, your app crashes. (Sometimes, that's actually a good thing!)
Here's where I think you didn't do things right - the second VC:
class ReceivingDataViewController: UIViewController, PassingDataViewControllerDelegate {
var usedImage: UIImage? = nil
let passingDataViewController = PassingDataViewController()
override func viewDidLoad() {
super.viewDidLoad()
passingDataViewController.delegate = self
}
func userHasChosen(image: UIImage) {
usedImage = image
print("delegation: \(image)")
}
}
Now, several things.
First, note that I'm creating an instance of the first VC - that's needed. Delegation is always a 1:1 kind of communication. What is still missing (because you haven't mentioned it in your question) is how the first VC is being presented. I know you don't want to use a segue, but how is your app "flow" going from the first to the second view controller? Usually without a segue that means presenting it modally (like you would with UIImagePickerController). You can also add both the view controller and it's view as a child VC and view in the hierarchy.
Second, I'm telling the first VC that the second VC is the it's delegate. If instead of using optionals (var delegate: PassingDataViewControllerDelegate? = nil) you force-unwrap things (var delegate: PassingDataViewControllerDelegate! = nil) you'd see your app crash because the second VC isn't the delegate for the first VC without passingDataViewController.delegate = self.
FINAL NOTE:
While I was writing this #TLG_Codin provided an answer. It could work - but *where is userImage declared as a public variable? In one of two places:
Globally, as in outside of a class. That's called a singleton, and be very, very careful on doing this. Since it's global, anyplace in your app can change it. That's why singletons are generally frowned upon.
Inside your first VC. The problem there is - how are you presenting this VC? Again, you've mentioned you don't want to use a segue. So... you're back to my note about presenting the first VC from the second or adding the VC and it's view to the hierarchy. Either way? Delegation works perfectly, and at the moment you wish to tell interested classes (in this case the second VC) that userImage has changed, you do.
Try using public variable. It will let you store anything you want in it and access it from anywhere in your project.
for example, put this line anywhere outside of any class:
public var userImage: UIImage? = nil
and then you can use it anywhere in your code by using userImage like any other variable.
Or, if you have multiple variables, you can create a struct with static variables:
struct sharedVariables {
static var userImage: UIImage? = nil
static var userList: [Any] = [64, "Hello, world!"]
static var integer: Int = 300
// Or add anything you want; just make sure to start with 'static'
}
And then you can use the variables in the struct like that:
sharedVariables.userList
Of course, there are more ways to do what you asked for, but this is the simplest one.

Filtering an array of objects by class not working

I have a UIStackView that contains UIViews or objects of a class I have created called MyView.
MyView is a subclass of UIView.
I want to extract from that array, all objects of class MyView.
This is what I have tried and the respective errors:
let views = Array< MyView >(allViews).filter { $0 is MyView }
type of expression is ambiguous without more context
I love these messages that say nothing.
let views = Array<Any>(allViews).filter { $0 is MyView }
I love how this compiles with Any.
No error in this case, but views contains the same objects as myViews, nothing is being filtered.
I understand that MyView is a subclass of UIView, so what swift is testing here is if the object is of class UIView. If this is true, why bothering allowing programmers to specify any class on the filter, if it can only filter some classes?
Is there a way to test for subclasses?
I'm guessing allViews is an array of UIView.
You should use compactMap, which will map to an array of the subclass, throwing away any nil values (which result from the as?):
let views = allViews.compactMap { $0 as? MyView }
Note here views is already of type [MyView]; take a look at compactMap's method signature to understand:
func compactMap<ElementOfResult>(_ transform: (Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult]

Lazy initialisation of individual array elements

In Swift, lazy properties allow us to only initialise a class member when we ask for it instead of directly at runtime - useful for computationally expensive operations.
I have a class in Swift 4 that is responsible for initialising a strategy from an array of compile-time (developer-hardcoded) provided StrategyProtocol objects. It looks something like this:
class StrategyFactory {
private var availableStrategies: [StrategyProtocol] = [
OneClassThatImplementsStrategyProtocol(),
AnotherThatImplementsStrategyProtocol() // etc
]
public func createStrategy(): StrategyProtocol {
// Depending on some runtime-calculated operation
// How do I do this nicely here?
}
}
However, from my understanding, placing () at the end of each strategy initialises the objects(?), when I may only want to create one depending on certain runtime conditions.
Either way, is it possible to place lazy somewhere around the values in an Array class member to only instantiate the one I want when I ask for it? Or would I have to go about this with closures or some other alternative?
Current attempt
Is this doing what I think it is? Until I get the first element of the array and execute it, it won't actually instantiate the strategy?
private var availableStrategies: [() -> (StrategyProtocol)] = [
{ OneClassThatImplementsStrategyProtocol() }
]
Your "Current attempt" does what you think it does. You have an array
of closures, and the strategy is initialized only when the closure is
executed.
A possible alternative: Store an array of types instead of
instances or closures (as Zalman Stern also suggested).
In order to create instances on demand, a
init() requirement has to be added to the protocol (which must then
be satisfied by a required init() unless the class is final,
compare Why use required Initializers in Swift classes?).
A possible advantage is that you can query static properties
in order to find a suitable strategy.
Here is a small self-contained example, where createStrategy()
creates and returns the first "fantastic" strategy:
protocol StrategyProtocol {
init()
static var isFantastic: Bool { get }
}
class OneClassThatImplementsStrategyProtocol : StrategyProtocol {
required init() { }
static var isFantastic: Bool { return false }
}
final class AnotherThatImplementsStrategyProtocol : StrategyProtocol {
init() { }
static var isFantastic: Bool { return true }
}
class StrategyFactory {
private var availableStrategies: [StrategyProtocol.Type] = [
OneClassThatImplementsStrategyProtocol.self,
AnotherThatImplementsStrategyProtocol.self // etc
]
public func createStrategy() -> StrategyProtocol? {
for strategy in availableStrategies {
if strategy.isFantastic {
return strategy.init()
}
}
return nil
}
}
ANYCLASS, META TYPE AND .SELF may answer your question. (I am not expert on Swift, but use of metaclasses is likely what you want and Swift, as I expected, appears to support them.) You can look through this Stack Overflow search.
EDIT: In case it wasn't clear, the idea is to have the array of strategies contain the metaclasses for the protocols rather than instantiations. Though this depends on whether you want a new strategy object for each instantiation of the class with the lazy property or whether strategies are effectively global and cached ones created. If the latter, then the lazy array approach for holding them might work better.

How do you use fully-specified types in arrays defined using shorthand form?

According to the Swift documentation, the two means of defining an array are "functionally identical," but Xcode is giving me an error when I try to define an array like this:
private var tweets = [[Twitter.Tweet]]()
The error is:
Cannot call value of non-function type '[Array<Tweet.Type>]'
And Xcode suggests deleting the trailing (). But both of the following are working fine:
private var tweets = [[Tweet]]()
private var tweets = [Array<Twitter.Tweet>]()
I need to fully specify the type as coming from the Twitter framework because I have just added a subclass of NSManagedObject called Tweet to work with CoreData in my project.
The Swift documentation says the shorthand form is preferred, so is there a way to use the shorthand form with a fully-specified type?
This is a known bug in the Swift compiler. There are several workarounds you can use until it's fixed:
Use a type annotation instead, with an empty array literal:
private var tweets: [[Twitter.Tweet]] = []
Use a type alias:
private typealias TwitterTweet = Twitter.Tweet
private var tweets = [[TwitterTweet]]()
Expand all the [T] syntactic sugar, and use Array<T> instead
private var tweets = Array<Array<Twitter.Tweet>>()
There is also some other way to prepare your array:
private var tweets: [[Twitter.Tweet]] = []
Full playground code:
import UIKit
import PlaygroundSupport
import CoreData
struct Twitter {
class Tweet: NSManagedObject {
//
}
}
private var tweets: [[Twitter.Tweet]] = []

Resources