Realm crash on iOS 10 with 'String' - ios

I have recently released a new version of our app and during beta testing, it's crashing on all iOS 10 devices but not other versions. Since we have Crashlytics, we found a strange crash message in the backend that we can confirm is the reason all iOS 10 crashing since it's 100% iOS 10 and there's like 40 of them.
It reads as follows:
Fatal Exception: RLMException
Property Article.id is declared as String, which is not a supported managed Object property type. If it is not supposed to be a managed property, either add it to ignoredProperties() or do not declare it as #objc dynamic. See https://realm.io/docs/swift/latest/api/Classes/Object.html for more information.
And here's the object:
class Article: Object {
#objc dynamic var id: String = UUID().uuidString
// others...
override static func primaryKey() -> String? {
return "id"
}
}
As you can see, this is perfectly nomral and runs fine on other iOS. In Realm's doc, it LITERALLY SAYS to use String with #objc dynamic and there's no way it's unsupported. I suspect there's nothing special about Article.id, and since Article starts with A, it happens to be the first String property of all realm Objects. Maybe somehow all Strings stopped working on iOS 10?
Can anyone offer some advice or insights?(Please don't say things like drop iOS 10 support. For now, we need it.)

We ran into the same issue a couple of times, trying to drag Realm fully into Swift. This is not really the answer but more of a workaround we've had success with when needing backward compatibility.
It's an ObjC object, not Swift.
There's something going on with the bridging, perhaps conforming to NSCopy'ing or something along those line, so just change it to read
#objc dynamic var id = NSUUID().uuidString
See the Getting Started Guide in the Models section which calls for using NSUUID
NSUUID: An object representing a universally unique value that bridges
to UUID; use NSUUID when you need reference semantics or other
Foundation-specific behavior.

Turns out it was a Realm's bug. We happen to have another app that runs just fine on iOS 10, and after some inspection we realized that it was using Realm 4.3.2, instead of 4.4.1. After we downgraded Realm to 4.3.2, this problem disappeared.

Related

iOS compatibility with Swift's String .isEmpty

What compatibility issues, if any, are there with using isEmpty on a string in Swift?
let str = "Hello, planet"
if !str.isEmpty {
print(str)
}
I'm developing with the latest Xcode+Swift versions, but I thought I once read (perhaps incorrectly) that .isEmpty will not work with older versions of iOS, and so str == "" should be used instead.
Thanks!
References (whose overlap confuses me some):
(String) "whether a string has no characters" ... From Protocol: Collection
(String) "whether the collection is empty"
(Range) "whether the collection is empty" ... SDK: Xcode 10+
The availability of isEmpty to check if a String is empty came into Swift, when String became a collection type with the release of Swift 4. So the isEmpty check on a String is quite similar to the isEmpty check on an Array, Dictionary or a Set.
And, features like these are not limited to the iOS or Xcode versions. These kind of changes are specific to the language version (Swift); so you can safely assume that it would work on all devices or OS which can run your application.
In the recent / latest version of Swift, you can just use the isEmpty, and it's the same with comparing == "".
but I thought I once read (perhaps incorrectly) that .isEmpty will not
work with older versions of iOS
The iOS/MacOS version is NOT related to the Swift version you are using (and you should use always the latest), and instead, some of the new provided APIs in Swift do support a minimum version of the iOS/MacOS. So don't be confused.

NSCoding and Bools Swift 3

For whatever reason my application crashes everytime it decodes my Bool value. It didn't crash before I updated to Swift 3. I have no idea what I'm doing wrong. If I take out the Bool value my application runs fine without crashing.
This has been changed for Swift 3. There are associated decode...() functions for various different types.
The correct syntax is now:
let myBool = aDecoder.decodeBool(forKey: PropertyKey.completedKey)

Swift App Extension: instance method count is unavailable

I just create my first app extension using XCode 7.1. One code file containing the code below is shared with both targets:
var str = "";
var l = str.count; //Compile error for extension target App: count is unavailable: There is no ...
The reason for this compile error seams to be that App extension compiles with swift 1.2 while the container target compiles with swift 2.0.
One solution would be importing the content App into the extension App doesn't appear to be a good solution from what i read about it. Sharing the code between targets can be difficult if both are not compiled using the same compiler.
I just run through all target settings and didn't find nothing that could be changed.
Can't find any post about this problem, witch is not so uncommon, so it is must likely i am interpreting something in a wrong way.
The only solution i can think of is using NSString instead of String but that is just an workaround for one class type. More problems of this kind will emerge in the future.
In Swift 2 it's
str.characters.count
Use str.characters.count to get String length in Swift 2

Parse SDK and Swift: Incorrect argument label in call PFObject 'withoutDataWithObjectId'

I subclass PFObject exactly as described here.
Then I create a new instance of the subclassed object without data, but since Swift 1.2 I get an error (It did work perfectly before):
var test = Armor(withoutDataWithObjectId: "1234567890")
-> Xcode complains:
"Incorrect argument label in call (have 'withoutDataWithObjectId:',
expected: 'className:')"
Why className? It should get the class name from the class function parseClassName
And I can under no circumstances create a new object with objectId but no data (which I MUST have to fetch it from the local datastore)
This is super annoying as my app doesn't compile any longer.
Update to the newest Parse SDK, available here.
The issue is caused due to necessary adaptions in the Parse SDK after the Swift language update. This issue also occurs with the most recent update to Swift 2.2. The newest (as of today) Parse SDK release 1.13.0 already fixes this.
UPDATE
Parse iOS SDK 1.13.0 has a typo and the function PFUser(withoutDataWithObjectId:) is called PFUser(outDataWithObjectId:). So upgrading the Parse SDK alone does solve this. Until this is fixed a temporary workaround would be to extend PFObject with a convenience initializer. To do this add a new Swift file to your project and insert this:
import Parse
extension PFObject {
convenience init(withoutDataWithObjectId objectId: String?) {
self.init(outDataWithObjectId: objectId)
}
}
It may be a little late to answer this question.
I use swift 1.2, and v 1.7.5 Parse SDK, and it works totally fine.
however, make sure you have define objective-c bridging header in "build setting".
and try to run it, even though there may reports some error

