Understanding Syntax issues about Parse latest SDK / Swift 1.2 - ios

Why does the 2nd snippet work and the first not work?
This code does not work:
func logIn() {
PFUser.logInWithUsernameInBackground(tv_username.text, password:tv_password.text) {
(user: PFUser!, error: NSError!) -> Void in
if user != nil {
// Yes, User Exists
//self.loginInitialLabel.text = "User Exists"
} else {
// No, User Doesn't Exist
}
}
}
This code works:
func logIn() {
PFUser.logInWithUsernameInBackground(tv_username.text, password:tv_password.text) {
(user, error) -> Void in
if user != nil {
// Yes, User Exists
//self.loginInitialLabel.text = "User Exists"
} else {
// No, User Doesn't Exist
}
}
}
Below is the error message. I am looking for a clear explanation of why some online docs have hte first example but only the 2nd one works. Did Parse change their SDK without changing documentation or is this some artifact of Swift 1.2 change? I am using XCode 6.3 and Swift 1.2.
Zoom of the error message:

In the first example, you specify the types of user and error explicitly (PFUser! and NSError!) respectively.
In the second example, you permit the type of user and error to be supplied implicitly.
Thus, the fact that the first example gives a compile error must mean that your explicit types are no longer correct. It could be the exclamation marks; try removing them.
The real way to figure out what types they are, though, is to use the second example, compile it, and then to put the cursor inside user and then inside error and read off the types from Quick Help on the right side of the Xcode window, as I do here:
That little trick has solved many Swift type mysteries for me!

Related

Returning void in PromiseKit 6

This is what I had working with PromiseKit 4.5
api.getUserFirstName().then { name -> Void in
print(name)
}
getUserFirstName() returns a Promsise<String>. I updated to PromiseKit 6 and this now throws an error:
Cannot convert value of type '(_) -> Void' to expected argument type '(_) -> _'
This error message makes little sense to me. How do I fix this?
EDIT: So this seems to fix it, but I have little understanding as to what's happening with this:
api.getUserFirstName().compactMap { name in
print(name)
}
What's the difference now between then() and compactMap()?
In according with PromiseKit 6.0 Guide then was split into then, done and map
then is fed the previous promise value and requires you return a promise.
doneis fed the previous promise value and returns a Void promise (which is 80% of chain usage)
map is fed the previous promise value and requires you return a non-promise, ie. a value.
Why that was happend? As said developers:
With PromiseKit our then did multiple things, and we relied on Swift to infer the correct then from context. However with multiple line thens it would fail to do this, and instead of telling you that the situation was ambiguous it would invent some other error. Often the dreaded cannot convert T to AnyPromise. We have a troubleshooting guide to combat this but I believe in tools that just work, and when you spend 4 years waiting for Swift to fix the issue and Swift doesn’t fix the issue, what do you do? We chose to find a solution at the higher level.
So probably in your case needs to use done
func stackOverflowExample() {
self.getUserFirstName().done { name -> Void in
print(name)
}
}
func getUserFirstName() -> Promise<String> {
return .value("My User")
}
compactMap lets you get error transmission when nil is returned.
firstly {
URLSession.shared.dataTask(.promise, with: url)
}.compactMap {
try JSONDecoder().decode(Foo.self, with: $0.data)
}.done {
//…
}.catch {
// though probably you should return without the `catch`
}
See more info at release guide
compactMap was renamed to flatMap see discussions here

Accidentally overriding getter with swift extension - Only during runtime when app enters foreground again

