I want to check if the user has an open basket if so send them to the menu if not then send send them to my open basket view but i m getting an error.
So my question is how to get rid of this problem ? and is my logic right ?
'NSInvalidArgumentException', reason: '-[Merchant merchantId]: unrecognized selector sent to instance 0x7fad19529490'.
i m using this method to check for open baskets when the button addToOrderButtonTapped is press in the menuview
[[OrderManager sharedManager]getBasketsForMerchant:merchant success:^(NSArray *baskets) {
for (Basket *basket in baskets){
if ([basket.status isEqualToString:kBasketStatusOpen]) {
[self.openBaskets addObject:basket];
}
}if (self.openBaskets.count > 0){
self.openBaskets = self.openBaskets;
dispatch_async(dispatch_get_main_queue(), ^{
[self performSegueWithIdentifier:kSegueMenu sender:self];
});
}
else {
dispatch_async(dispatch_get_main_queue(), ^{
[self performSegueWithIdentifier:KSegueOpenBasket sender:self];
});
}
} failure:^(NSError *error, NSHTTPURLResponse *response) {
dispatch_async(dispatch_get_main_queue(), ^{
NSString *localizedString = NSLocalizedString(#"Order.ErrorLoadingOpenOrders", "Get error message");
[self showMessage:localizedString withTitle:MessageTypeError];
[MBProgressHUD hideAllHUDsForView:self.view animated:YES];
});
}];
The menuview
//
// MenuItemViewController.m
// BaseApp
//
#import "MenuItemViewController.h"
#import "RoundedButton.h"
#import "MenuOptionCell.h"
#import "MenuItem.h"
#import "MenuOptionButton.h"
#import "MenuChoice.h"
#import "OrderManager.h"
#import "TypeOfChoiceCell.h"
#import "MenuOption.h"
#import "OrderPricingTableViewCell.h"
#import "RKManagedObjectStore.h"
#import "NSManagedObjectContext+RKAdditions.h"
#import "MerchantManager.h"
#import "Merchant.h"
#import "OrdersViewController.h"
#import "MenuItem.h"
#import "MenuViewController.h"
#import "OrderConfirmationViewController.h"
#import "OrderManager.h"
#import "APIClient.h"
#import "MenuManager.h"
#import "DiscoverViewController.h"
#import "DiscoverCollectionViewCell.h"
#import "MerchantManager.h"
#import "MenuViewController.h"
#import "MenuManager.h"
#import "MerchantDetailsViewController.h"
#import "OpenOrdersViewController.h"
typedef NS_ENUM(NSUInteger, MenuOptionsSection) {
MenuOptionsSectionRequired = 0,
MenuOptionsSectionOptional = 1
};
static NSString *const OpenBasketsSegue = #"OpenBaskets";
static NSString *const kSegueMenu = #"ShowMenu";
static NSString *const KSegueOpenBasket = #"OpenBaskets";
#interface MenuItemViewController () <UITableViewDelegate, UITableViewDataSource>
#property (nonatomic, weak) IBOutlet UILabel *menuItemName;
#property (nonatomic, weak) IBOutlet UILabel *quantityLabel;
#property (nonatomic, weak) IBOutlet UILabel *menuItemPrice;
#property (nonatomic, weak) IBOutlet UILabel *subTotalLabel;
#property (nonatomic, weak) IBOutlet UITextView *menuItemDescription;
#property (nonatomic, weak) IBOutlet RoundedButton *addToOrderButton;
#property (nonatomic, weak) IBOutlet UITableView *tableView;
#property (nonatomic, weak) IBOutlet UIView *cartView;
#property (nonatomic, weak) IBOutlet UIButton *subtractButton;
#property (nonatomic) NSDecimalNumber *temporarySubtotal;
#property (nonatomic, strong) NSMutableArray<MenuOption *> *requiredChoices;
#property (nonatomic, strong) NSMutableArray<MenuOption *> *optionalChoices;
#property (nonatomic, strong) NSMutableArray<NSMutableArray *> *optionChoicesSection;
#property (nonatomic, strong) NSMutableArray<NSDictionary *> *options;
#property (nonatomic, strong) NSMutableArray <Basket *>*openBaskets;
#property (nonatomic) BOOL launchViewFirstTime;
#end
#implementation MenuItemViewController
#pragma - Lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.title = #"Menu Item";
self.menuItemName.text = self.menuCategory.selectedBasketLine.menuItem.name;
self.menuItemPrice.text = [NSString stringWithFormat:#"%#", [Basket formattedCurrencyStringForAmount:self.menuCategory.selectedBasketLine.menuItem.price]];
self.quantityLabel.text = self.menuCategory.selectedBasketLine.quantity.stringValue;
self.temporarySubtotal = [self calculateTemporarySubtotal:[OrderManager sharedManager].currentBasket.subtotal menuItemPrice:self.menuCategory.selectedBasketLine.menuItem.price];
[self setSubtotalText:self.temporarySubtotal];
self.menuItemDescription.text = self.menuCategory.selectedBasketLine.menuItem.menuItemDescription;
self.subtractButton.alpha = 0.65;
self.requiredChoices = [[NSMutableArray alloc] init];
self.optionalChoices = [[NSMutableArray alloc] init];
self.optionChoicesSection = [[NSMutableArray alloc] init];
self.options = [[NSMutableArray alloc] init];
[self initializeChoiceArrays];
self.tableView.delegate = self;
self.tableView.dataSource = self;
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.launchViewFirstTime = YES;
if (self.optionChoicesSection.count > 0) {
[self.tableView reloadData];
} else {
self.tableView.hidden = YES;
}
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
self.launchViewFirstTime = NO;
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
for (MenuOption *menuOption in self.requiredChoices) {
[menuOption resetNumberOfChoicesSelected];
}
for (MenuOption *menuOption in self.optionalChoices) {
[menuOption resetNumberOfChoicesSelected];
}
self.menuCategory = nil;
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
[self.menuItemDescription setContentOffset:CGPointZero animated:YES];
}
#pragma - IBActions
- (IBAction)addButtonTapped:(id)sender {
NSInteger count = self.quantityLabel.text.integerValue;
count++;
if (count > 1) {
self.subtractButton.alpha = 1;
}
NSDecimalNumber *newSubTotal = [self.temporarySubtotal decimalNumberByAdding:self.menuCategory.selectedBasketLine.menuItem.price];
[self modifyCurrentBasketSubtotal:newSubTotal quantity:count];
}
- (IBAction)subtractButtonTapped:(id)sender {
NSInteger count = self.quantityLabel.text.integerValue;
if (count > 1) {
count--;
NSDecimalNumber *newSubTotal = [self.temporarySubtotal decimalNumberBySubtracting:self.menuCategory.selectedBasketLine.menuItem.price];
[self modifyCurrentBasketSubtotal:newSubTotal quantity:count];
if (count == 1) {
self.subtractButton.alpha = 0.65;
}
}
}
- (IBAction)addToOrderButtonTapped:(id)sender {
MenuOption *menuOption;
Merchant *merchant = [[Merchant alloc]init];
// First check if there are any missing required options that have to be selected
if ((menuOption = [self checkMissingRequiredOptionsHaveBeenSelected])) {
NSString *localizedString = NSLocalizedString(#"AddMenuItem.RequiredChoicesNotSelected", #"Get string for error");
NSString *formattedString = [NSString stringWithFormat:localizedString, menuOption.name];
[self showMessage:formattedString withTitle:MessageTypeError];
return;
}
// Now check if the minimum and maximum choice seletion have been met for the menu options
if ((menuOption = [self validateMinMaxForMenuOptionsHaveBeenMet])) {
NSString *localizedString = NSLocalizedString(#"AddMenuItem.MinMaxNotFulfilled", #"Get string for error");
NSString *formattedString = [NSString stringWithFormat:localizedString, menuOption.name];
[self showMessage:formattedString withTitle:MessageTypeError];
return;
}
// Add the menu item to the basket
if (self.menuItemAddedBlock) {
self.menuItemAddedBlock(self, self.menuCategory);
// [self dismissViewControllerAnimated:YES completion:nil];
//checking for open basket here
[[OrderManager sharedManager]getBasketsForMerchant:merchant success:^(NSArray *baskets) {
for (Basket *basket in baskets){
if ([basket.status isEqualToString:kBasketStatusOpen]) {
[self.openBaskets addObject:basket];
}
}if (self.openBaskets.count > 0){
self.openBaskets = self.openBaskets;
dispatch_async(dispatch_get_main_queue(), ^{
[self performSegueWithIdentifier:kSegueMenu sender:self];
});
}
else {
dispatch_async(dispatch_get_main_queue(), ^{
[self performSegueWithIdentifier:KSegueOpenBasket sender:self];
});
}
} failure:^(NSError *error, NSHTTPURLResponse *response) {
dispatch_async(dispatch_get_main_queue(), ^{
NSString *localizedString = NSLocalizedString(#"Order.ErrorLoadingOpenOrders", "Get error message");
[self showMessage:localizedString withTitle:MessageTypeError];
[MBProgressHUD hideAllHUDsForView:self.view animated:YES];
});
}];
}
}
- (IBAction)cancelButtonTapped:(UIBarButtonItem *)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
#pragma - UITableViewDelegate, UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return self.optionChoicesSection.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.optionChoicesSection[section].count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MenuOptionCell *cell = (MenuOptionCell *)[tableView dequeueReusableCellWithIdentifier:[MenuOptionCell reuseIdentifier]];
cell.menuOption = self.optionChoicesSection[indexPath.section][indexPath.row];
cell.menuOptionCellButtonPressedBlock = ^(MenuOptionButton *button, MenuOption *option, MenuChoice *choice) {
[self adjustSelectedOptions:option choice:choice indexPath:indexPath];
};
cell.menuOptionCellDefaultOptionDetectedBlock = ^(MenuOptionButton *button, MenuOption *option, MenuChoice *choice) {
[self adjustSelectedOptions:option choice:choice indexPath:indexPath];
};
cell.menuOptionCellDeselectedBlock = ^(MenuOptionButton *button, MenuOption *option, MenuChoice *choice) {
[self adjustSelectedOptions:option choice:choice indexPath:indexPath];
};
[cell configureCell:self.launchViewFirstTime];
return cell;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
TypeOfChoiceCell *cell = (TypeOfChoiceCell *)[tableView dequeueReusableCellWithIdentifier:[TypeOfChoiceCell reuseIdentifier]];
switch ((MenuOptionsSection)section) {
case MenuOptionsSectionRequired:
if (self.requiredChoices.count > 0) {
cell.title = NSLocalizedString(#"AddMenuItem.RequiredChoiceText", #"Get string for title");
return cell;
} else if (self.optionalChoices.count > 0) {
cell.title = NSLocalizedString(#"AddMenuItem.OptionalChoiceText", #"get string for title");
return cell;
}
case MenuOptionsSectionOptional:
if (self.optionalChoices.count > 0) {
cell.title = NSLocalizedString(#"AddMenuItem.OptionalChoiceText", #"Get string for title");
return cell;
}
}
return nil;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
MenuOption *option = self.optionChoicesSection[indexPath.section][indexPath.row];
return [MenuOptionCell heightForMenuOption:option];
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return [TypeOfChoiceCell height];
}
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
return 0.01f;
}
#pragma - Helpers
- (void)setSubtotalText:(NSDecimalNumber *)subtotal {
if (!subtotal) {
self.subTotalLabel.text = NSLocalizedString(#"AddMenuItem.SubtotalDefault", #"Get string for subtotal");
} else {
NSString *localizedString = NSLocalizedString(#"AddMenuItem.SubtotalFormatted", #"Get string for subtotal");
self.subTotalLabel.text = [NSString stringWithFormat:localizedString, [Basket formattedCurrencyStringForAmount:subtotal]];
}
}
- (void)modifyCurrentBasketSubtotal:(NSDecimalNumber *)subtotal quantity:(NSInteger)quantity {
self.menuCategory.selectedBasketLine.quantity = [NSNumber numberWithInteger:quantity];
self.menuCategory.selectedBasketLine.subtotal = subtotal;
self.temporarySubtotal = subtotal;
[self setSubtotalText:subtotal];
self.quantityLabel.text = self.menuCategory.selectedBasketLine.quantity.stringValue;
}
- (NSDecimalNumber *)calculateTemporarySubtotal:(NSDecimalNumber *)orderManagerSubTotal menuItemPrice:(NSDecimalNumber *)menuItemPrice {
if (orderManagerSubTotal == 0) {
return menuItemPrice;
} else {
return [orderManagerSubTotal decimalNumberByAdding:menuItemPrice];
}
}
- (MenuOption *)checkMissingRequiredOptionsHaveBeenSelected {
for (MenuOption *menuOption in self.requiredChoices) {
if (!menuOption.selected) {
return menuOption;
}
}
return nil;
}
- (MenuOption *)validateMinMaxForMenuOptionsHaveBeenMet {
for (MenuOption *menuOption in self.requiredChoices) {
if ([menuOption validateIndividualMinMaxForMenuOption]) {
return menuOption;
}
}
for (MenuOption *menuOption in self.optionalChoices) {
if (menuOption.selected) {
if ([menuOption validateIndividualMinMaxForMenuOption]) {
return menuOption;
}
}
}
return nil;
}
- (void)initializeChoiceArrays {
NSArray<MenuOption *> *menuOptions = [self.menuCategory.selectedBasketLine.menuItem.menuOptions allObjects];
for (MenuOption *menuOption in menuOptions) {
if (menuOption.minimumChoices == nil) {
menuOption.minimumChoices = [NSNumber numberWithInt:0];
}
if (menuOption.maximumChoices == nil) {
menuOption.maximumChoices = [NSNumber numberWithInt:0];
}
// For now make an optional choice required if minimumChoices > 0
if (menuOption.isRequired || [menuOption.minimumChoices intValue] > 0) {
menuOption.isRequired = YES;
[self.requiredChoices addObject:menuOption];
} else {
[self.optionalChoices addObject:menuOption];
}
}
if (self.requiredChoices.count > 0) {
[self.optionChoicesSection addObject:self.requiredChoices];
}
if (self.optionalChoices.count > 0) {
[self.optionChoicesSection addObject:self.optionalChoices];
}
}
- (void)adjustSelectedOptions:(MenuOption *)option choice:(MenuChoice *)choice indexPath:(NSIndexPath *)indexPath {
self.menuCategory.selectedBasketLine.subtotal = self.menuCategory.selectedBasketLine.menuItem.price;
if (option.selected && option.menuChoices.count == 0) {
[self.options addObject:#{kOption: option.menuOptionId ?: #"", kChoice: #""}];
if (option.price) {
self.temporarySubtotal = [self.temporarySubtotal decimalNumberByAdding:option.price];
}
if (option.isRequired) {
self.requiredChoices[indexPath.row].selected = option.selected;
self.requiredChoices[indexPath.row].numberOfChoicesSelected = option.numberOfChoicesSelected;
} else {
self.optionalChoices[indexPath.row].selected = option.selected;
self.optionalChoices[indexPath.row].numberOfChoicesSelected = option.numberOfChoicesSelected;
}
} else {
if (option.menuChoices.count == 0) {
[self.options removeObject:#{kOption: option.menuOptionId ?: #"", kChoice: #""}];
if (option.price) {
self.temporarySubtotal = [self.temporarySubtotal decimalNumberBySubtracting:option.price];
}
if (option.isRequired) {
self.requiredChoices[indexPath.row].selected = option.selected;
self.requiredChoices[indexPath.row].numberOfChoicesSelected = option.numberOfChoicesSelected;
} else {
self.optionalChoices[indexPath.row].selected = option.selected;
self.optionalChoices[indexPath.row].numberOfChoicesSelected = option.numberOfChoicesSelected;
}
}
}
if (choice.selected && option.menuChoices.count > 0) {
[self.options addObject:#{kOption: choice.menuOption.menuOptionId ?: #"", kChoice: choice.menuChoiceId ?: #""}];
if (choice.price) {
self.temporarySubtotal = [self.temporarySubtotal decimalNumberByAdding:choice.price];
}
if (option.isRequired) {
self.requiredChoices[indexPath.row].selected = choice.selected;
self.requiredChoices[indexPath.row].numberOfChoicesSelected = option.numberOfChoicesSelected;
} else {
self.optionalChoices[indexPath.row].selected = choice.selected;
self.optionalChoices[indexPath.row].numberOfChoicesSelected = option.numberOfChoicesSelected;
}
} else {
if (option.menuChoices.count > 0) {
[self.options removeObject:#{kOption: choice.menuOption.menuOptionId ?: #"", kChoice: choice.menuChoiceId ?: #""}];
if (choice.price) {
self.temporarySubtotal = [self.temporarySubtotal decimalNumberBySubtracting:choice.price];
}
if (option.isRequired) {
if ([option.numberOfChoicesSelected intValue] == 0) {
self.requiredChoices[indexPath.row].selected = option.selected;
} else {
self.requiredChoices[indexPath.row].selected = !choice.selected;
}
self.requiredChoices[indexPath.row].numberOfChoicesSelected = option.numberOfChoicesSelected;
} else {
self.optionalChoices[indexPath.row].selected = choice.selected;
self.optionalChoices[indexPath.row].numberOfChoicesSelected = option.numberOfChoicesSelected;
}
}
}
[self setSubtotalText:self.temporarySubtotal];
self.menuCategory.selectedBasketLine.attributes = self.options;
}
#end
This is the line of code thats giving me the error
+ (void)getBasketsForMerchant:(Merchant *)merchant success:(void (^)(NSArray *basket))success failure:(void (^)(NSError *, NSHTTPURLResponse *))failure {
NSMutableDictionary* params = #{#"expand": #"merchant"}.mutableCopy;
if (merchant) {
params[#"merchant"] = merchant.merchantId;
}
[[RKObjectManager sharedManager] getObjectsAtPath:kOrdersEndpoint parameters:params success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult){
NSArray* items = mappingResult.array;
if (success) {
success(items);
}
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
if (failure) {
failure(error, operation.HTTPRequestOperation.response);
} else {
_defaultFailureBlock(operation, error);
}
}];
}
This error:
'NSInvalidArgumentException', reason: '-[Merchant merchantId]: unrecognized selector sent to instance 0x7fad19529490'.
Means that you have an object at location 0x7fad19529490 and you tried to call "merchantId" on it. That object does not respond to merchantId.
So, look very carefully at the definition of Merchant. Did you get the spelling of merchantId right in fieldMappings? Is it merchantID with a capital D?
If you are sure that merchant has a property named merchantId exactly, then, the next likely thing is that the object at 0x7fad19529490 is not a Merchant.
The easiest thing to do is to add an exception breakpoint (use the +) at the bottom of the breakpoint navigator. When this exception happens, grab the pointer address from the error (it might be different each time) and see what it is in the debugger. To do that, at the (lldb) prompt, type:
po (NSObject*)(0x7fad19529490)
but use the address from the error. If it's a Merchant, I expect it to say something like:
<Merchant: 0x7fad19529490>
Or, if you have overridden description, it will be the output of that.
Related
I'm trying to run my application on iPhone 6 (iOS 9.3.2) and using XCode 7.3 but the problem is that it is getting crashed everytime I move from one tab to another. The Exception is pointing to this class and the method marked as "error".
Can anyone please help me out with this?
I'm attaching the code for your reference.
Thanks.
//
// AsyncImageView.m
//
#import "AsyncImageView.h"
#import <objc/message.h>
#define MAX_IMAGE_SIZE 1.5 * 1024 * 1024
#define RESIZE_IMG 0
NSString *const AsyncImageLoadDidFinish = #"AsyncImageLoadDidFinish";
NSString *const AsyncImageLoadDidFail = #"AsyncImageLoadDidFail";
NSString *const AsyncImageTargetReleased = #"AsyncImageTargetReleased";
NSString *const AsyncImageImageKey = #"image";
NSString *const AsyncImageURLKey = #"URL";
NSString *const AsyncImageCacheKey = #"cache";
NSString *const AsyncImageErrorKey = #"error";
#interface AsyncImageConnection : NSObject
#property (nonatomic, strong) NSURLConnection *connection;
#property (nonatomic, strong) NSMutableData *data;
#property (nonatomic, strong) NSURL *URL;
#property (nonatomic, strong) NSCache *cache;
#property (nonatomic, strong) id target;
#property (nonatomic, assign) SEL success;
#property (nonatomic, assign) SEL failure;
#property (nonatomic, readonly, getter = isLoading) BOOL loading;
#property (nonatomic, readonly) BOOL cancelled;
- (AsyncImageConnection *)initWithURL:(NSURL *)URL
cache:(NSCache *)cache
target:(id)target
success:(SEL)success
failure:(SEL)failure;
- (void)start;
- (void)cancel;
- (BOOL)isInCache;
#end
#implementation AsyncImageConnection
#synthesize connection = _connection;
#synthesize data = _data;
#synthesize URL = _URL;
#synthesize cache = _cache;
#synthesize target = _target;
#synthesize success = _success;
#synthesize failure = _failure;
#synthesize loading = _loading;
#synthesize cancelled = _cancelled;
- (AsyncImageConnection *)initWithURL:(NSURL *)URL
cache:(NSCache *)cache
target:(id)target
success:(SEL)success
failure:(SEL)failure
{
if ((self = [self init]))
{
self.URL = URL;
self.cache = cache;
self.target = target;
self.success = success;
self.failure = failure;
}
return self;
}
- (UIImage *)cachedImage
{
if ([_URL isFileURL])
{
NSString *path = [[_URL absoluteURL] path];
NSString *resourcePath = [[NSBundle mainBundle] resourcePath];
if ([path hasPrefix:resourcePath])
{
return [UIImage imageNamed:[path substringFromIndex:[resourcePath length]]];
}
}
return [_cache objectForKey:_URL];
}
- (BOOL)isInCache
{
return [self cachedImage] != nil;
}
- (void)loadFailedWithError:(NSError *)error
{
_loading = NO;
_cancelled = NO;
[[NSNotificationCenter defaultCenter] postNotificationName:AsyncImageLoadDidFail
object:_target
userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
_URL, AsyncImageURLKey,
error, AsyncImageErrorKey,
nil]];
}
- (void)cacheImage:(UIImage *)image
{
if (!_cancelled)
{
if (image && _URL)
{
BOOL storeInCache = YES;
if ([_URL isFileURL])
{
if ([[[_URL absoluteURL] path] hasPrefix:[[NSBundle mainBundle] resourcePath]])
{
//do not store in cache
storeInCache = NO;
}
}
if (storeInCache)
{
#if RESIZE_IMG
// resize the image before storing
NSData *data = UIImageJPEGRepresentation(image, 1.0);
if (data.length > MAX_IMAGE_SIZE)
{
image = [HFUtils imageWithImage:image scaledBy:MAX_IMAGE_SIZE / (float)data.length];
}
#endif
[_cache setObject:image forKey:_URL];
}
}
NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
image, AsyncImageImageKey,
_URL, AsyncImageURLKey,
nil];
if (_cache)
{
[userInfo setObject:_cache forKey:AsyncImageCacheKey];
}
_loading = NO;
[[NSNotificationCenter defaultCenter] postNotificationName:AsyncImageLoadDidFinish
object:_target
userInfo:[[userInfo copy] autorelease]];
}
else
{
_loading = NO;
_cancelled = NO;
}
}
- (void)processDataInBackground:(NSData *)data
{
#synchronized ([self class])
{
if (!_cancelled)
{
UIImage *image = [[UIImage alloc] initWithData:data];
if (image)
{
//add to cache (may be cached already but it doesn't matter)
[self performSelectorOnMainThread:#selector(cacheImage:)
withObject:image
waitUntilDone:YES];
[image release];
}
else
{
#autoreleasepool
{
NSError *error = [NSError errorWithDomain:#"AsyncImageLoader" code:0 userInfo:[NSDictionary dictionaryWithObject:#"Invalid image data" forKey:NSLocalizedDescriptionKey]];
[self performSelectorOnMainThread:#selector(loadFailedWithError:) withObject:error waitUntilDone:YES];
}
}
}
else
{
//clean up
[self performSelectorOnMainThread:#selector(cacheImage:)
withObject:nil
waitUntilDone:YES];
}
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
self.data = [NSMutableData data];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
//add data
[_data appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[self performSelectorInBackground:#selector(processDataInBackground:) withObject:_data];
self.connection = nil;
self.data = nil;
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
self.connection = nil;
self.data = nil;
[self loadFailedWithError:error];
}
- (void)start
{
if (_loading && !_cancelled)
{
return;
}
//begin loading
_loading = YES;
_cancelled = NO;
//check for nil URL
if (_URL == nil)
{
[self cacheImage:nil];
return;
}
//check for cached image
UIImage *image = [self cachedImage];
if (image)
{
//add to cache (cached already but it doesn't matter)
[self performSelectorOnMainThread:#selector(cacheImage:)
withObject:image
waitUntilDone:NO];
return;
}
//begin load
NSURLRequest *request = [NSURLRequest requestWithURL:_URL
cachePolicy:NSURLCacheStorageNotAllowed
timeoutInterval:[AsyncImageLoader sharedLoader].loadingTimeout];
_connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
[_connection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
[_connection start];
}
- (void)cancel
{
_cancelled = YES;
[_connection cancel];
self.connection = nil;
self.data = nil;
}
- (void)dealloc
{
[_connection release];
[_data release];
[_URL release];
[_target release];
[super ah_dealloc];
}
#end
#interface AsyncImageLoader ()
#property (nonatomic, strong) NSMutableArray *connections;
#end
#implementation AsyncImageLoader
#synthesize cache = _cache;
#synthesize connections = _connections;
#synthesize concurrentLoads = _concurrentLoads;
#synthesize loadingTimeout = _loadingTimeout;
+ (AsyncImageLoader *)sharedLoader
{
static AsyncImageLoader *sharedInstance = nil;
if (sharedInstance == nil)
{
sharedInstance = [[self alloc] init];
}
return sharedInstance;
}
+ (NSCache *)defaultCache
{
static NSCache *sharedInstance = nil;
if (sharedInstance == nil)
{
sharedInstance = [[NSCache alloc] init];
}
return sharedInstance;
}
- (AsyncImageLoader *)init
{
if ((self = [super init]))
{
self.cache = [[self class] defaultCache];
_concurrentLoads = 2;
_loadingTimeout = 60.0;
_connections = [[NSMutableArray alloc] init];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(imageLoaded:)
name:AsyncImageLoadDidFinish
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(imageFailed:)
name:AsyncImageLoadDidFail
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(targetReleased:)
name:AsyncImageTargetReleased
object:nil];
}
return self;
}
- (void)updateQueue
{
//start connections
NSUInteger count = 0;
for (AsyncImageConnection *connection in _connections)
{
if (![connection isLoading])
{
if ([connection isInCache])
{
[connection start];
}
else if (count < _concurrentLoads)
{
count ++;
[connection start];
}
}
}
}
- (void)imageLoaded:(NSNotification *)notification
{
//complete connections for URL
NSURL *URL = [notification.userInfo objectForKey:AsyncImageURLKey];
for (int i = (int)[_connections count] - 1; i >= 0; i--)
{
AsyncImageConnection *connection = [_connections objectAtIndex:(NSUInteger)i];
if (connection.URL == URL || [connection.URL isEqual:URL])
{
//cancel earlier connections for same target/action
for (int j = i - 1; j >= 0; j--)
{
AsyncImageConnection *earlier = [_connections objectAtIndex:(NSUInteger)j];
if (earlier.target == connection.target &&
earlier.success == connection.success)
{
[earlier cancel];
[_connections removeObjectAtIndex:(NSUInteger)j];
i--;
}
}
//cancel connection (in case it's a duplicate)
[connection cancel];
//perform action
UIImage *image = [notification.userInfo objectForKey:AsyncImageImageKey];
#if RESIZE_IMG
// resize image before sending it over
NSData *data = UIImageJPEGRepresentation(image, 1.0);
if (data.length > MAX_IMAGE_SIZE)
{
image = [HFUtils imageWithImage:image scaledBy:MAX_IMAGE_SIZE / (float)data.length];
}
#endif
objc_msgSend(connection.target, connection.success, image, connection.URL);
//remove from queue
[_connections removeObjectAtIndex:(NSUInteger)i];
}
}
//update the queue
[self updateQueue];
}
- (void)imageFailed:(NSNotification *)notification
{
//remove connections for URL
NSURL *URL = [notification.userInfo objectForKey:AsyncImageURLKey];
for (int i = (int)[_connections count] - 1; i >= 0; i--)
{
AsyncImageConnection *connection = [_connections objectAtIndex:(NSUInteger)i];
if ([connection.URL isEqual:URL])
{
//cancel connection (in case it's a duplicate)
[connection cancel];
//perform failure action
if (connection.failure)
{
NSError *error = [notification.userInfo objectForKey:AsyncImageErrorKey];
objc_msgSend(connection.target, connection.failure, error, URL);
}
//remove from queue
[_connections removeObjectAtIndex:(NSUInteger)i];
}
}
//update the queue
[self updateQueue];
}
- (void)targetReleased:(NSNotification *)notification
{
//remove connections for URL
id target = [notification object];
for (int i = (int)[_connections count] - 1; i >= 0; i--)
{
AsyncImageConnection *connection = [_connections objectAtIndex:(NSUInteger)i];
if (connection.target == target)
{
//cancel connection
[connection cancel];
[_connections removeObjectAtIndex:(NSUInteger)i];
}
}
//update the queue
[self updateQueue];
}
- (void)loadImageWithURL:(NSURL *)URL target:(id)target success:(SEL)success failure:(SEL)failure
{
if (URL == nil)
return;
//check cache
UIImage *image = [_cache objectForKey:URL];
if (image)
{
[self cancelLoadingImagesForTarget:self action:success];
if (success) [target performSelectorOnMainThread:success withObject:image waitUntilDone:NO];
return;
}
//create new connection
AsyncImageConnection *connection = [[AsyncImageConnection alloc] initWithURL:URL
cache:_cache
target:target
success:success
failure:failure];
BOOL added = NO;
for (NSUInteger i = 0; i < [_connections count]; i++)
{
AsyncImageConnection *existingConnection = [_connections objectAtIndex:i];
if (!existingConnection.loading)
{
[_connections insertObject:connection atIndex:i];
added = YES;
break;
}
}
if (!added)
{
[_connections addObject:connection];
}
[connection release];
[self updateQueue];
}
- (void)loadImageWithURL:(NSURL *)URL target:(id)target action:(SEL)action
{
[self loadImageWithURL:URL target:target success:action failure:NULL];
}
- (void)loadImageWithURL:(NSURL *)URL
{
[self loadImageWithURL:URL target:nil success:NULL failure:NULL];
}
- (void)cancelLoadingURL:(NSURL *)URL target:(id)target action:(SEL)action
{
for (int i = (int)[_connections count] - 1; i >= 0; i--)
{
AsyncImageConnection *connection = [_connections objectAtIndex:(NSUInteger)i];
if ([connection.URL isEqual:URL] && connection.target == target && connection.success == action)
{
[connection cancel];
[_connections removeObjectAtIndex:(NSUInteger)i];
}
}
}
- (void)cancelLoadingURL:(NSURL *)URL target:(id)target
{
for (int i = (int)[_connections count] - 1; i >= 0; i--)
{
AsyncImageConnection *connection = [_connections objectAtIndex:(NSUInteger)i];
if ([connection.URL isEqual:URL] && connection.target == target)
{
[connection cancel];
[_connections removeObjectAtIndex:(NSUInteger)i];
}
}
}
- (void)cancelLoadingURL:(NSURL *)URL
{
for (int i = (int)[_connections count] - 1; i >= 0; i--)
{
AsyncImageConnection *connection = [_connections objectAtIndex:(NSUInteger)i];
if ([connection.URL isEqual:URL])
{
[connection cancel];
[_connections removeObjectAtIndex:(NSUInteger)i];
}
}
}
- (void)cancelLoadingImagesForTarget:(id)target action:(SEL)action
{
for (int i = (int)[_connections count] - 1; i >= 0; i--)
{
AsyncImageConnection *connection = [_connections objectAtIndex:(NSUInteger)i];
if (connection.target == target && connection.success == action)
{
[connection cancel];
}
}
}
- (void)cancelLoadingImagesForTarget:(id)target
{
for (int i = (int)[_connections count] - 1; i >= 0; i--)
{
AsyncImageConnection *connection = [_connections objectAtIndex:(NSUInteger)i];
if (connection.target == target)
{
[connection cancel];
}
}
}
- (NSURL *)URLForTarget:(id)target action:(SEL)action
{
//return the most recent image URL assigned to the target for the given action
//this is not neccesarily the next image that will be assigned
for (int i = (int)[_connections count] - 1; i >= 0; i--)
{
AsyncImageConnection *connection = [_connections objectAtIndex:(NSUInteger)i];
if (connection.target == target && connection.success == action)
{
return [[connection.URL ah_retain] autorelease];
}
}
return nil;
}
- (NSURL *)URLForTarget:(id)target
{
//return the most recent image URL assigned to the target
//this is not neccesarily the next image that will be assigned
for (int i = (int)[_connections count] - 1; i >= 0; i--)
{
AsyncImageConnection *connection = [_connections objectAtIndex:(NSUInteger)i];
if (connection.target == target)
{
return [[connection.URL ah_retain] autorelease];
}
}
return nil;
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[_cache release];
[_connections release];
[super ah_dealloc];
}
#end
#implementation UIImageView(AsyncImageView)
- (void)setImageURL:(NSURL *)imageURL
{
[[AsyncImageLoader sharedLoader] loadImageWithURL:imageURL target:self action:#selector(setImage:)];
}
- (NSURL *)imageURL
{
return [[AsyncImageLoader sharedLoader] URLForTarget:self action:#selector(setImage:)];
}
#end
#interface AsyncImageView ()
#property (nonatomic, strong) UIActivityIndicatorView *activityView;
#end
#implementation AsyncImageView
#synthesize showActivityIndicator = _showActivityIndicator;
#synthesize activityIndicatorStyle = _activityIndicatorStyle;
#synthesize crossfadeImages = _crossfadeImages;
#synthesize crossfadeDuration = _crossfadeDuration;
#synthesize activityView = _activityView;
- (void)setUp
{
_showActivityIndicator = YES;
_activityIndicatorStyle = UIActivityIndicatorViewStyleGray;
_crossfadeImages = YES;
_crossfadeDuration = 0.4;
}
- (id)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame]))
{
[self setUp];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
if ((self = [super initWithCoder:aDecoder]))
{
[self setUp];
}
return self;
}
- (void)setImageURL:(NSURL *)imageURL
{
super.imageURL = imageURL;
if (_showActivityIndicator && !self.image && imageURL)
{
if (_activityView == nil)
{
_activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:_activityIndicatorStyle];
_activityView.hidesWhenStopped = YES;
_activityView.center = CGPointMake(self.bounds.size.width / 2.0f, self.bounds.size.height / 2.0f);
_activityView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin;
[self addSubview:_activityView];
}
[_activityView startAnimating];
}
}
- (void)setActivityIndicatorStyle:(UIActivityIndicatorViewStyle)style
{
_activityIndicatorStyle = style;
[_activityView removeFromSuperview];
self.activityView = nil;
}
- (void)setImage:(UIImage *)image //error
{
if (_crossfadeImages)
{
//implement crossfade transition without needing to import QuartzCore
id animation = objc_msgSend(NSClassFromString(#"CATransition"), #selector(animation));
objc_msgSend(animation, #selector(setType:), #"kCATransitionFade");
objc_msgSend(animation, #selector(setDuration:), _crossfadeDuration);
objc_msgSend(self.layer, #selector(addAnimation:forKey:), animation, nil);
}
super.image = image;
[_activityView stopAnimating];
}
- (void)dealloc
{
[[AsyncImageLoader sharedLoader] cancelLoadingURL:self.imageURL target:self];
[_activityView release];
[super ah_dealloc];
}
#end
I was trying to get a basic prototype of an API working. I have the web service setup and now I'm trying to have an iOS app request the data and display it in a table. It's pretty basic, however, random parts of the data are displaying as null in the table and I haven't been able to find a common thread.
But basically, I make a call to this API, I parse the data, i use a StopCollection class to createItem (of type StopItem) and add it to an NSMutableArray in StopCollection. And then in the MainTableViewController, I use that array as a data source.
The data comes in correctly from the API. I'm able to add it the NSMutableArray just fine. However, it seems that in the MainTableViewController, once I get to the following line, I no longer have access to the same data in the array and bunch of the values are null:
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
Here's what I'm working with:
MainTableViewController.m
#import "MainTableViewController.h"
#import "StopCollection.h"
#import "StopItem.h"
#interface MainTableViewController ()
#property (nonatomic, strong) NSURLSession *session;
#end
#implementation MainTableViewController
- (instancetype)init
{
self = [super initWithStyle:UITableViewStylePlain];
if (self) {
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
_session = [NSURLSession sessionWithConfiguration:config
delegate:nil
delegateQueue:nil];
[self fetchFeed];
}
return self;
}
- (void)fetchFeed
{
NSString *requestString = #"http://nexttrain.dev/station?include=stopTime";
NSURL *url = [NSURL URLWithString:requestString];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSessionDataTask *dataTask =
[self.session dataTaskWithRequest:request
completionHandler:
^(NSData *data, NSURLResponse *response, NSError *error) {
NSDictionary *jsonObject = [NSJSONSerialization JSONObjectWithData:data
options:0
error:nil];
for(NSDictionary *stop in jsonObject[#"data"])
{
for (NSDictionary *stopTime in stop[#"stopTime"][#"data"]) {
[[StopCollection sharedStore] createItem:stop[#"station"]
withRoute:stopTime[#"route"]
withHeadsign:stopTime[#"trip"]
withArrivalTime:stopTime[#"arrival_time"]];
NSLog(#"%#", stop[#"station"]);
}
}
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}];
[dataTask resume];
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self.tableView registerClass:[UITableViewCell class]
forCellReuseIdentifier:#"UITableViewCell"];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[[StopCollection sharedStore] stops] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"UITableViewCell"
forIndexPath:indexPath];
NSArray *allStops = [[StopCollection sharedStore] stops];
StopItem *stop = allStops[indexPath.row];
NSString *formattedText = [NSString stringWithFormat:#"%# - %# - %#", stop.station, stop.route, stop.arrivalTime];
cell.textLabel.text = formattedText;
cell.textLabel.font = [UIFont systemFontOfSize:12];
return cell;
}
StopCollection.m
#import "StopCollection.h"
#import "StopItem.h"
#interface StopCollection()
#property (nonatomic) NSMutableArray *privateStops;
#end
#implementation StopCollection
// Override Accessor for Stops, and return from privateStops.
- (NSArray *)stops
{
return [self.privateStops copy];
}
// Singleton Access to Object.
+ (instancetype)sharedStore
{
// Static will be Strong and will remain.
static StopCollection *sharedStore;
// Create initial instance.
if (!sharedStore) {
sharedStore = [[self alloc] initPrivate];
}
// Return instance if exists.
return sharedStore;
}
// Make defualt init inaccessible.
- (instancetype)init
{
// Throw Exception if accesssed.
[NSException raise:#"Singleton"
format:#"Use + (instancetype)sharedStore"];
return nil;
}
// Private initializer will call Super init.
- (instancetype)initPrivate
{
self = [super init];
if (self) {
_privateStops = [[NSMutableArray alloc] init];
}
return self;
}
- (void)createItem:(NSString *)station withRoute:(NSString *)route withHeadsign:(NSString *)headsign withArrivalTime:(NSString *)arrivalTime
{
StopItem *stop = [[StopItem alloc] initWithStation:station
lattitude:22
longitude:56
route:route
tripHeadsign:headsign
arrivalTime];
[self.privateStops addObject:stop];
}
#end
StopCollection.h
#import <Foundation/Foundation.h>
#interface StopCollection : NSObject
+ (instancetype)sharedStore;
#property(nonatomic, readonly, copy) NSArray *stops;
- (void)createItem:(NSString *)station
withRoute:(NSString *)route
withHeadsign:(NSString *)headsign
withArrivalTime:(NSString *)arrivalTime;
#end
StopItem.m
#import "StopItem.h"
#implementation StopItem
- (instancetype)initWithStation:(NSString *)station lattitude:(NSInteger)lattitude longitude:(NSInteger)longitude route:(NSString *)route tripHeadsign:(NSString *)headsign arrivalTime:(NSString *)arrivalTime
{
self = [super init];
if (self) {
_station = station;
_lattitude = lattitude;
_longitude = longitude;
_route = route;
_tripHeadsign = headsign;
_arrivalTime = arrivalTime;
}
return self;
}
- (instancetype)init {
return [self initWithStation:#"Hey" lattitude:0 longitude:0 route:#"" tripHeadsign:#"" arrivalTime:[[NSString alloc] init]];
}
#end
StopItem.h
#interface StopItem : NSObject
#property (nonatomic, weak) NSString *station;
#property (nonatomic) NSInteger lattitude;
#property (nonatomic) NSInteger longitude;
#property (nonatomic, weak) NSString *route;
#property (nonatomic, weak) NSString *tripHeadsign;
#property (nonatomic, weak) NSString *arrivalTime;
- (instancetype)initWithStation:(NSString *)station
lattitude:(NSInteger)lattitude
longitude:(NSInteger)longitude
route:(NSString *)route
tripHeadsign:(NSString *)headsign
arrivalTime:(NSString *)arrivalTime;
#end
And here's the JSON data set I'm working with:
{
"data":[
{
"station":"2 Av",
"lattitude":"40.723402",
"longitude":"-73.989938",
"stopTime":{
"data":[
{
"arrival_time":"02:40:30",
"trip":"JAMAICA - 179 ST",
"route":"some longer word with a -"
}
]
}
},
{
"station":"2 Av",
"lattitude":"40.723402",
"longitude":"-73.989938",
"stopTime":{
"data":[
{
"arrival_time":"00:54:00",
"trip":"CONEY ISLAND - STILLWELL AV",
"route":"some longer word with a -"
}
]
}
},
{
"station":"Delancey St",
"lattitude":"40.718611",
"longitude":"-73.988114",
"stopTime":{
"data":[
{
"arrival_time":"02:39:00",
"trip":"JAMAICA - 179 ST",
"route":"some longer word with a -"
}
]
}
},
{
"station":"Delancey St",
"lattitude":"40.718611",
"longitude":"-73.988114",
"stopTime":{
"data":[
{
"arrival_time":"00:55:30",
"trip":"CONEY ISLAND - STILLWELL AV",
"route":"some longer word with a -"
}
]
}
},
{
"station":"Essex St",
"lattitude":"40.718315",
"longitude":"-73.987437",
"stopTime":{
"data":[
{
"arrival_time":"01:23:30",
"trip":"JAMAICA CENTER - PARSONS/ARCHER",
"route":"some longer word with a -"
}
]
}
},
{
"station":"Essex St",
"lattitude":"40.718315",
"longitude":"-73.987437",
"stopTime":{
"data":[
{
"arrival_time":"00:52:30",
"trip":"BROAD ST",
"route":"some longer word with a -"
}
]
}
},
{
"station":"Bowery",
"lattitude":"40.72028",
"longitude":"-73.993915",
"stopTime":{
"data":[
{
"arrival_time":"01:22:00",
"trip":"JAMAICA CENTER - PARSONS/ARCHER",
"route":"some longer word with a -"
}
]
}
},
{
"station":"Bowery",
"lattitude":"40.72028",
"longitude":"-73.993915",
"stopTime":{
"data":[
{
"arrival_time":"00:54:00",
"trip":"BROAD ST",
"route":"some longer word with a -"
}
]
}
}
]
}
That's not related to dispatch. It's because you have weak properties, which get nilled whenever possible, i.e. when there is no reference holding them. Change weak to strong or copy in your StopItem model, and everything will work.
I have a Shearwell stick reader, which reads in cattle tags, sheep tags and goat tags via BLE. When connecting the BLE device, the device list shows up in the tableView of the SDLBluetoothConnectViewController then disappears. I am also not able to grab the information from the device using a segue. The bluetooth icon flashes 3 times and does not pair with the device. Can anyone see where my error is i've looked for hours!! thanks in advance!
here is the SDLStickreader.m
// A StickReader is a wrapper for a CBPeripheral that has been discovered using a filter for StickReader UUID.
#import "SDLStickReader.h"
#import "SDLStickReaderManager.h"
#import "SDLStickReaderPrivate.h"
#import "SDLSerialPort.h"
//#import "SDLIdentifier.h"
#interface SDLStickReader () <SerialPortDelegate>
#property (strong, nonatomic) CBPeripheral *peripheral;
#end
#implementation SDLStickReader
static SDLStickReaderManager * _manager;
NSMutableData *_data;
SDLSerialPort *serialPort;
BOOL waitingForConfigInfo = YES;
NSMutableString *configString;
+(SDLStickReaderManager *)manager {
if (nil == _manager) {
_manager = [[SDLStickReaderManager alloc] init];
}
return _manager;
}
+ (instancetype)forPeripheral: (CBPeripheral *)peripheral {
return [[self alloc] initWithPeripheral:peripheral];
}
- (instancetype)initWithPeripheral: (CBPeripheral *)peripheral {
self = [super init];
if (self) {
_detail = #"";
self.peripheral = peripheral;
//peripheral.delegate = self;
__tagReadService = [CBUUID UUIDWithString:SDL_STICKREADER_TAGREAD_UUID];
if (peripheral.state == CBPeripheralStateConnected) {
NSLog(#"periperal is connected");
} else {
NSLog(#"periperal is NOT connected");
}
_data = [[NSMutableData alloc] init];
serialPort = [[SDLSerialPort alloc] initWithPeripheral:peripheral andDelegate:self];
peripheral.delegate = self;
[serialPort open ];
}
return self;
}
-(BOOL)hasPeripheral: (CBPeripheral *)peripheral {
return [self.peripheral isEqual: peripheral];
}
-(NSUUID *)identifier {
return self.peripheral.identifier;
}
-(NSString *)name {
return self.peripheral.name;
}
-(NSString *)description {
return [NSString stringWithFormat: #"Peripheral: %#", self.name];
}
-(NSString *)state {
switch (self.peripheral.state) {
case CBPeripheralStateConnected:
return SDL_STICKREADER_STATE_CONNECTED;
case CBPeripheralStateConnecting:
return SDL_STICKREADER_STATE_CONNECTING;
case CBPeripheralStateDisconnected:
return SDL_STICKREADER_STATE_DISCONNECTED;
}
}
// Every time the peripheral sends new data, it calls the delegate peripheral:didUpdateValueForCharacteristic:error: method. The second argument contains the characteristic that you can read.
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic: (CBCharacteristic *)characteristic error:(NSError *)error {
if (error) {
NSLog(#"Error");
return;
}
if ([self.characteristics containsObject: characteristic.UUID]) {
NSString *stringFromData = [[NSString alloc] initWithData:characteristic.value encoding:NSUTF8StringEncoding];
// Have we got everything we need?
NSUInteger startOfEom = [stringFromData rangeOfString: SDL_STICKREADER_EOM].location;
if (NSNotFound == startOfEom) {
// it is not the EOM so append the data to what we have so far and wait for more.
[_data appendData: characteristic.value];
} else {
// contains EOM, so remove the
NSString *lastPart = [stringFromData substringToIndex:startOfEom];
NSString *message = [[NSString alloc] initWithData: _data encoding:NSUTF8StringEncoding];
if (nil != lastPart && lastPart.length > 0) {
message = [message stringByAppendingString:lastPart];
}
if (nil != self.listener) {
[self.listener stickReader:self didReadTag:message];
}
[_data setLength:0];
}
}
}
// Method that ensures that the CBCentral knows when a notification state for a given characteristic changes. Track it in order to understand when a characteristic state changes (update app values). You should check if the characteristic notification has stopped. If it has, you should disconnect from it:
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
if (![self.characteristics containsObject: characteristic.UUID]) {
return;
}
if (characteristic.isNotifying) {
NSLog(#"Notification began ");
} else {
// Notification has stopped
NSLog(#"Notification stopped ");
//[_manager cancelPeripheralConnection:peripheral];
}
}
/////////////////////////////////////////////////////////////////////////////////////
- (void) port: (SDLSerialPort*) serialPort event : (SPEvent) ev error: (NSInteger)err {
if (SP_EVT_OPEN == ev) {
//NSLog(#"serialPortOpened");
configString = [[NSMutableString alloc] init];
[serialPort write: [#"c\r" dataUsingEncoding: NSUTF8StringEncoding]];
} else {
NSLog(#"serialPortClosed");
}
}
- (void) writeComplete: (SDLSerialPort*) serialPort withError: (NSInteger)err {
NSLog(#"writeComplete");
}
- (void) port: (SDLSerialPort*) serialPort receivedData: (NSData*)data {
if (data.length > 0) {
NSCharacterSet *charSet = [NSCharacterSet characterSetWithCharactersInString: #"|()#"];
NSString *str = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
NSLog(#"receivedData: %#", str);
if ([str hasPrefix: #"Shearwell"]) {
_detail = str;
//NSLog(#"updating: %#", str);
[[SDLStickReader manager] addedShearwellStickReader: self];
} else if (NSNotFound != [str rangeOfCharacterFromSet: charSet].location ) {
// Data
if (waitingForConfigInfo) {
[configString appendString:str];
if (NSNotFound != [configString rangeOfString: #"(cS)"].location) {
// got end of config string, parse config.
NSRange found = [configString rangeOfString: #"#18|"];
if (NSNotFound != found.location) {
NSRange getRange = NSMakeRange(found.location + found.length, 1);
NSString *str = [configString substringWithRange: getRange];
self.eidFormat = [str integerValue];
NSLog(#"StickReader format: %d", self.eidFormat);
}
waitingForConfigInfo = NO;
[serialPort write: [#"v\r" dataUsingEncoding: NSUTF8StringEncoding]];
}
} else {
// Users data
[self.listener stickReader:self didReadData: str];
}
} else {
// Tag
if (nil != self.listener) {
[self.listener stickReader:self didReadTag: str];
}
}
}
}
and my SDLBluetoothConnectViewController.m
#import "SDLBluetoothConnectViewController.h"
#import "SDLStickReaderManager.h"
#import "SDLStickReader.h"
#import "SDLViewController.h"
#interface SDLBluetoothConnectViewController () <UITableViewDataSource, UITableViewDelegate, SDLStickReaderManagerListener>
#property (weak, nonatomic) IBOutlet UITableView *stickReaderListView;
#property (strong, nonatomic) IBOutlet UILabel *stickReaderNameLabel;
#property (strong, nonatomic) IBOutlet UILabel *stickReaderDescLabel;
#end
/*
This class starts the process of scanning in the 'viewDidLoad' method by setting the SDLStickReaderManager 'singleton' to self. When the scan button is pressed the SDLStickReaderManager scan is called, and each stick reader found is returned via the listener call. This listener call is used to refresh the table view.
When a user selects a stick reader from the list the StickReader view is started and passed the selected stick reader.
*/
#implementation SDLBluetoothConnectViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self.stickReaderListView setDelegate: self];
[self.stickReaderListView setDataSource: self];
[SDLStickReader manager].listener = self;
[self.stickReaderListView reloadData];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)scanButtonPressed:(id)sender {
[SDLStickReader.manager scan];
}
- (IBAction)cancelButtonPressed:(id)sender {
SDLStickReader.manager.listener = nil;
[self dismissViewControllerAnimated:YES completion:nil];
}
-(void)viewWillDisappear:(BOOL)animated {
/*
Every time the view disappears, you should stop the scanning process.
*/
[SDLStickReader.manager stopScan];
}
/////////////////////////////////////////////////////////////////////////////////
/////// Table handling //////////////////////////////////////////////////////////
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSArray *stickReaders = SDLStickReader.manager.discoveredStickReaders;
SDLStickReader * stickReader = [stickReaders objectAtIndex: indexPath.row];
[SDLStickReader.manager stopScan];
[self performSegueWithIdentifier:#"deviceInfoSegue" sender:self];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSArray *stickReaders = SDLStickReader.manager.discoveredStickReaders;
return stickReaders.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString * simpleTableIdentifier = #"SimpleTableCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: simpleTableIdentifier];
if (nil == cell) {
cell = [[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault reuseIdentifier: simpleTableIdentifier];
}
NSArray *stickReaders = SDLStickReader.manager.discoveredStickReaders;
SDLStickReader * stickReader = [stickReaders objectAtIndex: indexPath.row];
NSString *desc = [stickReader name];
cell.textLabel.text = desc;
return cell;
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"deviceInfoSegue"]) {
//some additional info here
}
}
////////////////////////////////////////////////////////////////////////////////// /
/// SDLStickReaderManager callbacks ///////////////////////////////////////////////
-(void)stickReader: (SDLStickReader *)stickReader addedToList: (NSArray *)discoveredStickReaders {
//NSLog(#"View StickReaderAdded");
[self.stickReaderListView reloadData];
//[SDLStickReader.manager connect: stickReader];
}
-(void)connectedStickReader: (SDLStickReader *)stickReader {
//NSLog(#"View StickReader connected");
[self.stickReaderListView reloadData];
}
-(void)disconnectedStickReader: (SDLStickReader *)stickReader {
//NSLog(#"View StickReader disconnected");
[self.stickReaderListView reloadData];
}
I have a custom UIProgressView class (from John Rommel Estropia) which works perfect on 64 bit iPhone simulator and device. But when I run it on 32bit simulators/devices it did not show on the screen? Any idea?
This problem also valid for another class for drawing circular progress.
Here is .m file:
#import "SPKProgress.h"
#interface SPKProgress()
#property (nonatomic, weak) UIImageView *trackImageView;
#property (nonatomic, weak) UIImageView *progressImageView;
#end
#implementation SPKProgress
#pragma mark - NSObject
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
[self setupProgressView];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self)
{
[self setupProgressView];
}
return self;
}
#pragma mark - UIView
- (void)layoutSubviews
{
[super layoutSubviews];
UIImageView *trackImageView = self.trackImageView;
UIImageView *progressImageView = self.progressImageView;
if (!trackImageView || !progressImageView)
{
return;
}
CGRect bounds = self.bounds;
CGFloat boundsTop = CGRectGetMinY(bounds);
UIImage *trackImage = self.trackImage;
if (trackImage)
{
CGRect trackFrame = trackImageView.frame;
CGFloat trackHeight = trackImage.size.height;
trackImageView.frame = (CGRect){
.origin.x = CGRectGetMinX(trackFrame),
.origin.y = (boundsTop
+ ((CGRectGetHeight(bounds) - trackHeight) * 0.5f)),
.size.width = CGRectGetWidth(trackFrame),
.size.height = trackHeight
};
}
UIImage *progressImage = self.progressImage;
if (progressImage)
{
CGRect progressFrame = progressImageView.frame;
CGFloat progressHeight = progressImage.size.height;
progressImageView.frame = (CGRect){
.origin.x = CGRectGetMinX(progressFrame),
.origin.y = (boundsTop
+ ((CGRectGetHeight(bounds) - progressHeight) * 0.5f)),
.size.width = CGRectGetWidth(progressFrame),
.size.height = progressHeight
};
}
}
#pragma mark - UIProgressView
- (void)setProgressImage:(UIImage *)progressImage
{
[super setProgressImage:progressImage];
self.progressImageView.image = progressImage;
}
- (void)setTrackImage:(UIImage *)trackImage
{
[super setTrackImage:trackImage];
self.trackImageView.image = trackImage;
}
#pragma mark - private
- (void)setupProgressView
{
if ([self compareVersionString:[UIDevice currentDevice].systemVersion
withVersionString:#"7.1"] == NSOrderedAscending)
{
return;
}
NSArray *subviews = self.subviews;
if ([subviews count] != 2)
{
return;
}
for (UIView *subview in subviews)
{
if (![subview isKindOfClass:[UIImageView class]])
{
return;
}
}
self.trackImageView = subviews[0];
self.progressImageView = subviews[1];
self.trackImageView.image = self.trackImage;
self.progressImageView.image = self.progressImage;
}
- (NSComparisonResult)compareVersionString:(NSString *)versionString1
withVersionString:(NSString *)versionString2
{
NSArray *components1 = [versionString1 componentsSeparatedByString:#"."];
NSArray *components2 = [versionString2 componentsSeparatedByString:#"."];
NSUInteger components1Count = [components1 count];
NSUInteger components2Count = [components2 count];
NSUInteger partCount = MAX(components1Count, components2Count);
for (NSInteger part = 0; part < partCount; ++part)
{
if (part >= components1Count)
{
return NSOrderedAscending;
}
if (part >= components2Count)
{
return NSOrderedDescending;
}
NSString *part1String = components1[part];
NSString *part2String = components2[part];
NSInteger part1 = [part1String integerValue];
NSInteger part2 = [part2String integerValue];
if (part1 > part2)
{
return NSOrderedDescending;
}
if (part1 < part2)
{
return NSOrderedAscending;
}
}
return NSOrderedSame;
}
#end
And the header file:
#import <UIKit/UIKit.h>
#interface SPKProgress : UIProgressView
#end
For some reason NSString objects just won't go into an NSMutableSet
The set remains empty - count stays 0 and firstInHandler is never called
I have tried everything..!
If anyone has any idea why I would greatly appreciate the help
VCKeySet.h
#import <Foundation/Foundation.h>
#interface VCKeySet : NSObject
{
void (^firstInHandler)();
void (^lastOutHandler)();
}
#property (atomic, readonly, strong) NSMutableSet* keys;
- (id) initWithFirstInHandler:(void(^)()) firstInHandler_ withLastOutHandler:(void(^)()) lastOutHandler_;
- (void) add:(NSString*) key_;
- (void) remove: (NSString*) key_;
- (BOOL) has: (NSString*) key;
#end
VCKeySet.m
#import "VCKeySet.h"
#implementation VCKeySet
#synthesize keys;
- (id) initWithFirstInHandler: (void(^)()) firstInHandler_ withLastOutHandler: (void(^)()) lastOutHandler_
{
if(self=[self init])
{
firstInHandler = firstInHandler_;
lastOutHandler = lastOutHandler_;
}
return self;
}
- (void) add:(NSString*) key_
{
for(NSString* key in self.keys)
{
if([key isEqualToString:key_])
{
return;
}
}
[self.keys addObject:key_];
if([self.keys count] == 1)
{
firstInHandler();
}
}
- (void) remove: (NSString*) key_
{
for(NSString* key in keys)
{
if([key isEqualToString:key_])
{
[keys removeObject:key];
if([keys count] == 0)
{
lastOutHandler();
}
}
}
}
- (BOOL) has: (NSString*) key_
{
for(NSString* key in keys)
{
if([key isEqualToString:key_])
{
return YES;
}
}
return NO;
}
#end
Here is how I try to add a key to the set
VCKeySet* lock = [[VCKeySet alloc] initWithFirstInHandler:^()
{
NSLog(#"Adding UI lock");
[application beginIgnoringInteractionEvents];
}
withLastOutHandler:^()
{
NSLog(#"Removing UI lock");
[application endIgnoringInteractionEvents];
}];
[lock add:#"InitialiseApp"];
Thanks
The problem is, you never allocate and initialize your set.
Refactor your add: method to look something like this:
- (void) add:(NSString*) key_ {
if (!self.keys) {
_keys = [[NSMutableSet alloc] init];
}
[self.keys addObject:key_];
if([self.keys count] == 1) {
firstInHandler();
}
}