If I have a struct defined like this:
struct Cat {
static let Siamese = "Siamese"
static let Tabby = "Tabby"
static let Fluffy = "Fluffy"
static func cat () -> [String] {
return [Siamese, Tabby, Fluffy]
}
}
Why can't I access it like this?
var cat:Cat = Cat.Siamese //"NSString" is not a subtype of Cat
You are trying to assign a String to a variable defined as a Cat. That is why you are getting an error.
All of your static members in your Cat struct are strings, not Cats.
Also, your struct doesn't have any actual members. I think you are intending to have a name property:
struct Cat {
let name: String
static let Siamese = Cat(name: "Siamese")
static let Tabby = Cat(name: "Tabby")
static let Fluffy = Cat(name: "Fluffy")
}
var cat : Cat = Cat.Siamese
You may be better served with an enum:
enum Cat : String {
case Siamese = "Siamese"
case Tabby = "Tabby"
case Fluffy = "Fluffy"
}
var cat: Cat = .Tabby
println(cat.toRaw()) // "Tabby"
Related
I want to switch between 2 image-sources.
I have the following struct:
struct AppConstants {
static var imageSource:String = "goral"
static var fixURL = URL(string: "https://picsum.photos/375/375/?random")
struct Domains {
static let baseurl = "http://www.m.myapp2go.de/services/"
}...
In the following code in my viewController and i can not access the var "AppConstants.fixURL" in the switch statement:
switch AppConstants.imageSource { // is filled with a value of a picker
case "goral":
let randomInt = Int.random(in: 1...12)
let AppConstants.fixURL = URL(string: "http://www.m.myapp2go.de/pics/\(randomInt)_goral_item.jpg")
case "picsum":
let randomInt = Int.random(in: 1...1080)
let AppConstants.fixURL = URL(string: "https://picsum.photos/375/375/?\(randomInt)")
default:
print("other")
}
With the error-message:
Reference to member 'fixURL' cannot be resolved without a contextual type
and
Type annotation missing in pattern
I want to check a value is there in section object.This code is working fine but if I write the complete name only it will get in to the filtered object.i need to get the filtered data when the search test matches with a substring in the string array
["A": ["Affenpoo", "Affenpug", "Affenshire", "Affenwich", "Afghan Collie", "Afghan Hound"], "B": ["Bagle Hound", "Boxer"]]
struct Objects {
var sectionName : String!
var sectionObjects : [String]
var sectionid:[String]!
var sectionph:[String]!
var sectionImage:[String]!
}
var objectArray = [Objects]()
var objectArrayFilter = [Objects]()
objectArrayFilter = objectArray.filter({$0.sectionObjects.contains(searchBar.text!)})
If you want filter like that if you enter string afg in UITextField then it should return only two object "Afghan Collie", "Afghan Hound" with section A, then you can make it like this.
objectArrayFilter = objectArray.flatMap {
var filterObjects = $0
filterObjects.sectionObjects = $0.sectionObjects.filter {
$0.range(of : searchBar.text!, options: .caseInsensitive) != nil
}
return filterObjects.sectionObjects.isEmpty ? nil : filterObjects
}
Edit: Struct that you have make is not proper what you need to do is make another struct and make with property object,id,ph and image all type of string and then make array of this struct inside your Object struct.
struct SubObjects {
var sectionObject: String!
var sectionid: String!
var sectionph: String!
var sectionImage: String!
}
struct Objects {
var sectionName : String!
var sectionObjects : [SubObjects]!
}
Now filter this way.
var objectArray = [Objects]()
var objectArrayFilter = [Objects]()
objectArrayFilter = objectArray.flatMap {
var filterObjects = $0
filterObjects.sectionObjects = $0.sectionObjects.filter {
$0.sectionObject.range(of : searchBar.text!, options: .caseInsensitive) != nil
}
return filterObjects.sectionObjects.isEmpty ? nil : filterObjects
}
Please try the following :
objectArrayFilter = objectArray.filter { $0.sectionObjects.contains(where: { $0.contains(searchBar.text!) }) }
Let's say I have the following struct in Swift:
struct Data {
let old: Double
let new: Double
}
Now I have a class with an array of Data structs:
class MyClass {
var myDataArray: [Data]
}
Now let's say I want to calculate the average of either the old or the new values:
func calculateAverage(oldOrNew: String) -> Double {
var total = 0.0
count = 0
for data in myDataArray {
total += data.oldOrNew
count++
}
return total / Double(count)
}
And then:
let oldAverage = calculateAverage("old")
let newAverage = calculateAverage("new")
But this obviously doesn't work, since oldOrNew is not a member of my struct.
How can I access old or new from "old" or "new" ?
What about this "reflection-less" solution?
struct Data {
let old: Double
let new: Double
func valueByPropertyName(name:String) -> Double {
switch name {
case "old": return old
case "new": return new
default: fatalError("Wrong property name")
}
}
}
Now you can do this
let data = Data(old: 0, new: 1)
data.valueByPropertyName("old") // 0
data.valueByPropertyName("new") // 1
You're looking for key-value-coding (KVC) that is accessing properties by key (path).
Short answer: A struct does not support KVC.
If the struct is not mandatory in your design use a subclass of NSObject there you get KVC and even operators like #avg for free.
class MyData : NSObject {
#objc let old, new: Double
init(old:Double, new:Double) {
self.old = old
self.new = new
}
}
let myDataArray : NSArray = [MyData(old: 1, new: 3), MyData(old:5, new: 9), MyData(old: 12, new: 66)]
let averageOld = myDataArray.value(forKeyPath:"#avg.old")
let averageNew = myDataArray.value(forKeyPath: "#avg.new")
Edit: In Swift 4 a struct does support Swift KVC but the operator #avg is not available
You wouldn't access a struct property by name in Swift any more than you would in C++. You'd provide a block.
Extemporaneous:
func calculateAverage(getter: (Data) -> Double) {
... total += getter(data) ...
}
...
calculateAverage({$0.old})
calculateAverage({$0.new})
Possibly with average {$0.old} being a more natural syntax — the verb isn't really helpful and if you're asserting what it is, not what the computer should do, then omitting the brackets looks fine.
I would like to replace my global string constants with a nested enum for the keys I'm using to access columns in a database.
The structure is as follows:
enum DatabaseKeys {
enum User: String {
case Table = "User"
case Username = "username"
...
}
...
}
Each table in the database is an inner enum, with the name of the table being the enum's title. The first case in each enum will be the name of the table, and the following cases are the columns in its table.
To use this, it's pretty simple:
myUser[DatabaseKeys.User.Username.rawValue] = "Johnny"
But I will be using these enums a lot. Having to append .rawValue to every instance will be a pain, and it's not as readable as I'd like it to be. How can I access the String value without having to use rawValue? It'd be great if I can do this:
myUser[DatabaseKeys.User.Username] = "Johnny"
Note that I'm using Swift 2. If there's an even better way to accomplish this I'd love to hear it!
While I didn't find a way to do this using the desired syntax with enums, this is possible using structs.
struct DatabaseKeys {
struct User {
static let identifier = "User"
static let Username = "username"
}
}
To use:
myUser[DatabaseKeys.User.Username] = "Johnny"
Apple uses structs like this for storyboard and row type identifiers in the WatchKit templates.
You can use CustomStringConvertible protocol for this.
From documentation,
String(instance) will work for an instance of any type, returning its
description if the instance happens to be CustomStringConvertible.
Using CustomStringConvertible as a generic constraint, or accessing a
conforming type's description directly, is therefore discouraged.
So, if you conform to this protocol and return your rawValue through the description method, you will be able to use String(Table.User) to get the value.
enum User: String, CustomStringConvertible {
case Table = "User"
case Username = "username"
var description: String {
return self.rawValue
}
}
var myUser = [String: String]()
myUser[String(DatabaseKeys.User.Username)] = "Johnny"
print(myUser) // ["username": "Johnny"]
You can use callAsFunction (New in Swift 5.2) on your enum that conforms to String.
enum KeychainKey: String {
case userId
case email
}
func callAsFunction() -> String {
return self.rawValue
}
usage:
KeychainKey.userId()
You can do this with custom class:
enum Names: String {
case something, thing
}
class CustomData {
subscript(key: Names) -> Any? {
get {
return self.customData[key.rawValue]
}
set(newValue) {
self.customData[key.rawValue] = newValue
}
}
private var customData = [String: Any]()
}
...
let cData = CustomData()
cData[Names.thing] = 56
Edit:
I found an another solution, that working with Swift 3:
enum CustomKey: String {
case one, two, three
}
extension Dictionary where Key: ExpressibleByStringLiteral {
subscript(key: CustomKey) -> Value? {
get {
return self[key.rawValue as! Key]
}
set {
self[key.rawValue as! Key] = newValue
}
}
}
var dict: [String: Any] = [:]
dict[CustomKey.one] = 1
dict["two"] = true
dict[.three] = 3
print(dict["one"]!)
print(dict[CustomKey.two]!)
print(dict[.three]!)
If you are able to use User as dictionary key instead of String (User is Hashable by default) it would be a solution.
If not you should use yours with a nested struct and static variables/constants.
I am confused on how I can have two keys as strings and one works and the other doesn't. Error occurs in the line near the end:
println("Here's a (car.year) (car.make) (car.model)")
What is it about the "make" variable that could be causing the problem?
protocol NSCoding {
}
class Car:NSObject {
var year: Int = 0
var make: String = ""
var model: String = ""
override init() {
super.init()
}
func encodeWithCoder(aCoder: NSCoder!) {
aCoder.encodeInteger(year, forKey:"year")
aCoder.encodeObject(make, forKey:"make")
aCoder.encodeObject(model, forKey:"model")
}
init(coder aDecoder: NSCoder!) {
super.init()
year = aDecoder.decodeIntegerForKey("year")
make = aDecoder.decodeObjectForKey("make") as String
model = aDecoder.decodeObjectForKey("model") as String
}
}
class CarData {
func archiveData () {
var documentDirectories:NSArray
var documentDirectory:String
var path:String
var unarchivedCars:NSArray
var allCars:NSArray
// Create a filepath for archiving.
documentDirectories = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
// Get document directory from that list
documentDirectory = documentDirectories.objectAtIndex(0) as String
// append with the .archive file name
path = documentDirectory.stringByAppendingPathComponent("swift_archiver_demo.archive")
var car1:Car! = Car()
var car2:Car! = Car()
var car3:Car! = Car()
car1.year = 1957
car1.make = "Chevrolet"
car1.model = "Bel Air"
car2.year = 1964
car2.make = "Dodge"
car2.model = "Polara"
car3.year = 1972
car3.make = "Plymouth"
car3.model = "Fury"
allCars = [car1, car2, car3]
// The 'archiveRootObject:toFile' returns a bool indicating
// whether or not the operation was successful. We can use that to log a message.
if NSKeyedArchiver.archiveRootObject(allCars, toFile: path) {
println("Success writing to file!")
} else {
println("Unable to write to file!")
}
// Now lets unarchive the data and put it into a different array to verify
// that this all works. Unarchive the objects and put them in a new array
unarchivedCars = NSKeyedUnarchiver.unarchiveObjectWithFile(path) as NSArray
// Output the new array
for car : AnyObject in unarchivedCars {
println("Here's a \(car.year) \(car.make) \(car.model)")
}
}
}
Use downcasting in your for loop. The compiler needs to know that car is of type Car and not just AnyObject.
for car in cars as [Car!] {
println("Here's a \(car.year) \(car.make) \(car.model)")
}