Swift 4: No such module 'CoreServices.DictionaryServices' - ios

I was reading an article that talks about Core Services and decided to use one of them. I import Core Services and add the library on XCode, but it doesn't work.
Sample code:
import Foundation
import CoreServices.DictionaryServices
func define(_ word: String) -> String? {
let nsstring = word as NSString
let cfrange = CFRange(location: 0, length: nsstring.length)
guard let definition = DCSCopyTextDefinition(nil, nsstring, cfrange) else {
return nil
}
return String(definition.takeUnretainedValue())
}
define("apple") // "apple | ˈapəl | noun 1 the round fruit of a tree..."
It gives a "no such module" error when I import it. I also look at Apple's documentation but there is no explanation for how to implement and use it.
The Solution:
Thanks to people who answered below my question, I made a little research and found the solution. Apparently, I can use the iOS dictionary by calling the UIReferenceLibraryViewController, but the Apple documentation says that we should not use this making for a dictionary app. It is obvious that it is not sufficient to make a dictionary app because it uses its own ViewController and is not customizable.
Here is the sample working code:
let dic = UIReferenceLibraryViewController(term: textLabelOutlet.text as! String)
dic.modalPresentationStyle = .popover // add this
let popover = dic.popoverPresentationController
popover?.sourceView = view
popover?.sourceRect = CGRect(x: 32, y: 32, width: 64, height: 64)
present(dic, animated: true)
Source: UIReferenceLibraryViewController cannot be presented as popup (always covers full screen)

CoreServices may be available on all platforms but DictionaryServices seems to be available on macOS only.
https://developer.apple.com/documentation/coreservices/1446842-dcscopytextdefinition
SDK lists only: macOS 10.5+

CoreServices.DictionaryServices is a framework of OS X, not of iOS.
you could click CoreServices.DictionaryServices, and see the page in a mac app project.
/*
DictionaryServices.h
DictionaryServices framework
*/
/*!
#typedef DCSDictionaryRef
#abstract Opaque CF object that represents a dictionary file
*/
public class DCSDictionary {
}
#available(OSX 10.5, *)
public func DCSGetTermRangeInString(_ dictionary: DCSDictionary?, _ textString: CFString, _ offset: CFIndex) -> CFRange

Related

Value of type 'MTLBuffer' has no member 'didModifyRange'

I am confused as I have created a MTLBuffer in Swift 4 yet am unable to use the method didModifyRange.
Interestingly enough I can still find this in the Apple documentation and have not heard of this being changed.
Why is the error Value of type 'MTLBuffer' has no member 'didModifyRange' happening?
The following code would generate this error in the latest version of XCode
let device = MTLCreateSystemDefaultDevice()
var buffer = device?.makeBuffer(length: 3, options: [])
let range = Range<Int>(NSRange())
buffer.didModifyRange(range)
According to documentation, signature of the method looks like this:
func didModifyRange(_ range: Range<Int>)
You pass NSRange which is obviously different from Swift Range<Int>. So to get it work, simply pass proper range object.
P.S. Range<Int> is defined with min...max scheme (e.g. 0...100).
EDIT:
Some Metal framework signatures are only available on macOS 11.1, including didModifyRange:, so if you try to call it on iOS, even having import Metal in the header, will give you that error.
So the following code will compile under macOS 11.1
import Metal
// ...
guard
let device = MTLCreateSystemDefaultDevice(),
let buffer = device.makeBuffer(length: 3, options: [])
else {
return
}
buffer.didModifyRange(Range<Int>(1...10))
...and will not, under iOS.
Once you have initialised your MTLBuffer buffer you can obtain a typed pointer to it using
let x = buffer.contents().bindMemory(to: Float.self, capacity: 1000)
This returns the same address as buffer.contents() but it's stride is aligned to the size of Float, where you have registered there being capacity floats in total. Change these parameters as required.
Now you can directly update/access the data via
x[0] = 3.14
x[5] = 1.59
etc.
See Apples's bindMemory documentation for more details.
On iOS there is no didModifyRange(). It's only available on macOS.
Instead I ended up doing like this:
var myVertices: [MyVertex]
...
myVertices[0].color = float4(x: 1, y: 0, z: 0, w: 1)
...
mltBuffer.contents().copyMemory(
from: myVertices,
byteCount: myVertices.count * MemoryLayout<MyVertex>.stride
)

Apple Watch input values via scribble only

