Swift optimization level 'Fastest' breaks sorting of array - ios

I have a really strange issue. I'm sorting an array of NSDictionary objects in my app, but it only works correctly when the app is running from Xcode. As soon as I distribute the app and install & run it on a device, the sorting no longer works.
Here's the code can be run in a playground, with some example NSDictionary objects. The code in the app is the same.
import UIKit
let p1 = NSDictionary(objects: ["Zoe", 32], forKeys: ["name", "age"])
let p2 = NSDictionary(objects: ["Adrian", 54], forKeys: ["name", "age"])
let p3 = NSDictionary(objects: ["Jeff", 23], forKeys: ["name", "age"])
let p4 = NSDictionary(objects: ["", 66], forKeys: ["name", "age"])
let p5 = NSDictionary(objects: [23], forKeys: ["age"])
let persons = [p1,p2,p3,p4,p5]
let sortedPersons = persons.sorted { (p1, p2) -> Bool in
(p2["name"] as? String) > (p1["name"] as? String)
}
As you can see, sorting in the playground does work correctly. Does anyone know what could be wrong?
Update
I found that the Swift Optimization Level is causing the problem. Setting this to -O (Fastest) will cause the sort to fail. Setting it to -Onone (None) will cause the sort to work properly.
Does anyone have any suggestions on how to change the code, so it will work with -O optimization?
Update 2
I've filed a bug report at Apple. For the time being I'm using an NSSet to sort the array, which seems to be working fine.
Last update
I haven't been able to reproduce this since Xcode 6.1.1

This appears to be down to your naming convention within your sorted closure. Changing (p1, p2) to different names will resolve it. With -Ofastest, the compiler seems to be incorrectly doing 2 things:
1) causing p1 and p2 within the closure to refer to the NSDictionarys themselves rather than the closure parameters
2) cleaning up the references to the NSDictionary objects prematurely, given #1
Change the code so the last section shows:
let sortedPersons = persons.sorted { (d1, d2) -> Bool in
(d2["name"] as? String) > (d1["name"] as? String)
}

Related

Update firebase database with time-based key on iOS

I'm using Firebase and iOS to keep a time series of an integer value over the last 24 hours. I have tried using both the .updateChildValues() and .setValue() from the docs, but haven't yet figured out how to keep firebase from overwriting every child value, not just the child value with the same key.
func writeStepsPost(withUserID userID: String, steps: NSNumber) {
let timeStamp = NSDate().timeIntervalSince1970 as NSNumber
let post_user: NSString = userID as NSString
let value: NSNumber = steps
let post = ["uid": post_user,
"steps": value,
"lastUpdate":timeStamp]
let childUpdates = ["/posts-steps-user/\(userID)/": post]
ref.updateChildValues(childUpdates)
let currentHour = Calendar.current.component(.hour, from: Date())
let hourlyPost = ["steps":value]
let dailyUpdates = ["/posts-steps-user/\(userID)/pastDay/\(currentHour):00/": hourlyPost]
print("posting hourly steps update")
ref.updateChildValues(dailyUpdates)
When the time changes from 10 to 11, the node for '10:00':123 is replaced by '11:00':243, when I need to add a node for 11 while leaving 10 in place until the next day. I suspect that since the function is pushing two updates, the first update replaces the existing node.
Any ideas?
Given a single key path like /posts-steps-user/\(userID)/, updateChildValues only updates data at the first child level, and any data passed in beyond the first child level is a treated as a setValue operation. Multi-path behavior allows longer paths to be used without overwriting data. It is documented very well in this Firebase Documentation.
I've tested with the following adjustment to your code where I define multiple paths for your first updateChildValues so it won't overwrite your pastDay and it is working properly.
let childUpdatePath = "/posts-steps-user/\(userID)/"
ref.updateChildValues([
"\(childUpdatePath)/uid": post_user,
"\(childUpdatePath)/steps": value,
"\(childUpdatePath)/lastUpdate": timeStamp
])
let currentHour = Calendar.current.component(.hour, from: Date())
let hourlyPost = ["steps":value]
let dailyUpdates = ["/posts-steps-user/\(userID)/pastDay/\(currentHour):00/": hourlyPost]
ref.updateChildValues(dailyUpdates)

passing data between sirikit intent handler and the app

ios does not let the containing app and the contained extensions to share a common container, so UserDefaults is the proposed solution.
I have tried using UserDefaults with sirikit intent handler assuming the handler behaves as an extension as follows :
inside IntentHandler.swift
let shared = UserDefaults(suiteName:XXXXXXXX.group...)
shared?.set("saved value 1", forKey: "key1")
shared?.set("saved value 2", forKey: "key2")
shared?.set("saved value 3", forKey: "key3")
inside ViewController.swift in viewDidLoad
let shared = UserDefaults(suiteName:XXXXXXXX.group...)
if let temp1 = shared?.string(forKey:"key1")
{
contentLabel.text = temp1
}
if let value = shared?.string(forKey: "key2")
{
valueLabel.text = value
}
if let key = shared?.string(forKey: "key3")
{
keyLabel.text = key
}
i can see the strings corresponding to key1 and key2 on my ipad screen but not for key3, peppering the code with synchronizes does not help.
here are my questions :
1) are sirikit handlers different from other extensions? if yes how to pass data to my app? if not am i using UserDefaults incorrectly?
2) is there a better way to handle IPC between the app and its extensions where i just need to pass simple string messages between them.
using swift 3.0 and xcode 8.2.1
Check that you have the App Group enabled for all targets you want to access the group from. Check in project -> your target -> capabilities under "App Groups".
There's something called MMWomhole. It will definitely do the work.

UITextChecker completionsForPartialWordRange

