didChangeAutomaticCapitalizationNotification not triggered - cocoa

What am I doing wrong? I don't get this notification. I have this function:
#objc func onAutocorrection (_ notification: Foundation.Notification) {
Swift.print("\(notification)")
}
later in the same class I do use it as follows:
NotificationCenter.default.addObserver(
self,
selector: #selector(onAutocorrection(_:)),
name: NSSpellChecker.didChangeAutomaticCapitalizationNotification,
object: nil)
The addObserver is executed, but the function is never called even when the application is capitalising in an NSTextView.
Why? Many thanks in advance!

It looks like I misunderstood the notification. It is not meant to be triggered when automatic capitalisation happens but when the systems preference of your Mac is changing.
See the comment of ever helpful Willeke and see Notification of autocorrect
In order to get to the intended result of reacting to autocapitalisation did I implement this function in the NSTextViewDelegate:
public func textView(_ view: NSTextView, didCheckTextIn range: NSRange, types checkingTypes: NSTextCheckingTypes, options: [NSSpellChecker.OptionKey : Any] = [:], results: [NSTextCheckingResult], orthography: NSOrthography, wordCount: Int) -> [NSTextCheckingResult] {
if !range.contains(0){
return results
}
var newResult = [NSTextCheckingResult]()
for result in results {
if let textToChange = view.string[range].components(separatedBy: " ").first, let replacement = result.replacementString?.components(separatedBy: " ").first {
let firstLetterCap = textToChange.capitalizingFirstLetter()
if replacement == firstLetterCap {
continue //don't add to results
}
}
newResult.append(result)
}
return newResult
}
This function will prevent that the first character will be capitalised.
Ultimately, I check whether the capitalised version of the first word of the range that must include position "0" is equal to the first word of the replacement string. And if it is then I remove that result/suggestion from the result list.

Related

is anyone able to restrict the type of the objects dropped on the mac in SwiftUI 3?

as per the documentation, it should be pretty straightforward. example for a List: https://developer.apple.com/documentation/swiftui/list/ondrop(of:istargeted:perform:)-75hvy#
the UTType should be the parameter restricting what a SwiftUI object can receive. in my case i want to accept only Apps. the UTType is .applicationBundle: https://developer.apple.com/documentation/uniformtypeidentifiers/uttype/3551459-applicationbundle
but it doesn't work. the SwiftUI object never changes status and never accepts the drop. the closure is never run. whether on Lists, H/VStacks, Buttons, whatever. the pdf type don't seem to work either, as well as many others. the only type that i'm able to use if fileURL, which is mainly like no restriction.
i'm not sure if i'm doing something wrong or if SwiftUI is half working for the mac.
here's the code:
List(appsToIgnore, id: \.self, selection: $selection) {
Text($0)
}
.onDrop(of: [.applicationBundle, .application], isTargeted: isTargeted) { providers in
print("hehe")
return true
}
replacing or just adding .fileURL in the UTType array makes the drop work but without any type restriction.
i've also tried to use .onInsert on a ForEach instead (https://developer.apple.com/documentation/swiftui/foreach/oninsert(of:perform:)-2whxl#), and to go through a proper DropDelegate (https://developer.apple.com/documentation/swiftui/dropdelegate#) but keep getting the same results. it would seem the SwiftUI drop for macOS is not yet working, but i can't find any official information about this. in the docs it is written macOS 11.0+ so i would expect it to work?
any info appreciated! thanks.
You need to validate manually, using DropDelegate of what kind of file is dragged over.
Here is a simplified demo of possible approach. Tested with Xcode 13 / macOS 11.6
let delegate = MyDelegate()
...
List(appsToIgnore, id: \.self, selection: $selection) {
Text($0)
}
.onDrop(of: [.fileURL], delegate: delegate) // << accept file URLs
and verification part like
class MyDelegate: DropDelegate {
func validateDrop(info: DropInfo) -> Bool {
// find provider with file URL
guard info.hasItemsConforming(to: [.fileURL]) else { return false }
guard let provider = info.itemProviders(for: [.fileURL]).first else { return false }
var result = false
if provider.canLoadObject(ofClass: String.self) {
let group = DispatchGroup()
group.enter() // << make decoding sync
// decode URL from item provider
_ = provider.loadObject(ofClass: String.self) { value, _ in
defer { group.leave() }
guard let fileURL = value, let url = URL(string: fileURL) else { return }
// verify type of content by URL
let flag = try? url.resourceValues(forKeys: [.contentTypeKey]).contentType == .applicationBundle
result = flag ?? false
}
// wait a bit for verification result
_ = group.wait(timeout: .now() + 0.5)
}
return result
}
func performDrop(info: DropInfo) -> Bool {
// handling code is here
return true
}
}