I'm working on WatchKit App. In this app, there are some fields that the user should fill it,
I searched how to deal with input fields in iWatch, and I found the following code:
presentTextInputController(withSuggestions: ["1"], allowedInputMode: WKTextInputMode.plain) { (arr: [Any]?) in
if let answers = arr as? [String] {
if let answer = answers[0] as? String {
self.speechLabel.setText(answer)
}
}
}
and this code gives me two choices: Diction and scribble, i.e
In my App, I want to support only the scribble not both of them,
I tried to pass withSuggestions parameter as nil, but the app direct me to dictiation, not to scribble.
Is there a way to let the user only use scribble?

Swift framework for iOS and macOS

currently i'm developing an app for my graduation project. The problem is, that it's not just one app, it consists of an iOS app that is made for the users and a macOS app for the "Owner". In the mac App the owner can create a file that should then be red in the iOS app.
Until now I have made a script that has all the necessary variables to transport the information, it can serialise and deserialise a file and assign it to the UI. Everything worked fine as long as I was working on the macOS app, the files can be created, deserialised and so on, however when I tried to go to the iOS app, I copied the exact same script that serves as the dataModel into the project and when I tried to deserialise a file created in the macOS file the app crashed and gave me an error saying:
cannot decode object of class (ExcursionCreator.ExcursionDataModel)
for key (root); the class may be defined in source code or a library
that is not linked'
I investigated on the error and found out that i cannot just copy the script from one project to the other, so I found a source that said that I should create a framework with just this file, however i haven't found any useful topics that talk about frameworks for cross platform.
So my question in a few words is, is it true that I have to create a framework for this purpose and if so how would I do it so it works on macOS as well on iOS. Any other advice would also be greatly appreciated. Thanks in advance Jorge. :)
Update
As asked by danielv here is my code where I store the information, it also conforms to the NSCoding protocol so i can serialise and deserialise it:
import Foundation
class ExcursionDataModel: NSObject, NSCoding {
var title: String
var imagesData = [Data]()
var thumbnailImageData: Data?
var shortText = String()
var completeText = String()
var difficulty: String = "Hard"
var duration: String = "Long"
var isFavourite: Bool = false
var type: excursionType = .other
enum excursionType: String {
case biking
case rafting
case other
}
static let possibleDifficulties = ["Hard", "Medium", "Easy"]
static let possibleDurations = ["Long", "Medium", "Short"]
init (title: String) {
self.title = title
}
override init() {
self.title = "Title"
}
struct PropertyKey {
static let titleKey = "title"
static let imagesKey = "images"
static let thumbnailImageKey = "thumbnailImage"
static let shortTextKey = "shortText"
static let completeTextKey = "completeText"
static let difficultyKey = "difficulty"
static let durationKey = "duration"
static let isFavouriteKey = "isFavourite"
static let typeKey = "type"
}
//MARK: - NSCoding
func encode(with aCoder: NSCoder) {
aCoder.encode(title, forKey: PropertyKey.titleKey)
aCoder.encode(imagesData, forKey: PropertyKey.imagesKey)
aCoder.encode(thumbnailImageData, forKey: PropertyKey.thumbnailImageKey)
aCoder.encode(shortText, forKey: PropertyKey.shortTextKey)
aCoder.encode(completeText, forKey: PropertyKey.completeTextKey)
aCoder.encode(difficulty, forKey: PropertyKey.difficultyKey)
aCoder.encode(duration, forKey: PropertyKey.durationKey)
aCoder.encode(isFavourite, forKey: PropertyKey.isFavouriteKey)
aCoder.encode(type.rawValue, forKey: PropertyKey.typeKey)
}
required convenience init?(coder aDecoder: NSCoder) {
let title = aDecoder.decodeObject(forKey: PropertyKey.titleKey) as! String
self.init(title: title)
self.imagesData = aDecoder.decodeObject(forKey: PropertyKey.imagesKey) as! [Data]
self.thumbnailImageData = aDecoder.decodeObject(forKey: PropertyKey.thumbnailImageKey) as? Data
self.shortText = aDecoder.decodeObject(forKey: PropertyKey.shortTextKey) as! String
self.completeText = aDecoder.decodeObject(forKey: PropertyKey.completeTextKey) as! String
self.difficulty = aDecoder.decodeObject(forKey: PropertyKey.difficultyKey) as! String
self.duration = aDecoder.decodeObject(forKey: PropertyKey.durationKey) as! String
self.isFavourite = aDecoder.decodeObject(forKey: PropertyKey.isFavouriteKey) as! Bool
self.type = excursionType(rawValue: aDecoder.decodeObject(forKey: PropertyKey.typeKey) as! String)!
}
}
From NSCoder documentation:
NSCoder operates on objects, scalars, C arrays, structures, and
strings, and on pointers to these types. It does not handle types
whose implementation varies across platforms, such as union, void *,
function pointers, and long chains of pointers. A coder object stores
object type information along with the data, so an object decoded from
a stream of bytes is normally of the same class as the object that was
originally encoded into the stream.
The class that you encoded in your MacOS app is ExcursionCreator.ExcursionDataModel. Your iOS app, however, probably has different module name, so even if you include the exact same swift class in your iOS app, the actual namespaced class name would be different, e.g ExcursionIOSApp.ExcursionDataModel.
NSCoder has no idea these are the same classes, when it tries to decode the ExcursionDataModel it doesn't find it, so you get the error.
Your options are:
Option #1
Use an open portable format for your serializations. JSON is a popular choice but there are others. This has a benefit of reusability in platforms outside of Apple's ecosystem, if you later decide to port your app to, say, Android.
There is a foundation JSON serialization support in MacOS/iOS and many open source projects that offer better/additional JSON support.
Option #2
Use a different class that shares same implementation and name across platform. For example, you could use NSDictionary as your data structure. Just be sure that everything you put there is compliant with the restrictions required by NSCoder
Option #3
Put your data class in a framework. This will force the class to have the same name when used from both projects.
To create a framework, choose a MacOS framework project when creating a new projects. Add your ExcursionDataModel to this framework.
Note that you will need to compile this framework for each platform separately. So once you have it for MacOS you will need to add additional target for it in xcode to compile it for iOS.
Then include your framework's project in each of you apps. Make sure to remove the class from your app's sources.
Check Apple's Framework Programming Guide,
Personally, I'd go with option #1 and use open and popular format and stay away from Apple's proprietary serialization.

