I am working with IOT weather connect display(Use Local Network) that connect with the router using iOS app. connection between display and iOS app is done by local network.popup came up at the time of connection for allowing the local network privacy but i want to check in advance that user has allowed or not local network permission. I refer this iOS 14 How to trigger Local Network dialog and check user answer? and https://developer.apple.com/forums/thread/663858
but i am looking for code in Objecitve-C. please help me with that
This is my original answer that is written in Swift - iOS 14 How to trigger Local Network dialog and check user answer? and you can use it from objc as well without any effort.
But if you have a pure ObjC project and don't want to add swift files this is the similar approach that works the same:
// LocalNetworkPrivacy.h
#interface LocalNetworkPrivacy : NSObject
- (void)checkAccessState:(void (^)(BOOL))completion;
#end
// LocalNetworkPrivacy.m
#import <UIKit/UIKit.h>
#import "LocalNetworkPrivacy.h"
#interface LocalNetworkPrivacy () <NSNetServiceDelegate>
#property (nonatomic) NSNetService *service;
#property (nonatomic) void (^completion)(BOOL);
#property (nonatomic) NSTimer *timer;
#property (nonatomic) BOOL publishing;
#end
#implementation LocalNetworkPrivacy
- (instancetype)init {
if (self = [super init]) {
self.service = [[NSNetService alloc] initWithDomain:#"local." type:#"_lnp._tcp." name:#"LocalNetworkPrivacy" port:1100];
}
return self;
}
- (void)dealloc {
[self.service stop];
}
- (void)checkAccessState:(void (^)(BOOL))completion {
self.completion = completion;
self.timer = [NSTimer scheduledTimerWithTimeInterval:2 repeats:YES block:^(NSTimer * _Nonnull timer) {
if (UIApplication.sharedApplication.applicationState != UIApplicationStateActive) {
return;
}
if (self.publishing) {
[self.timer invalidate];
self.completion(NO);
}
else {
self.publishing = YES;
self.service.delegate = self;
[self.service publish];
}
}];
}
#pragma mark - NSNetServiceDelegate
- (void)netServiceDidPublish:(NSNetService *)sender {
[self.timer invalidate];
self.completion(YES);
}
#end
How to use:
LocalNetworkPrivacy* localNetworkPrivacy = [LocalNetworkPrivacy new];
[localNetworkPrivacy checkAccessState:^(BOOL granted) {
NSLog(#"Granted: %#", granted ? #"YES" : #"NO");
}];
NOTE: You must set NSLocalNetworkUsageDescription and add "_lnp._tcp." to NSBonjourServices into your Info.plist.
Related
I have some Realm models in my app that all use a base class. In this class I wrote some generic functions like the one below:
- (void)save {
self.updatedAt = [NSDate date];
[self.realm beginWriteTransaction];
[self.realm addOrUpdateObject:self];
[self.realm commitWriteTransaction];
[[SyncEngine sharedInstance] store:self];
}
Now, I also wrote a class called SyncEngine, which checks if some available synchronization methods are enabled and then calls them:
- (void)store:(id)object {
if ([Preferences CloudKitEnabled]) {
[self.cloudKit store:object];
}
}
This is where my problem arises. I have written a base class called CloudKitManager which has some generic functions. I then create a specific CloudKitClass for every model in my app, so I'll end up with CloudKitRestaurant and CloudKitTable. All of these will contain a function (void)store:(id)sender. What would be the best way to call the store function of a specific CloudKit class, based on the class that is being stored in Realm?
Ideally, I'd like for RLMRestaurant to automatically use CloudKitRestaurant and not have to use and if else or switch statement.
For further clarity, this is how SyncEngine works.
#interface SyncEngine()
#property (nonatomic, strong) CloudKitManager *cloudKitManager;
#end
#implementation SyncEngine
static SyncEngine *sharedInstance = nil;
+ (SyncEngine *)sharedInstance {
if (sharedInstance == nil) {
sharedInstance = [[self alloc] init];
}
return sharedInstance;
}
- (instancetype)init {
self = [super init];
if (self) {
self.cloudKitManager = [[CloudKitManager alloc] init];
}
return self;
}
#end
In my opinion, you should keep type of CloudKitManager class inside RLMBase object. And when you need to call [[CloudKitManager sharedInstance] store:object], call [[object.cloudKitClass sharedInstance] store:object].
Try my code below.
#interface RLMBase : NSObject
- (Class)cloudKitClass;
#end
#implementation RLMBase
- (Class)cloudKitClass {
// Must be overridden in subclass.
return CloudKitManager.class;
}
#end
#interface RLMRestaurant : RLMBase
#end
#implementation RLMRestaurant
- (Class)cloudKitClass {
return CloudKitRestaurant.class;
}
#end
- (void)store:(RLMBase *)object {
if ([Preferences CloudKitEnabled]) {
[[object.cloudKitClass sharedInstance] store:object];
}
}
ANOTHER WAY
Put store: method from SyncEngine to RLMBase object.
#interface RLMBase : NSObject
- (Class)cloudKitClass;
- (void)store;
#end
#implementation RLMBase
- (Class)cloudKitClass {
// Must be overridden in subclass.
return CloudKitManager.class;
}
- (void)store {
if ([Preferences CloudKitEnabled]) {
[[self.cloudKitClass sharedInstance] store:self];
}
}
#end
And save method will become
- (void)save {
self.updatedAt = [NSDate date];
[self.realm beginWriteTransaction];
[self.realm addOrUpdateObject:self];
[self.realm commitWriteTransaction];
[self store];
}
I create an iPhone application (with a view) / Watch (with interface) that displays speed, distance and a timer with a Play / Pause, Stop and Clear.
I used to share WatchConnectivity Application Context data between (Send and receipt of Context). Everything works so far, but I would add another page / interfaces on which exchanges Watch the Context with the iPhone and there I do not know at all how.
My Interface Storyboard
If I turn on the Session 2 interfaces, information is lost
Here is my current code, I want to toggle the display of labels TimeLabel, distanceLabel Label and speed on the second interface
//
// InterfaceController.m
// Watch Extension
//
// Created by Arnaud Roy on 25/10/2015.
// Copyright © 2015 Burotica. All rights reserved.
//
#import "InterfaceController.h"
#import <WatchConnectivity/WatchConnectivity.h>
#interface InterfaceController() <WCSessionDelegate>
#property (strong, nonatomic) WCSession *session;
#property (unsafe_unretained, nonatomic) IBOutlet WKInterfaceButton *startLabel;
#property (unsafe_unretained, nonatomic) IBOutlet WKInterfaceLabel *timeLabel;
#property (unsafe_unretained, nonatomic) IBOutlet WKInterfaceLabel *distanceLabel;
#property (unsafe_unretained, nonatomic) IBOutlet WKInterfaceLabel *vitesseLabel;
#property (nonatomic,assign) BOOL running;
#end
#implementation InterfaceController
- (void)awakeWithContext:(id)context {
[super awakeWithContext:context];
// Configure interface objects here.
}
- (void)willActivate {
// This method is called when watch view controller is about to be visible to user
[super willActivate];
if ([WCSession isSupported]) {
WCSession *session = [WCSession defaultSession];
session.delegate = self;
[session activateSession];
//NSLog(#"SESSION AVAIBLE");
}
//Objective-C
if ([[WCSession defaultSession] isReachable]) {
//NSLog(#"SESSION REACHABLE");
}
self.running = false;
}
- (void)didDeactivate {
// This method is called when watch view controller is no longer visible
[super didDeactivate];
}
- (IBAction)startButton {
if(self.running == false) {
[self sendCmd:#"start"];
[self.startLabel setTitle:[NSString stringWithFormat:#"PAUSE"]];
self.running = true;
}
else
{
[self sendCmd:#"pause"];
[self.startLabel setTitle:[NSString stringWithFormat:#"PLAY"]];
self.running = false;
}
}
- (IBAction)clearButton {
[self sendCmd:#"clear"];
}
- (IBAction)plusmoinsButton {
[self sendCmd:#"plusmoins"];
}
- (IBAction)stopButton {
[self sendCmd:#"stop"];
}
-(void)sendCmd:(NSString*) cmdSendW{
WCSession *session = [WCSession defaultSession];
NSError *error;
[session updateApplicationContext:#{#"cmdSendW":cmdSendW} error:&error];
}
- (void)session:(nonnull WCSession *)session didReceiveApplicationContext:(nonnull NSDictionary<NSString *,id> *)applicationContext {
NSString *cmdSend = [applicationContext objectForKey:#"cmdSend"];
NSString *timeSend = [applicationContext objectForKey:#"timeSend"];
NSString *distanceSend = [applicationContext objectForKey:#"distanceSend"];
NSString *partielSend = [applicationContext objectForKey:#"partielSend"];
NSString *vitesseSend = [applicationContext objectForKey:#"vitesseSend"];
dispatch_async(dispatch_get_main_queue(), ^{
if([cmdSend isEqual: #"start"])
{
[self.startLabel setTitle:[NSString stringWithFormat:#"PAUSE"]];
[self.timeLabel setText:[NSString stringWithFormat:#"Time : %#", timeSend]];
[self.distanceLabel setText:[NSString stringWithFormat:#"Distance : %#", distanceSend]];
[self.vitesseLabel setText:[NSString stringWithFormat:#"Vitesse : %#", vitesseSend]];
self.running = true;
}
else if([cmdSend isEqual: #"pause"])
{
[self.startLabel setTitle:[NSString stringWithFormat:#"PLAY"]];
[self.timeLabel setText:[NSString stringWithFormat:#"Time : %#", timeSend]];
[self.distanceLabel setText:[NSString stringWithFormat:#"Distance : %#", distanceSend]];
[self.vitesseLabel setText:[NSString stringWithFormat:#"Vitesse : %#", vitesseSend]];
self.running = false;
}
else if([cmdSend isEqual: #"stop"])
{
[self.startLabel setTitle:[NSString stringWithFormat:#"PLAY"]];
[self.timeLabel setText:[NSString stringWithFormat:#"Time : %#", timeSend]];
[self.distanceLabel setText:[NSString stringWithFormat:#"Distance : %#", distanceSend]];
[self.vitesseLabel setText:[NSString stringWithFormat:#"Vitesse : %#", vitesseSend]];
self.running = false;
}
});
}
#end
Thanks for your help
I'd suggest having your UIApplicationDelegate and ExtensionDelegate create an instance of a new object you make that is the WCSessionDelegate. This object will persist incoming data to disk, and post notifications that the backing data store has been updated. Then each of the UI components can monitor for these notifications and update their UIs as appropriate.
My goal is to achieve synchronized communication to custom Device i.e. next command can be send only when reply is received. Now I'm doing it in this way
Device class implements DeviceDelegate protocol
//Device.h
#class Device;
#protocol DeviceDelegate <NSObject>
- (void)didReciveReplyWithData:(NSData *)data;
#end
#interface Device : NSObject {}
In DeviceViewController implementation:
#interface DeviceViewController()
{
BOOL waitingForReply = false;
}
#end
#implementation DeviceViewController
- (void)sendCommandWithData:(NSData *)data
{
if ( waitingForReply == false)
{
//send command code
waitingForReply = true;
}
}
- (void)didReciveReplyWithData:(NSData *)data
{
//code
waitingForReply = false;
}
#end
but I wish to do it in more elegant way i.e. by using GCD (semaphores?) with blocks (completionHandler?). Any ideas?
PS. Sorry, but I forgot to mention: all commands sended to device while
waitingForReply = true
should be ignored!!!.
Possibly the best approach here would be to create a queue of commands with NSOperationQueue.
Since, presumably, the communication with the device is asynchronous you will have to subclass NSOperation to encapsulate the communication.
#interface DeviceCommandOperation : NSOperation <DeviceDelegate>
#property (nonatomic, assign) BOOL waitingForReply;
#property (nonatomic, copy) NSData *dataToSend;
#property (nonatomic, copy) NSData *dataReceived;
#end
#implementation DeviceCommandOperation
- (instancetype)initWithData:(NSData *)dataToSend
{
self = [super init];
if (self)
{
_dataToSend = [dataToSend copy];
}
return self;
}
- (void)setWaitingForReply:(BOOL)waitingForReply
{
if (_waitingForReply != waitingForReply)
{
[self willChangeValueForKey:#"isExecuting"];
[self willChangeValueForKey:#"isFinished"];
_waitingForReply = waitingForReply;
[self didChangeValueForKey:#"isExecuting"];
[self didChangeValueForKey:#"isFinished"];
}
}
- (void)start
{
self.waitingForReply = YES;
// Simulate sending a command and waiting for response.
// You will need to replace this with your actual communication mechanism.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// In reality this call would presumably come from the Device
[self didReceiveReplyWithData:someData];
});
}
- (void)didReceiveReplyWithData:(NSData *)data
{
self.dataReceived = data;
self.waitingForReply = NO;
}
#pragma mark - NSOperation
- (BOOL)isAsynchronous
{
return YES;
}
- (BOOL)isExecuting
{
return _waitingForReply;
}
- (BOOL)isFinished
{
return !_waitingForReply;
}
#end
This operation could then be used from your DeviceViewController (it would probably be better architecturally to have this responsibility elsewhere but that's not the topic of this question).
#interface DeviceViewController ()
#property (nonatomic, strong) NSOperationQueue *operationQueue;
#end
#implementation DeviceViewController
- (NSOperationQueue *)operationQueue
{
if (_operationQueue == nil)
{
_operationQueue = [[NSOperationQueue alloc] init];
}
return _operationQueue;
}
- (void)sendNextCommand
{
NSData *data = // Get data for the next command
[self sendCommandWithData:data];
}
- (void)sendCommandWithData:(NSData *)data
{
NSLog(#"Queueing operation");
DeviceCommandOperation *operation = [[DeviceCommandOperation alloc] initWithData:data];
// The operation's completionBlock gets called on a background queue
[operation setCompletionBlock:^{
NSLog(#"DeviceCommandOperation completed");
// Process operation.dataReceived
[self sendNextCommand];
}];
[self.operationQueue addOperation:operation];
}
#end
This approach will allow you to determine what (if any) command to send next, based on the reply to the previous command.
If you know all of the "commands" you will want to send initially and don't need finer grained control you could create instances of DeviceCommandOperation for each command, set the queue's maxConcurrentOperationCount to 1, and add each DeviceCommandOperation to the queue (in the order you want them to be processed).
I am writing an iOS app where it counts the steps taken by the user when activated using a button. I am able to count the steps now, but I would like to be able to pause and reset the steps counter by user request. I am not that experienced with XCode, so there might be an easy way to do it. I used a code similar to one available on Stackoverflow:
#import "ViewController.h"
#import "DTStepModelController.h"
#import <CoreMotion/CoreMotion.h>
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UILabel *stepsCountingLabel; #property (nonatomic, strong) CMStepCounter *cmStepCounter;
#property (nonatomic, strong) NSOperationQueue *operationQueue;
#end
#implementation ViewController
{
DTStepModelController *_stepModel;
}
- (NSOperationQueue *)operationQueue
{
if (_operationQueue == nil)
{
_operationQueue = [NSOperationQueue new];
}
return _operationQueue;
}
- (void)updateStepCounterLabelWithStepCounter:(NSInteger)countedSteps
{
self.stepsCountingLabel.text = [NSString stringWithFormat:#"%ld", (long)countedSteps];
}
- (IBAction)StartCountingSteps:(id)sender {
if ([CMStepCounter isStepCountingAvailable])
{
self.cmStepCounter = [[CMStepCounter alloc] init];
[self.cmStepCounter startStepCountingUpdatesToQueue:self.operationQueue updateOn:1 withHandler:^(NSInteger numberOfSteps, NSDate *timestamp, NSError *error)
{
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self updateStepCounterLabelWithStepCounter:numberOfSteps];
}];
}];
}
}
Any insight, or suggessions?
I was able to find an answer for my question. Please note that my answer and the previous code in the question will not work if you're using iOS 8.2 or above as Apple discontinue supporting steps counting. In the new iOS version, you can query the M7 counter and save the value, store it, then subtract the new value from the old one.
Anyway, for the above code, you can stop the counter (PauseCounter method) but it will reset the counter to zero.
-(IBAction) PauseCounting: (id) sender {
[self.cmStepCounter stopStepCountingUpdates];
} - (IBAction) ResumeCounting: (id) sender {
[self.cmStepCounter startStepCountingUpdatesToQueue: self.operationQueue updateOn: 1 withHandler: ^ (NSInteger numberOfSteps, NSDate * timestamp, NSError * error) {
[
[NSOperationQueue mainQueue] addOperationWithBlock: ^ {
[self updateStepCounterLabelWithStepCounter: numberOfSteps];
}
];
}];
}
I finally got my leaderboard to show up. Now I just need to implement that my score will pop up.
My score is saved as an NSString in NSUserDefaults under the name score.
Here is some code:
Game_CenterViewController.h
#import <UIKit/UIKit.h>
#import <GameKit/GameKit.h>
#import "GameCenterManager.h"
#class GameCenterManager;
#interface Game_CenterViewController : UIViewController <UIActionSheetDelegate, GKLeaderboardViewControllerDelegate, GKAchievementViewControllerDelegate, GameCenterManagerDelegate> {
GameCenterManager *gameCenterManager;
int64_t currentScore;
NSString *currentLeaderBoard;
}
#property (nonatomic, retain) GameCenterManager *gameCenterManager;
#property (nonatomic, assign) int64_t currentScore;
#property (nonatomic, retain) NSString* currentLeaderBoard;
#end
Game_CenterViewController.m
#import "Game_CenterViewController.h"
#import "AppSpecificValues.h"
#import "GameCenterManager.h"
#implementation Game_CenterViewController
#synthesize gameCenterManager;
#synthesize currentScore;
#synthesize currentLeaderBoard;
- (void)dealloc {
[gameCenterManager release];
[currentLeaderBoard release];
[currentScoreLabel release];
[super dealloc];
}
#pragma mark - View lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
self.currentLeaderBoard = thescore101;
self.currentScore = score
if ([GameCenterManager isGameCenterAvailable]) {
self.gameCenterManager = [[[GameCenterManager alloc] init] autorelease];
[self.gameCenterManager setDelegate:self];
[self.gameCenterManager authenticateLocalUser];
} else {
// The current device does not support Game Center.
}
}
- (void)leaderboardViewControllerDidFinish:(GKLeaderboardViewController *)viewController {
[self dismissModalViewControllerAnimated: YES];
[viewController release];
}
- (void)viewDidUnload {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
self.gameCenterManager = nil;
self.currentLeaderBoard = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#end
The current score is where I'm trying to put the NSString in.
EDITED: I think it would be better to use int for the currentScore.
Is this what you are looking for?
- (void)submitMyScore:(int64_t)score
{
GKScore *myScoreValue = [[[GKScore alloc] initWithCategory:#"yourCat"] autorelease];
myScoreValue.value = (int)score;
[myScoreValue reportScoreWithCompletionHandler:^(NSError *error){
if(error != nil){
NSLog(#"Score Submission Failed");
} else {
NSLog(#"Score Submitted: %d",(int)score);
}
}];
}
So you should add a IBAction
- (IBAction)buttonPressed
{
[self submitMyScore:currentScore];
}
With this and connecting the SEND MY SCORE button to this IBAction, you will have your score submitted.
I hope this to be useful for you.