I'm building custom keyboard extension and want to implement autocompletion, like Apple does.
As I see method completionsForPartialWordRangereturns list of words sorted alphabetically. How can I get results sorted by usage?
The docs for completionsForPartialWordRange:inString:language: say:
The strings in the array are in the order they should be presented to the user—that is, more probable completions come first in the array.
However, the results are very clearly sorted in alphabetical order, and it's not true that "more probable completions come first in the array." The below was tested with iOS 9:
NSString *partialWord = #"th";
UITextChecker *textChecker = [[UITextChecker alloc] init];
NSArray *completions = [textChecker completionsForPartialWordRange:NSMakeRange(0, partialWord.length) inString:partialWord language:#"en"];
iOS word completions for "th":
thalami,
thalamic,
thalamus,
thalassic,
thalidomide,
thallium,
...
the,
...
So, the results will need to be sorted again after obtaining the word completions.
The OS X NSSpellChecker version of this method does not have the same problem:
NSString *partialWord = #"th";
NSArray *completions = [[NSSpellChecker sharedSpellChecker] completionsForPartialWordRange:NSMakeRange(0, partialWord.length) inString:partialWord language:#"en" inSpellDocumentWithTag:0];
List of complete words from the spell checker dictionary in the order they should be presented to the user.
Mac OS X word completions for "th":
the,
this,
that,
they,
thanks,
there,
that's,
...
Filing a radar bug report would be a good idea, so that the behavior will hopefully be fixed in a later version of iOS. I've reported this as rdar://24226582 if you'd like to duplicate.
Swift 4.0
func autoSuggest(_ word: String) -> [String]? {
let textChecker = UITextChecker()
let availableLangueages = UITextChecker.availableLanguages
let preferredLanguage = (availableLangueages.count > 0 ? availableLangueages[0] : "en-US");
let completions = textChecker.completions(forPartialWordRange: NSRange(0..<word.utf8.count), in: word, language: preferredLanguage)
return completions
}

ReactiveCocoa combine SignalProducers into one

I'm using ReactiveCocoa and I have several SignalProducers
let center = NSNotificationCenter.defaultCenter()
let signalProducer1 = center.rac_notification(name: notificationName1, object: nil)
let signalProducer2 = center.rac_notification(name: notificationName2, object: nil)
let signalProducer3 = center.rac_notification(name: notificationName3, object: nil)
I want to combine them into a single signal producer that produces a signal whenever one of them produces a signal.
At first the combineLatest function looked like a good solution
let combinedProducer = combineLatest(signalProducer1, signalProducer2, signalProducer3)
However, according to this article, the resulting producer only produces its first signal when all the three have produced a signal.
This interactive diagram shows exactly what I want, so I want to use the flatten function with the .Merge FlatteningStrategy. However, I'm having a hard time figuring out the syntax to achieve this.
Update: RAC 4.2.1 and upwards
Due to changes in how flatten works we need to help the compiler an be more explicit about the types:
let s1: SignalProducer<Int, NSError> = ...
let s2: SignalProducer<Int, NSError> = ...
let s3: SignalProducer<Int, NSError> = ...
let _: SignalProducer<Int, NSError> =
SignalProducer<SignalProducer<Int, NSError>, NSError>(values: [s1, s2, s3])
.flatten(.Merge)
That becomes a bit cumbersome, so you might want to split it:
let producers: SignalProducer<SignalProducer<Int, NSError>, NSError> =
SignalProducer(values: [s1, s2, s3])
let merged: SignalProducer<Int, NSError> = x.flatten(.Merge)
Thanks #Harry for the comment pointing the new version issue out.
RAC 4.2 and below
In RAC 4 this would be
let merged = SignalProducer(values: [signalProducer1, signalProducer2, signalProducer3])
.flatten(.Merge)
At the moment Xcode 7.1.1 doesn't suggest .flatten in the autocompletion window, which might result in you (or just me) thinking it is not there, but if you type it all it will work.
You can achieve that as follows:
let merged = SignalProducer(values: [ signalProducer1, signalProducer2, signalProducer3 ])
|> flatten(.Merge)

Swift: Extracting / downcasting CFType based CoreText types in a CFArray

I am trying to port elements of the CoreAnimationText sample to Swift. I cannot figure out though, how to extract or downcast the elements of CTRun from an array, in order to pass them to functions that expect and act upon the Swift-ified CTRun type. I either get runtime errors or linking errors from the playground snippet below
import CoreText
import QuartzCore
let text = NSAttributedString(string: "hello")
var line: CTLine = CTLineCreateWithAttributedString(text)
var ctRuns:CFArray = CTLineGetGlyphRuns(line)
let nsRuns:Array<AnyObject> = ctRuns as NSArray
nsRuns.count // == 1
// Playground execution failed: error: error: Couldn't lookup symbols:_OBJC_CLASS_$_CTRun
let nsRun = nsRuns[0] as CTRun
nsRun.self
println(nsRun.self)
let anyRuns = nsRuns as AnyObject[]
// can't unwrap Optional null
let anyRun = anyRuns[0] as CTRun
anyRun.self
println(anyRun.self)
let cft:AnyObject = anyRuns[0]
// CTRun is not contstructable with AnyObject
let runGlyphCount = CTRunGetGlyphCount(CTRun(cft));
// Can't unwrap Optional.None
let concreteRuns = anyRuns as CTRun[]
let concreteRun = concreteRuns[0] as CTRun
concreteRun.self
println(concreteRun.self)
Any ideas - am I missing something obvious? From the WWDC sessions and interop guide, I am led to believe that "Swift just takes care of this".
According to dev forums, this functionality is implemented in 6.1.
All your sample lines compile without errors and work as expected in
the latest Xcode, 6.1 (6A1052c) .
Interoperability with CF objects is impoving. You may find another
issues, in such case it'd be reported as bug.

Resources