So I created a Keyboard app extension that uses Parse to get some needed data. I initialized Parse after the keyboard loads like this:
func connectParser() {
// Initialize Parse
let configuration = ParseClientConfiguration {
$0.applicationId = "BlahBlahBlah123456"
$0.clientKey = "BlahBlahBlah123456"
$0.server = "https://parseapi.back4app.com"
}
Parse.initialize(with: configuration)
}
note: Initialization is normally done in AppDelegate, but since this is an App Extension I did it in the main InputView that gets loaded first
The problem occurs when the user switches out of the keyboard after loading it and then tries to switch back to the keyboard. The extension crashes and I get the following error:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Parse is already initialized.'
How do I check to see if Parse is initialized already so I don't re-initialize it and cause a crash?
So I didn't find any real solutions to this anywhere, but I did find my own solution and I'll keep it here and answer my own question in case anyone else runs into this:
Basically I check to see if Parse.currentConfiguration() == nil
if it does, then I know Parse hasn't been initialized yet. I don't know if this is the best way of going about this, so if you have a better alternative, please add it as an answer.
Final function looks like this:
func connectParse() {
if Parse.currentConfiguration() == nil {
// Initialize Parse
let configuration = ParseClientConfiguration {
$0.applicationId = "BlahBlahBlah123456"
$0.clientKey = "BlahBlahBlah123456"
$0.server = "https://parseapi.back4app.com"
}
}
}
Related
I have this test:
func testLogin() throws {
viewController.email.insertText("email#outlook.com")
viewController.password.insertText("secret")
viewController.btnLogin?.sendActions(for: .touchDown)
XCTAssert(viewController.loggedIn == true)
}
But it gives me this error on the first line when I try to insert the email
Unexpectedly found nil while implicitly unwrapping an Optional value
How can I get this test to work?
This code block looks incomplete...
You can't directly access the UIElements for XCTests.
As a first step need to create application object
var app = XCUIApplication()
then you need to launch the app with
app.launch()
then access respective UI element with accessibilityIdentifier as
let email = app.textFields["identifier_of_emil_textfield"]
UI elements needs to have accessibilityIdentifier to work with XCTest framework. If you haven't assign this then you can as emailTxt.accessibilityIdentifier = "identifier_of_emil_textfield" or directly from identity inspector of respective XIB element.
and at last you can use typeText property to add your text like
email.typeText("email#outlook.com")
I'm writing an application which has an NSSplitViewController as the main View-Controller. I have it linked-up so that clicking a button in the menubar will trigger an #IBAction which then calls a function in one of the sub-View-Controllers.
if let board = storyboard {
let imageController = board.instantiateController(withIdentifier: "VC_image_ID") as! VC_image
imageController.viewDidAppear() // necessary or else I'll get an "Unexpectedly found nil" later on
DispatchQueue.global().async{imageController.processImage(path)} // path variable was set earlier in the code, but not shown here
}
Inside the sub-View-Controller (named VC_image), I'm trying to change the stringValue of a label by using the following code:
public func processImage(_ path: String) {
DispatchQueue.main.async {
self.imageText.stringValue = path
print(self.imageText.stringValue)
}
}
Although the imageText.stringValue actually seems to have changed based on the fact that it prints the updated text (through the console), the text in the window never changes. Why is this happening? The reason is probably really obvious to professionals, but I'm still an amateur and can't figure it out. Thanks.
I have a function called iCloudIsOn() which checks whether a user has opted to use iCloud with the app by comparing the value of a UserDefaults key with a predefined one, and then I am using this on my initial View Controller to sync iCloud and implement pull to refresh, but only if the user is using iCloud, otherwise this code is not needed.
What's strange however is that the function only returns true if I call it right before I need to use it, even with just a print(iCloudIsOn())
The function itself looks like this:
func iCloudIsOn() -> Bool {
if UserDefaults.standard.url(forKey: "UDDocumentsPath") == iCloudPath { return true }
else { return false }
}
Then under viewDidLoad of my initial vc:
override func viewDidLoad() {
super.viewDidLoad()
if iCloudIsOn() {
// sync iCloud
}
}
This will not work however and iCloudIsOn() will return false at that time unless I add a print(iCloudIsOn()) before if iCloudIsOn() { // sync iCloud }
I tried asking iCloudIsOn to print both the value of the UserDefaults key and iCloudPath every time it is called and they are always identical: file:///private/var/mobile/Library/Mobile%20Documents/iCloud~cristian~thrive-storage/Documents/
So there isn't something in my code changing the value for one of them at some point (the value is printed before the return).
Any idea on why this is happening? Is it something to do with how UD works or am I missing something else? I find it a little strange, but I'm sure I'm just making a mistake somewhere.
Thanks in advance.
The problem ended up being that UserDefaults stored a value without a "/" the first time yet when being compared to the original URL, iCloudPath, iCloudPath had the original "/".
UserDefaults.standard.url(forKey: "UDDocumentsPath") = someURL
iCloudPath = someURL/
This would only happen on the first go around. Workaround is to cover both bases as you don't know which one you are on, especially if you are doing multiple checks in one session of the application.
if UserDefaults.standard.url(forKey: "UDDocumentsPath") == iCloudPath || UserDefaults.standard.url(forKey: "UDDocumentsPath").appendPathComponent("/") == iCloudPath
Technically, though, this is odd and unexpected behavior.
I recently started to update my app to Swift 2.0, but I've run into a problem that has a hundred different answers on SO, none of which seemed to be related to my problem.
(This worked before updating the application to Swift 2.0), but I havn't been able to find any changes made to tap gesture recognisers?
Here's the full error I'm getting:
[Stocks.SidePanelViewController proFormulaTapGesture]: unrecognized selector sent to instance 0x14f06ae00
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Stocks.SidePanelViewController proFormulaTapGesture]: unrecognized selector sent to instance 0x14f06ae00'
*** First throw call stack:
(0x1830904d0 0x197afff9c 0x1830971ec 0x183094188 0x182f9920c 0x188c3c58c 0x188899b50 0x18872d104 0x188c3bc30 0x1886ed28c 0x1886eb3ac 0x18872b088 0x18872a5ec 0x1886fb908 0x1886f9fac 0x183047d6c 0x183047800 0x183045500 0x182f75280 0x18e0ec0cc 0x188762df8 0x100158184 0x1983428b8)
libc++abi.dylib: terminating with uncaught exception of type NSException
It's a simple tap gesture. But it seems to not recognize the selector anymore.
Here's the code I use to set up the recognizer:
let proFormulaTap = UITapGestureRecognizer()
proFormulaTap.addTarget(self, action:"proFormulaTapGesture")
proFormulaView.addGestureRecognizer(proFormulaTap)
Here's the function I'm trying to run:
func proFormulaTapGesture() throws {
print("proFormulaTapGesture")
selectView(proFormulaView)
selectedMenuItem = 0
Formula = 1
menuTabBarController.selectedIndex = 0
navigationController?.navigationBar.topItem!.title = "BASIC FORMULA"
try (menuTabBarController.viewControllers as! [SuggestionsViewController])[0].loadSuggestions()
}
However since it never prints "proFormulaTapGesture" in the console. I definitely think the error occurs before the function. Also suggested by the error mentioning the selector.
Obviously try/catch has been added to the function since the update to Swift 2.0, but nothing has been changed in the setup of the tapGestureRecognizer.
I tried removing the "throws" and try from the function, but the problem perists. I also tried making a button instead of a tap gesture recognizer. But I'm still getting the same error, suggesting it might be a problem with the selector (function) and not the tap gesture / button. However all my other buttons in my app works just fine?
I also tried to rename the selector/function, and the tap gesture recognizer. Still the same.
The original Code was programmed in Swift, not Obj-C. The throws and try, was added during Apple's code conversion to Swift 2.0
Any help to why this is suddenly broken would be greatly appreciated.
Thanks!
The issue may be that UIKit does not know how to call a selector that throws, at least in this context.
Remember that UIKit will be calling this function in response to a tap action. It is not accustomed to calling a function that throws; obviously the throw must alter the method's call signature as far as the Objective-C runtime is concerned. Beyond that, if your function proFormulaTapGesture did throw an error, what would UIKit do with the error even if it could catch it?
I'm not sure where you added do/try/catch. But unless it was inside your action function proFormulaTapGesture, I do not see how it could be relevant. I believe that you will need to implement the try/catch error handling inside proFormulaTapGesture, so that it effectively swallows the error, so that proFormulaTapGesture does not throw.
I just created a quick test project myself and found that a throwing target action gives the same "Unrecognized selector" error you got:
override func viewDidLoad() {
super.viewDidLoad()
self.tapper = NSClickGestureRecognizer(target: self, action: "didClick")
self.view.addGestureRecognizer(self.tapper!)
}
func didClick() throws {
print("did click")
}
2015-07-27 00:16:43.260 Test1[71047:54227175] -[Test1.ViewController didClick]: unrecognized selector sent to instance 0x6000000c5010
...
I believe you will need to rework your code to handle the error internally in your action function so that it does not throw - something like this:
func proFormulaTapGesture() {
print("proFormulaTapGesture")
selectView(proFormulaView)
selectedMenuItem = 0
Formula = 1
menuTabBarController.selectedIndex = 0
navigationController?.navigationBar.topItem!.title = "BASIC FORMULA"
do {
try (menuTabBarController.viewControllers as! [SuggestionsViewController])[0].loadSuggestions()
} catch {
// Handle the error here somehow
}
}
If you are unable to handle the error within proFormulaTapGesture, you'll have to be more creative with passing it out of the function (think NSNotification, etc...).
To be clear, I created a new Single View iOS application in Xcode 7 beta 4, and used this exact code as my ViewController. No other changes. The app compiles and works just fine in the iOS simulator.
#robertvojta also points out that if didTap below is marked private, #objc is also required to avoid a dynamic dispatch crash.
import UIKit
class ViewController: UIViewController {
var tapper: UITapGestureRecognizer?
override func viewDidLoad() {
super.viewDidLoad()
self.tapper = UITapGestureRecognizer(target: self, action: "didTap")
self.view.addGestureRecognizer(self.tapper!)
}
func didTap() {
print("did Tap")
}
}
(Note I also tested on an OS X project, which is the code I used earlier in the answer).
With the code below, the local songs variable is never able to be iterated despite all the checks to the contrary ( println shows the value stored ). The other thing is that the Xcode debugger seems to jump all over the place in the init method.
let gLibraryManager = LibraryManager()
class LibraryManager {
var Songs = Dictionary<String, String>()
init() {
println("struct being initialized from NSDefaults")
let userDefaults = NSUserDefaults.standardUserDefaults();
var result:AnyObject = userDefaults.objectForKey(LIKED_LIST)
println(result)
var local = result as? Dictionary<String,String>
if local != nil {
println("local not nil: \(local!)")
for (id,title) in local! {
Songs[id] = title
}
if Songs.count > 0 {
println("NSDefaults detected: \(Songs)")
} else {
println("no NSDefaults detected. Initializing empty")
}
}
}
ok. i figured out what is was.
I had set the Swift Compiler - Code Generation. Optimization level to -Fastest. This was to prevent the extremely slow creation of Dictionaries.
However, it appears this breaks the ability to iterate structures.
It also seems to resolve the weird bouncing around of breakpoints.
This was a needle in a haystack that tooks many hours. I guess the moral of the story is not to mess with compiler flags yet.