PHAssetCollectionChangeRequest: addAssets() now accepts NSFastEnumeration. how to achieve this? - ios

My Application tries to import an image from photos and adds to an album.
But in latest swift changes. addAssets() accepts parameter as NSFastEnumeration. So I get an error as shown in the image.
even the Apple API document has this same code: https://developer.apple.com/library/prerelease/ios/documentation/Photos/Reference/PHAssetChangeRequest_Class/index.html#//apple_ref/occ/instp/PHAssetChangeRequest/placeholderForCreatedAsset
What is the alternative or how do I addAssets now?

So, I did some research, and according to NSHipster, NSEnumeration is a protocol implemented by NSArray, NSSet, and NSDictionary. This suggests that if you convert [assetPlaceholder] to an NSArray, you'll be able to use it in the method. And, in fact, this compiles:
let enumeration: NSArray = [assetPlaceholder!]
albumChangeRequest!.addAssets(enumeration)

Related

In Swift or Objective-C, having a NSData object, is there a way to determine programmatically the NSData.ReadingOptions used to create it?

In Swift, let's consider this line of code:
let data = try Data.init(contentsOf: url, options:NSData.ReadingOptions.alwaysMapped)
Is there a chance, at a later stage, to retrieve programmatically the NSData.ReadingOptions value used to instantiate the object ? Thanks

Swift 3 - How to declare an NSOrderedSet to use setVocabularyStrings?

I want to use this function's prototype for adding vocabulary to SiriKit. Apple's documentation shows the following sample code:
let workoutNames = self.sortedWorkoutNames()
let vocabulary = INVocabulary.shared()
vocabulary.setVocabularyStrings(workoutNames, of: .workoutActivityName)
According to the documentation, I need to use an NSOrderedSet type for self.sortedWorkoutNames().
How to declare it and set it with an array of Strings?
EDIT: About the context project, I'm using Intents with Siri. The goal here, is to use specific word like 'Benchpress' or 'Chicken' just to start my workout app with an INStartWorkoutIntent already implemented and working like a charm.
If you look at Apple's sample code for SiriKit, you can tell that their function sortedWorkoutNames() returns an NSOrderedSet. Look at the type of workoutNames in the Objective-C version:
NSOrderedSet* workoutNames = [self sortedWorkoutNames];
In Swift that would be
let workoutNames: NSOrderedSet = self.sortedWorkoutNames()
If you have an array that you want to pass to INVocabulary.setVocabularyStrings(_:of:), you'd need to convert it to an ordered set. As #larme said in his comment, NSOrderedSet has an initializer that takes an array as input.
Your code might look like this:
let myVocab = ["Benchpress", "Squats", "Deadlift"]
let myVocabSet = NSOrderedSet(array: myVocab) //Convert myVocab to an NSOrderedSet
let vocabulary = INVocabulary.shared()
vocabulary.setVocabularyStrings(myVocabSet, of: .workoutActivityName)

Failed to send custom object using WatchConnectivity (swift)

I was trying to pass my swift object from the iOS app to the Watch. However, I found it works for basic types like NSString, but my custom object type.
My custom object is able to cast to NSData
I've made my object implement NSObject and NSCoding, which works well. I can do following without problem:
let encodedChordProgression = NSKeyedArchiver.archivedDataWithRootObject(chordProgressions[1])
let decodedChordProgression = NSKeyedUnarchiver.unarchiveObjectWithData(encodedChordProgression) as! ChordProgression
NSLog("get decodedChordProgression = \(decodedChordProgression.description)")
WatchConnectivity code works for NSString
In iPhone:
try WatchSessionManager.sharedManager.updateApplicationContext(["data": "mystringishere"])
with Watch:
dispatch_async(dispatch_get_main_queue()) { [weak self] in
self?.dataSourceChangedDelegates.forEach { $0.dataSourceDidUpdate(applicationContext["data"] as! NSString)}
}
works.
My custom object with WatchConnectivity Failed
However, when I switch the object to my own object, it failed by not calling the dataSourceChangedDelegates callback function. That is:
In iPhone:
let encodedChordProgression = NSKeyedArchiver.archivedDataWithRootObject(chordProgressions[1])
try WatchSessionManager.sharedManager.updateApplicationContext(["data": encodedChordProgression])
with Watch:
dispatch_async(dispatch_get_main_queue()) { [weak self] in
self?.dataSourceChangedDelegates.forEach { $0.dataSourceDidUpdate(applicationContext["data"] as! NSData)}
}
and
func dataSourceDidUpdate(encodedChordProgression: NSData) {
let chordProgression = NSKeyedUnarchiver.unarchiveObjectWithData(encodedChordProgression) as! ChordProgression
NSLog("get something here: \(chordProgression.description)")
}
What I've tried & my problem
I've tried to read the system.log of both the iPhone app and Watch app, but I couldn't find any clue, which is the biggest problem I have now.
The full code is: here (checkout 7f2a72c6004f6580e2a38a2d7fd0ed2cef8a2b2e)
NSKeyedArchiver/NSKeyedUnarchiver won't work in this way unfortunately. This is because even though you may share class files between your watchkit and iOS targets, they are essentially different classes to the compiler because they are compiled for different architectures.
What I have done to get around this issue myself (because I initially tried to do the same thing) is serialize my custom objects to a json dictionary (or json NSData if you like) and send that. Here is a github project I have made that automatically serializes your swift objects to json for you (specifically with this use case in mind).
I tried with "NSKeyedArchiver/NSKeyedUnarchiver" and this is working perfectly.
no need to go for serialization and all.
your dictionary should have same type of data and Archiver is doing it very well.