The last 2 days i brooded over a strange behavior i found in my iOS app. It was one of these moments when you start to have doubts about everything. I found the problem and fixed it. But i want to write this here to search for answers and gain knowledge about the system behind.
I am the developer of "Controller for HomeKit", an app to control HomeKit devices. In HomeKit you have HMActionSets to execute many changes at once. E.g. Turn on all lights.
Executing HMActionSet is a basic functionality and worked always. Here with a swipe of UITableViewCell.
let performAction = UIContextualAction(style: .normal, title: AppStrings.Actions.perform, handler: { (ac: UIContextualAction, view: UIView, success: #escaping (Bool) -> Void) in
self.home?.executeActionSet(selectedActionSet, completionHandler: { (error: Error?) in
if let err = error {
UIApplication.printToConsole(text: err)
success(false)
return
}
success(true)
})
})
BUT suddenly it stopped working flawlessly with a new development version. It took me a while to reproduce the situation, when it wasn't executing the action set.
Every time i start the app from scratch, everything works.
If i execute the HMActionSet right after navigating from the previous UIViewController, everything works. (Most common use case)
It stops working when being in the view, press the home button, reentering the app. After that all executions won't work, until going one view backwards and forward again.
Console logs this error:
Error Domain=HMErrorDomain Code=2 "Object not found." UserInfo={NSLocalizedDescription=Object not found.}
I walked backwards every commit until the problem was solved. After digging in the dark in a too big commit, i found the root cause.
But why am i not getting compile errors? Why does going to the home screen breaks it?
For a new feature i needed a new way to identify a HomeKit object on multiple devices, because the .uniqueIdentifier parameter is salted on every device.
import Foundation
import HomeKit
protocol Identifyable {
func getUniqueIdentifier() -> String?
}
extension HMActionSet: Identifyable {
func getUniqueIdentifier() -> String? {
return self.name.toBase64()
}
}
I created a swift protocol and made an extension to HMActionSet. Of course now that i found the error it looks stupid, to name the method like a getter. But this should not be the discussion now. It seems like this methods overrides the attribute of HMActionSet. When executing the actionSet internal functions accidentally use the wrong uniqueIdentifier, therefore the error is correct. (No object found).
/*!
* #brief A unique identifier for the action set.
*/
#available(iOS 9.0, *)
open var uniqueIdentifier: UUID { get }
If i name the method func uniqueIdentifier() -> String? Xcode immediately shows me a warning.
Method 'uniqueIdentifier()' with Objective-C selector 'uniqueIdentifier' conflicts with getter for 'uniqueIdentifier' with the same Objective-C selector
As i know HomeKit is written in Objective-C. I am using swift 4.
My questions to you:
Why are there no compiler errors (getUniqueIdentifier())?
If its a runtime problem mixing a swift extension into a objective-c framework, why does it only occur when reentering the app and not at the first start or later?
Looking forward to read your explanations about the problem that stole my sleep. :)

Swift - Error propagation while maintaining api clarity

