How to get the value of variable within the blocks - ios

Is there anyway to check the value of 'type' variable with completionHandler.
-(void)sendApiMethod:(NSString*)apiName ApiType:(NSString*)type
{
[SendAPI setAPIWithName:#"APIName" completionHandler:^(NSArray *errors) {
if([type isEqualToString:#"Login"])
{
/// Call Some Other function
}
}];
}

I wrote a small piece of code to verify if works (only reading your question I would say yes as Droppy)
I added all there code in a ViewController in a Simple View App.
some assumption:
- all code there for sake of semplicity ....
- I have added a singleton as it seems You are calling a class method.
- instance method is a bit rough, it simply saves name and block
- I added a typedef for blocks to better reading it.
#import "ViewController.h"
typedef void (^CompletionBlock)(NSArray *errors);
#interface SendAPI : NSObject
-(void)setAPIWithName:(NSString*)name completionHandler: (CompletionBlock)completionHandler;
+(void)setAPIWithName:(NSString*)name completionHandler: (CompletionBlock)completionHandler;
+(SendAPI*)sharedInstance;
#property (strong) CompletionBlock completionBlock;
#property (strong) NSString * name;
#end
#implementation SendAPI : NSObject
static SendAPI * _singleton = nil;
+(SendAPI*)sharedInstance
{
if (_singleton == nil)
{
_singleton = [[SendAPI alloc] init];
}
return _singleton;
}
-(void)setAPIWithName:(NSString*)name completionHandler: (CompletionBlock)completionHandler;
{
self.completionBlock = completionHandler;
self.name = [name copy];
__weak SendAPI * weakRef = self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSError* err = [NSError errorWithDomain: #"delayed"
code:1111
userInfo: #{#"info": self.name}
];
weakRef.completionBlock(#[err]);
});
}
+(void)setAPIWithName:(NSString*)name completionHandler: (CompletionBlock)completionHandler;
{
[[SendAPI sharedInstance]setAPIWithName:name completionHandler:completionHandler];
}
#end
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self sendApiMethod:#"HELLO" ApiType: #"Login"];
}
-(void)sendApiMethod:(NSString*)apiName ApiType:(NSString*)type{
[SendAPI setAPIWithName:#"APIName" completionHandler:^(NSArray *errors) {
if([type isEqualToString:#"Login"])
{
/// Call Some Other function
NSLog(#"%#", errors);
}
}];
}
it does LOG correctly

Related

EXC_BAD_ACCESS objective-c block

I run this code inside my viewDidLoad method to fetch data from Firebase to put it in a UIPageViewController
#interface MapViewController () <RoutesPageDelegate>
#property (weak, nonatomic) IBOutlet MKMapView *mapView;
#property (weak, nonatomic) RoutesPageViewController *routesPageViewController;
#property (weak, nonatomic) FIRFirestore *db;
#end
#implementation MapViewController
- (void) viewDidLoad {
[super viewDidLoad];
self.db = [FIRFirestore firestore];
for (UIViewController *obj in self.childViewControllers) {
if ([obj isKindOfClass:[RoutesPageViewController class]]) {
self.routesPageViewController = (RoutesPageViewController *)obj;
self.routesPageViewController.routesPageDelegate = self;
}
}
FIRCollectionReference *routesRef = [self.db collectionWithPath:#"routes"];
[routesRef getDocumentsWithCompletion:^(FIRQuerySnapshot * _Nullable snapshot, NSError * _Nullable error) {
if (error != nil) {
// TODO: handle error
} else {
NSMutableArray<RouteModel*> *routes = [NSMutableArray array];
// For each route
for (FIRDocumentSnapshot *document in snapshot.documents) {
RouteModel *route = [[RouteModel alloc] init];
route.title = document.data[#"title"];
route.color = document.data[#"color"];
route.city = document.data[#"city"];
[routes addObject:route];
}
[self.routesPageViewController setRoutes:routes];
}
}];
}
And this is the called setRoutes method:
- (void) setRoutes:(NSMutableArray<RouteModel *> *)routes {
self.routes = routes;
NSMutableArray<RoutePageViewController *> * routeViewControllers = [NSMutableArray array];
for (RouteModel * route in routes) {
[routeViewControllers addObject:[self viewControllerAtIndex:[routes indexOfObject:route]]];
}
[self setViewControllers:routeViewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
}
When the setRoutes method gets executed it throws the error in the image below, saying that it cannot dereference it:
The setRoutes methods gets executed inside a block.
And I get this weird thread stack:
How can I solve this?
Your problem is here:
- (void) setRoutes:(NSMutableArray<RouteModel *> *)routes {
self.routes = routes;
invoking self.routes implicity calles the setter setRoutes which causes a recursive infinite calls as indicated by your stack.
At the time the block is passed onto getDocumentsWithCompletion method is executed, routes array has already been deallocated and set to nil because no one is retaining it anywhere outside the block.
You should either move it into the block or declare it as a class property so it won't be thrown out of memory while class instance is alive.
[routesRef getDocumentsWithCompletion:^(FIRQuerySnapshot *snapshot, NSError *error) {
NSMutableArray<RouteModel*> *routes = [NSMutableArray array];
...
[self.routesPageViewController setRoutes:routes];
}];
After the update:
Doing self.routes = routes invokes setRoutes: which in turn is causing a loop in your code. You should change it to:
- (void)setRoutes:(NSMutableArray<RouteModel *> *)routes {
if (_routes != routes) {
_routes = routes;
...
}
}

access objects from Bluetooth manager using singleton in ios

I am trying to create a singleton class for my BLEManager which gets called on viewdidload of the launch screen ViewController. How can I get the objects _transporter and _BLEAdapter in my view controller after the init is completed?
BLEManager.h
#interface MyManager : NSObject {
BLETransporter* _transporter;
BLEAdapter* _BLEAdapter;
}
#property (strong, nonatomic) BLETransporter* transporter;
#property (strong, nonatomic) BLEAdapter* BLEAdapter;
+ (id)sharedInstance;
#end
BLEManager.m
#implementation MyManager
#synthesize BLEAdapter=_BLEAdapter;
#synthesize transporter = _BLEAdapter;
+(BLEManager*)sharedInstance{
static BLEManager *sharedInstance = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
- (id)init {
if (self = [super init]) {
_transporter = [BLETransporter transporterWithIdentifier:nil serviceUUIDs:serviceUUIDs];
[_transporter connectWithBlock:^(NSInputStream * _Nullable inputStream, NSOutputStream * _Nullable outputStream) {
if ( !inputStream )
{
LOG( #"Could not connect to device" );
return;
}
_BLEAdapter = [BLEAdapter adapterWithInputStream:inputStream outputStream:outputStream];
[_BLEAdapter connect];
}];
}
return self
}

iOS - Issue When Consuming API - dispatch_async

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.

Healthkit: Data not showing first time running.

I have a strange problem when reading data out of healthkit. The first time when i press the button the weight is printed 0, the next times i press the button the weight is printed as normal.I would really appreciate it if someone could help me.
Thanks!
I have the following code.
HealtkitManager.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <HealthKit/HealthKit.h>
#interface HealthKitManager : NSObject
#property (nonatomic) float weight;
#property (nonatomic) HKHealthStore *healthStore;
+(HealthKitManager *)sharedManager;
-(void) requestAuthorization;
-(double) readWeight;
#end
Healthkitmanager.m
#import "HealthKitManager.h"
#import <healthKit/healthKit.h>
#import "StartScreen.h"
#interface HealthKitManager ()
#end
#implementation HealthKitManager
+(HealthKitManager *) sharedManager{
static dispatch_once_t pred = 0;
static HealthKitManager *instance = nil;
dispatch_once(&pred, ^{
instance = [[HealthKitManager alloc]init];
instance.healthStore = [[HKHealthStore alloc]init];
});
return instance;
}
-(void)requestAuthorization {
if([HKHealthStore isHealthDataAvailable]==NO){
return;
}
NSArray *readTypes = #[[HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyMass],
[HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyMassIndex],
[HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyFatPercentage]];
[self.healthStore requestAuthorizationToShareTypes:nil readTypes:[NSSet setWithArray:readTypes] completion:nil];
}
-(double) readWeight{
NSMassFormatter *massFormatter = [[NSMassFormatter alloc]init];
massFormatter.unitStyle = NSFormattingUnitStyleLong;
HKQuantityType *weightType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyMass];
[self fetchMostRecentDataOfQuantityType:weightType withCompletion:^(HKQuantity *mostRecentQuantity, NSError *error) {
if(!mostRecentQuantity){
NSLog(#"%#",#"Error. ");
}
else{
HKUnit *weightUnit = [HKUnit gramUnit];
_weight = [mostRecentQuantity doubleValueForUnit:weightUnit];
_weight = (float)_weight/1000.00f;
}
}];
return _weight;
}
- (void)fetchMostRecentDataOfQuantityType:(HKQuantityType *)quantityType withCompletion:(void (^)(HKQuantity *mostRecentQuantity, NSError *error))completion {
NSSortDescriptor *timeSortDescriptor = [[NSSortDescriptor alloc] initWithKey:HKSampleSortIdentifierEndDate ascending:NO];
HKSampleQuery *query = [[HKSampleQuery alloc] initWithSampleType:quantityType predicate:nil limit:1 sortDescriptors:#[timeSortDescriptor] resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error) {
if (!results) {
if (completion) {
completion(nil, error);
}
return;
}
if (completion) {
HKQuantitySample *quantitySample = results.firstObject;
HKQuantity *quantity = quantitySample.quantity;
completion(quantity, error);
}
}];
[self.healthStore executeQuery:query];
}
#end
Startscreen.m
#import "StartScreen.h"
#import "HealthKitManager.h"
#interface StartScreen ()
#property(nonatomic,weak) IBOutlet UILabel *weightLabel;
#end
#implementation StartScreen
- (void)viewDidLoad {
[super viewDidLoad];
[[HealthKitManager sharedManager] requestAuthorization];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(IBAction)readAgeButtonPressed:(id)sender{
HealthKitManager *readWeight = [[HealthKitManager sharedManager] init];
NSLog(#"%.2f",readWeight.readWeight);
}
#end
You use singleton HealthKitManager but your code
HealthKitManager *readWeight = [[HealthKitManager sharedManager] init];
in IBAction readAgeButtonPressed should be
HealthKitManager *readWeight = [HealthKitManager sharedManager];
You don't need init.
Also, you can change the code
[[HealthKitManager sharedManager] requestAuthorization];
to be
HealthKitManager *readWeight = [HealthKitManager sharedManager];
[readWeight requestAuthorization];
NSLog(#"%.2f",readWeight.readWeight);
checking the value of readWeight.

Losing instance variable values

.h
#interface sample : NSObject{
#public
NSData* data;
int val;
}
.m
#implementation sample
-(id) init:(NSData*)data{
if (self = [super init]) {
[self initdata:data];
}
NSLog(#"### self %#",self);
NSLog(#"### self->data %#",self->data);
return self;
}
-(void) initdata:(NSData*) data{
#try{
self->data = [[NSData alloc]initWithData:data];
self->val = 10;
}#catch (NSException* e) {
}
}
}
after returning from initdata method if i print values it's null.
When i install app from xcode to iphone everything works fine but when i install using testflight or itunes all the instance variables are null.
it looks simple, i tried but not able to solve.
Edit:
.m
-(id) init:(NSData*)data{
if (self = [super init]) {
[self initDecodeOffline:data];
}
NSLog(#"### VSF self %#",self);
NSLog(#"### VSF self->SBI %#",self->SBI);
return self;
}
-(void) initDecodeOffline:(NSData*) data{
#try{
self->SBI = [[NSData alloc]initWithData:SBI];
NSLog(#"### VSF SBI-->%#",self->SBI);
}#catch (NSException* e) {
self->inErrorState = true;
}
}
.h
#interface BarCodeProduct : NSObject{
#public
int selectedQuantity;
long rmsCartIndex;
int referenceId;
NSData* SBI;
int maxAllowed;
}
-(id) init:(NSData*)data;
#end

Resources