What should my Array+RemoveObject.Swift contain? - ios

I am using a template project to try to build a new app. When I run the template project, I get an error saying: '[T] does not have a member named 'indexOf'.
The existing code in the Array+RemoveObject.swift doc is:
import Foundation
public func removeObject<T: Equatable>(object: T, inout fromArray array: [T])
{ let index = array.indexOf(object)
if let index = index {
array.removeAtIndex(index)
}
}
Is the problem the use of indexOf? The odd thing is that when I tried using the solution of someone who answered a similar question, I got around 100 errors from the bond framework.

You function on its own works fine for me (Swift 2.1.1, Xcode 7.2). It seems as if you want this function to be a method of a public class of yours. For a minimum working example, you need at least to wrap your removeObject() method within the class you want it to belong to. Note also that you needn't use a separate line for assigning the result from .indexOf(..) call (possibly nil), but can add assignment and nil check in a single if let statement.
public class MyArrayOperations {
// ...
static public func removeObject<T: Equatable>(object: T, inout fromArray array: [T]) {
if let index = array.indexOf(object) {
array.removeAtIndex(index)
}
}
}
var arr = ["1", "2","3"]
MyArrayOperations.removeObject("2", fromArray: &arr)
print(arr) // ["1", "3"]
Also note that you can explicitly state the same behaviour using two generics, in case you want the array itself to conform to some protocol. You then use one separate generic for the array type and one for its elements, thereafter specifying that the element type should conform to the Generator.Element type of the array. E.g.:
func removeObject<T: Equatable, U: _ArrayType where U.Generator.Element == T>(object: T, inout fromArray array: U) {
if let index = array.indexOf(object) {
array.removeAtIndex(index)
}
}
With this approach, you could add an additional protocol at type constraint for the array generic U in the function signature above, e.g.
func removeObject<T: Equatable, U: protocol<_ArrayType, MyProtocol> where U.Generator.Element == T>(object: T, inout fromArray array: [T]) { ...
This can be especially useful when "simulating" generic Array extensions that conforms to some protocol. See e.g. the following for such an example:
extend generic Array<T> to adopt protocol

Related

Use of undeclared type 'Element' In Swift Extension Method (Array)

I am using Generics and protocol to make a name space for my Utils Class. and here when I goes to [Array] , I met some problem . here is the code :
in namespce.swift:
and in Array Extention:
can any one tell me how can i fix it?
Update:
I change my code to :
and here I have another problem , I have use "Self" in the function. but the T: Sequence type has no member of "index" .
Not sure what you are trying to achieve here (and if it's still relevant) but you should constrain your generic to RangeReplaceableCollection (to be able to use remove(at:)) instead of Sequence.
The firstIndex(of:) method (which returns the index of the first matching element) is available on Collection where Element: Equatable.
extension JX_TypeWrapper where T: RangeReplaceableCollection, T.Element: Equatable {
mutating func remove(object: T.Element) {
if let index = SELF.firstIndex(of: object) {
SELF.remove(at: index)
}
}
}
Which allows you to wrap an array into your JX_TypeWrapper:
var array: [Int] = [1, 2, 3]
var wrapped = JX_TypeWrapper(value: array)
wrapped.remove(object: 2)
wrapped.SELF // [1, 3]

Strange generics behavior in swift

Good day!
I am currently trying to learn programming swift and trying some examples of generics. Here is my example code
func findAll<T: Equatable>(arr: [T], _ elem: T) -> [Int] {
var indexesArr = [Int]()
var counter = 0
for i in arr {
if i == elem {
indexesArr.append(counter)
}
counter++
}
return indexesArr
}
findAll([5, 3, 7, 3, 9], 3)
findAll(["a", "b", "c", "b", "d"], 2.0)
It was supposed, that both arguments will be of the same type. But, unfortunately, as you can see I pass an array of string characters and a Double in my second function invocation and it still works! It returns an empty array []. No runtime or compile errors.
Please, explain me why it works and some workarounds maybe. Thank you!
Presumably you have import Cocoa or import Foundation in your code somewhere. If you remove that, you'll find that your code behaves as you're expecting.
The reason this happens is that importing Foundation causes Swift.Array to magically bridge to NSArray (aka [AnyObject]), and certain primitive value types to magically bridge to Foundation object types (like NSString and NSNumber). Once that happens, it's perfectly legal to call your function with parameters of types [AnyObject] and AnyObject.
I'm not aware of a good way around the magic conversion, but one way that works is to force your generic parameter to have a protocol that primitive value types conform to, but primitive values magically wrapped in Cocoa objects don't. One way to assure such is to create your own protocol:
public protocol NotAnyObject {}
extension Int: NotAnyObject {}
extension String: NotAnyObject {}
extension Double: NotAnyObject {}
func findAll<T: Equatable where T: NotAnyObject>(arr: [T], _ elem: T) -> [Int] { /*...*/ }
I'm not sure this is the best way, though... alternative proposals are welcome.
By the way, a more idiomatic/Swifty way to do something like this would be as a type extension (either a protocol extension, as below, or an extension on Array where Element: Equatable). You can even go functional-programming-style just for fun:
extension CollectionType where Generator.Element : Equatable {
public func allIndexesOf(element: Self.Generator.Element) -> [Self.Index] {
return zip(self.indices, self) // makes sequence of (index, element)
.filter { $0.1 == element }
.map { $0.0 }
}
}
Call it like this:
let threes = [5, 3, 7, 3, 9].allIndexesOf(3)
// returns [1, 3]
This is still subject to the same magic conversions issue, though:
let notThrees = ["twenty", "forty", "eight"].allIndexesOf[2.0]
// still compiles, returns []
So you'd still need to either use this extension only from Swift files that don't import Foundation, or apply a variant of the same hack as above, e.g.:
extension CollectionType where Generator.Element: Equatable,
Generator.Element: NotAnyObject { /*...*/ }
For the problem explanation see rickster's answer
Note that to solve problems like this, you can always just check how Swift libraries do it. The solution is to have both arguments as generics and using protocol constraints.
E.g. using SequenceType protocol instead of an Array.
func findAll<S: SequenceType, T: Equatable where S.Generator.Element == T>(arr: S, _ elem: T) -> [Int] {
var indexesArr = [Int]()
var counter = 0
for i in arr {
if i == elem {
indexesArr.append(counter)
}
counter++
}
return indexesArr
}

Swift how to "pass by value" of a object

I am quite new in Swift. And I create a class(for example):
class Fraction{
var a: Int
init(a:Int){
self.a = a
}
func toString() -> String{
return "\(self.a)"
}
}
and I also build a in other class function:
class func A_plusplus(f:Fraction){
f.a++
}
Then in the executive class I write:
var object = Fraction(a:10)
print("before run func = " + object.toString())
XXXclass.A_plusplus(object)
print("after ran func =" + object.toString() )
So the console output is
before run func = 10; after ran func =11
The question is how can I just send a copy of the "object" to keep its value which equal to 10
And if functions are always pass-by-reference, why we still need the keyword: "inout"
what does difference between A_plusplus(&object)//[if I make the parameter to be a inout parameter] and A_plusplus(object)
Universally, I don't want to use struct. Although this will solve my
problem exactly, I do pass-by-value rarely.So I don't want program's
copying processes slow my user's phone down :(
And It seems conforming the NSCopying protocol is a good option.But
I don't know how to implement the function:
func copyWithZone(zone:
NSZone)-> AnyObject? correctly
If your class is subclass of NSObject,better to use NSCopying
class Fraction:NSObject,NSCopying{
var a:Int
var b:NSString?
required init(a:Int){
self.a = a
}
func toString() -> String{
return "\(self.a)"
}
func copyWithZone(zone: NSZone) -> AnyObject {
let theCopy=self.dynamicType.init(a: self.a)
theCopy.b = self.b?.copy() as? NSString
return theCopy
}
}
class XXXclass{
class func A_plusplus(f:Fraction){
f.a++
f.b = "after"
}
}
var object = Fraction(a:10)
object.b = "before"
print("before run func = " + object.toString())
print(object.b!) //“Before”
XXXclass.A_plusplus(object.copy() as! Fraction)
print("after ran func =" + object.toString() )
print(object.b!)//“Before”
If it is just a common swift class,You have to create a copy method
class Fraction{
var a: Int
init(a:Int){
self.a = a
}
func toString() -> String{
return "\(self.a)"
}
func copy()->Fraction{
return Fraction(a: self.a)
}
}
class XXXclass{
class func A_plusplus(f:Fraction){
f.a++
}
}
var object = Fraction(a:10)
print("before run func = " + object.toString())
XXXclass.A_plusplus(object.copy())
print("after ran func =" + object.toString() )
To make it clear,you have to know that there are mainly two types in swift
Reference types. Like Class instance,function type
Value types,Like struct and others(Not class instance or function type)
If you pass in a Reference types,you pass in the copy of Reference,it still point to the original object.
If you pass in a Copy type,you pass in the copy of value,so it has nothing to do with the original value
Let us talk about inout,if you use it,it pass in the same object or value.It has effect on Value type
func add(inout input:Int){
input++
}
var a = 10
print(a)//10
add(&a)
print(a)//11
Swift has a new concept so called "struct"
You can define Fraction as struct (Not class)
And
struct Fraction{
...
}
var object = Fraction(a:10)
var object1 = object //then struct in swift is value type, so object1 is copy of object (not reference)
And if you use struct then try to use inout in A_plusplus function
Hope this will help you.
how can I just send a copy of the "object" to keep its value which equal to 10
In Swift classes and functions are always passed by reference. Structs, enums and primitive types are passed by value. See this answer.
You can't pass an object by value. You would have to manually copy it before passing it by reference (if that's what you really want).
Another way is to turn your class into a struct, since it would then be passed by value. However, keep in mind there a few other differences between classes and structs, and it might not necessarily be what you want.
And if functions are always pass-by-reference, why we still need the keyword: "inout"
According to the swift documentation, inout is used when
you want a function to modify a parameter’s value, and you want those changes to persist after the function call has ended, define that parameter as an in-out parameter instead.
So in practice with inout you can pass a value type (such as struct or primitive) by reference. You shouldn't really use this very often. Swift provides tuples, that could be used instead.
what does difference between A_plusplus(&object)//[if I make the parameter to be a inout parameter] and A_plusplus(object)
There is no difference for your A_plusplus function. In that function you don't modify the parameter f itself, you modify the f.a property.
The following example shows the effect of using inout when passing a class object. Both functions are the same, differing only in its parameter definition.
class Person {
var name: String
init(name: String) { self.name = name }
}
var me = Person(name: "Lennon") // Must be var to be passed as inout
// Normal object by reference with a var
func normalCall(var p: Person) {
// We sure are able to update p's properties,
// and they will be reflected back to me
p.name = "McCartney"
// Now p points to a new object different from me,
// changes won't be reflected back to me
p = Person(name: "Ringo")
}
// Inout object reference by value
func inoutCall(inout p: Person) {
// We still can update p's properties,
p.name = "McCartney"
// p is an alias to me, updates made will persist to me
p = Person(name: "Ringo")
}
print("\(me.name)") //--> Lennon
normalCall(me)
print("\(me.name)") //--> McCartney
inoutCall(&me)
print("\(me.name)") //--> Ringo
In normalCall p and me are different variables that happen to point to the same object. When you instantiate and assign a new object to p, they no longer refer to the same object. Hence, further changes to this new object will not be reflected back to me.
Stating that p is a var argument just means that its value can change throughout the function, it does not mean the new value will be assigned to what was passed as argument.
On the other hand, in inoutCall you can think of p and me as aliases. As such, assigning a new object to p is the exact same as assigning a new object to me. Any and every change to p is persisted in me after the function ends.

How to covert NSMutableOrderedSet to generic array?

I have this for loop, p is a NSManagedObject, fathers is a to-many relationship, so I need to cast NSMutableOrderedSet to [Family] but it does not work, why?
for f in p.fathers as [Family] {
}
You can obtain an array representation of the set via the array property - then you can downcast it to the proper type and assign to a variable:
let families = p.fathers.array as [Family]
but of course you can also use it directly in the loop:
for f in p.fathers.array as [Family] {
....
}
Update
A forced downcast is now required using the ! operator, so the code above should be:
let families = p.fathers.array as! [Family]
The simple solution by Antonio should be used in this case. I'd just like to discuss this a bit more. If we try to enumerate an instance of 'NSMutableOrderedSet' in a 'for' loop, the compiler will complain:
error: type 'NSMutableOrderedSet' does not conform to protocol
'SequenceType'
When we write
for element in sequence {
// do something with element
}
the compiler rewrites it into something like this:
var generator = sequence.generate()
while let element = generator.next() {
// do something with element
}
'NS(Mutable)OrderedSet' doesn't have 'generate()' method i.e. it doesn't conform to the 'SequenceType' protocol. We can change that. First we need a generator:
public struct OrderedSetGenerator : GeneratorType {
private let orderedSet: NSMutableOrderedSet
public init(orderedSet: NSOrderedSet) {
self.orderedSet = NSMutableOrderedSet(orderedSet: orderedSet)
}
mutating public func next() -> AnyObject? {
if orderedSet.count > 0 {
let first: AnyObject = orderedSet.objectAtIndex(0)
orderedSet.removeObjectAtIndex(0)
return first
} else {
return nil
}
}
}
Now we can use that generator to make 'NSOrderedSet' conform to 'SequenceType':
extension NSOrderedSet : SequenceType {
public func generate() -> OrderedSetGenerator {
return OrderedSetGenerator(orderedSet: self)
}
}
'NS(Mutable)OrderedSet' can now be used in a 'for' loop:
let sequence = NSMutableOrderedSet(array: [2, 4, 6])
for element in sequence {
println(element) // 2 4 6
}
We could further implement 'CollectionType' and 'MutableCollectionType' (the latter for 'NSMutableOrderedSet' only) to make 'NS(Mutable)OrderedSet' behave like Swift's standard library collections.
Not sure if the above code follows the best practises as I'm still trying to wrap my head around details of all these protocols.

How do I do indexOfObject or a proper containsObject

With an array: How do I do indexOfObject or a proper containsObject?
I mean I know I could just bridge the Array to NSArray and do it there ^^
But there must be a 'native' way of doing this
P.S. for the containsObject I guess I could filter the array too but for indexOf?
You can use the built-in find, and thus avoid bridging to Objective-C — but only if your element type is Equatable. (If it isn't Equatable, you can make it so with a comparison function and an extension.)
Example:
func == (lhs:Piece,rhs:Piece) -> Bool {
return lhs.val == rhs.val
}
class Piece:Equatable,Printable {
var val : Int
var description : String { return String(val) }
init (_ v:Int) {
val = v
}
}
Now you can call find(arr,p) where arr is an Array<Piece> and p is a Piece.
Once you have this, you can develop utilities based on it. For example, here's a global function to remove an object from an array without bridging to Objective-C:
func removeObject<T:Equatable>(inout arr:Array<T>, object:T) -> T? {
if let found = find(arr,object) {
return arr.removeAtIndex(found)
}
return nil
}
And test it like this:
var arr = [Piece(1), Piece(2), Piece(3)]
removeObject(&arr,Piece(2))
println(arr)
You can do this for NSObject subclasses too. Example:
func == (v1:UIView, v2:UIView) -> Bool {
return v1.isEqual(v2)
}
extension UIView : Equatable {}
Now you can call find on an Array of UIView. It's sort of a pain in the butt, though, having to do this for every single class where you want to be able to use find on an Array of that class. I have filed an enhancement request with Apple requesting that all NSObject subclasses be considered Equatable and that == should fall back on isEqual: automatically.
EDIT Starting in Seed 3, this is automatic for UIView and other NSObject classes. So find now just works for them.
EDIT 2 Starting in Swift 2.0, indexOf will exist as a method:
let s = ["Manny", "Moe", "Jack"]
let ix = s.indexOf("Moe") // 1
Alternatively, it takes a function that returns Bool:
let ix2 = s.indexOf {$0.hasPrefix("J")} // 2
Again, this works only on collections of Equatable, since obviously you cannot locate a needle in a haystack unless you have a way of identifying a needle when you come to it.
EDIT 3 Swift 2.0 also introduces protocol extensions. This means I can rewrite my global function removeObject as a method!
For example:
extension RangeReplaceableCollectionType where Generator.Element : Equatable {
mutating func removeObject(object:Self.Generator.Element) {
if let found = self.indexOf(object) {
self.removeAtIndex(found)
}
}
}
Since Array adopts RangeReplaceableCollectionType, now I can write code like this:
var arr = [Piece(1), Piece(2), Piece(3)]
arr.removeObject(Piece(2))
Oh, happy day!
Its actually able to be done in Swift. To get the index use find(YourArray, ObjectToFind)
As I was told, this isn't available yet / I have to bridge it to NSArray
I don't like this and it feels dirty so I went and did this in an extension. that way it hides the usage of NSArray and allows apple to provide it later
import Foundation
extension Array {
func contains(object:AnyObject!) -> Bool {
if(self.isEmpty) {
return false
}
let array: NSArray = self.bridgeToObjectiveC();
return array.containsObject(object)
}
func indexOf(object:AnyObject!) -> Int? {
var index = NSNotFound
if(!self.isEmpty) {
let array: NSArray = self.bridgeToObjectiveC();
index = array.indexOfObject(object)
}
if(index == NSNotFound) {
return Optional.None;
}
return index
}
//#pragma mark KVC
func getKeyPath(keyPath: String!) -> AnyObject! {
return self.bridgeToObjectiveC().valueForKeyPath(keyPath);
}
}
https://gist.github.com/Daij-Djan/9d1c4b1233b4017f3b67
Apple provide an example of exactly this in the The Swift Programming Language book. Specifically, see the section on Type Constraints in Action (p621 in the iBook).
func findIndex<T: Equatable>(array: [T], valueToFind: T) -> Int? {
for (index, value) in enumerate(array) {
if value == valueToFind {
return index
}
}
return nil
}
Everything depends upon your type implementing Equatable.
The Swift Programming Language covers that and explains how to implement that protocol:
“The Swift standard library defines a protocol called Equatable, which
requires any conforming type to implement the equal to operator (==)
and the not equal to operator (!=) to compare any two values of that
type. ”
NSHipster has a couple of relevant posts on this subject:
Swift Default Protocol Implementations
Swift Comparison Protocols
I also found this answer very useful in implementing Equatable:
How do I implement Swift's Comparable protocol?
Alhough it mentions Comparable, Equatable is a subset and the explanation is good.
Excerpts above from: Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/gb/jEUH0.l
One other option is to use filter:
haystack.filter({$0 == needle}).count > 0
This checks to see if array haystack contains object needle.
You can't. That's why NSArray is still there. However, the Apple documentation reads as follows about String and NSString:
Swift’s String type is bridged seamlessly to Foundation’s NSString
class. If you are working with the Foundation framework in Cocoa or
Cocoa Touch, the entire NSString API is available to call on any
String value you create, in addition to the String features described
in this chapter. You can also use a String value with any API that
requires an NSString instance.
Following that approach, the NSArray API should be available on Array, but it isn't because the native Swift Array is a primitive (most likely a struct or similar), so you have to "convert" it to an NSArray.
It appears that not all of the toll-free bridging from NS/CF space is in place. However, if you declare your array as an NSArray, it works fine:
let fruits: NSArray = [ "apple", "orange", "tomato (really?)" ]
let index = fruits.indexOfObject("orange")
println("Index of Orange: \(index)")
If your array elements are objects and you want to find an identical object in that array, you can use this function:
func findObject<C: CollectionType where C.Generator.Element: AnyObject>(domain: C, value: C.Generator.Element) -> Int? {
for (index, element) in enumerate(domain) {
if element === value {
return index
}
}
return nil
}

Resources