Swift: Unable to downcast AnyObject to SKPhysicsBody

Apple has the following method in the SKPhysicsBody class.
/* Returns an array of all SKPhysicsBodies currently in contact with this one */
func allContactedBodies() -> [AnyObject]!
I noticed it returns an array of AnyObject. So I read about how to deal with down casting AnyObject Here
I want to loop through the allContactedBodies array of my physics body. The problem is, no matter what I try I just can't get things to work.
I tried this first:
for body in self.physicsBody.allContactedBodies() as [SKPhysicsBody] {
}
But I get this error.
fatal error: array cannot be downcast to array of derived
I also tried this:
for object in self.physicsBody.allContactedBodies() {
let body = object as SKPhysicsBody
}
But this also crashes with the following:
And similarly I tried this:
for object in self.physicsBody.allContactedBodies() {
let body = object as? SKPhysicsBody
}
There is no crash, but "body" becomes nil.
And if I don't cast at all, I don't get a crash. For example:
for object in self.physicsBody.allContactedBodies() {
}
But obviously I need to cast if I want to use the actual type.
So then as a test I just tried this:
let object: AnyObject = SKPhysicsBody()
let body = object as SKPhysicsBody
And this also results in the same crash that is in the picture.
But other types won't crash. For example, this won't crash.
let object: AnyObject = SKNode()
let node = object as SKNode
So my question is, how can I correctly loop through the allContactedBodies array?
Edit: I am running Xcode 6 beta 4 on iOS 8 beta 4 device.
Edit 2: More Information
Ok so I just did some more testing. I tried this:
let bodies = self.physicsBody.allContactedBodies() as? [SKPhysicsBody]
If "allContactedBodies" is empty, then the cast is successful. But if "allContactedBodies" contains objects, then the cast fails and "bodies" will become nil, so I can't loop through it. It seems that currently it is just NOT POSSIBLE to cast AnyObject to SKPhysicsBody, making it impossible to loop through the "allContactedBodies" array, unless someone can provide a workaround.
Edit 3: Bug still in Xcode 6 beta 5. Workaround posted below still works
Edit 4: Bug still in Xcode 6 beta 6. Workaround posted below still works
Edit 5: Disappointed. Bug still in Xcode 6 GM. Workaround posted below still works
EDIT 6: I have received the following message from Apple:
Engineering has provided the following information:
We believe this issue has been addressed in the latest Xcode 6.1 beta.
BUT IT IS NOT, the bug is still in Xcode 6.1.1!!! Workaround still works.
Edit 7: Xcode 6.3, still not fixed, workaround still works.
After much trial and error, I have found a workaround to my problem. It turns out that you don't need to downcast at all to access the properties of the SKPhysicsBody, when the type is AnyObject.
for object in self.physicsBody.allContactedBodies() {
if object.node??.name == "surface" {
isOnSurface = true
}
}
Update: This was a bug, and it's fixed in iOS 9 / OS X 10.11. Code like the following should just work now:
for body in self.physicsBody.allContactedBodies() {
// inferred type body: SKPhysicsBody
print(body.node) // call an API defined on SKPhysicsBody
}
Leaving original answer text for posterity / folks using older SDKs / etc.
I noticed this in the related questions sidebar while answering this one, and it turns out to be the same underlying issue. So, while Epic Byte has a workable workaround, here's the root of the problem, why the workaround works, and some more workarounds...
It's not that you can't cast AnyObject to SKPhysicsBody in general — it's that the thing(s) hiding behind these particular AnyObject references can't be cast to SKPhysicsBody.
The array returned by allContactedBodies() actually contains PKPhysicsBody objects, not SKPhysicsBody objects. PKPhysicsBody isn't public API — presumably, it's supposed to be an implementation detail that you don't see. In ObjC, it's totally cool to cast a PKPhysicsBody * to SKPhysicsBody *... it'll "just work" as long as you call only methods that the two classes happen to share. But in Swift, you can cast with as/as?/as! only up or down the type hierarchy, and PKPhysicsBody and SKPhysicsBody are not a parent class and subclass.
You get an error casting let obj: AnyObject = SKPhysicsBody(); obj as SKPhysicsBody because even the SKPhysicsBody initializer is returning a PKPhysicsBody. Most of the time you don't need to go through this dance (and have it fail), because you get a single SKPhysicsBody back from an initializer or method that claims to return an SKPhysicsBody — all the hand-wavy casting between SKPhysicsBody and PKPhysicsBody is happening on the ObjC side, and Swift trusts the imported ObjC API (and calls back to the original API through the ObjC runtime, so it works just as it would in ObjC despite the type mismatch).
But when you cast an entire array, a runtime typecast needs to happen on the Swift side, so Swift's stricter type-checking rules come into play... casting a PKPhysicsBody instance to SKPhysicsBody fails those rules, so you crash. You can cast an empty array to [SKPhysicsBody] without error because there aren't any objects of conflicting type in the array (there aren't any objects in the array).
Epic Byte's workaround works because Swift's AnyObject works like ObjC's id type: the compiler lets you call methods of any class on it, and you just hope that at runtime you're dealing with an object that actually implements those methods.
You can get back a little bit of compile-time type safety by explicitly forcing a side cast:
for object in self.physicsBody.allContactedBodies() {
let body = unsafeBitCast(object, SKPhysicsBody.self)
}
After this, body is an SKPhysicsBody, so the compiler will let you call only SKPhysicsBody methods on it... this behaves like ObjC casting, so you're still left hoping that the methods you call are actually implemented by the object you're talking to. But at least the compiler can help keep you honest. (You can't unsafeBitCast an array type, so you have to do it to the element, inside the loop.)
This should probably be considered a bug, so please let Apple know if it's affecting you.

Resources