Enum with array of string - arrays

I'm a newbie in Swift and I found on the internet a utiliy class to handle errors when I use Objective C classes from swift. Here the utility class, which is a enum:
enum Result<A> {
case Success(Box<A>), Error(NSError)
static func success(v: A) -> Result<A> {
return .Success(Box(v))
}
static func error(e: NSError) -> Result<A> {
return .Error(e)
}
}
final class Box<A> {
let value: A
init(_ value: A) {
self.value = value
}
}
I have a function that returns an Array of strings of type result
func getFiles(account:DBAccount, curFolder:String) ->Result<[String]>{ ...}
Now how can I access the results in an easy way. A println on the console gives me (Enum Value).
I tried to get the results using the following:
let dicList = getFiles(account, currFolder)
switch dicList {
case let .Success(aBox): results = aBox.value
case let .Error(err): results = []
}
now the array results contains the data, but is there no easier way to access the results.
thanks
arnold

Related

Why does the Swift Compiler throw an error on inferring the type of my Generic Element when the Element is clearly constrained?

As I was working with implementing an Array that keeps weak references to its elements I stumbled on a Compile Error as soon as I used the methods of the Collection extension methods, before using the Collection method the code compiled correctly and expectedly.
Expected Behavior
The code should compile with no error.
Current Behavior
The compiler throws the two following Errors:
WeakRef requires that Element? be a class type
Could not infer type for 'items'
Possible Solution
The only solution I found is to make the property items public and use a for-loop instead of the Collection extensions methods. After doing this the compiler was able to infer the type for items and even the Collection methods worked.
Steps to Reproduce
First implement the WeakRef class:
final class WeakRef<T: AnyObject> {
weak var value: T?
init(_ value: T) {
self.value = value
}
}
Second implement the WeakArray struct:
struct WeakArray<Element: AnyObject> {
public var items: [WeakRef<Element>] = []
init(_ elements: [Element]) {
items = elements.map { WeakRef($0) }
}
}
Third implement the Collection extension implementation:
extension WeakArray: Collection {
var startIndex: Int { return items.startIndex }
var endIndex: Int { return items.endIndex }
subscript(_ index: Int) -> Element? {
return items[index].value
}
func index(after idx: Int) -> Int {
return items.index(after: idx)
}
}
Fourth create an instance of the WeakArray property not in the same source file however as WeakArray for example:
var objects: WeakArray<UIViewController> = WeakArray.init([])
Fifth and final step call a method of the Collection protocol for example:
objects.forEach({ $0?.view.backgroundColor = .white })
Context (Environment)
This code won't compile on the version of Xcode Version 9.3.1 (9E501) using Swift 4.1
Additional Description
The solution for the above code was found in the following links:
https://marcosantadev.com/swift-arrays-holding-elements-weak-references/
https://www.objc.io/blog/2017/12/28/weak-arrays/
Thank you in advance to any help provided. This post was completely edited to fit Stackoverflow standards of asking a question. Special thanks to MartinR for guiding me to post a good question on Stackoverflow.
A Collection has an associated Element type, and that seems to
conflict with your generic Element placeholder. Using a different
name E for the placeholder solves the problem:
struct WeakArray<E: AnyObject> {
public var items: [WeakRef<E>] = []
init(_ elements: [E]) {
items = elements.map { WeakRef($0) }
}
}
extension WeakArray: Collection {
var startIndex: Int { return items.startIndex }
var endIndex: Int { return items.endIndex }
subscript(_ index: Int) -> E? {
return items[index].value
}
func index(after idx: Int) -> Int {
return items.index(after: idx)
}
}

Swift - Shuffling a filtered array of structs doesn't change originating array

