Lets assume that 3 strings have been copied on the UIPasteBoard:
UIPasteboard.generalPasteboard().string="hello"
UIPasteboard.generalPasteboard().string="world"
UIPasteboard.generalPasteboard().string="!"
I use
UIPasteboard.generalPasteboard().string=""
Will it clear the pasteboard? Is there any similar func for UIPasteBoard like there is clearContents() for NSPasteBoard?
If you know that your program is the only one manipulating the specific pasteboard, then yes, setting the string property to "" will effectively clear the pasteboard.
You can easily test this in Playground
var pb = UIPasteboard.generalPasteboard()
pb.string = "hello"
pb.string
pb.items
pb.string = ""
pb.string
pb.items
which outputs
<UIPasteboard: 0x7fed6bd0a750>
<UIPasteboard: 0x7fed6bd0a750>
"hello"
[["public.utf8-plain-text": "hello"]]
<UIPasteboard: 0x7fed6bd0a750>
nil
[[:]]
However, note that string property of UIPasteboard is a shorthand for the first pasteboard item that is of type string. All items of type string can be accessed through strings property.
All underlying pasteboard items are modelled in items property, which is an array of dictionaries of type [String: AnyObject]. Each dictionary contains the type information of an object in the key and pasteboard value in the value.
Because you are using a system-wide generalPasteboard, it can also be manipulated by other programs, thus, to clear all items from the pasteboard, you should use
UIPasteboard.generalPasteboard().items = []
If you are using the pasteboard for your internal application purposes, it is better to create an internal pasteboard than to use a system-wide generalPasteboard. See pasteboardWithUniqueName()
Related
I'm just starting up with RealmSwift, and I'm trying to store an array of Strings in Realm. It doesn't work, so now I'm using List<String>() as an alternative. However, how do I convert these Realm Lists back to [String] again? And if I can't do that, are there any alternatives?
Thanks
However, how do I convert these Realm Lists back to [String] again
You can simply cast List to Array, because List has Sequence Support:
let list = List<String>()
let array = Array(list)
Bear in mind that by converting to an array you'll lose the 'dynamic' quality of a Realm collection (i.e. you'll receive a static array, whereas keeping the original List will provide automatic updating should the source change). But you can create an array by using an extension, e.g.:-
extension RealmCollection
{
func toArray<T>() ->[T]
{
return self.compactMap{$0 as? T}
}
}
Then use:-
let stringList = object.strings.toArray()
Where object is the realm object, and strings is your field.
Here are the details. how to assign an array in the realm list model.
jim.dogs.append(objectsIn: someDogs)
This is more for interest rather than a problem, but I have an NSMutableSet, retrieved from UserDefaults and my objective is to append an item to it and then write it back. I am using an NSMutableSet because I only want unique items to be inserted.
The type of object to be inserted is a custom class, I have overrode hashCode and isEqual.
var stopSet: NSMutableSet = []
if let ud = UserDefaults.standard.object(forKey: "favStops") as? Data {
stopSet = NSKeyedUnarchiver.unarchiveObject(with: ud) as! NSMutableSet
}
stopSet.add(self.theStop!)
let outData = NSKeyedArchiver.archivedData(withRootObject: stopSet)
UserDefaults.standard.set(outData, forKey: "favStops")
NSLog("Saved to UserDefaults")
I get the set, call mySet.add(obj) and then write the set back to UserDefaults. Everything seems to work fine and (as far as I can see) there don't appear to be duplicates.
However is it possible to tell whether a call to mySet.add(obj) actually caused an item to be written to the set. mySet.add(obj) doesn't have a return value and if you use Playgrounds (rather than a project) you get in the output on the right hand side an indication of whether the set was actually changed based on the method call.
I know sets are not meant to store duplicate objects so in theory I should just trust that, but I was just wondering if the set did return a response that you could access - as opposed to just getting the length before the insert and after if I really wanted to know!
Swift has its own native type, Set, so you should use it instead of NSMutableSet.
Set's insert method actually returns a Bool indicating whether the insertion succeeded or not, which you can see in the function signature:
mutating func insert(_ newMember: Element) -> (inserted: Bool, memberAfterInsert: Element)
The following test code showcases this behaviour:
var set = Set<Int>()
let (inserted, element) = set.insert(0)
let (again, newElement) = set.insert(0)
print(inserted,element) //true, 0
print(again,oldElement) //false,0
The second value of the tuple returns the newly inserted element in case the insertion succeeded and the oldElement otherwise. oldElement is not necessarily equal in every aspect to the element you tried to insert. (since for custom types you might define the isEqual method in a way that doesn't compare each property of the type).
You don't need to handle the return value of the insert function, there is no compiler warning if you just write insert like this:
set.insert(1)
I got the string value from server like this.
let filename = "\(eventList[index]["filename"])"
But I got the value with Optional(Optional(stringValue)).
So I changed that like this.
let filename = "\(eventList[index]["filename"]!)"
Then I got the value with Optional(stringValue).
I can't do any more for this error.
How can I read the filename without any optional?
Use nil-coalescing operator aka double question mark operation. It is used to provide a default value when unwrapping an optional type.
let filename = eventList[index]["filename"] ?? ""
R̶e̶f̶:̶ ̶h̶t̶t̶p̶:̶/̶/̶w̶w̶w̶.̶j̶e̶e̶n̶a̶l̶i̶n̶f̶o̶t̶e̶c̶h̶.̶c̶o̶m̶/̶b̶l̶o̶g̶s̶/̶i̶o̶s̶/̶h̶o̶w̶-̶t̶o̶-̶d̶o̶-̶o̶p̶t̶i̶o̶n̶a̶l̶-̶v̶a̶r̶i̶a̶b̶l̶e̶-̶a̶s̶s̶i̶g̶n̶m̶e̶n̶t̶-̶w̶i̶t̶h̶-̶d̶e̶f̶a̶u̶l̶t̶-̶v̶a̶l̶u̶e̶-̶d̶o̶u̶b̶l̶e̶-̶q̶u̶e̶s̶t̶i̶o̶n̶-̶m̶a̶r̶k̶/̶
https://medium.com/#milanpanchal24/
Use if-let syntax to unwrap optional:
if let fileName = eventList[index]["filename"] {
// use fileName
}
eventList[index] accesses an array item at the given index. The item you are referring seems to be an optional dictionary so before accessing the dictionary item it needs to be unwrapped: eventLists[index]! (assuming it exists and valid of course otherwise it will crash)
then you can access the dictionary require value which is an optional as well:
eventLists[index]!["fileName"]!
assuming your list is valid you will get the desired String object.
I recommend using the safety checks (if-let or other variants) for preventing crashes
I'm currently reading the excellent Advanced Swift book from objc.io, and I'm running into something that I don't understand.
If you run the following code in a playground, you will notice that when modifying a struct contained in a dictionary a copy is made by the subscript access, but then it appears that the original value in the dictionary is replaced by the copy. I don't understand why. What exactly is happening ?
Also, is there a way to avoid the copy ? According to the author of the book, there isn't, but I just want to be sure.
import Foundation
class Buffer {
let id = UUID()
var value = 0
func copy() -> Buffer {
let new = Buffer()
new.value = self.value
return new
}
}
struct COWStruct {
var buffer = Buffer()
init() { print("Creating \(buffer.id)") }
mutating func change() -> String {
if isKnownUniquelyReferenced(&buffer) {
buffer.value += 1
return "No copy \(buffer.id)"
} else {
let newBuffer = buffer.copy()
newBuffer.value += 1
buffer = newBuffer
return "Copy \(buffer.id)"
}
}
}
var array = [COWStruct()]
array[0].buffer.value
array[0].buffer.id
array[0].change()
array[0].buffer.value
array[0].buffer.id
var dict = ["key": COWStruct()]
dict["key"]?.buffer.value
dict["key"]?.buffer.id
dict["key"]?.change()
dict["key"]?.buffer.value
dict["key"]?.buffer.id
// If the above `change()` was made on a copy, why has the original value changed ?
// Did the copied & modified struct replace the original struct in the dictionary ?
dict["key"]?.change() // Copy
is semantically equivalent to:
if var value = dict["key"] {
value.change() // Copy
dict["key"] = value
}
The value is pulled out of the dictionary, unwrapped into a temporary, mutated, and then placed back into the dictionary.
Because there's now two references to the underlying buffer (one from our local temporary value, and one from the COWStruct instance in the dictionary itself) – we're forcing a copy of the underlying Buffer instance, as it's no longer uniquely referenced.
So, why doesn't
array[0].change() // No Copy
do the same thing? Surely the element should be pulled out of the array, mutated and then stuck back in, replacing the previous value?
The difference is that unlike Dictionary's subscript which comprises of a getter and setter, Array's subscript comprises of a getter and a special accessor called mutableAddressWithPinnedNativeOwner.
What this special accessor does is return a pointer to the element in the array's underlying buffer, along with an owner object to ensure that the buffer isn't deallocated from under the caller. Such an accessor is called an addressor, as it deals with addresses.
Therefore when you say:
array[0].change()
you're actually mutating the actual element in the array directly, rather than a temporary.
Such an addressor cannot be directly applied to Dictionary's subscript because it returns an Optional, and the underlying value isn't stored as an optional. So it currently has to be unwrapped with a temporary, as we cannot return a pointer to the value in storage.
In Swift 3, you can avoid copying your COWStruct's underlying Buffer by removing the value from the dictionary before mutating the temporary:
if var value = dict["key"] {
dict["key"] = nil
value.change() // No Copy
dict["key"] = value
}
As now only the temporary has a view onto the underlying Buffer instance.
And, as #dfri points out in the comments, this can be reduced down to:
if var value = dict.removeValue(forKey: "key") {
value.change() // No Copy
dict["key"] = value
}
saving on a hashing operation.
Additionally, for convenience, you may want to consider making this into an extension method:
extension Dictionary {
mutating func withValue<R>(
forKey key: Key, mutations: (inout Value) throws -> R
) rethrows -> R? {
guard var value = removeValue(forKey: key) else { return nil }
defer {
updateValue(value, forKey: key)
}
return try mutations(&value)
}
}
// ...
dict.withValue(forKey: "key") {
$0.change() // No copy
}
In Swift 4, you should be able to use the values property of Dictionary in order to perform a direct mutation of the value:
if let index = dict.index(forKey: "key") {
dict.values[index].change()
}
As the values property now returns a special Dictionary.Values mutable collection that has a subscript with an addressor (see SE-0154 for more info on this change).
However, currently (with the version of Swift 4 that ships with Xcode 9 beta 5), this still makes a copy. This is due to the fact that both the Dictionary and Dictionary.Values instances have a view onto the underlying buffer – as the values computed property is just implemented with a getter and setter that passes around a reference to the dictionary's buffer.
So when calling the addressor, a copy of the dictionary's buffer is triggered, therefore leading to two views onto COWStruct's Buffer instance, therefore triggering a copy of it upon change() being called.
I have filed a bug over this here. (Edit: This has now been fixed on master with the unofficial introduction of generalised accessors using coroutines, so will be fixed in Swift 5 – see below for more info).
In Swift 4.1, Dictionary's subscript(_:default:) now uses an addressor, so we can efficiently mutate values so long as we supply a default value to use in the mutation.
For example:
dict["key", default: COWStruct()].change() // No copy
The default: parameter uses #autoclosure such that the default value isn't evaluated if it isn't needed (such as in this case where we know there's a value for the key).
Swift 5 and beyond
With the unofficial introduction of generalised accessors in Swift 5, two new underscored accessors have been introduced, _read and _modify which use coroutines in order to yield a value back to the caller. For _modify, this can be an arbitrary mutable expression.
The use of coroutines is exciting because it means that a _modify accessor can now perform logic both before and after the mutation. This allows them to be much more efficient when it comes to copy-on-write types, as they can for example deinitialise the value in storage while yielding a temporary mutable copy of the value that's uniquely referenced to the caller (and then reinitialising the value in storage upon control returning to the callee).
The standard library has already updated many previously inefficient APIs to make use of the new _modify accessor – this includes Dictionary's subscript(_:) which can now yield a uniquely referenced value to the caller (using the deinitialisation trick I mentioned above).
The upshot of these changes means that:
dict["key"]?.change() // No copy
will be able to perform an mutation of the value without having to make a copy in Swift 5 (you can even try this out for yourself with a master snapshot).
I'm trying to save a dictionary to NSUserDefaults using the setObject() function but when I use the objectForKey() function to retrieve the dictionary it returns nil. Why is this happening?
var data = NSUserDefaults.standardUserDefaults();
var scoreboard = [Int : String]()
let scores = "scoresKey"
scoreboard[3] = "spencer"
scoreboard[6] = "brooke"
scoreboard[11] = "jason"
data.setObject(scoreboard, forKey: scores)
data.objectForKey(scores) // Returns nil
The first problem was that it's not possible to use NSUserDefaults in a Playground.
See: https://stackoverflow.com/a/31210205/3498950
A second problem is found when the code above runs in a normal iOS project. It throws an NSInvalidArgumentException since the dictionary was a non-property list object because the keys needed to be of type String.
Although NSDictionary and CFDictionary objects allow their keys to be
objects of any type, if the keys are not string objects, the
collections are not property-list objects.
See: "What is a Property List?" in the Apple Docs