Swift Array of Self-defined Objects Contains: Requires to Insert 'where:' - arrays

Hi I'm new to Swift and I'm trying to implement a Graph structure. I have defined Node and Edge structs, and now I'm trying to check whether a particular edge exists in the graph or not. The class and related function is like this:
class Graph<T: Equatable> {
private var storedNodes = [Node]()
private var storedEdges = [Edge]()
func containsSimpleGraphEdge(targetEdge: Edge) -> Bool {
let simpleGraphEdges = self.storedEdges.map({edge in (edge.source, edge.dest)})
let targetSimpleGraphEdge = (targetEdge.source, targetEdge.dest)
return simpleGraphEdges.contains(targetSimpleGraphEdge)
}
But Xcode is complaining that I should insert 'where: ' in the last contains statement. May I know why it's like this? My purpose is just to check whether an edge exists in the graph or not. The mapping to tuples is done because in the Edge implementation there are other properties; here only the source node and the destination node need to be checked.

But Xcode is complaining that I should insert where: in the last
contains statement. May I know why it's like this?
As pointed out by #Keiwan below, tuples don't conform to Equatable, even if Swift's standard lib provides us with equality testing operator == for tuples up to 6 members, given that all members themselves conform to Equatable; the latter as described in the following Swift Evolution proposal:
Se-0015: Tuple comparison operators
Hence, your attempted call to contains(_:) fails as this method is not accessible for non-Equatable elements.
You could, however, make use of contains(where:) to explicitly provide a closure telling contains how to test equality. Given that your Node type conforms to Equatable, you'll want to modify the contains call in the return of your method into:
return simpleGraphEdges.contains(where: { $0 == targetSimpleGraphEdge })
// note also your typo here, 'targetSimpleEdge' should be 'targetSimpleGraphEdge'
Or, supplying the predicate as a trailing closure:
return simpleGraphEdges.contains { $0 == targetSimpleGraphEdge }

If you don't want to make Edge equatable (such as when it has multiple context-dependant definitions of "equality"), you can just use contains(where:).
func contains(simpleGraphEdge e: Edge) -> Bool {
return self.storedEdges.contains(where: {
$0.source == e.source && $0.dest == e.dest
})
}

Related

Large C array not imported in Swift 5.2

I am trying to import a C array that is 64kb meant to be a circular buffer for a log on a device. When I attempt to access this array in Swift I am met with the error
Value of type 'logging' has no member 'data'
To attempt to answer potential questions about bridging, I am using other types from my imported C library that are defined in the same file that this mock code originates.
The code is setup as follows:
logging.h:
typedef struct _logging {
uint8_t data[64*1024];
} logging;
In ReadLog.swift:
struct ReadLog {
var log: logging = logging()
func read() {
//...Do some stuff...
let char = log.data.0 // <- Error here 'Value of type 'logging' has no member 'data'
}
}
Changing the size of data to 256 imported this member with no issue. (I don't know what the cutoff is).
What would be the best way to handle this?
The problem is that C arrays are imported to Swift as a tuple, and tuples have a maximal arity (see for example What is the limit (if any) to the tuple cardinality in Swift?). The current limit in Swift 5 seems to be 4096 tuple elements.
A possible workaround is to create a C helper function which returns a pointer to the C array. With the SE-0044 Import as member feature this can made to look like a Swift property.
You only have to add the following code either in the C header file where the logging structure is defined, or in the bridging header file:
// Pointer to the data elements:
__attribute__((swift_name("getter:logging.dataPtr(self:)")))
static inline const uint8_t * _Nonnull loggingDataPtr(const logging * _Nonnull log)
{
return log->data;
}
Now you can read the log data in the Swift code via the pointer:
struct ReadLog {
var log = logging()
func read() {
//...Do some stuff...
let char = log.dataPtr[0]
}
}
But be aware that there is no array bounds checking, you must ensure that log.dataPtr is only used with indices within the array size.
If you want to read and write to the logging structure from Swift then change the helper function to
// Pointer to the data elements:
__attribute__((swift_name("getter:logging.dataPtr(self:)")))
static inline uint8_t * _Nonnull loggingDataPtr(logging * _Nonnull log)
{
return log->data;
}

Unable to append string to mutable array in Swift

Context - I'm currently learning Swift Struct. So I decided to create my own Phone structure in Playground. (see below)
Problem -
The downloadApp method on the phone struct is throwing the following error.. Cannot use mutating member on immutable value: 'self' is immutable.
Expect outcome - To peacefully append new strings to my apps property, which is an array of strings.
Swift code
struct Phone {
let capacity : Int
let Brand : Sting
var name: String
let model: String
var apps: [String]
var contacts: [String]
var formatCapacity: String {
return "\(capacity)GB"
}
func downloadApp(name: String){
apps.append(name) // ERROR
}
}
You simply need to mark downloadApp as mutating. The issue is caused by the fact that downloadApp is declared in a struct type, which is a value type and hence if you mutate any properties (namely, the apps array here) of the struct, you actually mutate the struct itself. By marking the function as mutating, the compiler allows such modification of the struct through a member function of the type.
mutating func downloadApp(name: String){
apps.append(name)
}

Using UILocalizedIndexedCollation.section(for: collationStringSelector:) with array of structs

Assume I have an array of structs like so:
struct Record {
let name: String
}
let array = [Record(name: "John"), Record(name: "Jim"), Record(name: "Bob")]
I would like to get the index of each element using UILocalizedIndexedCollation.section(for: collationStringSelector:). The problem is, when I pass:
#selector(getter: record.name)
the following error is returned:
Argument of '#selector' refers to var 'name' that is not exposed to
Objective-C
Is there any way of exposing an instance value in a struct to a #selector? (NB: the struct I am passing is used extensively throughout my app and I don't really want to change it to a class)
Converting the struct variable to an NSString and using one of NSString's methods / variables is a work around that fixed the issue:
let index = UILocalizedIndexedCollation.current().section(for: (record.name as NSString), collationStringSelector: #selector(getter: NSString.uppercased))

Check empty struct that have array fields

I have a nested (not embedded) struct for which some of field types are arrays.
How can I check if an instance of this struct is empty? (not using iteration!!)
Note that there can't use StructIns == (Struct{}) or by an empty instance! This code have this error:
invalid operation: user == model.User literal (struct containing model.Configs cannot be compared)
user.Configs.TspConfigs:
type TspConfigs struct {
Flights []Flights `form:"flights" json:"flights"`
Tours []Tours `form:"tours" json:"tours"`
Insurances []Insurances`form:"insurances" json:"insurances"`
Hotels []Hotels `form:"hotels" json:"hotels"`
}
Those are slices, not arrays. It's important to emphasize as arrays are comparable but slices are not. See Spec: Comparision operators. And since slices are not comparable, structs composed of them (structs with fields having slice types) are also not comparable.
You may use reflect.DeepEqual() for this. Example:
type Foo struct {
A []int
B []string
}
f := Foo{}
fmt.Println("Zero:", reflect.DeepEqual(f, Foo{}))
f.A = []int{1}
fmt.Println("Zero:", reflect.DeepEqual(f, Foo{}))
Output (try it on the Go Playground):
Zero: true
Zero: false

Swift Array.filter method

Swift's filter method is defined as follows:
func filter(includeElement: (T) -> Bool) -> [T]
Why does filter definition in Swift's array does not have <T> in its definition (i.e. filter(...))?
filter is a method of the Array<T> class, so T is specified at class level and there is no need to replicate that in the method - actually doing that at method level is a mistake:
struct Array<T> ... {
func filter<V>(includeElement: (V) -> Bool) -> [V]
}
because V is a different type that has no relationship with T (unless you set constraints in the generic definition). By mistake I mean T and V are different types, whereas it could be thought they are the same. Having a generic class with a generic method is perfectly legit though.
it is because this function is defined in an extension to Array struct and the definition of Array has the in its definition : struct Array
class AA<T> {
}
extension AA {
func something(aThing: T) -> T{
return aThing
}
}

Resources