Coded in Swift I implemented after the Tutorial.
DBAccountManager is setup in AppDelegate on applicationDidFinishLaunching.
Later, when the user activates dropbox support in my application I'm trying to link the account.
The Window Panel is displayed and my application is waiting for the callback.
Sometimes I do not get a linked account, even the user logs in and accepts.
The Log says
"[ERROR] unable to verify link request"
When this occurs on a machine it wont't work, you can retry and retry...
if it worked, it works like a charm and in future I always get the linked account directly from the library without the login window.
What does this error mean and what can I do?
AppDelegate:
func applicationDidFinishLaunching(aNotification: NSNotification) {
// Dropbox API Key & Secret
let _appKey = "------"
let _appSecret = "------"
// Accountmanager
if (DBAccountManager.sharedManager() == nil)
{
let accountManager = DBAccountManager(appKey: _appKey, secret: _appSecret)
DBAccountManager.setSharedManager(accountManager)
}
....
}
The linking in my class, when user clicked to activate dropbox:
internal func __start(parentWindow:NSWindow?, callback:((Bool) -> Void))
{
let am = DBAccountManager.sharedManager()
if am == nil
{
NSLog("Dropbox not available!")
callback!(false)
return
}
// link account
let linkedAccount = am!.linkedAccount
if (linkedAccount != nil)
{
// Already linked
DLog("Dropbox link found.")
let fileSystem = DBFilesystem(account: linkedAccount!)
DBFilesystem.setSharedFilesystem(fileSystem)
callback(true)
}
else
{
// link with window must be in mainthread
dispatch_async(dispatch_get_main_queue())
{
am!.linkFromWindow(parentWindow) {
account in
if (account != nil)
{
DLog("Dropbox linked")
let fileSystem = DBFilesystem(account: account!)
DBFilesystem.setSharedFilesystem(fileSystem)
callback(true)
}
else
{
DLog("NOT LINKED (Dropbox)")
callback(false)
} // if - else account
} // accountmanager block
} // dispatchblock main
} // if - else linkedaccount
}
Here the full log, the app is not doing anything else:
2015-02-23 10:25:39.443 TestApp[39226:30958267] Dropbox init
<<<< MediaValidator >>>> mv_ValidateRFC4281CodecId: Unrecognized codec 1.(null). Failed codec specific check.
<<<< MediaValidator >>>> mv_LookupCodecSupport: Unrecognized codec 1
[10:25:40.979] mv_LowLevelCheckIfVideoPlayableUsingDecoder signalled err=-12956 (kFigMediaValidatorError_VideoCodecNotSupported) (video codec 1) at line 1851
<<<< MediaValidator >>>> mv_TestCodecSupportUsingDecoders: Unrecognized codec 1
<<<< MediaValidator >>>> mv_ValidateRFC4281CodecId: Unrecognized codec 1.(null). Failed codec specific check.
<<<< MediaValidator >>>> mv_LookupCodecSupport: Unrecognized codec 1
[10:25:40.979] mv_LowLevelCheckIfVideoPlayableUsingDecoder signalled err=-12956 (kFigMediaValidatorError_VideoCodecNotSupported) (video codec 1) at line 1851
<<<< MediaValidator >>>> mv_TestCodecSupportUsingDecoders: Unrecognized codec 1
2015-02-23 10:25:43.873 TestApp[39226:30958267] [ERROR] unable to verify link request
2015-02-23 10:25:43.879 TestApp[39226:30958267] NOT LINKED (Dropbox)
I have same issue [ERROR] unable to verify link request after long research and studying the DropBoxSDK I come to the point that this error occurs when state ID is different from value saved at key KDBKLinkNonce. Every time at new Session it generates new state ID. See below code of [[DBSession sharedSession] handleOpenURL:url] method.
- (BOOL)handleOpenURL:(NSURL *)url {
NSString *expected = [NSString stringWithFormat:#"%#://%#/", [self appScheme], kDBDropboxAPIVersion];
if (![[url absoluteString] hasPrefix:expected]) {
return NO;
}
NSArray *components = [[url path] pathComponents];
NSString *methodName = [components count] > 1 ? [components objectAtIndex:1] : nil;
if ([methodName isEqual:#"connect"]) {
NSDictionary *params = [DBSession parseURLParams:[url query]];
NSString *token = [params objectForKey:#"oauth_token"];
NSString *secret = [params objectForKey:#"oauth_token_secret"];
NSString *userId = [params objectForKey:#"uid"];
NSString *state = [params objectForKey:#"state"];
NSString *nonce = [[NSUserDefaults standardUserDefaults] objectForKey:kDBLinkNonce];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:kDBLinkNonce];
[[NSUserDefaults standardUserDefaults] synchronize];
if (![nonce isEqual:state]) {
DBLogError(#"unable to verify link request");
return NO;
}
[self updateAccessToken:token accessTokenSecret:secret forUserId:userId];
} else if ([methodName isEqual:#"cancel"]) {
DBLogInfo(#"DropboxSDK: user cancelled Dropbox link");
}
return YES; }
For further reference please check this link dropbox-sdk-ios
I found something and since I found a fix for that, even the dropbox error is gone.
The problem seems to be, that NSUserDefaults did not store any data (not in memory and not on disk!). Since Dropbox uses NSUserDefaults to check the state before and after, this killed the whole process.
With getting the NSUserDefaults back working it seems the whole dropbox problem was gone.
OSX NSUserDefaults not Working
Related
I am using the following CKNotification Info and this seems to work fine:
CKNotificationInfo *note = [[CKNotificationInfo alloc] init];
note.alertBody = #"Something Happened";
note.shouldBadge = NO;
note.shouldSendContentAvailable = NO;
When something changes on an iOS device, my Mac app receives a Push notification based on a subscription with this notification. However, didReceiveRemoteNotification is never called so I can't process the event. I need to be able to refresh and fetch new changes. How do I do that?
Calling registerForRemoteNotificationTypes: and implementing didRegisterForRemoteNotificationsWithDeviceToken:
should be enough code, and the App ID should include the Push Notifications service.
I'm using CloudKit in a cross-platform (iOS/OS X) app to synchronize favorites between devices like so:
// OS X specific code
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[NSApp registerForRemoteNotificationTypes:NSRemoteNotificationTypeNone];// silent push notification!
}
- (void)application:(NSApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
[self.favCon handleCloudKitNotificationWithUserInfo:userInfo];
}
Note the usage of NSRemoteNotificationTypeNone which means silent push notification! This is how I set up CloudKit in the FavController class:
- (void)getOrCreateFavZoneWithCompletionHandler:(successCompletionHandler)handler {
// check if FavZone exists op
__block int createZone = 0;
CKFetchRecordZonesOperation *fetchRecZonesOp = [[CKFetchRecordZonesOperation alloc] initWithRecordZoneIDs:#[[FavController favRecordZoneID]]];
CKModifyRecordZonesOperation *saveRecZoneOp = [[CKModifyRecordZonesOperation alloc] initWithRecordZonesToSave:nil recordZoneIDsToDelete:nil];
fetchRecZonesOp.fetchRecordZonesCompletionBlock = ^(NSDictionary *recordZonesByZoneID, NSError *operationError) {
if (recordZonesByZoneID.count == 0) {// zone doesn't exist
createZone = 1;
CKRecordZone *favZone = [[CKRecordZone alloc] initWithZoneName:UTXAFavZoneName];
saveRecZoneOp.recordZonesToSave = #[favZone];
NSLog(#"Creating new Zone %#", favZone.zoneID.zoneName);
} else {
NSLog(#"Zone %# already exists.", [FavController favRecordZoneID].zoneName);
}
};
// create FavZone op
saveRecZoneOp.modifyRecordZonesCompletionBlock = ^(NSArray *savedRecordZones, NSArray *deletedRecordZoneIDs, NSError *operationError) {
[self successCompletionHandler:(savedRecordZones.count == createZone) error:operationError informDelegate:YES handler:handler];
};
[saveRecZoneOp addDependency:fetchRecZonesOp];
[[FavController favDatabase] addOperation:fetchRecZonesOp];
[[FavController favDatabase] addOperation:saveRecZoneOp];
}
- (void)subscribeToFavChanges:(successCompletionHandler)handler {
// get current subscription
[[FavController favDatabase] fetchSubscriptionWithID:UTXAFavConCKSubscriptionID completionHandler:^(CKSubscription *subscription, NSError *error) {
if (subscription) {
NSLog(#"using existing subscription: %#", subscription);
[self successCompletionHandler:YES error:nil informDelegate:NO handler:handler];
} else {
CKSubscription *sub = [[CKSubscription alloc] initWithZoneID:[FavController favRecordZoneID]
subscriptionID:UTXAFavConCKSubscriptionID
options:0];// "You must specify 0 for this parameter. Zone subscriptions currently do not support any options."
[[FavController favDatabase] saveSubscription:sub completionHandler:^(CKSubscription *subscription, NSError *error) {
NSLog(#"created new subscription: %# %#", subscription, error);
[self successCompletionHandler:(error == nil) error:error informDelegate:YES handler:handler];
}];
}
}];
}
As soon as I add or remove a record on one device, I'll get a notification on all other device, which I handle like so in the FavController class:
/// #abstract Handle push notifications sent by iCloud.
/// #discussion App delegates call this method when they receive a push notification through didReceiveRemoteNotification.
/// Currently, only airport favorites produce a PN, it is of type CKNotificationTypeRecordZone.
/// #param userInfo The userInfo dict tied to each push notification.
- (void)handleCloudKitNotificationWithUserInfo:(NSDictionary *)userInfo {
[self recursivelyCheckForPreviousCloudKitNotifications];
}
- (void)recursivelyCheckForPreviousCloudKitNotifications {
CKFetchNotificationChangesOperation *fetchOp = [[CKFetchNotificationChangesOperation alloc] initWithPreviousServerChangeToken:_defCon.notificationChangeToken];
__weak CKFetchNotificationChangesOperation *weakOp = fetchOp;
fetchOp.notificationChangedBlock = ^(CKNotification *notification) {
[self handleNotification:notification];
};
fetchOp.fetchNotificationChangesCompletionBlock = ^( CKServerChangeToken *serverChangeToken, NSError *operationError) {
NSLog(#"new notification change token: %#", serverChangeToken);
_defCon.notificationChangeToken = serverChangeToken;
if (weakOp.moreComing) {
NSLog(#"more coming!!");
[self recursivelyCheckForPreviousCloudKitNotifications];
} else {
NSLog(#"done handling notification changes.");
}
};
[[FavController favContainer] addOperation:fetchOp];
}
- (void)handleNotification:(CKNotification *)notification {// withCompletionHandler:(successCompletionHandler)handler {
if (notification.notificationType == CKNotificationTypeRecordZone) {// make sure we handle only zone changes
CKRecordZoneNotification *noti = (CKRecordZoneNotification *)notification;
if ([noti.recordZoneID.zoneName isEqualToString:[FavController favRecordZoneID].zoneName]) {
// received an update for the fav zone
[self queuedFavUpdateFromCloud];
} else {
// received an update for an unknown zone
NSLog(#"WARNING: received an update for an unknown zone: %#", noti.recordZoneID.zoneName);
}
} else {
NSLog(#"WARNING: received unknown notification: %#", notification);
}
}
Okay I've finally figured it out. If you use a CKNotificationInfo for your alerts, didReceiveRemoteNotification will NOT be called on the Mac until and unless you set CKNotificationInfo.soundName to an empty string! This looks like a bug only in OS X (10.10 & 10.11 so far) but can be worked around by this simple change.
I have a profile view controller where the user can set or change his or her profile picture but am getting constant errors that do not make sense to me.
-(void)getProfilePicture
{
PFUser *user = [PFUser currentUser];
NSLog(#"file--%#",[user objectForKey:PF_USER_PICTURE]);
userName = user[PF_USER_USERNAME];
status = user[PF_USER_STATUS];
if ([user objectForKey:PF_USER_PICTURE]) {
[imageUser setFile:[user objectForKey:PF_USER_PICTURE]];
[imageUser loadInBackground];
}
else{
imageUser.image = [UIImage imageNamed:#"blank_profile#2x.png"];
}
// fieldName.text = user[PF_USER_FULLNAME];
}
#pragma mark - UIActionSheetDelegate
I receive the following errors portrayed in my ProfileViewController.m (I can provide/add .h if needed):
"No visible #interface for 'UIImageView' declares the selector 'setFile:'
"No visible #interface for 'UIImageView' declares the selector 'loadInBackground'
Any help would be much appreciated or any supporting code thats needed, thanks.
Try this and see what it does for you:
-(void)getProfilePicture
{
PFUser *user = [PFUser currentUser];
NSLog(#"file--%#",[user objectForKey:PF_USER_PICTURE]);
userName = user[PF_USER_USERNAME];
status = user[PF_USER_STATUS];
if ([user objectForKey:PF_USER_PICTURE]) {
userImage.file = [user objectForKey:PF_USER_PICTURE];
[userImage loadInBackground:^(UIImage *image, NSError *error) {
if (!error) {
imageUser.image = image;
[imageUser setNeedsDisplay];
} else {
NSLog(#"%#", error);
}
}];
}
else{
imageUser.image = [UIImage imageNamed:#"blank_profile#2x.png"];
}
// fieldName.text = user[PF_USER_FULLNAME];
}
#pragma mark - UIActionSheetDelegate
This is assuming your userImage is a PFImageView and [user objectForKey:PF_USER_PICTURE] is a PFFile.
EDIT
Actually, looking at your error it seems that userImage is just a UIImageView not a PFImageView, which might be the source of all your problems. Try making the userImage a PFImageView
I'm converting our app over to use the Photos Framework of iOS8, the ALAsset framework is clearly a second class citizen under iOS8.
I'm having a problem is that our architecture really wants an NSURL that represents the location of the media on "disk." We use this to upload the media to our servers for further processing.
This was easy with ALAsset:
ALAssetRepresentation *rep = [asset defaultRepresentation];
self.originalVideo = rep.url;
But I'm just not seeing this ability in PHAsset. I guess I can call:
imageManager.requestImageDataForAsset
and then write it out to a temp spot in the file system but that seems awfully heavyweight and wasteful, not to mention potentially slow.
Is there a way to get this or am I going to have refactor more of my app to only use NSURLs for iOS7 and some other method for iOS8?
If you use [imageManager requestAVAssetForVideo...], it'll return an AVAsset. That AVAsset is actually an AVURLAsset, so if you cast it, you can access it's -url property.
I'm not sure if you can create a new asset out of this, but it does give you the location.
SWIFT 2.0 version
This function returns NSURL from PHAsset (both image and video)
func getAssetUrl(mPhasset : PHAsset, completionHandler : ((responseURL : NSURL?) -> Void)){
if mPhasset.mediaType == .Image {
let options: PHContentEditingInputRequestOptions = PHContentEditingInputRequestOptions()
options.canHandleAdjustmentData = {(adjustmeta: PHAdjustmentData) -> Bool in
return true
}
mPhasset.requestContentEditingInputWithOptions(options, completionHandler: {(contentEditingInput: PHContentEditingInput?, info: [NSObject : AnyObject]) -> Void in
completionHandler(responseURL : contentEditingInput!.fullSizeImageURL)
})
} else if mPhasset.mediaType == .Video {
let options: PHVideoRequestOptions = PHVideoRequestOptions()
options.version = .Original
PHImageManager.defaultManager().requestAVAssetForVideo(mPhasset, options: options, resultHandler: {(asset: AVAsset?, audioMix: AVAudioMix?, info: [NSObject : AnyObject]?) -> Void in
if let urlAsset = asset as? AVURLAsset {
let localVideoUrl : NSURL = urlAsset.URL
completionHandler(responseURL : localVideoUrl)
} else {
completionHandler(responseURL : nil)
}
})
}
}
If you have a PHAsset, you can get the url for said asset like this:
[asset requestContentEditingInputWithOptions:editOptions
completionHandler:^(PHContentEditingInput *contentEditingInput, NSDictionary *info) {
NSURL *imageURL = contentEditingInput.fullSizeImageURL;
}];
Use the new localIdentifier property of PHObject. (PHAsset inherits from this).
It provides similar functionality to an ALAsset URL, namely that you can load assets by calling the method
+[PHAsset fetchAssetsWithLocalIdentifiers:identifiers options:options]
All the above solutions won't work for slow-motion videos. A solution that I found handles all video asset types is this:
func createFileURLFromVideoPHAsset(asset: PHAsset, destinationURL: NSURL) {
PHCachingImageManager().requestAVAssetForVideo(self, options: nil) { avAsset, _, _ in
let exportSession = AVAssetExportSession(asset: avAsset!, presetName: AVAssetExportPresetHighestQuality)!
exportSession.outputFileType = AVFileTypeMPEG4
exportSession.outputURL = destinationURL
exportSession.exportAsynchronouslyWithCompletionHandler {
guard exportSession.error == nil else {
log.error("Error exporting video asset: \(exportSession.error)")
return
}
// It worked! You can find your file at: destinationURL
}
}
}
See this answer here.
And this one here.
In my experience you'll need to first export the asset to disk in order to get a fully accessible / reliable URL.
The answers linked to above describe how to do this.
Just want to post the hidden gem from a comment from #jlw
#rishu1992 For slo-mo videos, grab the AVComposition's
AVCompositionTrack (of mediaType AVMediaTypeVideo), grab its first
segment (of type AVCompositionTrackSegment), and then access its
sourceURL property. – jlw Aug 25 '15 at 11:52
In speking of url from PHAsset, I had once prepared a util func on Swift 2 (although only for playing videos from PHAsset). Sharing it in this answer, might help someone.
static func playVideo (view:UIViewController, asset:PHAsset)
Please check this Answer
Here's a handy PHAsset category:
#implementation PHAsset (Utils)
- (NSURL *)fileURL {
__block NSURL *url = nil;
switch (self.mediaType) {
case PHAssetMediaTypeImage: {
PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
options.synchronous = YES;
[PHImageManager.defaultManager requestImageDataForAsset:self
options:options
resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {
url = info[#"PHImageFileURLKey"];
}];
break;
}
case PHAssetMediaTypeVideo: {
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[PHImageManager.defaultManager requestAVAssetForVideo:self
options:nil
resultHandler:^(AVAsset *asset, AVAudioMix *audioMix, NSDictionary *info) {
if ([asset isKindOfClass:AVURLAsset.class]) {
url = [(AVURLAsset *)asset URL];
}
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
break;
}
default:
break;
}
return url;
}
#end
I had similiar problem with video files, what worked for me was:
NSString* assetID = [asset.localIdentifier substringToIndex:(asset.localIdentifier.length - 7)];
NSURL* videoURL = [NSURL URLWithString:[NSString stringWithFormat:#"assets-library://asset/asset.mov?id=%#&ext=mov", assetID]];
Where asset is PHAsset.
I have quite a simple problem. I want to put the audiofiles into my table view. How do I distinguish them from pdfs and movies, etc?
I get them from iTunes over Scripting Bridge:
iTunesSource *source = [[[self iTunes] sources] objectAtIndex:0];
iTunesPlaylist *mainPlaylist = [[source libraryPlaylists] objectAtIndex:0] ;
library_ = [[NSArray arrayWithArray:[mainPlaylist tracks]] retain ] ;
This gives me an error saying the class iTunesFileTrack could not be found ( at linking time)
:
[track get];
if(![track isKindOfClass:[iTunesFileTrack class]]) {
DLog1(#"SKIPPING kind: %#", [track kind]);
}
I'm sure I'm missing something simple :)
On a related note: Is there a faster way to read the iTunes library? I just advice on loading it from an xml file but that seems unsafe to me. If apple changes anything in the next release I'm screwed.
Thank you
EDIT: With sdef /Applications/iTunes.app | sdp -fhm --basename iTunes I can generate the .m file I need to check for the class. But it does not seem to work:
[track get];
if(![[track className] isEqualToString:#"ITunesFileTrack"]) {
DLog1(#"SKIPPING kind: %#", [track kind]);
continue;
}
Skipps just my streams :P Not the movies. (Even when I add (track.videoKind != iTunesEVdKNone)). Even the PDF's are iTunesFileTracks. But the .h states:
// a track representing an audio file (MP3, AIFF, etc.)
#interface iTunesFileTrack : iTunesTrack
I use something like this in my code so it should work (app is the iTunes SBApplication):
1.) First get the library source
- (ITunesSource *)librarySource {
NSArray *sources = [[app sources] get];
NSArray *libs = [sources filteredArrayUsingPredicate:[NSPredicate
predicateWithFormat:#"kind == %i",
ITunesESrcLibrary]];
if ([libs count]) {
return [libs objectAtIndex:0];
}
return nil;
}
2.) Iterate through the library playlist(s)
NSArray *libraryLists = [[[self librarySource] libraryPlaylists] get];
for (ITunesLibraryPlaylist *list in libraryLists) {
NSArray *listTracks = [[list fileTracks] get];
for (ITunesTrack *listTrack in listTracks) {
// do stuff...
}
[listTracks release];
}
3.) You can check for other track types like so
if (track.videoKind != ITunesEVdKNone || track.podcast) {
// track is not of type music
}
My OSX app needs to display a list of the user's known wifi networks. I've already figured out how to do this using the CoreWLAN framework. The code I'm using is:
CWInterface *interface = [[CWInterface alloc] init];
NSArray *knownNetworks = interface.configuration.preferredNetworks;
This works fine, except that when I do this, OSX prompts the user saying my app needs keychain access for each network that has a passphrase stored. The "preferredNetworks" property returns an array of CWWirelessProfile objects. One of the properties of this class is "passphrase". I believe this property is why my app needs keychain access.
I don't need, or even want, the passphrases for the user's known networks. All I care about is the SSID. Is there a way to query a list of known SSIDs without also pulling the passphrase? I would prefer it if my app didn't prompt the user that it needs keychain access. Also, the prompt is useless in my case because regardless if the user hits "Allow" or "Deny", I am still able to access the network's SSID.
It turns out that Bavarious was correct; I can utilize the System Configuration framework to retrieve a list of know wifi networks without prompting the user for admin access. Here is the class I ended up creating that handles this:
static NSString *configPath = #"/Library/Preferences/SystemConfiguration/preferences.plist";
#implementation KnownWifiNetworks
/**
This method reads the SystemConfiguration file located at configPath. Its schema is described in Apple's
Documentation at this url:
http://developer.apple.com/library/mac/#documentation/Networking/Conceptual/SystemConfigFrameworks/SC_Components/SC_Components.html
TODO: Cache the results so we don't have the read the file every time?
*/
+ (NSArray *)allKnownNetworks {
NSMutableArray *result = [NSMutableArray arrayWithCapacity:50];
#try {
NSDictionary *config = [NSDictionary dictionaryWithContentsOfFile:configPath];
NSDictionary *sets = [config objectForKey:#"Sets"];
for (NSString *setKey in sets) {
NSDictionary *set = [sets objectForKey:setKey];
NSDictionary *network = [set objectForKey:#"Network"];
NSDictionary *interface = [network objectForKey:#"Interface"];
for(NSString *interfaceKey in interface) {
NSDictionary *bsdInterface = [interface objectForKey:interfaceKey];
for(NSString *namedInterfaceKey in bsdInterface) {
NSDictionary *namedInterface = [bsdInterface objectForKey:namedInterfaceKey];
NSArray *networks = [namedInterface objectForKey:#"PreferredNetworks"];
for (NSDictionary *network in networks) {
NSString *ssid = [network objectForKey:#"SSID_STR"];
[result addObject:ssid];
}
}
}
}
} #catch (NSException * e) {
NSLog(#"Failed to read known networks: %#", e);
}
return result;
}
#end
I've been able to use the CoreWLAN framework classes to get a list of known network SSIDs without requiring keychain access like so:
NSMutableArray *result = [NSMutableArray arrayWithCapacity:50];
CWInterface *interface = [CWInterface interface];
NSEnumerator *profiles = [interface.configuration.networkProfiles objectEnumerator];
CWNetworkProfile *profile;
while (profile = [profiles nextObject]) {
[result addObject:profile.ssid];
}
return result;
It seems that CWInterface.configuration.preferredNetworks is deprecated but this solution works quite well.