I'm writing a card game with a number of cards collected together in one array.
The data structure of the cards is the same, but the data is different.
Note: My shuffle does work.
I'm wanting to filter this array, and only shuffle the cards I have filtered.
However, whilst I can shuffle the cards, I've noticed that my originating array of cards does not change at all.
I believe that this issue is caused because my card model is a struct and not a class.
More background info.
Whilst the data for each card is different, the structure is exactly the same; both types have a name, and a numeric value.
This is modelled thusly;
enum FSCardType: Int {
case property
case anotherType
case yetAnotherType
}
// Card Model
struct FSCard {
let type: FSCardType
let name: String
let value: Int
var description: String {
return ("Name: \(self.name) Value: \(self.value), Type: \(self.type)")
}
}
I create my cards in a static function like this:
class FSCardAPI: NSObject {
public static func createCards() -> [FSCard] {
var cards:[FSCard] = [FSCard]()
let properties:[FSCard] = FSCardAPI.createProperties()
cards.append(contentsOf: properties)
return cards
}
public static func shuffleCards(cards:[FSCard]) -> [FSCard] {
let shuffled:[FSCard] = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: cards) as! [FSCard]
return shuffled
}
fileprivate static func createProperties() -> [FSCard] {
var properties:[FSCard] = [FSCard]()
for idx in 1...30 {
let property:FSCard = FSCard(type: .property, name: "Property #\(idx)", value: idx, owner: nil)
properties.append(property)
}
return properties
}
}
Okay, so now I only want to shuffle my property cards within this cards array.
So in my XCTest file first filter all cards that are of type: property
func testShuffleProperties() {
var propertyCards = gameModel.cards.filter { (fs:FSCard) -> Bool in
return (fs.type == .property)
}
propertyCards = FSCardAPI.shuffleCards(cards: propertyCards)
print(propertyCards)
print(" ---- ")
print(gameModel.cards)
}
This calls:
// Inside my FSCardAPI
public static func shuffleCards(cards:[FSCard]) -> [FSCard] {
let shuffled:[FSCard] = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: cards) as! [FSCard]
return shuffled
}
The issue:
When I run my XCTest, I notice that whilst the propertyCards is shuffled, my gameModel.cards are not shuffled;
Test Case 'testShuffleProperties' started.
// contents of `propertyCards`
[FSCard(type: FSCardType.property, name: "Property #4", value: 4, owner: nil),
FSCard(type: FSCardType.property, name: "Property #16", value: 16, owner: nil),
// .. etc
// contents of `gameModel.cards`
[FSCard(type: FSCardType.property, name: "Property #1", value: 1, owner: nil),
FSCard(type: FSCardType.property, name: "Property #2", value: 2, owner: nil),
// .. etc
Summary
I have an array of cards (ie: 30 cards)
Cards are separated by types (ie: property)
I want to filter the property cards and shuffle those cards only
I want the original array to reflect these changes
In my test, the array gets shuffled; but the original array remains the same.
One way I can think of is that I do all the shuffling, and then sort the array by card types and then update the gameModel.cards, but that seems a bit over the top.
Another obvious way I can think about solving this is to change my struct to a class or; perhaps I need another layer in between the struct?
So my query is:
How do I filter an array of structs, only shuffle those results and change the state of the originating array?
Many thanks
Edit:
The properties array is the only item to shuffle.
If I add another array to the list it should not shuffle the entire contents.
IE; if I do this:
public static func createCards() -> [FSCard] {
var cards:[FSCard] = [FSCard]()
let properties:[FSCard] = FSCardAPI.createProperties()
let cheqs:[FSCard] = FSCardAPI.createCheques()
cards.append(contentsOf: properties)
cards.append(contentsOf: cheqs)
return cards
}
I should only shuffle the property cards within themselves without impacting the cheqs.
I guess I could make it easier and just have 2 arrays, but at the same time I think there is no reason to do this because the data structure is the same.
You are not assigning to gameModel.cards and not actually changing the array. so I would not expect gameModel.cards to be shuffled.
You should either just assign the shuffled array back to gameModel.cards like so:
func testShuffleProperties() {
var propertyCards = gameModel.cards.filter { (fs:FSCard) -> Bool in
return (fs.type == .property)
}
propertyCards = FSCardAPI.shuffleCards(cards: propertyCards)
gameModel.cards = propertyCards
print(propertyCards)
print(" ---- ")
print(gameModel.cards)
}
Or if you want to mutate the array directly you should look at passing the cards by reference, or in Swift.. using an inout parameter. An inout parameter passes the value to the function by reference, or passed the memory addres of the value, so that it can be modified directly. Check the shuffle cards function below (how it is defined and used)
(I replaced the shuffle function with a swift extension for ease of use in a playground)
extension MutableCollection where Indices.Iterator.Element == Index {
/// Shuffles the contents of this collection.
mutating func shuffle() {
let c = count
guard c > 1 else { return }
for (firstUnshuffled , unshuffledCount) in zip(indices, stride(from: c, to: 1, by: -1)) {
let d: IndexDistance = numericCast(arc4random_uniform(numericCast(unshuffledCount)))
guard d != 0 else { continue }
let i = index(firstUnshuffled, offsetBy: d)
swap(&self[firstUnshuffled], &self[i])
}
}
}
extension Sequence {
/// Returns an array with the contents of this sequence, shuffled.
func shuffled() -> [Iterator.Element] {
var result = Array(self)
result.shuffle()
return result
}
}
enum FSCardType: Int {
case property
case anotherType
case yetAnotherType
}
// Card Model
struct FSCard {
let type: FSCardType
let name: String
let value: Int
var description: String {
return ("Name: \(self.name) Value: \(self.value), Type: \(self.type)")
}
}
class FSCardAPI: NSObject {
public static func createCards() -> [FSCard] {
var cards:[FSCard] = [FSCard]()
let properties:[FSCard] = FSCardAPI.createProperties()
cards.append(contentsOf: properties)
return cards
}
public static func shuffleCards(cards:inout [FSCard]) {
cards = cards.shuffled()
}
fileprivate static func createProperties() -> [FSCard] {
var properties:[FSCard] = [FSCard]()
for idx in 1...30 {
let property:FSCard = FSCard(type: .property, name: "Property #\(idx)", value: idx)
properties.append(property)
}
return properties
}
}
var cards = FSCardAPI.createCards()
FSCardAPI.shuffleCards(cards: &cards)
Output:
Read up on inout parameters here in the In-Out parameters section.
Exert from the documentation:
Function parameters are constants by default. Trying to change the value of a function parameter from within the body of that function results in a compile-time error. This means that you can’t change the value of a parameter by mistake. If 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.
You write an in-out parameter by placing the inout keyword right before a parameter’s type. An in-out parameter has a value that is passed in to the function, is modified by the function, and is passed back out of the function to replace the original value. For a detailed discussion of the behavior of in-out parameters and associated compiler optimizations, see In-Out Parameters.
You can only pass a variable as the argument for an in-out parameter. You cannot pass a constant or a literal value as the argument, because constants and literals cannot be modified. You place an ampersand (&) directly before a variable’s name when you pass it as an argument to an in-out parameter, to indicate that it can be modified by the function.
EDIT: Try this updated shuffle function, pass in the whole array and see if does what you need.
public static func shuffleCards(cards:inout [FSCard]) {
let propertyCards = cards.filter({ $0.type == .property })
let indexes = propertyCards.map { Int(cards.index(of: $0)!) }
let shuffledCards = propertyCards.shuffled()
for (idx, val) in indexes.enumerated() {
cards[val] = shuffledCards[idx]
}
}
The problem is that after you shuffle your property cards, you are not doing anything with them. You should replace property cards in your original card list with the ones in your shuffled property card list.
var propertyCardsShuffledOriginalArray = originalArray.map {
var card = $0
if $0.type == .property {
card = shuffledPropertyCards.first as! FSCard
shuffledPropertyCards.removeFirst()
}
return card
}
propertyCardsShuffledOriginalArray is what you need

Cast to right generic from array in Swift

I have a Protocol called Composite.
This protocol has an array composites: [Composite]
I also have a generic subclass GenericSubclass<T>: Composite
When iterating over the array the best I can come up with looks like this:
for item in composites {
if let item = item as? GenericSubclass<A> {
let sc = SomeOtherClass<A>
} else if let item = item as? GenericSubclass<B> {
let sc = SomeOtherClass<B>
} //and so on...
}
Is there any way to get a hold of GenericSubclass without specifying the Generic? In my use case there is absolutely no need for me to know about the T. I just have to instantiate another class with the same generic type.
Any help is much appreciated.
It's not clear what you're trying to accomplish with the "generic" (pun intended) class names you've chosen. I don't think there's a way to directly accomplish what you want. I.e. you can't just leave it as a generic T because the compiler needs some way to determine what T will be in use at runtime.
However, one way to solve the issue is to hoist the API into the Composite protocol:
protocol Composite {
var composites: [Composite] { get set }
func otherClass() -> OtherProtocol
}
protocol OtherProtocol { }
class GenericSubclass<T>: Composite {
var composites: [Composite] = []
func otherClass() -> OtherProtocol {
return SomeOtherClass<T>()
}
}
class SomeOtherClass<T>: OtherProtocol {}
So now when you implement your loop, you can rely on the fact that since each element is a Composite, you know it must provide an instance of OtherProtocol via the otherClass() method:
var c = GenericSubclass<Int>()
c.composites = [GenericSubclass<Double>(), GenericSubclass<Int>(), GenericSubclass<Character>()]
for item in c.composites {
let sc = item.otherClass()
print(sc)
}
Alternatively, if only GenericSubclass should vend an OtherProtocol, you can make the return type Optional and define an extension for all the other implementations of Composite:
protocol Composite {
var composites: [Composite] { get set }
func optionalClass() -> OtherProtocol?
}
extension Composite {
func optionalClass() -> OtherProtocol? {
return nil
}
}
I did some experiment on this in the playground and i came up with this
protocol Composite {
var composites: [Composite] { get set }
}
class GenericSubclass<T>: Composite {
var composites: [Composite] = []
}
let subclass = GenericSubclass<String>()
for item in subclass.composites {
let className = String(describing: type(of: item))
let aClassType = NSClassFromString(className) as! NSObject.Type
let instance = aClassType.init() // we create a new object
print(instance) //Output: GenericSubclass<String>
}
Hope this will help someone.
I think it's not possible to do that in array.
While you creat some different GenericSubclass<T> then put it in array , you will lose <T> no matter the composites is [Composite] or [Any].
// this line won't compile
let array = [GenericSubclass<Int>(),GenericSubclass<Double>()]
//error: heterogenous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional
You want donging something like this func below, the param should be GenericSubclass<T> to compile success
func genericFunc<T>(param:GenericSubclass<T>) {
let sc = SomeOtherClass<T>()
print(sc)
}
Anyway you can implement it with member var for the instance like the code below:
class Subclass {
var type : Any
init(type : Any) {
self.type = type
}
}
class SomeOtherClass : CustomDebugStringConvertible{
var type : Any
init(type : Any) {
self.type = type
}
var debugDescription: String{
return String(describing: type.self)
}
}
let array : [Subclass] = [Subclass(type : Int.self),Subclass(type : Double.self),Subclass(type : String.self)]
let scArray = array.flatMap {SomeOtherClass(type:$0.type.self)}
print(scArray) // prints [Int, Double, String]
You need to add one method to protocol which creates new item of Type supported this protocol. So now you can use enums, structs and classes without any knowledge of creating object of specific type.
You can play in playground with the following code:
import UIKit
//This is your protocol
protocol MyAwesomeProtocol {
//this methods leaves implementaion detailes
//to concrete type
func createNewObject()->MyAwesomeProtocol
}
//Just create empty string
extension String: MyAwesomeProtocol {
func createNewObject() -> MyAwesomeProtocol {
return String()
}
}
//create Enum with default value
extension UIControlState: MyAwesomeProtocol {
func createNewObject() -> MyAwesomeProtocol {
return UIControlState.normal
}
}
//create viewController of any type
extension UIViewController: MyAwesomeProtocol {
func createNewObject() -> MyAwesomeProtocol {
return type(of:self).init()
}
}
//This is test function
//it creates array of newly created items and prints them out
//in terminal
func doSomeCoolStuffWith(items:[MyAwesomeProtocol]){
var newItems = [MyAwesomeProtocol]()
for anItem in items {
let newOne = anItem.createNewObject()
newItems.append(newOne)
}
print("created new ones:\n\(newItems)\nfrom old ones:\n\(items)\n")
}
doSomeCoolStuffWith(items: [UIControlState.focused,UIControlState.disabled])
doSomeCoolStuffWith(items: [UISplitViewController(),UINavigationController(),UICollectionViewController()])
doSomeCoolStuffWith(items: ["I","love","swift"])
This will produce the following result:
created new ones:
[__C.UIControlState(rawValue: 0), __C.UIControlState(rawValue: 0)]
from old ones:
[__C.UIControlState(rawValue: 8), __C.UIControlState(rawValue: 2)]
created new ones:
[<UISplitViewController: 0x7fa8ee7092d0>, <UINavigationController: 0x7fa8f0044a00>, <UICollectionViewController: 0x7fa8ee705f30>]
from old ones:
[<UISplitViewController: 0x7fa8ee7011e0>, <UINavigationController: 0x7fa8f004e600>, <UICollectionViewController: 0x7fa8ee708fb0>]
created new ones:
["", "", ""]
from old ones:
["I", "love", "swift"]

How to check if an object is in array

Edit: The problem is already solved by #vacawama. But if you are looking for an answer for NSObject classes, you should implement isEqual function which is NSObjectProtocol. Otherwise you gonna get an error says: " Redundant conformance of 'classname' to protocol 'Equatable' "
You can check this for details: Swift 2.2, Contains Method not working
In swift, how can i check if an object is in array?
I have a simple class like this;
class Test: {
private var _number: Int!
private var _type: String!
var number: Int {
return _number
}
var type: String {
return _type
}
init (number: Int, type: String) {
self._number = number
self._type = type
}
}
Also i have this class;
class TestRandom {
private let _numberArr: [Int] = [1,2,3,4,5,6,7,8,9,10]
private let _typeArr: [String] = ["x","y","z"]
public private(set) var _testArr: [Test] = []
private var _randomTest: Test!
func randomTestPicker () {
repeat {
let randomNumber = Int(arc4random_uniform(UInt32(self._numberArr.count)))
let randomType = Int(arc4random_uniform(UInt32(self._typeArr.count)))
self._randomTest = Test(number: self._numberArr[randomNumber], type: self._typeArr[randomType])
} while self._testArr.contains(_randomTest)
}
}
All i want to do is to pick different objects. Lets say i have x2,y4,x6,z3,z8,y2 in _testArr. When i call randomTestPicker, it should not pick x2 or z8. Because they are already in array.
I have tried contains as you see. However it did not work for me. Is there any solution that i can use for this purpose? Or what is the best way to do this?
Edit: I tried self._testArr.contains{$0 === _randomTest} but not working neither.
You can't use contains that way since your class doesn't conform to the Equatable protocol.
Add :Equatable to your class definition and implement the == function which compares two of your objects:
class Test: Equatable {
private var _number: Int!
private var _type: String!
var number: Int {
return _number
}
var type: String {
return _type
}
init (number: Int, type: String) {
self._number = number
self._type = type
}
}
func ==(lhs: Test, rhs: Test) -> Bool {
return lhs.number == rhs.number && lhs.type == rhs.type
}
The other way this could have been done is to use the predicate form of contains. The predicate takes two objects and returns a Bool indicating if they match. In that case, you would write:
self._testArr.contains { $0.number == _randomTest.number && $0.type == _randomTest.type }
As you can see, in this case the closure is essentially the == function from above, so implementing the Equatable protocol is the cleaner way to do it.
The closure { $0 === _randomTest } doesn't work because that only tests if the objects are the same instance. In your case, you need to check if the two objects have the same properties, and you are not interested if they are same instance. The way you are creating the objects, you never would create an instance that is already in the array, so this check would always return false.

Store generic Arrays in NSUserDefaults

I'm trying to store a generic Array in NSUserDefaults but I get the following error: Cannot convert value of type 'Array<T>' to expected argument type 'AnyObject?'.
How can I solve this problem?
public class PropertyStore {
private let userDefaults = NSUserDefaults.standardUserDefaults()
public func loadSet<T>(key: String) -> Set<T>? {
guard let array = userDefaults.objectForKey(key) as? [T] else {
return nil
}
return Set<T>(array)
}
public func saveSet<T>(key: String, value: Set<T>) {
let array = Array(value)
userDefaults.setObject(array, forKey: key) // <- ERROR
}
}
Like #lucasD said, T needs to conform to NSCoding atleast. So the code looks like this.
public func saveSet<T: NSCoding>(key: String, value: Set<T>) {
let array = Array(value)
userDefaults.setObject(array, forKey: key)
}
However, this will not work for many reasons like:
public func loadSet<T: NSCoding>(key: String) -> Set<T>? {
guard let array = userDefaults.objectForKey(key) as? [T] else {
return nil
}
return Set<T>(array)
}
let defaults = PropertyStore()
defaults.saveSet("array", value: [1,2,3])
///defaults.loadSet<Int>("array") ===> Cannot explicitly specialize a function
///defaults.loadSet("array") ===> Cannot infer Type T
//let a: Set<Int>? = defaults.loadSet("array") ==> T cannot be inferred
In the case of loadSet type T cannot be inferred properly because we cannot specify it from outside, as far as i know. I would first try to return NSObject or Set<AnyObject> or Set<NSCoding> and then type cast it explicitly. Let me know if theres a better way though.
You can take a look at this SO post for more information on why a generics parameter cannot be specialised from outside. SO Generics specialization
You should define T as NSCoding conforming class. So when you're going to store the array, you are going to store an array of NSKeyedArchiver.archiveDataWithRootObject() results. Then, to return a Set<T> in the loadSet method, you should unarchive all the objects:
let storedData = NSUserDefaults.standardUserDefaults. ...
return storedData.flatMap { return NSKeyedUnarchiver.unarchiveObjectWithData($0) }

Resources