I have been trying to fix all my code since swift 2.0 update. I have a problem that seems to be the way tuples work now:
public func generate() -> AnyGenerator <(String, JSON)> {
switch self.type {
case .Array:
let array_ = object as! [AnyObject]
var generate_ = array_.generate()
var index_: Int = 0
return anyGenerator{
if let element_: AnyObject = generate_.next() {
return ("\(index_++)", JSON(element_))
} else {
return nil
}
}
case .Dictionary:
let dictionary_ = object as! [String : AnyObject]
var generate_ = dictionary_.generate()
return anyGenerator{
if let (key_: String, value_: AnyObject) = generate_.next() {
return (key_, JSON(value_))
} else {
return nil
}
}
default:
return anyGenerator{
return nil
}
}
}
Specifically the line:
if let (key_: String, value_: AnyObject) = generate_.next()
Is throwing the error: Tuple pattern element label 'key' must be '_'
I tried to make that change already, but I didnt work...
Any ideas?
The problem is: We cannot use type annotation inside of tuple patterns anymore.
In the release notes:
Type annotations are no longer allowed in patterns and are considered part of the outlying declaration. This means that code previously written as:
var (a : Int, b : Float) = foo()
needs to be written as:
var (a,b) : (Int, Float) = foo()
if an explicit type annotation is needed. The former syntax was ambiguous with tuple element labels. (20167393)
So, you can:
if let (key_, value_): (String, AnyObject) = generate_.next() {
But in this case, you could omit : (String, AnyObject):
if let (key_, value_) = generate_.next() {
Related
I have a method that loads an array of dictionaries from a propertylist. Then I change those arrays of dictionaries to array of a defined custom type;
I want to write that method in generic form so I call that method with the type I expect, then the method loads it and returns an array of my custom type rather than dictionaries
func loadPropertyList(fileName: String) -> [[String:AnyObject]]?
{
if let path = NSBundle.mainBundle().pathForResource(fileName, ofType: "plist")
{
if let plistXML = NSFileManager.defaultManager().contentsAtPath(path)
{
do {
if let temp = try NSPropertyListSerialization.propertyListWithData(plistXML, options: .Immutable, format: nil) as? [[String:AnyObject]]
{
return temp
}
}catch{}
}
}
return nil
}
//
func loadList<T>(fileName: String) -> [T]?{//**Here the answer I am expecting**}
I am assuming your function to read from a Plist works and that you don't want to subclass NSObject.
Since Swift reflecting does not support setting values this is not possible without some implementation for each Type you want this to work for.
It can however be done in a pretty elegant way.
struct PlistUtils { // encapsulate everything
static func loadPropertyList(fileName: String) -> [[String:AnyObject]]? {
if let path = NSBundle.mainBundle().pathForResource(fileName, ofType: "plist") {
if let plistXML = NSFileManager.defaultManager().contentsAtPath(path) {
do {
if let temp = try NSPropertyListSerialization.propertyListWithData(plistXML, options: .Immutable, format: nil) as? [[String:AnyObject]] {
return temp
}
} catch {
return nil
}
}
}
return nil
}
}
This protocol will be used in a generic fashion to get the Type name and read the corresponding Plist.
protocol PListConstructible {
static func read() -> [Self]
}
This protocol will be used to implement Key Value setters.
protocol KeyValueSettable {
static func set(fromKeyValueStore values:[String:AnyObject]) -> Self
}
This is the combination of both to generate an array of objects. This does require that the Plist is named after the Type.
extension PListConstructible where Self : KeyValueSettable {
static func read() -> [Self] {
let name = String(reflecting: self)
var instances : [Self] = []
if let data = PlistUtils.loadPropertyList(name) {
for entry in data {
instances.append(Self.set(fromKeyValueStore: entry))
}
}
return instances
}
}
This is some Type.
struct Some : PListConstructible {
var alpha : Int = 0
var beta : String = ""
}
All you have to do is implement the Key Value setter and it will now be able to be read from a Plist.
extension Some : KeyValueSettable {
static func set(fromKeyValueStore values: [String : AnyObject]) -> Some {
var some = Some()
some.alpha = (values["alpha"] as? Int) ?? some.alpha
some.beta = (values["beta"] as? String) ?? some.beta
return some
}
}
This is how you use it.
Some.read()
so i've created function and it does something, but i want it to do something else if the parameter type is different, for example:
func (parameter: unknownType){
if(typeof parameter == Int){
//do this
}else if(typeof parameter == String){
//do that
}
}
i've done this in javascript or other programming languages, but i don't know how to do this in swift
i've created function which takes 1 argument UITextField and centers it using constraints
now i want to center my button, but since button is not UITextField type it does not work, so is there a way i can tell function to do the same on UIButton??
Use Overload:
class Example
{
func method(a : String) -> NSString {
return a;
}
func method(a : UInt) -> NSString {
return "{\(a)}"
}
}
Example().method("Foo") // "Foo"
Example().method(123) // "{123}"
The equivalent of the Javascript code would be:
func doSomething(parameter: Any?) {
if let intValue = parameter as? Int {
// do something with the int
} else if let stringValue = parameter as? String {
// do something with the string
}
}
But be warned, this approach makes you loose the type safety which is one of most useful feature of Swift.
A better approach would be to declare a protocol that is implemented by all types that you want to allow to be passed to doSomething:
protocol MyProtocol {
func doSomething()
}
extension Int: MyProtocol {
func doSomething() {
print("I am an int")
}
}
extension String: MyProtocol {
func doSomething() {
print("I am a string")
}
}
func doSomething(parameter: MyProtocol) {
parameter.doSomething()
}
doSomething(1) // will print "I am an int"
doSomething("a") // will print "I am a string"
doSomething(14.0) // compiler error as Double does not conform to MyProtocol
It can
Sample code:
func temFunc(obj:AnyObject){
if let intValue = obj as? Int{
print(intValue)
}else if let str = obj as? String{
print(str)
}
}
You can make use of Any and downcasting:
func foo(bar: Any){
switch(bar) {
case let a as String:
/* do something with String instance 'a' */
print("The bar is a String, bar = " + a)
case let a as Int:
/* do something with Int instance 'a' */
print("The bar is an Int, bar = \(a)")
case _ : print("The bar is neither an Int nor a String, bar = \(bar)")
}
}
/* Example */
var myString = "Hello"
var myInt = 1
var myDouble = 1.5
foo(myString) // The bar is a String, bar = Hello
foo(myInt) // The bar is an Int, bar = 1
foo(myDouble) // The bar is neither an Int nor a String, bar = 1.5
Check this solution:
https://stackoverflow.com/a/25528882/256738
You can pass an object AnyObject and check the class in order to know what kind of object it is.
UPDATE
Good point #Vojtech Vrbka
Here an example:
let x : AnyObject = "abc"
switch x {
case is String: println("I'm a string")
case is Array: println("I'm an Array")
// Other cases
default: println("Unknown")
}
I have a weird issue trying to validate user input. I'm using a wrapper for a form framework and I want it to be able to validate user input.
The trouble is, when I call the closure with the userValue argument, it ends up being nil and all checks return false:
class FormRowWrap {
var tag: String
var row: AnyObject
var verification: (value: Any?) -> Bool
init(tag: String, row:AnyObject, verification:(Any?) -> Bool) {
self.tag = tag
self.row = row
self.verification = verification
}
}
class InsertViewController: FormViewController {
let rows = [
{
let tag = "Fuel Name"
let row = FormRowWrap(tag: tag,
row:TextRow(tag) {
$0.title = tag
// $0.value = last known user default
},
verification:({(value: Any?) -> Bool in
if let thing = value as? String {
//^----- the value in a **breakpoint here is nil**
//
if !thing.isEmpty {
return true
}
}
return false
}))
return row
}() as FormRowWrap,
{
let tag = "Price"
let row = FormRowWrap(tag: tag,
...
func formValuesAreValid(values: [String: Any?]) -> Bool {
var result = false
for rowWrap in self.rows {
let userValue = values[rowWrap.tag]
print("userValue \(userValue) forTag: \(values[rowWrap.tag])")
// ^---- this prints userValue **Optional(Optional("Ghh")) forTag: Optional(Optional("Ghh"))**
let entryIsValid = rowWrap.verification(value: userValue)
if (!entryIsValid) {
result = false
return result
}
}
result = true
return result
}
If I run rowWrap.verification(value:"test") it returns true, so I think it's an issue about properly unwrapping values.
Your function needs an Optional but userValue is an Optional inside another Optional:
Optional(Optional("Ghh"))
So when you force unwrap it with
let entryIsValid = rowWrap.verification(value: userValue!)
what happens actually is that you unwrap the first layer and get back an Optional:
Optional("Ghh")
which is what your function signature
(value: Any?) -> Bool
needs.
About why it's wrapped twice:
with
formValuesAreValid(values: [String: Any?])
the values in the values dictionary are Optionals, and then when you access the dictionary:
let userValue = values[rowWrap.tag]
you get yet another Optional - because accessing a dictionary always returns an Optional, so in this case values[rowWrap.tag] returns an "Optional Optional".
Then somewhere else you unwrap once thinking you'll get the value if let thing = value as? String but you get the inner Optional instead and your next check fails.
Found a way to make it work by force unwrapping the "userValue":
let entryIsValid = rowWrap.verification(value: userValue!)
I still don't understand why this works and why it doesn't work with the argument as wrapped optional.
I'm developing an iOS application in Swift.
When I updated the Xcode to 7.0, I'm getting error in swiftyJSON.
static func fromObject(object: AnyObject) -> JSONValue? {
switch object {
case let value as NSString:
return JSONValue.JSONString(value as String)
case let value as NSNumber:
return JSONValue.JSONNumber(value)
case let value as NSNull:
return JSONValue.JSONNull
case let value as NSDictionary:
var jsonObject: [String:JSONValue] = [:]
for (k:AnyObject, v:AnyObject) in value {// **THIS LINE- error: "Definition conflicts with previous value"**
if let k = k as? NSString {
if let v = JSONValue.fromObject(v) {
jsonObject[k] = v
} else {
return nil
}
}
}
What's the problem? Can you help, please?
for (k:AnyObject, v:AnyObject) in value { .. }
must be written in Swift 2 as
for (k, v) : (AnyObject, AnyObject) in value { .. }
From the Xcode 7 release notes:
Type annotations are no longer allowed in patterns and are considered
part of the outlying declaration. This means that code previously
written as:
var (a : Int, b : Float) = foo()
needs to be written as:
var (a,b) : (Int, Float) = foo()
if an explicit type annotation is needed. The former syntax was
ambiguous with tuple element labels.
But in your case the explicit annotation is actually not needed at all:
for (k, v) in value { .. }
because NSDictionary.Generator is already defined as a generator
returning (key: AnyObject, value: AnyObject) elements.
I want to query a custom object from an NSArray, here is a function I wrote:
func retrieveObject (objects: [CustomClass], identifier : String) -> CustomClass {
var retrievedObject : [CustomClass] = objects.filter({
return $0.identifier == identifier
})
return retrievedObject.first!
}
When I use it, the resulted object seems to have lost most of the property values in that object:
let obj : CustomClass = self.retrieveObject(objectList as! [CustomClass], "one")
println("\(obj.propertyA)")
The result shows "", while printing the object from the original object list shows value:
println("\(objectList.first!.propertyA)")
What seems to be the issue?
More Information:
The objectList above is a result of an asynchronous web request, let's assume that the objects in it are problem-free because they return the correct property value when printed.
Code of one step above before the array filter:
private var objectList : [AnyObject]!
private var object : CustomClass
self.webRequest(request, onSuccess: {(objects: [AnyObject]!) -> Void in
self.objectList = objects
self.object = self.retrieveObject(self.objectList, identifier: "one")
//I tried passing both self.objectList and objects
})
Problem Solved
This is not an issue with Swift or whatever. This is a data issue. The above code works fine.
Not sure, there doesn't seem to be anything wrong with the code you've provided us. I've recreated it in playgrounds and seems to print normally.
Code:
class CustomClass {
let identifier: String
let propertyA = "Printing!"
init(identifier: String) {
self.identifier = identifier
}
}
let objectList = [CustomClass(identifier: "one")]
func retrieveObject (objects: [CustomClass], identifier: String) -> CustomClass {
return objects.filter { $0.identifier == identifier }.first!
}
let object = retrieveObject(objectList, "one")
println("\(object.propertyA)") // Prints "Printing!"
println("\(objectList.first!.propertyA)") // Prints "Printing!"
EDIT:
Simplified it a bit
Except for line:
var retrievedObject : [CustomClass] = objects.filter({
return $0.identifier == identifier
})
that should be:
var retrievedObject : [CustomClass] = objects.filter { $0.identifier == identifier;};
In your version, you pass a strange value for filter argument (because of misplaced parenthesis) that does not match awaited filter argument type: (T) -> Bool
I tested Jacobson's code and I confirm it is working.