swift alternative for fontDescriptorWithSymbolicTraits ios 8 bug

I'm trying to create a Swift custom text editor (lite version) and got stuck on iOS 8 fontDescriptorWithSymbolicTraits bug, returning nil. Are there any Swift workarounds?
I've created a small project sample here , you can download and run the following scenario:
Select a text
Click on "B" button
Click on "I" button
The app crashes, fontDescriptorWithSymbolicTraits returns nil :(
private func addOrRemoveTraitWithName(traitName: String, traitValue: UInt32) {
let range = self.textEditor.selectedRange
let currentAttributesDict = self.textEditor.textStorage.attributesAtIndex(range.location, effectiveRange: nil)
let currentFont = currentAttributesDict[NSFontAttributeName]
let fontDescriptor = currentFont?.fontDescriptor()
let fontNameAttribute = fontDescriptor?.fontAttributes()[UIFontDescriptorNameAttribute]
var changedFontDescriptor: UIFontDescriptor?
if fontNameAttribute?.rangeOfString(traitName).location == NSNotFound {
let existingTraitsWithNewTrait = UIFontDescriptorSymbolicTraits(rawValue: fontDescriptor!.symbolicTraits.rawValue | traitValue)
changedFontDescriptor = fontDescriptor?.fontDescriptorWithSymbolicTraits(existingTraitsWithNewTrait)
} else {
let existingTraitsWithoutTrait = UIFontDescriptorSymbolicTraits(rawValue: fontDescriptor!.symbolicTraits.rawValue & ~traitValue)
changedFontDescriptor = fontDescriptor?.fontDescriptorWithSymbolicTraits(existingTraitsWithoutTrait)
}
let updatedFont = UIFont(descriptor: changedFontDescriptor!, size: 0.0)
var dict = [String : AnyObject]()
dict[NSFontAttributeName] = updatedFont
self.textEditor.textStorage.beginEditing()
self.textEditor.textStorage.setAttributes(dict, range: range)
self.textEditor.textStorage.endEditing()
}
I highly recommend to take a quick look at sample project
What options do I have?
I also seen that on Apple documentation the method that was used by other is missing from the SDK & documentation
What others say about this:
Font Descriptor returns nil in iOS 8
http://www.openradar.me/19922049
You're right about this. The bug was fixed in iOS 8.3, fortunately. The only reliable workaround is to drop down to the level of Core Text and perform the trait application there. It's a pain, but it solves the problem.
Thus, for instance, before iOS 8.3, this doesn't work:
if let body = UIFont(name: "GillSans", size: 15),
emphasis = body.fontDescriptor().fontDescriptorWithSymbolicTraits(.TraitItalic) {
fbody = body
femphasis = UIFont(descriptor: emphasis, size: 0)
}
So you have to import CoreText and drop down to that level:
if let body = UIFont(name: "GillSans", size: 15),
result = CTFontCreateCopyWithSymbolicTraits(body as CTFont, 0, nil, .ItalicTrait, .ItalicTrait) {
fbody = body
femphasis = result as UIFont
}
Fortunately, Swift 1.2 and later is aware that UIFont and CTFont are now toll-free bridged. Before Swift 1.2, this was even more complicated! And of course in earlier systems, they were not toll-free bridged and this was still more difficult.

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