With the addition of ErrorType to Swift, it's now possible to express error and failure events in a cleaner, more concise manner. We're no longer bound as iOS developers to the old way of NSError, clunky and hard to use.
ErrorType is great for a few reasons:
Generic parameters in functions
Any enum can conform to and implement it
Works better with the catch/throw paradigm
There are however some problems and one problem in particular that I've been running into lately and I'm curious to see how others solve this.
Say for example, you're build a social networking application akin to Facebook and you have Group model. When the user first loads your application you want to do two/three things:
Fetch the relevant groups from your server.
Fetch the already persisted groups on disk (Realm, CoreData, etc)
Update the local copy with the remote copy just fetched.
Throughout this process, you can break the types of errors up into two distinct categories: PersistenceError and NetworkError where the ErrorType conforming enums might look like
enum PersistenceError: ErrorType {
case CreateFailed([String: AnyObject)
case FetchFailed(NSPredicate?)
}
enum NetworkError: ErrorType {
case GetFailed(AnyObject.Type) // where AnyObject is your Group model class
}
There are several ways/design patterns you can use for delivering errors. The most common of course is try/catch.
func someFunc() throws {
throw .GetFailed(Group.self)
}
Here, because functions that throw can't yet specify what type of error they're throwing, although I suspect that will change, you can easily throw a NetworkError or a PersistenceError.
The trouble comes in when using a more generic or functional approach, such as ReactiveCocoa or Result.
func fetchGroupsFromRemote() -> SignalProducer<[Group], NetworkError> {
// fetching code here
}
func fetchGroupsFromLocal() -> SignalProducer<[Group], PersistenceError> {
// fetch from local
}
Then wrapping the two calls:
func fetch() -> SignalProducer<Group, ???> {
let remoteProducer = self.fetchGroupsFromRemote()
.flatMap(.Concat) ) { self.saveGroupsToLocal($0) // returns SignalProducer<[Group], PersistenceError> }
let localProducer = self.fetchGroupsFromLocal()
return SignalProducer(values: [localProducer, remoteProducer]).flatten(.Merge)
}
What error type goes in the spot marked ???? Is it NetworkError or PersistenceError? You can't use ErrorType because it can't be used as a concrete type and if you try using it as a generic constraint, <E: ErrorType>, the compiler will still complain saying it expects an argument list of type E.
So the issue becomes, less so with try/catch and more so with functional approaches, how to maintain an error hierarchy structure so that error information can be preserved throughout different ErrorType conformances while still having that descriptive error api.
The best I can come up with so far is:
enum Error: ErrorType {
// Network Errors
case .GetFailed
// Persistence Errors
case .FetchFailed
// More error types
}
which is essentially one long error enum so that any and all errors are of the same type and even the deepest error can be propagated up the chain.
How do other people deal with this? I enjoy the benefits of having one universal error enum but the readability and api clarify suffer. I'd much rather have each function describe what specific error cluster they return rather than have each return Error but again, I don't see how to do that without losing error information along the way.
Just trying a solution to your problem, may not be a perfect one. Using protocol to easily pass around the error objects :
//1.
protocol Error {
func errorDescription()
}
//2.
enum PersistenceError: ErrorType, Error {
case CreateFailed([String: AnyObject)
case FetchFailed(NSPredicate?)
func errorDescription() {
}
}
//3.
enum NetworkError: ErrorType, Error {
case GetFailed(AnyObject.Type) // where AnyObject is your Group model class
func errorDescription() {
}
}
//5.
func fetch() -> SignalProducer<Group, Error> {
...
}

Swift: Should NSError now be considered as legacy?

The docs and popular blogs suggest Swift error handling be done with do-catch and to handle an ErrorType enum or an NSError instance.
Are ErrorType enum and NSError instances mutually exclusive in a try catch block? If not, how do you implement a function that throws both?
I have associated an NSError instance to an enum like so, which seems to work, but is this the de facto way of returning detailed error information?
enum Length : ErrorType {
case NotLongEnough(NSError)
case TooLong(NSError)
}
func myFunction() throws {
throw Length.NotLongEnough(NSError(domain: "domain", code: 0, userInfo: [NSLocalizedFailureReasonErrorKey: "Not long enough mate"]))
}
do {
try myFunction()
} catch Length.NotLongEnough(let error) {
print("\(error)")
}
This example shows how ErrorType can be cast to NSError.
do {
let str = try NSString(contentsOfFile: "Foo.bar",
encoding: NSUTF8StringEncoding)
}
catch let error as NSError {
print(error.localizedDescription)
}
I can't find an error enum that conforms to ErrorType for NSString so should we assume it will be an NSError instance ? Granted we could run the code to be sure, but surely the docs should let us know. (I appreciate I might have mis-read the docs)
Any ErrorType can be successfully cast to an NSError, which means that if you want to handle ErrorType as a priority, or specific ErrorType conformant Swift types, you should have those cases before a case where you cast to NSError (which, again, will succeed for all ErrorType conform ant types).
I'm not sure if there are any canonical opinions expressed by Apple at this stage on how to deal with this duality right now, but personally I try to stick to ErrorType conforming Swift types for my own errors and only involve casting to NSError when I want to make conditional behaviour based on some Cocoa or 3rd party NSError based code where the domain & code combo is somehow meaningful to my logic (for instance if specific errors can be filtered out from either logging or from responses).
Problems with the existing facilities in Swift for error handling arise mostly from not being able to tell what kind of errors are thrown by a method, combined with ErrorType lacking some key contextual information included in NSError that gives extra bespoke work every time you want to present ErrorTypes in UI to the user somehow. Unlike NSError, ErrorType does not have a way to pass UI intended information such as a "reason", or "recovery suggestion", or indeed a "description". It looks like this also will not get addressed yet for Swift 3 (there has been some recent discussion on this on the Swift development mailing list).
Regarding "I can't find an error enum that conforms to ErrorType for NSString" I'm really not sure I understood this phrase correctly or whether it is worded correctly overall, but maybe the following is relevant:
I personally follow the convention of making my ErrorType implementations CustomStringConvertible and using the description property to describe a human (UI intended) readable description of the error. This is by no means perfect and especially on OSX where NSResponder nicely enough gives you the presentError method which makes a very nice and clear error dialog if you fill in the description & recovery suggestion info.
NSError class adopts ErrorType interface and any ErrorType-conformant class can be casted to NSError. These features are described here in the docs.
You can safely stick to ErrorType, especially if you're planning to interoperate with Swift only.
enum CommonError: ErrorType {
case InternalInconsistency(String)
}
func boom() throws {
throw CommonError.InternalInconsistency("Boom!")
}
do {
try boom()
} catch {
print(error) // InternalInconsistency("Boom!")
print(error as NSError) // Error Domain=CommonError Code=0 "(null)"
}
do {
try boom()
} catch let CommonError.InternalInconsistency(msg) {
print("Error: \(msg)") // Error: Boom!
}

'PFObject' does not have a member named 'subscript'

I understand, this particular error was already posted here and there and the code is somewhat basic, but I myself still unable to figure this one out & I need advice.
The thing is when I add the first two lines of code provided on parse.com for saving objects
var gameScore = PFObject(className:"GameScore")
gameScore["score"] = 1337
I get the following error for the second line:
'PFObject' does not have a member named 'subscript'
I'm on Xcode 6.3 beta 2.
All required libraries are linked with binary, <Parse/Parse.h> imported via BridgeHeader.
What syntax should I use?
This is happening due to the 1.6.4 version of the parse sdk which added Objective-C Nullability Annotations to the framework. In particular the file Parse/PFObject.h defines:
- (PF_NULLABLE_S id)objectForKeyedSubscript:(NSString *)key;
which is causing the Swift compile error. Removing the PF_NULLABLE_S fixes the problem.
On the other hand it seems correct that an object for a keyed subscript might be nil, so I suspect this is a Swift bug...
The problem seems to be the changed method signature, as kashif suggested. Swift doesn't seem to be able to bridge to the Objective-C method because the signature no longer matches the subscript method names.
Workaround 1
You can work around this without modifying the framework by calling the subscript method directly, instead of using the [] operator:
Instead of using the instruction below for getting the value of a particular key:
let str = user["key-name"] as? Bool
Please use the following instruction:
let str = user.objectForKey("key-name") as? Bool
and
Instead of using the instruction below for setting the value of a particular key:
user["key-name"] = "Bla bla"
Please use the following instruction:
user.setObject("Bla bla", forKey: "key-name")
Workaround 2
Another solution is to add an extension on PFObject that implements the subscript member and calls setValue:forKey::
extension PFObject {
subscript(index: String) -> AnyObject? {
get {
return self.valueForKey(index)
}
set(newValue) {
if let newValue: AnyObject = newValue {
self.setValue(newValue, forKey: index)
}
}
}
}
Note that this second workaround isn't entirely safe, since I'm not sure how Parse actually implements the subscript methods (maybe they do more than just calling setValue:forKey - it has worked in my simple test cases, so it seems like a valid workaround until this is fixed in Parse/Swift.
I've successfully run your exact code.
First, make sure you are indeed saving the object in the background, after you set the new value:
gameScore.save()
I would double check for misspellings in the class name and subclass; if they are incorrect, it will not work.
If that's not the problem, verify in Parse that the "score" subclass is set to be a number. If you accidentally set it to be a string, setting it as an integer will not work.
If these suggestions have not hit the solution, then I'm not sure what the problem is. Hope I helped.
I encountered a similar error with Parse 1.6.4 and 1.6.5 in the PFConstants.h header file with method parameters.
Xcode 6.3 beta 4 has a note in the "New in..." section regarding nullability operators.
Moving the nullability operator between the pointer operator and the variable name seems to comply/compile.
Changing:
PF_NULLABLE_S NSError *error
to:
NSError * PF_NULLABLE_S error
(i.e., NSError* __nullable error)
... resolved the compiler error.
This also worked for block parameters defined with id. So...
PF_NULLABLE_S id object
becomes:
id PF_NULLABLE_S object
In the above case, perhaps:
- (id PF_NULLABLE_S)objectForKeyedSubscript:(NSString *)key;
I.e., the nullability operator is after the pointer type.
I know its been a while but I encountered a similar problem when I was starting my first Parse application with SDK version 1.9.1.
The Quickstart guide had the code below as an example as to how to save values:
let testObject = PFObject(className: "TestObject")
testObject["foo"] = "bar"
testObject.saveInBackgroundWithBlock { (success: Bool, error: NSError?) -> Void in
println("Object has been saved.")
}
But, the line testObject["foo"] = "bar" returned the error 'PFObject' does not have a member named 'subscript'. I was able to work around the problem by changing the line to testObject.setValue("bar", forKey: "foo"). (As suggested by a tutorial video on YouTube: https://www.youtube.com/watch?v=Q6kTw_cK3zY)

Resources