Anybody using iRareMedia's iCloudDocumentSync with Swift? I used this framework successfully with Objective-C, but can't get started with Swift. In Objective C, the app initializes iCloud with the following:
[[iCloud sharedCloud] setDelegate:self]; // Set this if you plan to use the delegate
[[iCloud sharedCloud] setVerboseLogging:YES]; // We want detailed feedback about what's going on with iCloud, this is OFF by default
[[iCloud sharedCloud] setupiCloudDocumentSyncWithUbiquityContainer:nil];
I cannot figure out how to convert two of these to Swift. The class is subscribed to the iCloudDelegate protocol:
class AppDelegate: UIResponder, UIApplicationDelegate, iCloudDelegate {
For the first
[[iCloud sharedCloud] setDelegate:self];
I tried iCloud.sharedCloud().setDelegate(self) but I get a compiler error: Cannot invoke 'setDelegate' with an argument list of type '(AppDelegate)'
I've also tried iCloud.sharedCloud().delegate = self (since delegate is a property of iCloud), but I get the compiler message: Ambiguous use of 'delegate'
The second [[iCloud sharedCloud] setVerboseLogging:YES]; converts just fine with iCloud.sharedCloud().setVerboseLogging = true
The third [[iCloud sharedCloud] setupiCloudDocumentSyncWithUbiquityContainer:nil]; I've tried
iCloud.sharedCloud().setupiCloudDocumentSyncWithUbiquityContainer(nil)
which gets me: Value of type 'AnyObject' has no member 'setupiCloudDocumentSyncWithUbiquityContainer' with the error caret under the 's' of sharedCloud.
I'm baffled. I've done lots of Objective-c to Swift method conversions, so I'm not exactly new to this game, but I can't figure this out. If anyone is using iRareMedia's iCloudDocumentSync framwork with Swift, I'd appreciate any help and insights.
Thanks.
I don't know why, but setting up the iCloud delegate and initializing statements in a UIViewController allows them to all work in Swift. In the first viewcontroller in my app, I have this:
cloud = iCloud.sharedCloud() as! iCloud
cloud.delegate = self
cloud.verboseLogging = true
cloud.updateFiles()
and everything works as it's supposed to.
Related
I am exploring the use of Swift for a Mac Cocoa application - using Xcode 8.2.1, and it seems I hit a roadblock regarding Cocoa bindings.
In this toy Core Data document-based project, I added an NSArrayController in the StoryBoard. I need to bind it to a NSManagedObjectContext so that my user interface works by itself. I followed the solution outlined in Technical Q&A QA1871 (https://developer.apple.com/library/content/qa/qa1871/_index.html).
So I want to add a property of type ManagedObjectContext to my ViewController class.
I naturally declared it as an optional:
var moc: ManagedObjectContext?
But when I enter the property name in the bindings inspector of InterfaceBuilder, it complains: there is a red exclamation mark, and hovering over it pops up this error message:
“The Managed Object Context binding expects to be bound to an object of type NSObject, but moc is of type ManagedObjectContext?”
And it fails at run time too.
changing the type to ManagedObjectContext! doesn’t help: IB complains in exactly the same way.
changing the type to a non optional ManagedObjectContext silences the IB error, but now my ViewController class doesn’t compile anymore. The error I get is:
class ViewController has no initialiser
I completely understand this error message. And I can add an initialiser. But I get this new error message:
property ‘self.moc’ not initialised at super.init call.
I understand that one too, but what can I do? At initialiser-time, the managedObjectContext is not yet known. In Objective-C I would set it to nil, which I cannot do since the property is not an optional any more.
Do I really need to allocate a dummy sentinel ManagedObjectContext, just to make the compiler happy?
This would be ugly as hell, far worse than the nil value we use in Objective-C. Swift in that case, would not be safer, but less safe than Obj-C.
I find this idea repulsive. Or did I miss something?
Or is Swift fundamentally incompatible with this Apple-recommended Cocoa bindings pattern? That would be a pity too.
Note that an equivalent Objective-C implementation works just fine. In Objective-C, the property is declared as:
#property (nonatomic, strong) NSManagedObjectContext * _Nullable moc;
Edit: a zip archive containing two toy sample projects, one in Objective-C, the other in Swift 3, can be downloaded from http://dl.free.fr/pWkoKQLOc
Edit: here is where I set the viewController's managed object context:
class Document: NSPersistentDocument {
override func makeWindowControllers() {
let myMoc = self.managedObjectContext
// Returns the Storyboard that contains your Document window.
let storyboard = NSStoryboard(name: "Main", bundle: nil)
let windowController = storyboard.instantiateController(withIdentifier: "Document Window Controller") as! NSWindowController
let myViewController = windowController.contentViewController as! CarViewController
myViewController.moc = myMoc
self.addWindowController(windowController)
}
}
myViewController is created by storyboard, and I can only set its moc after it has been initialised. This in turns requires that the property be an optional or to have a dummy actual value to use as a sentinel value in replacement for nil
I’ve created new SKEmitterNode object using copy() method. After that i’ve tried to write emitter.position but Xcode said «Ambiguous reference to member ‘position’». But, when i use type conversion «as! SKEmitterNode» after the «copy()», everything is ok. Can you explain me, please, why am i need to use «as!» in this case? I can’t understand this because when i check value type of «emit» variable in debugger, i can see that it’s already have the type SKEmitterNode, even without using «as! SKEmitterNode» after «copy()».
class GameScene: SKScene, SKPhysicsContactDelegate {
let bangEmitter : SKEmitterNode = SKEmitterNode(fileNamed: "MyParticle")!
func makeBang(position: CGPoint) {
// this method causes an error in second line
// but, emit is already have type SKEmitterNode, as debugger says
var emit = bangEmitter.copy()
emit.position = position
// this works ok
var emit = bangEmitter.copy() as! SKEmitterNode
emit.position = position
}
}
Because copy() is a method defined by NSObject and is meant to be overriden by subclasses to provide their own implementation. NSObject itself doesn't support it and will throw an exception if you call copy() on it.
Since it's meant for subclassing, there's no way to tell what the class of the object that will be returned. In Objective-C, it returns an id; in Swift, this becomes AnyObject. Since you, the programmer, know what kind of object you are copying from, you can use as! SomeClass to tell the compiler what kind of object the copy is.
This also speaks to the difference between ObjectiveC and Swift. Objective-C is dynamic. In Objective-C, every time you send a message, the run time will check if the object responds to the message. This happens at run time. In Swift, you call a method and this happens at compile time. The compiler must know the object's type in order to call the right function / method.
This explains why you get emit as an SKEmitterNode in the debugger - this is run time. The compiler doesn't know that at compile time.
Using the as! is an indicator that a check may fail.
Swift 1.2 separates the notions of guaranteed conversion and forced conversion into two distinct operators. Guaranteed conversion is still performed with the as operator, but forced conversion now uses the as! operator. The ! is meant to indicate that the conversion may fail. This way, you know at a glance which conversions may cause the program to crash.
Reference: https://developer.apple.com/swift/blog/?id=23
Look up the definition of the function copy() and you'll see that it always returns Any, therefore you always need to cast it to the object that you're seeking.
first post!
i've been working with swift for a while now, but decided to move some of my code to using shared variables and objects. I'm having a fundamental issue with being able to reference appDelegate.SharedApplication().delegate. Even with a basic test application (to see if I could see any fundamental problems), I cannot get a reference to the shared variable -
// AppDelegate.swift
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
let info: String = "test"
}
trying to add - "let ref = UIApplication.sharedApplication().delegate as appDelegate" in another class complains of use of undeclared type app delegate.
removing that cast allows that, but once I try to create another variable reference to the info string, complains of no member named ref.
this seems such an obvious and confusing issue that I thought it time to ask for an answer :) all i want is to be able to use the appdelegate for controlling cllocationmanager and storing the return data in a variable for a view controller class to refer to.
xcode 6.1.1
cheers
Maybe you are confusing the compiler because you are not using the proper case (upper or lower) when referring to variables or classes.
A class starts with an UpperCase letter.
Any variable starts with a lowerCase letter.
While this is not a rule for variables, it is a well-heeded convention.
Thus:
let stringFromDelegate =
(UIApplication.sharedApplication() as AppDelegate).info
I have a swift project with a storyboard using Size Classes, a ViewController which conforms to the UITraitEnvironment protocol and have implemented the function traitCollectionDidChange.
When I first launch the app traitCollectionDidChange is called which seems reasonable - it allows developers to handle the first size class presented by the application, however the previousTraitCollection variable passed to it is nil. I can't check that this value is nil as its NOT optional, and accessing it in any way causes a crash. Changing the parameter to optional in the function declaration causes a build error, complaining that I have not implemented required methods in the protocol.
For now I have managed to work around this by creating a separate function which takes an optional variable of the same type that I can then check against, but I would like to know why this is happening.
If its any use to anyone at all, here is the function I am overriding
override func traitCollectionDidChange(previousTraitCollection: UITraitCollection) {
// Accessing previousTraitCollection causes a crash
}
Can anyone shed any light on this?
You have to declare that the type of the argument is optional, because accessing it forces the compiler to try and unpack the address which causes the crash. Replace it with:
override func traitCollectionDidChange(previousTraitCollection: UITraitCollection?) {
// Accessing previousTraitCollection causes a crash
}
I'm trying to figure out the SQLite functionality for the iPhone, and I'm having some problems reading my database file from an overridden UITableViewDataSource function. I am storing the database file location in an ivar (as an NSString) in my application delegate, however when I try to access that string from an overridden UITableViewDataSource function, it returns some other object. If I access the string from any of the classes own instance methods, it works fine.
Is there any way to access the application delegate from within overridden UITableViewDataSource functions?
The Application is a singleton which maintains a reference to the app delegate. You can always access your app delegate using:
[UIApplication sharedApplication].delegate
You may need to cast the return to your own app delegate class to get rid of warnings. Even better, write an accessor that returns your upcast app delegate:
#pragma mark access to app delegate etc.
+ (MyAppDelegateClass*) sharedAppDelegate; {
return (MyAppDelegateClass*)[[UIApplication sharedApplication] delegate];
}
Roger is correct, but I personally find it extremely confusing to mix dot syntax and bracket syntax in the same statement.
If this confuses you as well, the equivalent syntax, using only bracket notation, is:
[[UIApplication sharedApplication] delegate];
and yes, you may need to cast the result to be your application delegate class, rather than the generic UIApplicationDelegate class, or you will get a number of compiler warnings, most likely.
While I don't like sticking too much into my AppDelegate, I'll often need to access it to get to other singletons in my app, which makes the method call + cast a little cumbersome. So in most of my apps, I'll define a quick macro in my global header file.
Example follows:
#define MY_DELEGATE (AppDelegate*)[[UIApplication sharedApplication] delegate]
It's a lot easier to refer to MY_DELEGATE.
The problem turned out to be really simple. I had created the NSString to hold the path to my database file using stringByAppendingPathComponent: . I failed to realize that this was going to be autoreleased, and so I didn't bother to explicitly retain it. That reason it was returning a different type of object was because that memory had been reused once the string had been autoreleased.
Explicitly retaining the string holding the path to the database file solved the problem.