Just updated to Yosemite and Xcode 6.0.01 and this code no longer works:
- (NSImage *)songImage {
if (!_songImage) {
AVAsset *asset = [AVAsset assetWithURL:self.fileURL];
for (AVMetadataItem *metadataItem in asset.commonMetadata) {
if ([metadataItem.commonKey isEqualToString:#"artwork"]){
NSDictionary *imageDataDictionary =
(NSDictionary *)metadataItem.value;
NSData *imageData = [imageDataDictionary objectForKey:#"data"];
_songImage =[[NSImage alloc] initWithData:imageData];
}
}
}
if (!_songImage) {
return Nil;
}
return _songImage;
}
I'm not sure if the process was replaced or removed but I now get a message:
2014-10-17 14:36:23.756 FSC Music[3317:122917] -[__NSCFData objectForKey:]: unrecognized
selector sent to instance 0x600000241cb0 2014-10-17 14:36:23.764
FSC Music[3317:122917] -[__NSCFData objectForKey:]: unrecognized selector sent to instance
0x600000241cb0
need to research a solution, but wanted to ask if anyone else has come across this?
I changed to the following code to get it working again.
-(NSImage *)songImage {
if (!_songImage) {
AVAsset *asset = [AVAsset assetWithURL:self.fileURL];
NSArray *metadata = [asset commonMetadata];
for ( AVMetadataItem *item in metadata ) {
if ([item.commonKey isEqualToString:#"artwork"]){
NSData *thePix = (NSData *)item.value;
_songImage =[[NSImage alloc] initWithData:thePix];
}
}
}
if (!_songImage) {
return Nil;
}
return _songImage;
}
Related
I'm having trouble getting AVAudioEngine (OS X) to play nice with all sample rates.
Here's my code for building the connections:
- (void)makeAudioConnections {
auto hardwareFormat = [self.audioEngine.outputNode outputFormatForBus:0];
auto format = [[AVAudioFormat alloc] initStandardFormatWithSampleRate:hardwareFormat.sampleRate channels:2];
NSLog(#"format: %#", format);
#try {
[self.audioEngine connect:self.avNode to:self.audioEngine.mainMixerNode format:format];
[self.audioEngine connect:self.audioEngine.inputNode to:self.avNode format:format];
} #catch(NSException* e) {
NSLog(#"exception: %#", e);
}
}
On my audio interface, the render callback is called for 44.1, 48, and 176.4kHz. It is not called for 96 and 192 kHz. On the built-in audio, the callback is called for 44.1, 48, 88 but not 96.
My AU's allocateRenderResourcesAndReturnError is being called for 96kHz. No errors are returned.
- (BOOL) allocateRenderResourcesAndReturnError:(NSError * _Nullable *)outError {
if(![super allocateRenderResourcesAndReturnError:outError]) {
return NO;
}
_inputBus.allocateRenderResources(self.maximumFramesToRender);
_sampleRate = _inputBus.bus.format.sampleRate;
return YES;
}
Here's my AU's init method, which is mostly just cut & paste from Apple's AUv3 demo:
- (instancetype)initWithComponentDescription:(AudioComponentDescription)componentDescription options:(AudioComponentInstantiationOptions)options error:(NSError **)outError {
self = [super initWithComponentDescription:componentDescription options:options error:outError];
if (self == nil) {
return nil;
}
// Initialize a default format for the busses.
AVAudioFormat *defaultFormat = [[AVAudioFormat alloc] initStandardFormatWithSampleRate:44100. channels:2];
// Create the input and output busses.
_inputBus.init(defaultFormat, 8);
_outputBus = [[AUAudioUnitBus alloc] initWithFormat:defaultFormat error:nil];
// Create the input and output bus arrays.
_inputBusArray = [[AUAudioUnitBusArray alloc] initWithAudioUnit:self busType:AUAudioUnitBusTypeInput busses: #[_inputBus.bus]];
_outputBusArray = [[AUAudioUnitBusArray alloc] initWithAudioUnit:self busType:AUAudioUnitBusTypeOutput busses: #[_outputBus]];
self.maximumFramesToRender = 256;
return self;
}
To keep things simple, I'm setting the sample rate before starting the app.
I'm not sure where to begin tracking this down.
Update
Here's a small project which reproduces the issue I'm having:
Xcode project to reproduce issue
You'll get errors pulling from the input at certain sample rates.
On my built-in audio running at 96kHz the render block is called with alternating 511 and 513 frame counts and errors -10863 (kAudioUnitErr_CannotDoInCurrentContext) and -10874 (kAudioUnitErr_TooManyFramesToProcess) respectively. Increasing maximumFramesToRender doesn't seem to help.
Update 2
I simplified my test down to just connecting the input to the main mixer:
[self.audioEngine connect:self.audioEngine.inputNode to:self.audioEngine.mainMixerNode format:nil];
I tried explicitly setting the format argument.
This still will not play through at 96kHz. So I'm thinking this may be a bug in AVAudioEngine.
For play-through with AVAudioEngine, the input and output hardware formats and all the connection formats must be at the same sample rate. So the following should work.
AVAudioFormat *outputHWFormat = [self.audioEngine.outputNode outputFormatForBus:0];
AVAudioFormat *inputHWFormat = [self.audioEngine.inputNode inputFormatForBus:0];
if (inputHWFormat.sampleRate == outputHWFormat.sampleRate) {
[self.audioEngine connect:self.audioEngine.inputNode to:self.audioEngine.mainMixerNode format:inputHWFormat];
[self.audioEngine connect:self.audioEngine.mainMixerNode to:self.audioEngine.outputNode format:inputHWFormat];
}
How do I make a unit test for the following piece code??
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
NSString *sumOfTotalPaidBySelf = #"sumOfTotalPaidBySelf";
expressionDescription.name = sumOfTotalPaidBySelf;
expressionDescription.expression = [NSExpression expressionForKeyPath:#"#sum.moneyInMainCurrency"];
expressionDescription.expressionResultType = NSDoubleAttributeType;
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"MCPayment"];
fetchRequest.resultType = NSDictionaryResultType;
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"payingPerson = %#", self];
fetchRequest.propertiesToFetch = #[expressionDescription];
NSError *error = nil;
NSArray *fetchResult = [[self managedObjectContext] executeFetchRequest:fetchRequest error:&error];
if (error) {
NSLog(#"%#: error fetching: %#", self, error);
return nil;
}
return [[fetchResult firstObject] objectForKey:sumOfTotalPaidBySelf];
What I've learned so far is, that this code should work when querying a database that already contains data, which is stored on disk. However for the unit tests I use a persistentStoreType of NSInMemoryStoreType, which gets pre populated with some data during setup and that data still resides in the managedObjectContext.
This is the unit test I have.
MCSharedBill *tonightsBill = [MCSharedBill addSharedBillToContext:_context];
MCPerson *fred = [tonightsBill addPerson];
fred.firstName = #"Fred";
MCPerson *anna = [tonightsBill addPerson];
anna.firstName = #"Anna";
MCPayment *drinks = [tonightsBill addPayment];
drinks.payingPerson = fred;
drinks.money = #(10);
drinks.descriptionOfPayment = #"coffee";
NSNumber *totalPaidByFred = fred.totalSumPaid;
// NSError *error = nil;
// [_context save:&error];
// XCTAssertFalse(error);
XCTAssertEqualWithAccuracy(#(10).doubleValue, totalPaidByFred.doubleValue, 0.001);
Any help would be appreciated.
Cheers.
My App deals with downloading coupons & save into Passbook. But each time I download a different coupon, file is getting replaced on Passbook.
Below given is my code to add my coupons to Passbook :
Step 1 : Added 'PassKit' framework to the project & imported the same.
Step 2 : Added 'PKAddPassesViewControllerDelegate' on my h file.
Step 3 :
- (void) generatePass {
if (![PKPassLibrary isPassLibraryAvailable]) {
[[[UIAlertView alloc] initWithTitle:#"Error"
message:#"PassKit not available"
delegate:nil
cancelButtonTitle:#"Pitty"
otherButtonTitles: nil] show];
return;
}
else {
NSData *passData = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://(url).pkpass"]];
NSError* error = nil;
PKPass *newPass = [[PKPass alloc] initWithData:passData
error:&error];
if (error!=nil) {
[[[UIAlertView alloc] initWithTitle:#"Passes error"
message:[error
localizedDescription]
delegate:nil
cancelButtonTitle:#"Ooops"
otherButtonTitles: nil] show];
return;
}
PKAddPassesViewController *addController =
[[PKAddPassesViewController alloc] initWithPass:newPass];
addController.delegate = self;
[self presentViewController:addController
animated:YES
completion:nil];
}
}
Passbook indexes passes by serialNumber and passTypeIdentifier. When adding a pass, if a pass with a matching serialNumber and passTypeIdentifier already exists in a user's pass library, that pass will be overwritten by the pass being added.
To add multiple passes for the same passTypeIdentifer you will have to generate a unique serialNumber for each new pass.
Here is my problem:
I want to use core data - speed and connectivity issues to build my IOS app. The data stored in core data is coming from a SQLServer database which I can access through a yet-to-be-defined web service.
Any changes to the data stored in core data needs to be synchronized with the SQLServer via a web service. In addition, I need to buffer changes that don't get synchronized because of connectivity issues.
I also need to update core data with any changes that have occured on the server. This could happen on a schedule set in user preferences.
Solutions I've Explored:
Using NSIncrementalStore class (new in IOS 5). I'm very confused on what this does exactly but it sounds promising. From what I can tell, you subclass NSIncrementalStore which allows you to intercept the regular core data API calls. I could then pass on the the information to core data as well as sync it with the external database via a web service. I could be completely wrong. But assuming I'm right, how would I sync deltas if the connection to the internet is down?
AFIncrementalStore - This is a subclass off of NSIncrementalStore using AFNetworking to do the web services piece.
RestKit - I'm a little concerned on how active this API is and it seems to be going through a transition to block functionality. Has anyone used this extensively?
I'm leaning towards AFIncrementalStore since this is using (what seems to be) a more standard approach. The problem is, I could be completely off on what NSIncrementalStore really is.
A link to some sample code or tutorial would be great!
My solution to this was to store two copies of the data set in a CoreData database. One represents the last-known server state and is immutable. The other is edited by the user.
When it is time to sync changes, the app creates a diff between the edited and immutable copies of the data. The app sends the diff to a web service which applies the diff to its own copy of the data. It replies with a full copy of the data set, which the app overwrites onto both of its copies of the data.
The advantages are:
If there is no network connectivity, no changes are lost: the diff is calculated each time the data set needs to be sent, and the immutable copy is only changed on a successful sync.
Only the minimum amount of information that needs to be sent is transmitted.
Multiple people can edit the same data at the same time without using locking strategies with a minimum opportunity for data loss via overwrites.
The disadvantages are:
Writing the diffing code is complex.
Writing the merging service is complex.
Unless you are a metaprogramming guru, you'll find that your diff/merge code is brittle and has to change whenever you change your object model.
Here are some of the considerations I had when coming up with the strategy:
If you allow changes to be made offline, checkin/checkout locking won't work (how can you establish a lock with no connection?).
What happens if two people edit the same data at the same time?
What happens if one person edits data on one iOS device when connectionless, switches it off, edits on another device and then turns the original device back on?
Multithreading with CoreData is an entire problem class in itself.
The closest thing I've heard of to out-of-the-box support to do anything remotely like this is the new iCloud/CoreData syncing system in iOS6, which automatically transmits entities from a CoreData database to iCloud when they change. However, that means you have to use iCloud.
EDIT: This is very late, I know, but here's a class that is capable of producing a diff between two NSManagedObject instances.
// SZManagedObjectDiff.h
#interface SZManagedObjectDiff
- (NSDictionary *)diffNewObject:(NSManagedObject *)newObject withOldObject:(NSManagedObject *)oldObject
#end
// SZManagedObjectDiff.m
#import "SZManagedObjectDiff.h"
#implementation SZManagedObjectDiff
- (NSDictionary *)diffNewObject:(NSManagedObject *)newObject withOldObject:(NSManagedObject *)oldObject {
NSDictionary *attributeDiff = [self diffAttributesOfNewObject:newObject withOldObject:oldObject];
NSDictionary *relationshipsDiff = [self diffRelationshipsOfNewObject:newObject withOldObject:oldObject];
NSMutableDictionary *diff = [NSMutableDictionary dictionary];
if (attributeDiff.count > 0) {
diff[#"attributes"] = attributeDiff;
}
if (relationshipsDiff.count > 0) {
diff[#"relationships"] = relationshipsDiff;
}
if (diff.count > 0) {
diff[#"entityName"] = newObject ? newObject.entity.name : oldObject.entity.name;
NSString *idAttributeName = newObject ? newObject.entity.userInfo[#"id"] : oldObject.entity.userInfo[#"id"];
if (idAttributeName) {
id itemId = newObject ? [newObject valueForKey:idAttributeName] : [oldObject valueForKey:idAttributeName];
if (itemId) {
diff[idAttributeName] = itemId;
}
}
}
return diff;
}
- (NSDictionary *)diffRelationshipsOfNewObject:(NSManagedObject *)newObject withOldObject:(NSManagedObject *)oldObject {
NSMutableDictionary *diff = [NSMutableDictionary dictionary];
NSDictionary *relationships = newObject == nil ? [[oldObject entity] relationshipsByName] : [[newObject entity] relationshipsByName];
for (NSString *name in relationships) {
NSRelationshipDescription *relationship = relationships[name];
if (relationship.deleteRule != NSCascadeDeleteRule) continue;
SEL selector = NSSelectorFromString(name);
id newValue = nil;
id oldValue = nil;
if (newObject != nil && [newObject respondsToSelector:selector]) newValue = [newObject performSelector:selector];
if (oldObject != nil && [oldObject respondsToSelector:selector]) oldValue = [oldObject performSelector:selector];
if (relationship.isToMany) {
NSArray *changes = [self diffNewSet:newValue withOldSet:oldValue];
if (changes.count > 0) {
diff[name] = changes;
}
} else {
NSDictionary *relationshipDiff = [self diffNewObject:newValue withOldObject:oldValue];
if (relationshipDiff.count > 0) {
diff[name] = relationshipDiff;
}
}
}
return diff;
}
- (NSDictionary *)diffAttributesOfNewObject:(NSManagedObject *)newObject withOldObject:(NSManagedObject *)oldObject {
NSMutableDictionary *diff = [NSMutableDictionary dictionary];
NSArray *attributeNames = newObject == nil ? [[[oldObject entity] attributesByName] allKeys] : [[[newObject entity] attributesByName] allKeys];
for (NSString *name in attributeNames) {
SEL selector = NSSelectorFromString(name);
id newValue = nil;
id oldValue = nil;
if (newObject != nil && [newObject respondsToSelector:selector]) newValue = [newObject performSelector:selector];
if (oldObject != nil && [oldObject respondsToSelector:selector]) oldValue = [oldObject performSelector:selector];
newValue = newValue ? newValue : [NSNull null];
oldValue = oldValue ? oldValue : [NSNull null];
if (![newValue isEqual:oldValue]) {
diff[name] = #{ #"new": newValue, #"old": oldValue };
}
}
return diff;
}
- (NSArray *)diffNewSet:(NSSet *)newSet withOldSet:(NSSet *)oldSet {
NSMutableArray *changes = [NSMutableArray array];
// Find all items that have been newly created or updated.
for (NSManagedObject *newItem in newSet) {
NSString *idAttributeName = newItem.entity.userInfo[#"id"];
NSAssert(idAttributeName, #"Entities must have an id property set in their user info.");
id newItemId = [newItem valueForKey:idAttributeName];
NSManagedObject *oldItem = nil;
for (NSManagedObject *setItem in oldSet) {
id setItemId = [setItem valueForKey:idAttributeName];
if ([setItemId isEqual:newItemId]) {
oldItem = setItem;
break;
}
}
NSDictionary *diff = [self diffNewObject:newItem withOldObject:oldItem];
if (diff.count > 0) {
[changes addObject:diff];
}
}
// Find all items that have been deleted.
for (NSManagedObject *oldItem in oldSet) {
NSString *idAttributeName = oldItem.entity.userInfo[#"id"];
NSAssert(idAttributeName, #"Entities must have an id property set in their user info.");
id oldItemId = [oldItem valueForKey:idAttributeName];
NSManagedObject *newItem = nil;
for (NSManagedObject *setItem in newSet) {
id setItemId = [setItem valueForKey:idAttributeName];
if ([setItemId isEqual:oldItemId]) {
newItem = setItem;
break;
}
}
if (!newItem) {
NSDictionary *diff = [self diffNewObject:newItem withOldObject:oldItem];
if (diff.count > 0) {
[changes addObject:diff];
}
}
}
return changes;
}
#end
There's more information about what it does, how it does it and its limitations/assumptions here:
http://simianzombie.com/?p=2379
Use the Parse platform and its IOS SDK to structure and store info. It can cache data locally so you can retrieve it quickly and when there is no connectivity.
So this is what I have so far. Am I doing something wrong or is there a bug in 3.0.0.3?
var Repository = new SimpleRepository("DBConnectionName");
using (TransactionScope ts = new TransactionScope())
{
using (SharedDbConnectionScope scs = new SharedDbConnectionScope("connstring", "providerName"))
{
try
{
for (int i = 0; i < 5; i++)
{
Supplier s = new Supplier();
s.SupplierCode = i.ToString();
s.SupplierName = i.ToString();
Repository.Add<Supplier>(s);
}
ts.Complete();
}
catch
{
}
}
}
I'm getting an error in SubSonic DbDataProvider
public DbConnection CurrentSharedConnection
{
get { return __sharedConnection; }
protected set
{
if(value == null)
{
__sharedConnection.Dispose();
etc..
__sharedConnection == null :( Object Null Reference Exception :(
Finally solved this for myself. All of the above code does not work for me (SubSonic 3.0.0.3, using SQLite) but adding BeginTransaction() caused it to work as expected, greatly speeding up the transaction and rolling back the updates if any exceptions occur.
using (SharedDbConnectionScope sharedConnectionScope = new SharedDbConnectionScope(Access.Provider))
{
using (IDbTransaction ts = sharedConnectionScope.CurrentConnection.BeginTransaction())
{
IRepository repo = new SimpleRepository(Access.Provider);
//Do your database updates
//throw new ApplicationException("Uncomment this and see if the updates get rolled back");
ts.Commit();
}
}
For completeness: Access.Provider is a static property in a helper class for me that returns return SubSonic.DataProviders.ProviderFactory.GetProvider(ConnectionString, "System.Data.SQLite");
Perhaps switching the SharedDbConnectionScope and TransactionScope around may help.
using (SharedDbConnectionScope scs = new SharedDbConnectionScope("connstring", "providerName"))
{
using (TransactionScope ts = new TransactionScope())
{
}
}
This will happen when Migration is set - On tablemigration the dbconnection will be closed.
Try the SimpleRepository with SimpleRepositoryOptions.None.
Don't know if this is a bug. I think the transactions don't work with SimpleRepository, I've always half of the data saved when throwing an exception in the transaction... perhaps it's only for ActiveRecord? Anybody knows?