Sometimes I get errors when defining a variable as NSArray/String/URL, it can often be solved by changing it to NSMutableArray/String/URL.
What is the difference between them? People say you can't change the value of a NSString, but why not since I defined it as a variable with a var?
Please use Swift 2 code to explain.
Here's code example:
enum Router: URLRequestConvertible {
static let baseURLString = "https://api.500px.com/v1"
static let consumerKey = "My_Key"
case PopularPhotos(Int)
case PhotoInfo(Int, ImageSize)
case Comments(Int, Int)
var URLRequest: NSURLRequest {
//Error: Type does not conform to protocol 'URLRequestConvertible' with Alamofire.
//I solved this problem by changing it to NSMutableRequest.
let (path, parameters) : (String, [String: AnyObject]) = {
switch self {
//3 cases.
}
}()
let URL = NSURL(string: Router.baseURLString)
let URLRequest = NSURLRequest(URL: URL!.URLByAppendingPathComponent(path))
let encoding = Alamofire.ParameterEncoding.URL
return encoding.encode(URLRequest, parameters: parameters).0
}
}
NS types are coming from Cocoa (some would say Objective-c, but actually they don't exist in Objective-C so they were created in the NextStep libraries and then used by Apple in Cocoa after they bought NextStep -> hence the NS prefix)
NSArray/String/URL are similar to let myVar = "aString"
and
NSMutableArray/String/URL are similar to var myVar = "aString"
the first one cannot be changed after it's set the second one can be. Why are you using the Cocoa/Objective-C types and not native Swift types ?
EDIT (see comments)
in Swift you can define a string:
var anEmtpyString = String()
or
var anEmtpyString = ""
in the second case Swift is intelligent enough to deduct that the type is a String. If you use 'var' the string is Mutable, meaning you can change it afterwards. So I can do :
var myString = "first text"
myString = "another text"
if you're using 'let' you create a references to the string, also called Immutable
let myString = "first text"
myString = "another text" // <- the compiler won't accept this !
In those examples I have used the native String type from Swift, which exists in both Swift 1.2 and 2
I could also use the Cocoa/Objective types NSString, NSMutableString but most of the time there is no need for it. It can be handy if you're using frameworks which are written in Objective-C. It's easier then to you since you don't have to cast from String to NSString/NSMutableString. (which is of course possible)
In Objective-C there is no let or var keyword so there you used NSString (for let) and NSMutableString (for var).
The same goes for NSArray/NSMutableArray
More info : https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html
PS the answer from gnasher729 is a bit more technical and tell you how it goes around behind the scenes. It al has to do with pointers, which were very present in Objective-C but are much more hidden in Swift. If you come from a background where you have learned pointers it will make thing clearer, if not I'm not sure if it'll help you. If you want me to expand on this please tell.
A.
In Objective-C many types of your daily routine are not built-in types. That means that they are not part of the programming language.
Therefore the framework Foundation defines that types as classes as any other class. There is no difference to classes like NSImage, NSManagedObject, NSWhatever. You do not have a language level documentation. They are documented as part of the Framework. You can treat them as every other class including subclassing, categories on it and so on. There is simply nothing special with them. (You could completely replace them, but there is little reason to do so.)
B.
In Objective-C every instance object is allocated on heap ("dynamically"). Since instances of NSString are usual instance objects, you do the same with them. The return value of such a construction is not an instance object, but a reference to an instance object. That means that all instance objects including instances of NSString are treated "by reference".
When you define a variable with the "type of string" you do not define a string instance, but a reference to the string:
NSString *referenceToString;
This referene is always a variable. In Swift it would look like this:
var string : String;
The analogon to a const in Objective-C is defined with the key word const.
NSString * const referenceToString;
But there is a big difference: Since the variable you declare is a reference, the constness can be related to the reference or to the object. In the first case you couldn't change the reference, in the latter case you could not change the object. The above syntax means: Constant reference to an NSString instance. Therefore you are not allowed to assign a value to that reference. (I. e. referencing another string object.)
These constant references are not very popular in Objective-C, because in most cases the constness of the reference is meaningless.
The constness of the string object itself is modeled by the type of the string object. NSString means constant string object, NSMutableString means variable string object.
Therefore, if you want to have a sting that can be mutated, use NSMutableString instead of NSString.
NSMutableString *referenceToAMutableString;
NSString *referenceToAnImmutableString;
There are value objects and reference objects. For example, Int and String are value objects, NSNumber and NSString are reference object. Swift struct instance are value objects, Swift class instances are reference objects.
For a reference object, the variable is not the object. It is a reference to the object. So a "let" variable cannot be changed, which means it refers to one object and cannot be made to refer to another object. A "var" variable can be changed, which means it can be made to refer to a different object. But both say nothing about the object itself. An NSArray object cannot be modified, an NSMutableArray can be modified. Whether you use a let or var variable to refer to the object only means the variable cannot or can be made to refer to a different variable, it doesn't say whether you can modify the object itself.
Related
I need to cast a return value to a specific type that I need to keep dynamic, like
let cellType = "CellTypeToBeResolved"
cell = (tableView.dequeueReusableCellWithIdentifier("myID") as? CellTypeToBeResolved)!
How is this possible in Swift 2.0?
Thnx!
You can't do it, because Swift is (deliberately) missing not one but two pieces of the puzzle:
You can't turn a string into a class.
More important, you can't cast down to a class expressed as a variable. The class to cast down to must be a literal class, i.e. it must be known at compile time. The compiler needs to know that this cast is legal, plus it needs to know at compile time how to treat this variable. If you want to be able to send MyCoolTableCell instance messages to this cell, you need to use the literal MyCoolTableCell name in your code — not a string, not a variable containg the type, but the type itself.
You could use NSClassFromString to convert your string to the class you want, then you can use that to cast the tableviewCell
I'm aware of what the as operator does and how to use them. But I'm more curious in the architectural side of the as operator. Why is it there? What's the huge reason? What does it help?
Example:
var objectData: NSData = NSUserDefaults.standardUserDefaults().objectForKey("myKey") as NSData
...wouldn't it be better if it's:
var objectData: NSData = NSUserDefaults.standardUserDefaults().objectForKey("myKey")
You're right to be a bit confused, as prior to Swift 1.2, the as keyword was overloaded with multiple meanings. Swift 1.2 makes this a lot clearer, as there are now 3 versions of as: as, as? and as!. (in prior versions, as and as! where conflated as just as)
In your example (which I'm assuming is prior to 1.2 as otherwise it wouldn't compile), you are using as to do two things simultaneously.
NSUserDefaults.objectForKey's return type is an AnyObject?, an optional AnyObject. That is, it could be any kind of object, and it might not be an object at all (because there might not have been a value set for the key "myKey").
To make use of this result, you need to do two things – unwrap the optional (i.e. test if it contains a value), and convert the AnyObject to a more useful type (in this case, NSData).
Here's how you can do this:
if let objectData = NSUserDefaults.standardUserDefaults()
.objectForKey("myKey") as? NSData
// note the question mark --^
{
// use the value
}
else {
// handle the value not being present – perhaps set it to a default value
}
This is using as? to test, tentatively, if the value returned by objectForKey is of type NSData. The if let tests if there was a valid value, and if it is of the correct type.
Bear in mind, there is a chance that the value stored for "myKey" is not compatible with the type you want. Suppose instead of an NSData you wanted an Int. You'd do if let intData = NSUserDefaults.standardUserDefaults().objectForKey("myKey") as? Int. If the value stored for "myKey" was the string "foo", this cannot be converted to an Int an you'd get an error.
If instead of using as? above, you use as, you force Swift to unwrap the value and convert the type without checking. If everything works out, that's fine. But if there was no value or if the data was not compatible with the type, you'll get a runtime assertion and your program will crash.
This is so dangerous that in Swift 1.2, this use of as has been renamed as!, the exclamation part indicating a dangerous "forcing" of the conversion. If you try to compile your example code in 1.2, you'll get an error asking if you meant as? or as!.
The as keyword remains for type clarifications that are always safe. For example, suppose you have an overloaded function like so:
func f(i: Int?) { println("called with Int") }
func f(s: String) { println("called with String") }
// try to call f with nil – Swift will complain because it
// doesn't know if this is a nil int or a nil string
f(nil)
// you can tell it which with as:
f(nil as Int?) // prints "called with Int"
f(nil as String?) // prints "called with String"
There are also some kinds of Swift <-> Objective-C bridging conversions that are guaranteed to always succeed, which you can also use as for:
let a = [1,2,3]
// you can always convert a Swift array to an NSArray
let n = a as NSArray
The benefit of as is that it confirms that the object on its left side is of the type on its right side. It differentiates between, for example:
myAnimalReallyADog as Dog
and
myAnimalReallyACat as Dog
Without it you would have to use other, probably more long-winded ways to test whether the object cast is legal.
The as concept is common among many languages and not unique to Swift, so it's a well-established paradigm and pretty clearly useful.
First, we should know when to use it. Usually you use this operator in order to perform a "downcast" - that is, the operator tries to return an object reference as a derived class - and succeeds iff the object is actually of that kind. That implies, that the call-site has to make a guess about the actual kind of the object. This downcast can fail, too.
Thus, the answer is really twofold:
As OldPeculier already pointed out, "it's a well established paradigm and clearly useful".
However, there are other, better designs which could avoid a downcast which can potentially fail. Frequent use of a downcast is a code smell. You should strive to re-think your design, possibly using protocols, or class extensions and so force.
What is the difference of declaring variables this way?
var contacts: [Person]!
var contacts = [Person]()
By using var contacts: [Person]! you don't actually initialize a Person array.
var contacts: [Person]! // contacts still nil
var contacts = [Person]() // Person array with 0 objects
If you use () instead you initialize an empty Person array.
Variable declarations in Swift take the following form:
var name: Type = initialValue
That is, you declare a variable called name of some Type, and set it to an initialValue.
There are many shorthand forms though, so you will see various alternatives. The one you see the most often is leaving off the Type part. If you do, then the type of name is “inferred” from the initialValue.
This is what is happening with var contacts = [Person](). The type is an Array of Person. The () is calling the initializer (i.e. creating the array).
Alternatively, you can declare a variable, with a type, but not give it an initial value. But the compiler won’t let you use it until you are guaranteed to have set it with an initial value. So for example, you could write var contacts: [Person], then later contacts = [Person]().
When you write var contacts: [Person]!, with a !, you are declaring a variable of type Optional<[Person]> – that is, a type that can either be nil, or contain an array. Unlike regular arrays, optionals of arrays have a default value if you don’t initialize them. The default value is nil – that is, that the optional does not contain an array.
But the ! (instead of the more common ?) means it is declared to be a special kind of optional, called an “implicitly-unwrapped optional” – that is, an optional that, when you use it in certain ways, will act as if it isn’t optional. The big downside of this is that it will let you use it as if it isn’t an optional. But if you do and it is nil then your program will crash. So before anyone uses contacts, it will need to be initialized (such as with contacts = [Person]() or assigning some existing array to it)
For this reason, it’s best to not use these implicitly-unwrapped optionals except in very specific circumstances. They sometimes seem like they’re convenient but they’re usually not the best option as they’re dangerous.
The operator : is not declaring, its just saying which type a variable is
Operator = is as in every other language a declaring operator which in this case followed by the () which in this case declared a new array of Person.
Other people provided the differences, I'll give you the scenarios of when either should be used.
var contacts: [Person]! //implicitly unwrapped optional. #1
var contacts = [Person]() //array initialization. #2
I use #1 when I create a variable that won't be initialized by it's own class. This situation occurs when I implement a detail view that takes in information that is passed by another controller.
For #2, use when you want to initialized a new empty array.
I updated xcode and now I have error in my project and I dont have idea what to do with it.
struct Program {
let name : String
let url : String
}
self.arrayOfPrograms = [Program(name: "First", url: "http://1.com"), Program(name: "Second", url: "http://2.com"), Program(name: "Third", url: "http://2.com")]
and I'm getting error "Type'Program' does not conform to protocol 'Any Object'"
As reported in the documentation:
AnyObject can represent an instance of any class type.
A struct is not a class, so it cannot be cast to AnyObject
You should either:
turn Program into a class
define your array as Array<Any>
if your array is supposed to hold instances of Program only, declare it as Array<Program>
Needless to say, the last is the best solution, whereas the first is the one I wouldn't recommend because it requires you to make design changes (there's a reason why you declared it as a value type and not a reference type).
Side note: arrays and dictionaries can be cast to AnyObject because they are automatically bridged respectively to NSArray and NSDictionary, which are classes.
check this link you got your answare I recently wrapped my head around
something that I have found very strange in Swift. Swift provides two
high level protocols called Any and AnyObject. Any can be used for
both value types (like structs) and reference types (classes) while
AnyObject can only be used for classes.
enter link description here
I am writing a library which can parse typed Ids from JSON. However, I am finding the typecasting rules a little baffling.
Example:
class AccountId : NSString { }
let json : AnyObject? = "user-1" // Returned by NSJSONSerialization.JSONObjectWithData
let s = json as? NSString // Succeeds, s == Some("user-1")
let a = json as? AccountId // Fails, a == nil
Why does the first typecast succeed while the second one fail? Is there something magical about NSString which does not crossover to Swift-only classes?
I am using XCode Version 6.1 (6A1030) (the latest at the time of writing).
As a general rule, if you have a hierarchy of classes A -> B -> C (C inherits from B, which in turn inherits from A), and you have an instance of B, you can upcast to A, but you cannot downcast to C.
The reason is that C might add properties which are not available in B, so the compiler or the runtime wouldn't know how to initialize the additional data, not to mention that it must also be allocated.
Note that polymorphism allows you do to upcast and downcast with variables - which means, if you have an instance of C stored in a variable of type A, you can cast that variable to B and C, because it actually contains an instance of C. But if the variable contains an instance of B, you can downcast to B, but not to C.
In your case, rather than downcasting you should specialize a constructor accepting NSString, but I suspect that in this specific case of NSString it cannot be done (there's no NSString designated initializer accepting a string as argument). If you are able to do that, then your code would look like:
var json: AnyObject? = "test"
if let string = json as? NSString {
let a = AccountId(string: string)
}
and at that point you can use a where an instance of either AccountId or NSString is expected
Is there something magical about NSString which does not crossover to Swift-only classes?
Yes. Swift's String class is automatically bridged to NSString and vice versa. From the docs:
Swift automatically bridges between the String type and the NSString
class. This means that anywhere you use an NSString object, you can
use a Swift String type instead and gain the benefits of both
types—the String type’s interpolation and Swift-designed APIs and the
NSString class’s broad functionality. For this reason, you should
almost never need to use the NSString class directly in your own code.
In fact, when Swift imports Objective-C APIs, it replaces all of the
NSString types with String types. When your Objective-C code uses a
Swift class, the importer replaces all of the String types with
NSString in imported API.
Why does the first typecast succeed while the second one failed?
Because of the strong relationship between String and NSString, the compiler knows how to convert from one to the other. On the other hand, because your AccountId class is a more specialized version of NSString, you can't cast from String to AccountId.