compare two secKey (public keys) in ios Swift

I want to ssl public key pinning in swift, I read lot of examples how to do that, last think who I can't find is How to compare two public keys in SecKey object format.
Example:
let serverPublicKey = SecTrustCopyPublicKey(secTrust) /*return SecKey object from actual SecTrust*/
let clientPublicKey = getLocalPublicKeyFromDer() /*return SecKey from .der local*/
how to compare them? At now I do that and it works:
if(serverPublicKey! as AnyObject).isEqual(clientPublicKey){
/*Key is the same, pinning OK!*/
}
find it way on gitHub: https://github.com/teamcarma/IOS-AlamofireDomain/blob/master/Source/ServerTrustPolicy.swift
but is cast to AnyObject a good idea? How to work isEqual on casted SecKey? Can any explain me?
ps.
Another idea is getting base64 from SecKey - I try and it also works, but it require a KeyChain temp operations and look no profesional.
Cited from the headers:
"Most SecKeychainItem functions will work on an SecKeyRef."*
You may cast SecKeyRef to a SecKeychainItem. If this is a valid operation (that is, the key is a keychain item), you may apply function
SecKeychainItemCreatePersistentReference
and get a CFData object, filled with attributes and data. Then check with memcpyon the bytes or cast it to a NSData object and check with isEqualToData. Don't forget to release the CFData object.
Edit
On iOS, as far as I known, the only reliable approach is to copy the data (or secret) into the keychain, using a temporary key, so that you can find it again, and then extract the data. It's cumbersome, but if you just implement it in a minimalistic way, it should not take more than 30 lines of code. I have a working example.
I The usual disclaimer: Use this at your own risk, and always be careful with security stuff.
iOS10 added:
CFDataRef _Nullable SecKeyCopyExternalRepresentation(SecKeyRef key, CFErrorRef *error)
so you can now create two Data (NSData) objects, then compare those.
Have a look at this answer for just getting the NSData: Can I get the modulus or exponent from a SecKeyRef object in Swift?
You can then compare the two NSData instances using isEqualToData:
I don't have expereince in the domain, but if they are two strings (irrespectiveof their content), you would basically do a simple check:
if(string1 == string2)
{
//condition logic
}

Swift complains "extraneous argument label"

Trying to start some Swift work. I am using
var imageData = UIImageJPEGRepresentation(image, compressionQuality:1.0)
but I get a warning "extraneous argument label 'compressionQuality' in call. I thought that in Swift the secondary parameters were either required or 'allowed' to be labelled, but this won't let me use it at all- fails building if I leave it. Since this is a system function, I can't use the # to require it. But I WOULD like to be able to name as many parameters as possible to make code more readable for myself, I like the ObjC method names, as verbose as they sometimes are.
Is there a way to set a compiler flag to allow extra argument labels?
You can't do like that, because that function doesn't declare any external parameter name. Internal parameter names can only be used within the function that declares them.
In Swift UIImageJPEGRepresentation method is declared as:
func UIImageJPEGRepresentation(_ image: UIImage!,
_ compressionQuality: CGFloat) -> NSData!
Check both parameters, both have internal name only so you can't use:
var imageData = UIImageJPEGRepresentation(image, compressionQuality:1.0)
Change that to:
var imageData = UIImageJPEGRepresentation(image,1.0)
Update Swift 4.2:
In swift 4.2, the above mentioned methods are no longer available. Instead of that you need to use:
// For JPEG
let imageData = image.jpegData(compressionQuality: 1.0)
// For PNG
let imageData = image.pngData()
Refer the API Document for more: Images & PDF
I had a similar problem, but Xcode was complaining about it in one of my funcions.
Turned out to be an extra } in my code, making the subsequent function declarations to be outside my class.
The error message was weird as hell, so I hope it hepls somebody else.

Resources