ORSSerialPort doesn't send data

I am junior Swift user but on this stage of life I need use serial connection in my project.
when I looking for availeble port ,I see them without problem but when try to send something I have problem why?
I have problem with ORSSerial this my code:
func applicationDidFinishLaunching(_ aNotification: Notification) {
let portEvString = ORSSerialPortManager.shared().availablePorts
let pathString = portEvString[0].path
let portClass = SerialController(path:pathString)
portClass.open()
portClass.SendString(data: "hello")
// Insert code here to initialize your application
RunLoop.current.run()
}
class SerialController : NSObject, ORSSerialPortDelegate {
var port : ORSSerialPort?
init(path: String){
port = ORSSerialPort(path: path)
port?.close()
}
func open(){
port?.baudRate=9600
port?.delegate=self
port?.open()
}
func close(){
port?.delegate=nil
port?.close()
}
func SendString(data: String){
let dataa = Data(data.utf8)
port?.send(dataa)
}
func serialPortWasOpened(serialPort: ORSSerialPort!) {
print("PORT IS OPEN....")
}
func serialPortWasClosed(serialPort: ORSSerialPort!) {
print("PORT IS CLOSE")
}
func serialPort(serialPort: ORSSerialPort!, didReceiveData data: NSData!) {
print(NSString(data: data as Data, encoding: String.Encoding.utf8.rawValue))
}
func serialPortWasRemovedFromSystem(_ serialPort: ORSSerialPort!) {
print("PORT REMOVED")
}
func serialPort(serialPort: ORSSerialPort!, didEncounterError error: NSError!) {
print("PORT ERR \(error)")
}
}
I believe the primary problem is that you don't store the instance of SerialController in a strong reference, so it gets deallocated immediately after you call SendString() on it. You need to store it in a property like so:
var serialController: SerialController?
func applicationDidFinishLaunching(_ aNotification: Notification) {
let availablePorts = ORSSerialPortManager.shared().availablePorts
let pathString = availablePorts[0].path
serialController = SerialController(path:pathString)
serialController?.open()
serialController?.SendString(data: "hello")
}
Beyond that problem, there are a number of things about this code that you should fix. The things that stick out most:
Don't call RunLoop.current.run() in applicationDidFinishLaunching(_:). You rarely need to manually run the run loop, and you definitely don't need to here.
As documented in ORSSerialPort, you must release any strong references to the port in your implementation of serialPortWasRemovedFromSystem(_:). That means in that method, you need to do port = nil.
The force unwrap operators (!) scattered through this code make me wonder if you're using an old version of ORSSerialPort, from before I added nullability annotations. Please make sure you're using the latest version (2.0.2 as of this writing).
Use more descriptive names for your variables. For example, portEvString is not a good name, because it's difficult to understand (what does "Ev" mean?), and its type is not a string, it's an array of ORSSerialPort objects (ie. [ORSSerialPort].
Your SendString method shouldn't start with a capital letter. Function (and method) names in Swift should always start with a lowercase letter.
After the disable SandBox, everything is OK.

Reusable XCTests for several products

I need UI tests for XCode project that is a platfrom for several products.
It means that some elements are custom for some products. For instance, they can have different colors, text styles, etc. from product to product, or the same element could be visible for one project, but hided for another one.
How can I configure my XCode UI tests to make them reusable for various products? I understand I need different schemas. But what about visability elements, for example? It seems I need to check it inside the UI test code? But I think it would be better to use any config file. Am I right? Does anyone have any ideas? I'd be grateful for all advices.
I recommend building out a suite of test helpers that you use in each of your products. These are generic, parameterized functions, such as logging a user in or adding an item to the shopping cart. Instead of hard coding your UI element representations in these helpers, parameterize the inputs. Then you can use them over and over.
func addItemToCart(named: String, saveButtonName: String)
func login(username: String, password: String, submitButtonText: String)
func tapTableCell(imageNamed: String)
Once you create a basic scaffolding for navigation you can move on to assertion helpers. Leaving the complex logic in the helpers enables you to reuse them and keep your product-specific tests lean and readable.
func assertCurrentScreen(named: String)
func assertHighlightedCell(colorNamed: String)
func assertCartTotal(cents: String, containerIdentifier: String)
For all of these functions, I recommend adding two default parameters at the end to note the callers file and line number. If you make any custom assertions you can then pass these references in to show your failure at the callers line, not the helpers.
func assertScreen(titled: String, file: StaticString = #file, line: UInt = #line) {
if !XCUIApplication().navigationBars[titled].exists {
XCTFail("Item was not added to cart.", file: file, line: line)
}
}
I am using a function at the beginning of each test.
class SomeTestsClass: XCTestCase {
func testSomeTest() {
var externalConfigVariable_1 = "value for defult test"
var externalConfigVariable_2 = "value for defult test"
// here You use the external config to override
// the default test logc
if let testConfig = getConfig(for: self.name) {
// read config parameters here
externalConfigVariable_1 = testConfig["test_var_1"]
externalConfigVariable_2 = testConfig["test_var_2"]
// ..........
}
// use the variables as You like
// ......
}
}
extension XCTestCase {
public func getConfig(for name: String?) -> [String: Any]? {
let nameComponents = name?.replacingOccurrences(of: "-", with: "").replacingOccurrences(of: "[", with: "").replacingOccurrences(of: "]", with: "").components(separatedBy: " ")
if let fileName = nameComponents?.last {
let testBundle = Bundle(for: type(of: self))
guard let path = testBundle.url(forResource: fileName, withExtension: "JSON") else {
return nil
}
if let data = try? Data(contentsOf: path) {
if let testConfig = (try? JSONSerialization.jsonObject(with: data, options: .allowFragments)) as? [String: Any] {
return testConfig
}
}
}
return nil
}
}
Here is an example JSON:
{
"test_var_1": "Some var",
"test_var_2": "Some other var"
}

PromiseKit 3.0: chaining with loops

I'm using promisekit 3.0 to help chain alamofire callbacks in a clean way. The objective is to start with a network call, with a promise to return an array of urls.
Then, I'm looking to execute network calls on as many of those urls as needed to find the next link i'm looking for. As soon as this link is found, I can pass it to the next step.
This part is where I'm stuck.
I can pick an arbitrary index in the array that I know has what I want, but I can't figure out the looping to keep it going until the right information is returned.
I tried learning from this obj-c example, but i couldn't get it working in swift.
https://stackoverflow.com/a/30693077/1079379
He's a more tangible example of what i've done.
Network.sharedInstance.makeFirstPromise(.GET, url: NSURL(string: fullSourceLink)! )
.then { (idArray) -> Promise<AnyObject> in
let ids = idArray as! [String]
//how do i do that in swift? (from the example SO answer)
//PMKPromise *p = [PMKPromise promiseWithValue: nil]; // create empty promise
//only thing i could do was feed it the first value
var p:Promise<AnyObject> = Network.sharedInstance.makePromiseRequestHostLink(.POST, id: ids[0])
//var to hold my eventual promise value, doesn't really work unless i set it to something first
var goodValue:Promise<AnyObject>
for item in ids {
//use continue to offset the promise from before the loop started
continue
//hard part
p = p.then{ returnValue -> Promise<AnyObject> in
//need a way to check if what i get is what i wanted then we can break the loop and move on
if returnValue = "whatIwant" {
goodvalue = returnValue
break
//or else we try again with the next on the list
}else {
return Network.sharedInstance.makeLoopingPromise(.POST, id: item)
}
}
}
return goodValue
}.then { (finalLink) -> Void in
//do stuck with finalLink
}
Can someone show me how to structure this properly, please?
Is nesting promises like that anti-pattern to avoid? In that case, what is the best approach.
I have finally figured this out with a combination of your post and the link you posted. It works, but I'll be glad if anyone has input on a proper solution.
func download(arrayOfObjects: [Object]) -> Promise<AnyObject> {
// This stopped the compiler from complaining
var promise : Promise<AnyObject> = Promise<AnyObject>("emptyPromise")
for object in arrayOfObjects {
promise = promise.then { _ in
return Promise { fulfill, reject in
Service.getData(stuff: object.stuff completion: { success, data in
if success {
print("Got the data")
}
fulfill(successful)
})
}
}
}
return promise
}
The only thing I'm not doing is showing in this example is retaining the received data, but I'm assuming you can do that with the results array you have now.
The key to figuring out my particular issue was using the "when" function. It keeps going until all the calls you inputted are finished. The map makes it easier to look at (and think about in my head)
}.then { (idArray) -> Void in
when(idArray.map({Network.sharedInstance.makePromiseRequest(.POST, params: ["thing":$0])})).then{ link -> Promise<String> in
return Promise { fulfill, reject in
let stringLink:[String] = link as! [String]
for entry in stringLink {
if entry != "" {
fulfill(entry)
break
}
}
}
}.then {
}
}

