Comments (44)
regarding sponsoring, I will ask for it!
from injectioniii.
this seems to work on the private iOS17 USB based network 🎉
from injectioniii.
Hi, thanks very much for spending the time on this. There is some very similar code at the end of SimpleSocket.mm though it no longer seems to work. My first impression is that I'd be reluctant to build something iOS 17 specific into the app for now but if you come up with a PR that is backward compatible I'll take a look after testing it out. As it stands I don't think the environment variable override variable is an entirely bad solution for the minority of people who are using device injection (which didn't even work 6 months ago though the number using it does seem to be steadily increasing.) Apologies in advance for the time you spend refactoring ClientBoot.mm. It's overdue for a rewrite.
from injectioniii.
no worries, seems like the current solution with multicasts no longer works, but this solution with broadcasts does work ... not sure why, I can try to dig into the current code and see if I can fix it.
from injectioniii.
I look forward to the reviewing it; You write quality C. I may have a try myself now you've suggested using broadcast instead of multicast addresses.
from injectioniii.
hi @johnno1962 seems like this idea have some issues:
- It seems like the iOS17 network ip address is only set up after around 30-60 seconds of connection over USB.
- no support for older devices.
- the device fails to find the host if it connected to wifi.
A colleague of mine suggested looking at the Peertalk library and try using it instead, looking at the library and the examples.
Am I right to assume that all of the communication in Injection/Hotreloading is done by the SimpleSocket
file is that right?
seems like behind the scenes, all of the network code there boils down to this method:
- (BOOL)perform:(io_func)io ofBytes:(const void *)buffer
length:(size_t)length cmd:(SEL)cmd {
size_t rd, ptr = 0;
SLog(@"#%d %s %lu [%p] %s", clientSocket, io == read ?
"<-" : "->", length, buffer, sel_getName(cmd));
while (ptr < length &&
(rd = io(clientSocket, (char *)buffer+ptr, length-ptr)) > 0)
ptr += rd;
if (ptr < length) {
NSLog(@"[%@ %s:%p length:%lu] error: %lu %s",
self, sel_getName(cmd), buffer, length, ptr, strerror(errno));
return FALSE;
}
return TRUE;
}
so maybe we can re-implement it over their network stack?
from injectioniii.
Hi again, my first impression is I'd be reluctant to change the network "stack" at this stage so I'd be wanting to try other alternatives first. I also note that project hasn't been updated for three years which is either very good or very not good when one is thinking of adopting it. I've a tried few simple broadcast things this morning but the problem is getting the local address(es) of the device to construct the broadcast addresses to try. I'll try a bit harder later on unless you can suggest other alternatives. Thanks for continuing to look into this. It would be nice to find a better solution for devices.
from injectioniii.
I haven't found any easy solutions to device discovery. I may try dusting off some old "Bonjour" code tomorrow but unless that suddenly starts working (I think I've already tried it) I'll probably be leaving things as they are unless you present something both functional and compatible. This is a niche requirement for which there are already workarounds.
from injectioniii.
@johnno1962 have you looked into Multipeer Connectivity
?
https://developer.apple.com/documentation/multipeerconnectivity
Or the Network.framework - https://developer.apple.com/documentation/network
from injectioniii.
Thanks @zenangst, I tried Bonjour but couldn't get some very old code to work from a phone though it's probably the "right" way to do this. I gave up too soon though, in end I went back and got the broadcasting code above to work instead of trying to use multicast groups. @oryonatan, can you update to the latest release candidate and HotReloading, "filelist" branch and give it a try for me please? johnno1962/HotReloading@c3ec5e5
from injectioniii.
Erk, I just saw the following which is probably why Bonjour didn't work:
Important
Apps that use the local network must provide a usage string in their Info.plist with the key NSLocalNetworkUsageDescription. Apps that use Bonjour must also declare the services they browse, using the NSBonjourServices key.
Think we probably want to avoid having to ask users to do that though.
from injectioniii.
I've updated the filelist
branch to be a little more flexible in how it tries to connect. I have a slightly unusual home network where my dev machine is not directly on our Wi-Fi network which works better using the hostname from Package.swift. For most people though the broadcast option will be more reliable so it now tries both.
from injectioniii.
Hi, just checked out the code at filelist
at home, connected the iPhone and the development device into the different networks and it seems like it works perfectly 🎉
I will check back tomorrow at office, and see if this still works with under our office IT constraints.
from injectioniii.
Just wanted to update that I checked and seems to also work with one small caveat - if the device is connected to wifi the initial broadcast will fail to find the host
also - you need to set up the INJECTION_DAEMON
flag for this to work.
from injectioniii.
I don't know about the INJECTION_DAEMON thing but are you sure the InjectionIII app you're running is the latest release candidate (build 7889 which you can see by hovering over the "Quit InjectionIII" menu item")? Otherwise it will still be waiting for a multicast.
I've made some progress this end understanding what's going on with being/not being able to connect. In Xcode 14.3 I need to enable Wi-Fi on my dev machine and have broadcasts find a way to connect. If I use Xcode 15 and unplug and reconnect my phone the 169.254.255.255 network becomes active over USB and it can find a route to my host by resolving its hostname provided by HotReloading's Package.swift. This resolves what was a mystery for me how on earth the phone was able to connect to my dev machine which effectively has its own network; It is using the 169.254 network (though broadcasts on this network don't seem to work by themselves). It tries all three possibilities now in parallel and the first that resolves becomes the injection client.
I've just pushed a couple of aesthetic tweaks to the filelist
branch if you want to update and make sure you're running the most recent release candidate of the InjectionIII.app from yesterday. Sorry I didn't explicitly mention that!
from injectioniii.
Hi - regarding injectionIII.app version - I've compiled it myself from commit 368ca02 - Multicast becomes Broadcast.
from injectioniii.
So what is the current situation? It works but only if you define an environment variable INJECTION_DAEMON
?
from injectioniii.
I've pushed a commit to the InjectionIII repo picking up the most recent HotReloading changes.
from injectioniii.
broadcast seems to work over USB in XCode 15 and iOS17 when
- The dev machine is connected to the internet.
- The iPhone is not connected to wifi
Connecting the iPhone to another wifi (different than the dev machine) causes this to stop working (although I do gent the printed message of Broadcasting 169.254.255.255
)
from injectioniii.
(I suppose connecting them to the same network will also work, but didn't check that)
(also I think that the INJECTION_DAEMON
flag is redundant, this seems to work anyway)
from injectioniii.
Regarding what is going on - I tried running wireshark on the dev machine and saw this:
- When the iPhone is not connected to wifi - after
if (sendto(multicastSocket, &msgbuf, sizeof msgbuf, 0,
(struct sockaddr *)&addr, sizeof(addr)) < 0) {
is called I see a UDP packet originating from the iPhone on the host machine
2. When the iPhone is connected to wifi - I don't see this packet
from injectioniii.
Ah, the irony of a configuration that is only able to connect when Wi-Fi is turned off on the device! Probably to do with "service order" where it chooses which interface to route the request through, though it should be sending out the request on all interfaces. With Wi-Fi switched off is your app able to use the internet of your dev machine via NAT or the cellular connection, i.e. do vanilla network requests inside the app work? Is there a reason why you can't connect your dev machine and phone to the same network? Otherwise, broadcasts don't help us.
from injectioniii.
- when I disconnect the wifi from the iPhone - it loses connection to the internet, there is no NAT here.
- I don't have cellular connection on the device (it is a company device) so I am not sure.
- for "security" reasons our IT systems have the iPhone and the Dev devices on different networks, I guess this might sound a bit niche or ridiculous, but changing the company IT infra is quite challenging.
- I wonder if we can change the aforementioned "service order" in order to force
multicastSocket
to use a specific interface ... it seems like in Linux there is anSO_BINDTODEVICE
option for a socket, and FreeBSD should have anIP_SENDIF
option, but I think it is not available in Darwin, so maybe something else will work.
from injectioniii.
maybe this will work:
https://lists.apple.com/archives/darwin-dev/2014/Jan/msg00004.html
from injectioniii.
ok this code snippet seems to work, just need to find out which interface to use
[self forEachInterface:^(in_addr_t laddr, in_addr_t nmask) {
//
// uint32_t Anet = ntohl(laddr) >> 24;
// if (Anet == 127 || Anet == 10) return;
struct ifaddrs *ifaddr, *ifa;
int family, s;
if (getifaddrs(&ifaddr) == -1) {
perror("getifaddrs");
exit(EXIT_FAILURE);
}
/* Walk through linked list, maintaining head pointer so we can free list later */
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
if (ifa->ifa_addr == NULL)
continue;
family = ifa->ifa_addr->sa_family;
/* Display interface name and family (including IPv4 or IPv6) */
printf("%s\tAF_%s\n", ifa->ifa_name,
(family == AF_INET) ? "INET" :
(family == AF_INET6) ? "INET6" : "UNKNOWN");
int idx = if_nametoindex(ifa->ifa_name);
setsockopt(multicastSocket, IPPROTO_IP, IP_BOUND_IF, &idx, sizeof(idx) );
addr.sin_addr.s_addr = laddr | ~nmask;
printf("Broadcasting %s\n", inet_ntoa(addr.sin_addr));
if (sendto(multicastSocket, &msgbuf, sizeof msgbuf, 0,
(struct sockaddr *)&addr, sizeof(addr)) < 0) {
[self error:@"Could not send multicast ping: %s"];
}
}
}];
from injectioniii.
Interesting, Thanks for that! I've pushed a commit to filelist branch to use if_nametoindex().
from injectioniii.
seems to work for me now on commit * c9674e5 -Use if_nametoindex - John Holdsworth (HEAD, origin/filelist)
from injectioniii.
I just wanted to add that I really appreciate what you are doing here, thanks!
from injectioniii.
Thanks for your help and raising this issue in the first place! Don't be afraid to mention to your boss he can sponsor me on github if this all pans out.. I could see the broadcast to 169.254 getting through now with your change. Are we good now then? I'll take the opportunity to make the multicastHash which distinguishes between users a little more unique since multicast/broadcast are incomparable anyway and roll a new release candidate for you later.
from injectioniii.
Thanks for your help and raising this issue in the first place! Don't be afraid to mention to your boss he can sponsor me on github if this all pans out.. I could see the broadcast to 169.254 getting through now with your change. Are we good now then? I'll take the opportunity to make the multicastHash which distinguishes between users a little more unique since multicast/broadcast are incomparable anyway and roll a new release candidate for you later.
We are good!
from injectioniii.
I've merged the filelist branch onto HotReloading main, you'll need to download the latest release candidate to work with it.
from injectioniii.
Hi @johnno1962
First thing - regarding sponsorship, we had a bit of a technical issues with Sponsoring using the company Gihub enterprise account - but I think they managed to solve it - you should see something from LightricksSponsors I think :).
Another thing - we previously used to work with Injection by loading the injection binary bundle, this is easier for us then using SPM + Hotreloading, as this means that 1. we can internally deploy our binary injection bundle and control when it is loaded 2. We don't have to use SPM - and it seems like in SPM it is complicated to include Hotreloading just in Debug builds.
It seems like when using injection this way - by loading a bundle, the fixes you added here don't really work on device, I am getting a Injection bundle loaded but could not connect. Is InjectionIII.app running?
message.
So is there a way to make the broadcast trick work also when loading a bundle? or alternatively is there an easy way to make Hotreloading be added to the app only on debug builds (seems like SPM does not support debug only packages)
from injectioniii.
Hi, thanks for the sponsorship (twice)! It's appreciated and helps keep motivation up. Unfortunately there is no way to include HotReloading only on Debug builds though I have raised various radars about this. For me the easiest way to do this would be to be able to use #if DEBUG in the Package.swift but you can't alas.
You can't load the bundle on a device as it isn't on the file system of your phone so for device injection you have to use the HotReloading project. This also allows the Package.swift to build the hostname of your development Mac into the executable which is one way other than broadcasts to facilitate device discovery.
from injectioniii.
oh, but we can load the bundle on a device, simply by adding a build script that copies the bundle, we have something like this:
rsync -a "$SRCROOT/iOSInjection.bundle" "$BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME/"
codesign -f --sign "$EXPANDED_CODE_SIGN_IDENTITY" --timestamp\=none --preserve-metadata\=identifier,entitlements,flags --generate-entitlement-der "$BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME/iOSInjection.bundle/Frameworks/SwiftTrace.framework/SwiftTrace"
codesign -f --sign "$EXPANDED_CODE_SIGN_IDENTITY" --timestamp\=none --preserve-metadata\=identifier,entitlements,flags --generate-entitlement-der "$BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME/iOSInjection.bundle"
defaults write com.johnholdsworth.InjectionIII "$PROJECT_FILE_PATH" $EXPANDED_CODE_SIGN_IDENTITY
from injectioniii.
Very interesting! And it loads despite being built for the simulator? I guess it wouldn't take much to change ClientBoot.mm to attempt the device connecting even in the bundle. I'll take a quick look. Thanks for all these ideas. Keep 'em coming...
from injectioniii.
well, it works but I am using few hacks that @byohay made to make it work on device, I'll check if I can send you his changes
from injectioniii.
this is his commit that made the bundle build for iPhone 2090207
from injectioniii.
You're ahead of me here. I've been using the maciOSInjection.bundle which almost works.
The changes you need to make to try connecting from the bundle are:
ClientBoot.mm, line 61: #if !defined(INJECTION_III_APP) || 1 <<- enable the code.
SimpleSocket.mm, line 359: if (//[self multicastHash] == msgbuf.hash && <<- comment out and rebuild the >app<
With these changes it might work but I've only been able to get a program with the maciOS bundle to run once and now I'm getting weird errors from the device. This leaves the problem of how to distinguish between more than one developer on a network sending broadcasts which is what the "multicastHash" is all about.
from injectioniii.
OK, I've been able to see this working by making the following changes and using the "maciOSInjection.bundle":
diff --git a/Sources/HotReloadingGuts/ClientBoot.mm b/Sources/HotReloadingGuts/ClientBoot.mm
index 44a6475..48a2a30 100644
--- a/Sources/HotReloadingGuts/ClientBoot.mm
+++ b/Sources/HotReloadingGuts/ClientBoot.mm
@@ -58,7 +58,7 @@ + (void)tryConnect:(Class)clientClass {
"https://github.com/johnno1962/InjectionIII/releases\n"
APP_PREFIX"And have typed: defaults write com.johnholdsworth.InjectionIII deviceUnlock any\n";
BOOL isVapor = dlsym(RTLD_DEFAULT, VAPOR_SYMBOL) != nullptr;
-#if !defined(INJECTION_III_APP)
+#if !defined(INJECTION_III_APP) || 1
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_OSX
BOOL isiOSAppOnMac = false;
if (@available(iOS 14.0, *)) {
@@ -83,7 +83,7 @@ + (void)tryConnect:(Class)clientClass {
injectionHost = [clientClass
getMulticastService:HOTRELOADING_MULTICAST port:HOTRELOADING_PORT
message:APP_PREFIX"Connecting to %s (%s)...\n"];
- socketAddr = [injectionHost stringByAppendingString:socketAddr];
+ socketAddr = [injectionHost stringByAppendingString:@HOTRELOADING_PORT];
if (injectionClient)
return;
#endif
diff --git a/Sources/HotReloadingGuts/SimpleSocket.mm b/Sources/HotReloadingGuts/SimpleSocket.mm
index 001bad0..04bd902 100644
--- a/Sources/HotReloadingGuts/SimpleSocket.mm
+++ b/Sources/HotReloadingGuts/SimpleSocket.mm
@@ -42,6 +42,8 @@
@implementation SimpleSocket
+static BOOL isConnected;
+
+ (int)error:(NSString *)message {
NSLog([@"%@/" stringByAppendingString:message],
self, strerror(errno));
@@ -98,6 +100,7 @@ + (void)runServer:(NSString *)address {
if (v4Addr->sin_addr.s_addr == addr)
client.isLocalClient = TRUE;
}];
+ isConnected = TRUE;
[client run];
}
}
@@ -274,6 +277,7 @@ - (BOOL)writeCommand:(int)command withString:(NSString *)string {
}
- (void)dealloc {
+ isConnected = FALSE;
close(clientSocket);
}
@@ -356,7 +360,7 @@ + (void)multicastListen:(NSNumber *)socket {
[self multicastHash], msgbuf.hash);
gethostname(msgbuf.host, sizeof msgbuf.host);
- if ([self multicastHash] == msgbuf.hash &&
+ if (!isConnected &&//[self multicastHash] == msgbuf.hash &&
sendto(multicastSocket, &msgbuf, sizeof msgbuf, 0,
(struct sockaddr *)&addr, addrlen) < sizeof msgbuf) {
[self error:@"Could not send to multicast: %s"];
diff --git a/Sources/injectiond/DeviceServer.swift b/Sources/injectiond/DeviceServer.swift
index 9076f2a..67b335b 100644
--- a/Sources/injectiond/DeviceServer.swift
+++ b/Sources/injectiond/DeviceServer.swift
@@ -21,8 +21,14 @@ class DeviceServer: InjectionServer {
#if !SWIFT_PACKAGE
override func validateConnection() -> Bool {
- return readInt() == HOTRELOADING_SALT &&
- readString()?.hasPrefix(NSHomeDirectory()) == true
+ switch readInt() {
+ case INJECTION_SALT:
+ return readString() == INJECTION_KEY
+ case HOTRELOADING_SALT:
+ return readString()?.hasPrefix(NSHomeDirectory()) == true
+ default:
+ return false
+ }
}
#endif
Need to think a little more about how to distinguish clients and the other changes I had to make but this looks promising. The weird device problems I could only resolve by performing a full Archive/Notarise build cycle.
from injectioniii.
GM @oryonatan, I've pushed this idea through to its conclusion and released a new release candidate from branch dev-bundle
. The InjectionIII.app contains a new script copy_bundle.sh
which you use instead of the build phase you mentioned to copy the bundle into the app package and codesign it. I've updated the README on that branch but it's still a bit ragged. Would you like to give the new release candidate a try and let me know how you get on? I was able to differentiate users by writing the user's home directory into the Info.plist of the copied bundle in the finish.
from injectioniii.
thanks @johnno1962, I'll check this version out
from injectioniii.
Hi! it works!
I didn't check the copy_bundle.sh
script though
from injectioniii.
Happy to hear it though I don't quite know how it can be working using broadcasts if you're not using the script. It must have found another way!
from injectioniii.
Just a totally random thought and maybe doesn't even apply, but couldn't help to notice the correlation. Lots of people have started getting problems with REALLY slow debuggers after iOS 17 just because they enforce the use of this private network between the phone and the mac. And the only way to speed it up is to disable the wifi when debugging.
And then you notice this behaviour where there's no IP assigned when you enable the wifi. Coincidence? Here a thread of it being discussed: https://developer.apple.com/forums/thread/737875
from injectioniii.
Related Issues (20)
- error: no such module 'FirebaseFirestoreInternalWrapper' HOT 4
- Error loading, when using mac target HOT 3
- Crash on reload every time HOT 6
- Loading .dylib has failed HOT 15
- EOF received from server.. HOT 2
- Setting up for macOS app dev HOT 21
- Is there a way to make injected macOS app not become active when it's reloaded? HOT 2
- Re-compilation failed HOT 5
- InjectionClient/Could not connect: Connection refused 🔥 HOT 18
- Hello, I reported the error when I used the real machine to debug, looking forward to your answer, thank you HOT 6
- Meaning of colors HOT 2
- It doesn't work on Xcode 15.3 iOS 17.4 HOT 14
- It doesn't work on xcode 15.3 (15E204a) HOT 7
- Loading .dylib has failed HOT 1
- TCA ObservableState crash HOT 3
- Unknown argument: -interposable HOT 2
- Could not bind service socket: Address already in use HOT 5
- Is support arm mac??? HOT 19
- ld: building for 'iOS', but linking in object file built for 'visionOS-simulator' HOT 5
- dlopen() error: Library not loaded: @rpath/libswift_Concurrency.dylib HOT 15
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from injectioniii.