Swift 2: Unexpectedly writing to a property when trying to read it

The problem I'm having is that my property's willSet and didSet are being called even though I'm only reading the property, and this breaks my app.
I condensed my problem into a playground. Uncomment #1 to see the problem, or #2 to see the expected behavior.
What's going on here?
protocol Departure
{
var line: String? { get }
}
class MyDeparture : Departure
{
var line: String? = "SomeString"
}
#if true
// #1: this causes a write to tableContet later (!)
typealias TableSection = (title: String, rows: [Departure])
#else
// #2: this doesn't cause a write to tableContent later
typealias TableSection = (title: String, rows: [MyDeparture])
#endif
var tableContent: [TableSection] = [ TableSection(title: "MySectionTitle", rows: [ MyDeparture() ]) ]
{
willSet { print("willSet tableContent") }
didSet { print("didSet tableContent") }
}
func getDepartureDescription() -> String? {
print("start getDepartureDescription")
defer { print("end getDepartureDescription") }
#if true
// writes to tableContent in case #1
let lineNumber = tableContent[0].rows[0].line
#else
// never writes to table content
let row = tableContent[0].rows[0]
let lineNumber = row.line
#endif
return "Your line is \(lineNumber)"
}
getDepartureDescription()
This prints
start getDepartureDescription
willSet tableContent
didSet tableContent
end getDepartureDescription
I'm using Xcode 7 (7A218) GM seed. Everything worked as expected in Xcode 6.4 and Swift 1.2.
Side note:
At first I thought that the runtime was--on reading TableSection.rows--creating a new [Departure] array from the [MyDeparture] array that was assigned to it. But even correcting for that in the most explicit way I could think of didn't get rid of the problem:
// More correct types makes no difference:
var departures: [Departure] {
var result = Array<Departure>()
result.append(MyDeparture())
return result
}
var tableContent: [TableSection] = [ TableSection(title: "MyTitle", rows: departures ) ]
There's some sort of lazy initialisation going on. It's only when it gets to the line
let lineNumber = tableContent[0].rows[0].line
that the run time seems to be filling in the contents of the array.
If you declared the array as containing elements that conform to the Departure protocol, the runtime does not know how big the elements of tableContent actually are because both classes and structs can conform to Departure. Hence, I think it is recreating the array and triggering didSet and willSet erroneously.
I tried limiting your protocol to classes like so:
protocol Departure: class
{
var line: String? { get }
}
and the problem goes away because now the runtime knows the array can only contain class references.
I think this is a bug and you should raise it with Apple.

Resources